blob: 76b57d86aa2cf3b57b9665c82f555ea28e315ebd [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()) {
rossberg@chromium.org28a37082011-08-22 11:03:23 +0000659 MaybeObject* result = js_object->GetElementsAccessor()->Get(
660 js_object->elements(),
661 index,
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000662 js_object,
rossberg@chromium.org28a37082011-08-22 11:03:23 +0000663 receiver);
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000664 if (result != heap->the_hole_value()) return result;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000665 }
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000666 }
667
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000668 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000669}
670
671
672Object* Object::GetPrototype() {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000673 if (IsSmi()) {
674 Heap* heap = Isolate::Current()->heap();
675 Context* context = heap->isolate()->context()->global_context();
676 return context->number_function()->instance_prototype();
677 }
678
679 HeapObject* heap_object = HeapObject::cast(this);
680
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000681 // The object is either a number, a string, a boolean,
682 // a real JS object, or a Harmony proxy.
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000683 if (heap_object->IsJSReceiver()) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000684 return heap_object->map()->prototype();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000685 }
686 Heap* heap = heap_object->GetHeap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000687 Context* context = heap->isolate()->context()->global_context();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000688
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000689 if (heap_object->IsHeapNumber()) {
690 return context->number_function()->instance_prototype();
691 }
692 if (heap_object->IsString()) {
693 return context->string_function()->instance_prototype();
694 }
695 if (heap_object->IsBoolean()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000696 return context->boolean_function()->instance_prototype();
697 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000698 return heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000699 }
700}
701
702
whesse@chromium.org023421e2010-12-21 12:19:12 +0000703void Object::ShortPrint(FILE* out) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000704 HeapStringAllocator allocator;
705 StringStream accumulator(&allocator);
706 ShortPrint(&accumulator);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000707 accumulator.OutputToFile(out);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000708}
709
710
711void Object::ShortPrint(StringStream* accumulator) {
712 if (IsSmi()) {
713 Smi::cast(this)->SmiPrint(accumulator);
714 } else if (IsFailure()) {
715 Failure::cast(this)->FailurePrint(accumulator);
716 } else {
717 HeapObject::cast(this)->HeapObjectShortPrint(accumulator);
718 }
719}
720
721
whesse@chromium.org023421e2010-12-21 12:19:12 +0000722void Smi::SmiPrint(FILE* out) {
723 PrintF(out, "%d", value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724}
725
726
727void Smi::SmiPrint(StringStream* accumulator) {
728 accumulator->Add("%d", value());
729}
730
731
732void Failure::FailurePrint(StringStream* accumulator) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000733 accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000734}
735
736
whesse@chromium.org023421e2010-12-21 12:19:12 +0000737void Failure::FailurePrint(FILE* out) {
738 PrintF(out, "Failure(%p)", reinterpret_cast<void*>(value()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000739}
740
741
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000742// Should a word be prefixed by 'a' or 'an' in order to read naturally in
743// English? Returns false for non-ASCII or words that don't start with
744// a capital letter. The a/an rule follows pronunciation in English.
745// We don't use the BBC's overcorrect "an historic occasion" though if
746// you speak a dialect you may well say "an 'istoric occasion".
747static bool AnWord(String* str) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000748 if (str->length() == 0) return false; // A nothing.
749 int c0 = str->Get(0);
750 int c1 = str->length() > 1 ? str->Get(1) : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000751 if (c0 == 'U') {
752 if (c1 > 'Z') {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000753 return true; // An Umpire, but a UTF8String, a U.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000754 }
755 } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000756 return true; // An Ape, an ABCBook.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000757 } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
758 (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
759 c0 == 'S' || c0 == 'X')) {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000760 return true; // An MP3File, an M.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000761 }
762 return false;
763}
764
765
lrn@chromium.org303ada72010-10-27 09:33:13 +0000766MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000767#ifdef DEBUG
768 // Do not attempt to flatten in debug mode when allocation is not
769 // allowed. This is to avoid an assertion failure when allocating.
770 // Flattening strings is the only case where we always allow
771 // allocation because no GC is performed if the allocation fails.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000772 if (!HEAP->IsAllocationAllowed()) return this;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000773#endif
774
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000775 Heap* heap = GetHeap();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000776 switch (StringShape(this).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000777 case kConsStringTag: {
778 ConsString* cs = ConsString::cast(this);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000779 if (cs->second()->length() == 0) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000780 return cs->first();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000781 }
782 // There's little point in putting the flat string in new space if the
783 // cons string is in old space. It can never get GCed until there is
784 // an old space GC.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000785 PretenureFlag tenure = heap->InNewSpace(this) ? pretenure : TENURED;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000786 int len = length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000787 Object* object;
788 String* result;
ager@chromium.org5ec48922009-05-05 07:25:34 +0000789 if (IsAsciiRepresentation()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000790 { MaybeObject* maybe_object = heap->AllocateRawAsciiString(len, tenure);
lrn@chromium.org303ada72010-10-27 09:33:13 +0000791 if (!maybe_object->ToObject(&object)) return maybe_object;
792 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000793 result = String::cast(object);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000794 String* first = cs->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000795 int first_length = first->length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000796 char* dest = SeqAsciiString::cast(result)->GetChars();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000797 WriteToFlat(first, dest, 0, first_length);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000798 String* second = cs->second();
799 WriteToFlat(second,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000800 dest + first_length,
801 0,
802 len - first_length);
803 } else {
lrn@chromium.org303ada72010-10-27 09:33:13 +0000804 { MaybeObject* maybe_object =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000805 heap->AllocateRawTwoByteString(len, tenure);
lrn@chromium.org303ada72010-10-27 09:33:13 +0000806 if (!maybe_object->ToObject(&object)) return maybe_object;
807 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000808 result = String::cast(object);
809 uc16* dest = SeqTwoByteString::cast(result)->GetChars();
ager@chromium.org870a0b62008-11-04 11:43:05 +0000810 String* first = cs->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000811 int first_length = first->length();
812 WriteToFlat(first, dest, 0, first_length);
ager@chromium.org870a0b62008-11-04 11:43:05 +0000813 String* second = cs->second();
814 WriteToFlat(second,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000815 dest + first_length,
816 0,
817 len - first_length);
818 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000819 cs->set_first(result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000820 cs->set_second(heap->empty_string());
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000821 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000822 }
823 default:
824 return this;
825 }
826}
827
828
ager@chromium.org6f10e412009-02-13 10:11:16 +0000829bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
lrn@chromium.org32d961d2010-06-30 09:09:34 +0000830 // Externalizing twice leaks the external resource, so it's
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000831 // prohibited by the API.
832 ASSERT(!this->IsExternalString());
ager@chromium.org6f10e412009-02-13 10:11:16 +0000833#ifdef DEBUG
ager@chromium.org3811b432009-10-28 14:53:37 +0000834 if (FLAG_enable_slow_asserts) {
ager@chromium.org6f10e412009-02-13 10:11:16 +0000835 // Assert that the resource and the string are equivalent.
836 ASSERT(static_cast<size_t>(this->length()) == resource->length());
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000837 ScopedVector<uc16> smart_chars(this->length());
838 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
839 ASSERT(memcmp(smart_chars.start(),
ager@chromium.org6f10e412009-02-13 10:11:16 +0000840 resource->data(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000841 resource->length() * sizeof(smart_chars[0])) == 0);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000842 }
843#endif // DEBUG
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000844 Heap* heap = GetHeap();
ager@chromium.org6f10e412009-02-13 10:11:16 +0000845 int size = this->Size(); // Byte size of the original string.
846 if (size < ExternalString::kSize) {
847 // The string is too small to fit an external String in its place. This can
848 // only happen for zero length strings.
849 return false;
850 }
851 ASSERT(size >= ExternalString::kSize);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000852 bool is_ascii = this->IsAsciiRepresentation();
ager@chromium.org6f10e412009-02-13 10:11:16 +0000853 bool is_symbol = this->IsSymbol();
854 int length = this->length();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000855 int hash_field = this->hash_field();
ager@chromium.org6f10e412009-02-13 10:11:16 +0000856
857 // Morph the object to an external string by adjusting the map and
858 // reinitializing the fields.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000859 this->set_map(is_ascii ?
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000860 heap->external_string_with_ascii_data_map() :
861 heap->external_string_map());
ager@chromium.org6f10e412009-02-13 10:11:16 +0000862 ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
863 self->set_length(length);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000864 self->set_hash_field(hash_field);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000865 self->set_resource(resource);
866 // Additionally make the object into an external symbol if the original string
867 // was a symbol to start with.
868 if (is_symbol) {
869 self->Hash(); // Force regeneration of the hash value.
870 // Now morph this external string into a external symbol.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000871 this->set_map(is_ascii ?
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000872 heap->external_symbol_with_ascii_data_map() :
873 heap->external_symbol_map());
ager@chromium.org6f10e412009-02-13 10:11:16 +0000874 }
875
876 // Fill the remainder of the string with dead wood.
877 int new_size = this->Size(); // Byte size of the external String object.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000878 heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000879 return true;
880}
881
882
883bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
884#ifdef DEBUG
ager@chromium.org3811b432009-10-28 14:53:37 +0000885 if (FLAG_enable_slow_asserts) {
ager@chromium.org6f10e412009-02-13 10:11:16 +0000886 // Assert that the resource and the string are equivalent.
887 ASSERT(static_cast<size_t>(this->length()) == resource->length());
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000888 ScopedVector<char> smart_chars(this->length());
889 String::WriteToFlat(this, smart_chars.start(), 0, this->length());
890 ASSERT(memcmp(smart_chars.start(),
ager@chromium.org6f10e412009-02-13 10:11:16 +0000891 resource->data(),
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000892 resource->length() * sizeof(smart_chars[0])) == 0);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000893 }
894#endif // DEBUG
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000895 Heap* heap = GetHeap();
ager@chromium.org6f10e412009-02-13 10:11:16 +0000896 int size = this->Size(); // Byte size of the original string.
897 if (size < ExternalString::kSize) {
898 // The string is too small to fit an external String in its place. This can
899 // only happen for zero length strings.
900 return false;
901 }
902 ASSERT(size >= ExternalString::kSize);
903 bool is_symbol = this->IsSymbol();
904 int length = this->length();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000905 int hash_field = this->hash_field();
ager@chromium.org6f10e412009-02-13 10:11:16 +0000906
907 // Morph the object to an external string by adjusting the map and
908 // reinitializing the fields.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000909 this->set_map(heap->external_ascii_string_map());
ager@chromium.org6f10e412009-02-13 10:11:16 +0000910 ExternalAsciiString* self = ExternalAsciiString::cast(this);
911 self->set_length(length);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000912 self->set_hash_field(hash_field);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000913 self->set_resource(resource);
914 // Additionally make the object into an external symbol if the original string
915 // was a symbol to start with.
916 if (is_symbol) {
917 self->Hash(); // Force regeneration of the hash value.
918 // Now morph this external string into a external symbol.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000919 this->set_map(heap->external_ascii_symbol_map());
ager@chromium.org6f10e412009-02-13 10:11:16 +0000920 }
921
922 // Fill the remainder of the string with dead wood.
923 int new_size = this->Size(); // Byte size of the external String object.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000924 heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
ager@chromium.org6f10e412009-02-13 10:11:16 +0000925 return true;
926}
927
928
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000929void String::StringShortPrint(StringStream* accumulator) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000930 int len = length();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000931 if (len > kMaxShortPrintLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000932 accumulator->Add("<Very long string[%u]>", len);
933 return;
934 }
935
936 if (!LooksValid()) {
937 accumulator->Add("<Invalid String>");
938 return;
939 }
940
941 StringInputBuffer buf(this);
942
943 bool truncated = false;
kasper.lund7276f142008-07-30 08:49:36 +0000944 if (len > kMaxShortPrintLength) {
945 len = kMaxShortPrintLength;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000946 truncated = true;
947 }
948 bool ascii = true;
949 for (int i = 0; i < len; i++) {
950 int c = buf.GetNext();
951
952 if (c < 32 || c >= 127) {
953 ascii = false;
954 }
955 }
956 buf.Reset(this);
957 if (ascii) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000958 accumulator->Add("<String[%u]: ", length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000959 for (int i = 0; i < len; i++) {
960 accumulator->Put(buf.GetNext());
961 }
962 accumulator->Put('>');
963 } else {
964 // Backslash indicates that the string contains control
965 // characters and that backslashes are therefore escaped.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000966 accumulator->Add("<String[%u]\\: ", length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000967 for (int i = 0; i < len; i++) {
968 int c = buf.GetNext();
969 if (c == '\n') {
970 accumulator->Add("\\n");
971 } else if (c == '\r') {
972 accumulator->Add("\\r");
973 } else if (c == '\\') {
974 accumulator->Add("\\\\");
975 } else if (c < 32 || c > 126) {
976 accumulator->Add("\\x%02x", c);
977 } else {
978 accumulator->Put(c);
979 }
980 }
981 if (truncated) {
982 accumulator->Put('.');
983 accumulator->Put('.');
984 accumulator->Put('.');
985 }
986 accumulator->Put('>');
987 }
988 return;
989}
990
991
992void JSObject::JSObjectShortPrint(StringStream* accumulator) {
993 switch (map()->instance_type()) {
994 case JS_ARRAY_TYPE: {
995 double length = JSArray::cast(this)->length()->Number();
996 accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
997 break;
998 }
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +0000999 case JS_WEAK_MAP_TYPE: {
1000 int elements = JSWeakMap::cast(this)->table()->NumberOfElements();
1001 accumulator->Add("<JS WeakMap[%d]>", elements);
1002 break;
1003 }
ager@chromium.org236ad962008-09-25 09:45:57 +00001004 case JS_REGEXP_TYPE: {
1005 accumulator->Add("<JS RegExp>");
1006 break;
1007 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001008 case JS_FUNCTION_TYPE: {
1009 Object* fun_name = JSFunction::cast(this)->shared()->name();
1010 bool printed = false;
1011 if (fun_name->IsString()) {
1012 String* str = String::cast(fun_name);
1013 if (str->length() > 0) {
1014 accumulator->Add("<JS Function ");
1015 accumulator->Put(str);
1016 accumulator->Put('>');
1017 printed = true;
1018 }
1019 }
1020 if (!printed) {
1021 accumulator->Add("<JS Function>");
1022 }
1023 break;
1024 }
1025 // All other JSObjects are rather similar to each other (JSObject,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001026 // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001027 default: {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001028 Map* map_of_this = map();
1029 Heap* heap = map_of_this->heap();
1030 Object* constructor = map_of_this->constructor();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001031 bool printed = false;
1032 if (constructor->IsHeapObject() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001033 !heap->Contains(HeapObject::cast(constructor))) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001034 accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
1035 } else {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001036 bool global_object = IsJSGlobalProxy();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001037 if (constructor->IsJSFunction()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001038 if (!heap->Contains(JSFunction::cast(constructor)->shared())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001039 accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
1040 } else {
1041 Object* constructor_name =
1042 JSFunction::cast(constructor)->shared()->name();
1043 if (constructor_name->IsString()) {
1044 String* str = String::cast(constructor_name);
1045 if (str->length() > 0) {
1046 bool vowel = AnWord(str);
1047 accumulator->Add("<%sa%s ",
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001048 global_object ? "Global Object: " : "",
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001049 vowel ? "n" : "");
1050 accumulator->Put(str);
1051 accumulator->Put('>');
1052 printed = true;
1053 }
1054 }
1055 }
1056 }
1057 if (!printed) {
1058 accumulator->Add("<JS %sObject", global_object ? "Global " : "");
1059 }
1060 }
1061 if (IsJSValue()) {
1062 accumulator->Add(" value = ");
1063 JSValue::cast(this)->value()->ShortPrint(accumulator);
1064 }
1065 accumulator->Put('>');
1066 break;
1067 }
1068 }
1069}
1070
1071
1072void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001073 // if (!HEAP->InNewSpace(this)) PrintF("*", this);
1074 Heap* heap = GetHeap();
1075 if (!heap->Contains(this)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001076 accumulator->Add("!!!INVALID POINTER!!!");
1077 return;
1078 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001079 if (!heap->Contains(map())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001080 accumulator->Add("!!!INVALID MAP!!!");
1081 return;
1082 }
1083
1084 accumulator->Add("%p ", this);
1085
1086 if (IsString()) {
1087 String::cast(this)->StringShortPrint(accumulator);
1088 return;
1089 }
1090 if (IsJSObject()) {
1091 JSObject::cast(this)->JSObjectShortPrint(accumulator);
1092 return;
1093 }
1094 switch (map()->instance_type()) {
1095 case MAP_TYPE:
1096 accumulator->Add("<Map>");
1097 break;
1098 case FIXED_ARRAY_TYPE:
1099 accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
1100 break;
1101 case BYTE_ARRAY_TYPE:
1102 accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
1103 break;
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001104 case EXTERNAL_PIXEL_ARRAY_TYPE:
1105 accumulator->Add("<ExternalPixelArray[%u]>",
1106 ExternalPixelArray::cast(this)->length());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001107 break;
ager@chromium.org3811b432009-10-28 14:53:37 +00001108 case EXTERNAL_BYTE_ARRAY_TYPE:
1109 accumulator->Add("<ExternalByteArray[%u]>",
1110 ExternalByteArray::cast(this)->length());
1111 break;
1112 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
1113 accumulator->Add("<ExternalUnsignedByteArray[%u]>",
1114 ExternalUnsignedByteArray::cast(this)->length());
1115 break;
1116 case EXTERNAL_SHORT_ARRAY_TYPE:
1117 accumulator->Add("<ExternalShortArray[%u]>",
1118 ExternalShortArray::cast(this)->length());
1119 break;
1120 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
1121 accumulator->Add("<ExternalUnsignedShortArray[%u]>",
1122 ExternalUnsignedShortArray::cast(this)->length());
1123 break;
1124 case EXTERNAL_INT_ARRAY_TYPE:
1125 accumulator->Add("<ExternalIntArray[%u]>",
1126 ExternalIntArray::cast(this)->length());
1127 break;
1128 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
1129 accumulator->Add("<ExternalUnsignedIntArray[%u]>",
1130 ExternalUnsignedIntArray::cast(this)->length());
1131 break;
1132 case EXTERNAL_FLOAT_ARRAY_TYPE:
1133 accumulator->Add("<ExternalFloatArray[%u]>",
1134 ExternalFloatArray::cast(this)->length());
1135 break;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001136 case EXTERNAL_DOUBLE_ARRAY_TYPE:
1137 accumulator->Add("<ExternalDoubleArray[%u]>",
1138 ExternalDoubleArray::cast(this)->length());
1139 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001140 case SHARED_FUNCTION_INFO_TYPE:
1141 accumulator->Add("<SharedFunctionInfo>");
1142 break;
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001143 case JS_MESSAGE_OBJECT_TYPE:
1144 accumulator->Add("<JSMessageObject>");
1145 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001146#define MAKE_STRUCT_CASE(NAME, Name, name) \
1147 case NAME##_TYPE: \
ager@chromium.org381abbb2009-02-25 13:23:22 +00001148 accumulator->Put('<'); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001149 accumulator->Add(#Name); \
ager@chromium.org381abbb2009-02-25 13:23:22 +00001150 accumulator->Put('>'); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001151 break;
1152 STRUCT_LIST(MAKE_STRUCT_CASE)
1153#undef MAKE_STRUCT_CASE
1154 case CODE_TYPE:
1155 accumulator->Add("<Code>");
1156 break;
1157 case ODDBALL_TYPE: {
1158 if (IsUndefined())
1159 accumulator->Add("<undefined>");
1160 else if (IsTheHole())
1161 accumulator->Add("<the hole>");
1162 else if (IsNull())
1163 accumulator->Add("<null>");
1164 else if (IsTrue())
1165 accumulator->Add("<true>");
1166 else if (IsFalse())
1167 accumulator->Add("<false>");
1168 else
1169 accumulator->Add("<Odd Oddball>");
1170 break;
1171 }
1172 case HEAP_NUMBER_TYPE:
1173 accumulator->Add("<Number: ");
1174 HeapNumber::cast(this)->HeapNumberPrint(accumulator);
1175 accumulator->Put('>');
1176 break;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001177 case FOREIGN_TYPE:
1178 accumulator->Add("<Foreign>");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001179 break;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001180 case JS_GLOBAL_PROPERTY_CELL_TYPE:
1181 accumulator->Add("Cell for ");
1182 JSGlobalPropertyCell::cast(this)->value()->ShortPrint(accumulator);
1183 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001184 default:
1185 accumulator->Add("<Other heap object (%d)>", map()->instance_type());
1186 break;
1187 }
1188}
1189
1190
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001191void HeapObject::Iterate(ObjectVisitor* v) {
1192 // Handle header
1193 IteratePointer(v, kMapOffset);
1194 // Handle object body
1195 Map* m = map();
1196 IterateBody(m->instance_type(), SizeFromMap(m), v);
1197}
1198
1199
1200void HeapObject::IterateBody(InstanceType type, int object_size,
1201 ObjectVisitor* v) {
1202 // Avoiding <Type>::cast(this) because it accesses the map pointer field.
1203 // During GC, the map pointer field is encoded.
1204 if (type < FIRST_NONSTRING_TYPE) {
1205 switch (type & kStringRepresentationMask) {
1206 case kSeqStringTag:
1207 break;
1208 case kConsStringTag:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001209 ConsString::BodyDescriptor::IterateBody(this, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001210 break;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001211 case kSlicedStringTag:
1212 SlicedString::BodyDescriptor::IterateBody(this, v);
1213 break;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001214 case kExternalStringTag:
1215 if ((type & kStringEncodingMask) == kAsciiStringTag) {
1216 reinterpret_cast<ExternalAsciiString*>(this)->
1217 ExternalAsciiStringIterateBody(v);
1218 } else {
1219 reinterpret_cast<ExternalTwoByteString*>(this)->
1220 ExternalTwoByteStringIterateBody(v);
1221 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001222 break;
1223 }
1224 return;
1225 }
1226
1227 switch (type) {
1228 case FIXED_ARRAY_TYPE:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001229 FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001230 break;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001231 case FIXED_DOUBLE_ARRAY_TYPE:
1232 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001233 case JS_OBJECT_TYPE:
ager@chromium.org32912102009-01-16 10:38:43 +00001234 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001235 case JS_VALUE_TYPE:
1236 case JS_ARRAY_TYPE:
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00001237 case JS_WEAK_MAP_TYPE:
ager@chromium.org236ad962008-09-25 09:45:57 +00001238 case JS_REGEXP_TYPE:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001239 case JS_GLOBAL_PROXY_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001240 case JS_GLOBAL_OBJECT_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001241 case JS_BUILTINS_OBJECT_TYPE:
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001242 case JS_MESSAGE_OBJECT_TYPE:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001243 JSObject::BodyDescriptor::IterateBody(this, object_size, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001244 break;
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001245 case JS_FUNCTION_TYPE:
1246 reinterpret_cast<JSFunction*>(this)
1247 ->JSFunctionIterateBody(object_size, v);
1248 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001249 case ODDBALL_TYPE:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001250 Oddball::BodyDescriptor::IterateBody(this, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001251 break;
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001252 case JS_PROXY_TYPE:
1253 JSProxy::BodyDescriptor::IterateBody(this, v);
1254 break;
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001255 case FOREIGN_TYPE:
1256 reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001257 break;
1258 case MAP_TYPE:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001259 Map::BodyDescriptor::IterateBody(this, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001260 break;
1261 case CODE_TYPE:
1262 reinterpret_cast<Code*>(this)->CodeIterateBody(v);
1263 break;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001264 case JS_GLOBAL_PROPERTY_CELL_TYPE:
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001265 JSGlobalPropertyCell::BodyDescriptor::IterateBody(this, v);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001266 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001267 case HEAP_NUMBER_TYPE:
1268 case FILLER_TYPE:
1269 case BYTE_ARRAY_TYPE:
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001270 case EXTERNAL_PIXEL_ARRAY_TYPE:
ager@chromium.org3811b432009-10-28 14:53:37 +00001271 case EXTERNAL_BYTE_ARRAY_TYPE:
1272 case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
1273 case EXTERNAL_SHORT_ARRAY_TYPE:
1274 case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
1275 case EXTERNAL_INT_ARRAY_TYPE:
1276 case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
1277 case EXTERNAL_FLOAT_ARRAY_TYPE:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00001278 case EXTERNAL_DOUBLE_ARRAY_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001279 break;
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001280 case SHARED_FUNCTION_INFO_TYPE:
1281 SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001282 break;
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001283
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001284#define MAKE_STRUCT_CASE(NAME, Name, name) \
1285 case NAME##_TYPE:
1286 STRUCT_LIST(MAKE_STRUCT_CASE)
1287#undef MAKE_STRUCT_CASE
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001288 StructBodyDescriptor::IterateBody(this, object_size, v);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001289 break;
1290 default:
1291 PrintF("Unknown type: %d\n", type);
1292 UNREACHABLE();
1293 }
1294}
1295
1296
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001297Object* HeapNumber::HeapNumberToBoolean() {
1298 // NaN, +0, and -0 should return the false object
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001299#if __BYTE_ORDER == __LITTLE_ENDIAN
1300 union IeeeDoubleLittleEndianArchType u;
1301#elif __BYTE_ORDER == __BIG_ENDIAN
1302 union IeeeDoubleBigEndianArchType u;
1303#endif
1304 u.d = value();
1305 if (u.bits.exp == 2047) {
1306 // Detect NaN for IEEE double precision floating point.
1307 if ((u.bits.man_low | u.bits.man_high) != 0)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001308 return GetHeap()->false_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001309 }
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001310 if (u.bits.exp == 0) {
1311 // Detect +0, and -0 for IEEE double precision floating point.
1312 if ((u.bits.man_low | u.bits.man_high) == 0)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001313 return GetHeap()->false_value();
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001314 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001315 return GetHeap()->true_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001316}
1317
1318
whesse@chromium.org023421e2010-12-21 12:19:12 +00001319void HeapNumber::HeapNumberPrint(FILE* out) {
1320 PrintF(out, "%.16g", Number());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001321}
1322
1323
1324void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
1325 // The Windows version of vsnprintf can allocate when printing a %g string
1326 // into a buffer that may not be big enough. We don't want random memory
1327 // allocation when producing post-crash stack traces, so we print into a
1328 // buffer that is plenty big enough for any floating point number, then
1329 // print that using vsnprintf (which may truncate but never allocate if
1330 // there is no more space in the buffer).
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001331 EmbeddedVector<char, 100> buffer;
1332 OS::SNPrintF(buffer, "%.16g", Number());
1333 accumulator->Add("%s", buffer.start());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001334}
1335
1336
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001337String* JSReceiver::class_name() {
1338 if (IsJSFunction() && IsJSFunctionProxy()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001339 return GetHeap()->function_class_symbol();
ager@chromium.orga1645e22009-09-09 19:27:10 +00001340 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001341 if (map()->constructor()->IsJSFunction()) {
1342 JSFunction* constructor = JSFunction::cast(map()->constructor());
1343 return String::cast(constructor->shared()->instance_class_name());
1344 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001345 // If the constructor is not present, return "Object".
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001346 return GetHeap()->Object_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001347}
1348
1349
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001350String* JSReceiver::constructor_name() {
ager@chromium.orga1645e22009-09-09 19:27:10 +00001351 if (map()->constructor()->IsJSFunction()) {
1352 JSFunction* constructor = JSFunction::cast(map()->constructor());
1353 String* name = String::cast(constructor->shared()->name());
vegorov@chromium.org42841962010-10-18 11:18:59 +00001354 if (name->length() > 0) return name;
1355 String* inferred_name = constructor->shared()->inferred_name();
1356 if (inferred_name->length() > 0) return inferred_name;
1357 Object* proto = GetPrototype();
1358 if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name();
ager@chromium.orga1645e22009-09-09 19:27:10 +00001359 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001360 // TODO(rossberg): what about proxies?
ager@chromium.orga1645e22009-09-09 19:27:10 +00001361 // If the constructor is not present, return "Object".
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001362 return GetHeap()->Object_symbol();
ager@chromium.orga1645e22009-09-09 19:27:10 +00001363}
1364
1365
lrn@chromium.org303ada72010-10-27 09:33:13 +00001366MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
1367 String* name,
1368 Object* value) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001369 int index = new_map->PropertyIndexFor(name);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001370 if (map()->unused_property_fields() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001371 ASSERT(map()->unused_property_fields() == 0);
1372 int new_unused = new_map->unused_property_fields();
lrn@chromium.org303ada72010-10-27 09:33:13 +00001373 Object* values;
1374 { MaybeObject* maybe_values =
1375 properties()->CopySize(properties()->length() + new_unused + 1);
1376 if (!maybe_values->ToObject(&values)) return maybe_values;
1377 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001378 set_properties(FixedArray::cast(values));
1379 }
1380 set_map(new_map);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001381 return FastPropertyAtPut(index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001382}
1383
1384
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +00001385static bool IsIdentifier(UnicodeCache* cache,
1386 unibrow::CharacterStream* buffer) {
1387 // Checks whether the buffer contains an identifier (no escape).
1388 if (!buffer->has_more()) return false;
1389 if (!cache->IsIdentifierStart(buffer->GetNext())) {
1390 return false;
1391 }
1392 while (buffer->has_more()) {
1393 if (!cache->IsIdentifierPart(buffer->GetNext())) {
1394 return false;
1395 }
1396 }
1397 return true;
1398}
1399
1400
lrn@chromium.org303ada72010-10-27 09:33:13 +00001401MaybeObject* JSObject::AddFastProperty(String* name,
1402 Object* value,
1403 PropertyAttributes attributes) {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00001404 ASSERT(!IsJSGlobalProxy());
1405
ager@chromium.org8c51fc92009-04-22 11:54:55 +00001406 // Normalize the object if the name is an actual string (not the
1407 // hidden symbols) and is not a real identifier.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001408 Isolate* isolate = GetHeap()->isolate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001409 StringInputBuffer buffer(name);
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +00001410 if (!IsIdentifier(isolate->unicode_cache(), &buffer)
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001411 && name != isolate->heap()->hidden_symbol()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00001412 Object* obj;
1413 { MaybeObject* maybe_obj =
1414 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1415 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1416 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001417 return AddSlowProperty(name, value, attributes);
1418 }
1419
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001420 DescriptorArray* old_descriptors = map()->instance_descriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001421 // Compute the new index for new field.
1422 int index = map()->NextFreePropertyIndex();
1423
1424 // Allocate new instance descriptors with (name, index) added
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001425 FieldDescriptor new_field(name, index, attributes);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001426 Object* new_descriptors;
1427 { MaybeObject* maybe_new_descriptors =
1428 old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
1429 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1430 return maybe_new_descriptors;
1431 }
1432 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001433
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001434 // Only allow map transition if the object isn't the global object and there
1435 // is not a transition for the name, or there's a transition for the name but
1436 // it's unrelated to properties.
1437 int descriptor_index = old_descriptors->Search(name);
1438
1439 // External array transitions are stored in the descriptor for property "",
1440 // which is not a identifier and should have forced a switch to slow
1441 // properties above.
1442 ASSERT(descriptor_index == DescriptorArray::kNotFound ||
1443 old_descriptors->GetType(descriptor_index) != EXTERNAL_ARRAY_TRANSITION);
1444 bool can_insert_transition = descriptor_index == DescriptorArray::kNotFound ||
1445 old_descriptors->GetType(descriptor_index) == EXTERNAL_ARRAY_TRANSITION;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001446 bool allow_map_transition =
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001447 can_insert_transition &&
1448 (isolate->context()->global_context()->object_function()->map() != map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001449
ager@chromium.org7c537e22008-10-16 08:43:32 +00001450 ASSERT(index < map()->inobject_properties() ||
1451 (index - map()->inobject_properties()) < properties()->length() ||
1452 map()->unused_property_fields() == 0);
1453 // Allocate a new map for the object.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001454 Object* r;
1455 { MaybeObject* maybe_r = map()->CopyDropDescriptors();
1456 if (!maybe_r->ToObject(&r)) return maybe_r;
1457 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001458 Map* new_map = Map::cast(r);
1459 if (allow_map_transition) {
1460 // Allocate new instance descriptors for the old map with map transition.
1461 MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001462 Object* r;
1463 { MaybeObject* maybe_r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
1464 if (!maybe_r->ToObject(&r)) return maybe_r;
1465 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001466 old_descriptors = DescriptorArray::cast(r);
1467 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001468
ager@chromium.org7c537e22008-10-16 08:43:32 +00001469 if (map()->unused_property_fields() == 0) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001470 if (properties()->length() > MaxFastProperties()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00001471 Object* obj;
1472 { MaybeObject* maybe_obj =
1473 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1474 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1475 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001476 return AddSlowProperty(name, value, attributes);
1477 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001478 // Make room for the new value
lrn@chromium.org303ada72010-10-27 09:33:13 +00001479 Object* values;
1480 { MaybeObject* maybe_values =
1481 properties()->CopySize(properties()->length() + kFieldsAdded);
1482 if (!maybe_values->ToObject(&values)) return maybe_values;
1483 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001484 set_properties(FixedArray::cast(values));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001485 new_map->set_unused_property_fields(kFieldsAdded - 1);
1486 } else {
1487 new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001488 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001489 // We have now allocated all the necessary objects.
1490 // All the changes can be applied at once, so they are atomic.
1491 map()->set_instance_descriptors(old_descriptors);
1492 new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1493 set_map(new_map);
1494 return FastPropertyAtPut(index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001495}
1496
1497
lrn@chromium.org303ada72010-10-27 09:33:13 +00001498MaybeObject* JSObject::AddConstantFunctionProperty(
1499 String* name,
1500 JSFunction* function,
1501 PropertyAttributes attributes) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001502 ASSERT(!GetHeap()->InNewSpace(function));
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00001503
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001504 // Allocate new instance descriptors with (name, function) added
1505 ConstantFunctionDescriptor d(name, function, attributes);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001506 Object* new_descriptors;
1507 { MaybeObject* maybe_new_descriptors =
1508 map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
1509 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1510 return maybe_new_descriptors;
1511 }
1512 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001513
1514 // Allocate a new map for the object.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001515 Object* new_map;
1516 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
1517 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
1518 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001519
1520 DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
1521 Map::cast(new_map)->set_instance_descriptors(descriptors);
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001522 Map* old_map = map();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001523 set_map(Map::cast(new_map));
1524
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001525 // If the old map is the global object map (from new Object()),
1526 // then transitions are not added to it, so we are done.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001527 Heap* heap = old_map->heap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001528 if (old_map == heap->isolate()->context()->global_context()->
1529 object_function()->map()) {
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001530 return function;
1531 }
1532
1533 // Do not add CONSTANT_TRANSITIONS to global objects
1534 if (IsGlobalObject()) {
1535 return function;
1536 }
1537
1538 // Add a CONSTANT_TRANSITION descriptor to the old map,
1539 // so future assignments to this property on other objects
1540 // of the same type will create a normal field, not a constant function.
1541 // Don't do this for special properties, with non-trival attributes.
1542 if (attributes != NONE) {
1543 return function;
1544 }
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001545 ConstTransitionDescriptor mark(name, Map::cast(new_map));
lrn@chromium.org303ada72010-10-27 09:33:13 +00001546 { MaybeObject* maybe_new_descriptors =
1547 old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
1548 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1549 // We have accomplished the main goal, so return success.
1550 return function;
1551 }
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001552 }
1553 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1554
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001555 return function;
1556}
1557
1558
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001559// Add property in slow mode
lrn@chromium.org303ada72010-10-27 09:33:13 +00001560MaybeObject* JSObject::AddSlowProperty(String* name,
1561 Object* value,
1562 PropertyAttributes attributes) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001563 ASSERT(!HasFastProperties());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001564 StringDictionary* dict = property_dictionary();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001565 Object* store_value = value;
1566 if (IsGlobalObject()) {
1567 // In case name is an orphaned property reuse the cell.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001568 int entry = dict->FindEntry(name);
1569 if (entry != StringDictionary::kNotFound) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001570 store_value = dict->ValueAt(entry);
1571 JSGlobalPropertyCell::cast(store_value)->set_value(value);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001572 // Assign an enumeration index to the property and update
1573 // SetNextEnumerationIndex.
1574 int index = dict->NextEnumerationIndex();
1575 PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
1576 dict->SetNextEnumerationIndex(index + 1);
1577 dict->SetEntry(entry, name, store_value, details);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001578 return value;
1579 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001580 Heap* heap = GetHeap();
lrn@chromium.org303ada72010-10-27 09:33:13 +00001581 { MaybeObject* maybe_store_value =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001582 heap->AllocateJSGlobalPropertyCell(value);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001583 if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
1584 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001585 JSGlobalPropertyCell::cast(store_value)->set_value(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001586 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001587 PropertyDetails details = PropertyDetails(attributes, NORMAL);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001588 Object* result;
1589 { MaybeObject* maybe_result = dict->Add(name, store_value, details);
1590 if (!maybe_result->ToObject(&result)) return maybe_result;
1591 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001592 if (dict != result) set_properties(StringDictionary::cast(result));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001593 return value;
1594}
1595
1596
lrn@chromium.org303ada72010-10-27 09:33:13 +00001597MaybeObject* JSObject::AddProperty(String* name,
1598 Object* value,
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001599 PropertyAttributes attributes,
1600 StrictModeFlag strict_mode) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001601 ASSERT(!IsJSGlobalProxy());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001602 Map* map_of_this = map();
1603 Heap* heap = map_of_this->heap();
1604 if (!map_of_this->is_extensible()) {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001605 if (strict_mode == kNonStrictMode) {
1606 return heap->undefined_value();
1607 } else {
1608 Handle<Object> args[1] = {Handle<String>(name)};
1609 return heap->isolate()->Throw(
1610 *FACTORY->NewTypeError("object_not_extensible",
1611 HandleVector(args, 1)));
1612 }
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001613 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001614 if (HasFastProperties()) {
1615 // Ensure the descriptor array does not get too big.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001616 if (map_of_this->instance_descriptors()->number_of_descriptors() <
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001617 DescriptorArray::kMaxNumberOfDescriptors) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001618 if (value->IsJSFunction() && !heap->InNewSpace(value)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001619 return AddConstantFunctionProperty(name,
1620 JSFunction::cast(value),
1621 attributes);
1622 } else {
1623 return AddFastProperty(name, value, attributes);
1624 }
1625 } else {
1626 // Normalize the object to prevent very large instance descriptors.
1627 // This eliminates unwanted N^2 allocation and lookup behavior.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001628 Object* obj;
1629 { MaybeObject* maybe_obj =
1630 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1631 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1632 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001633 }
1634 }
1635 return AddSlowProperty(name, value, attributes);
1636}
1637
1638
lrn@chromium.org303ada72010-10-27 09:33:13 +00001639MaybeObject* JSObject::SetPropertyPostInterceptor(
1640 String* name,
1641 Object* value,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001642 PropertyAttributes attributes,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001643 StrictModeFlag strict_mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001644 // Check local property, ignore interceptor.
1645 LookupResult result;
1646 LocalLookupRealNamedProperty(name, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00001647 if (result.IsFound()) {
1648 // An existing property, a map transition or a null descriptor was
1649 // found. Use set property to handle all these cases.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001650 return SetProperty(&result, name, value, attributes, strict_mode);
ager@chromium.org5c838252010-02-19 08:53:10 +00001651 }
1652 // Add a new real property.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001653 return AddProperty(name, value, attributes, strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001654}
1655
1656
lrn@chromium.org303ada72010-10-27 09:33:13 +00001657MaybeObject* JSObject::ReplaceSlowProperty(String* name,
1658 Object* value,
1659 PropertyAttributes attributes) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001660 StringDictionary* dictionary = property_dictionary();
1661 int old_index = dictionary->FindEntry(name);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001662 int new_enumeration_index = 0; // 0 means "Use the next available index."
1663 if (old_index != -1) {
1664 // All calls to ReplaceSlowProperty have had all transitions removed.
1665 ASSERT(!dictionary->DetailsAt(old_index).IsTransition());
1666 new_enumeration_index = dictionary->DetailsAt(old_index).index();
1667 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001668
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001669 PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001670 return SetNormalizedProperty(name, value, new_details);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001671}
1672
ager@chromium.org6141cbe2009-11-20 12:14:52 +00001673
lrn@chromium.org303ada72010-10-27 09:33:13 +00001674MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition(
ager@chromium.org7c537e22008-10-16 08:43:32 +00001675 String* name,
1676 Object* new_value,
1677 PropertyAttributes attributes) {
1678 Map* old_map = map();
lrn@chromium.org303ada72010-10-27 09:33:13 +00001679 Object* result;
1680 { MaybeObject* maybe_result =
1681 ConvertDescriptorToField(name, new_value, attributes);
1682 if (!maybe_result->ToObject(&result)) return maybe_result;
1683 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001684 // If we get to this point we have succeeded - do not return failure
1685 // after this point. Later stuff is optional.
1686 if (!HasFastProperties()) {
1687 return result;
1688 }
1689 // Do not add transitions to the map of "new Object()".
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001690 if (map() == old_map->heap()->isolate()->context()->global_context()->
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001691 object_function()->map()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001692 return result;
1693 }
1694
1695 MapTransitionDescriptor transition(name,
1696 map(),
1697 attributes);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001698 Object* new_descriptors;
1699 { MaybeObject* maybe_new_descriptors = old_map->instance_descriptors()->
1700 CopyInsert(&transition, KEEP_TRANSITIONS);
1701 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
1702 return result; // Yes, return _result_.
1703 }
1704 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001705 old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
1706 return result;
1707}
1708
1709
lrn@chromium.org303ada72010-10-27 09:33:13 +00001710MaybeObject* JSObject::ConvertDescriptorToField(String* name,
1711 Object* new_value,
1712 PropertyAttributes attributes) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001713 if (map()->unused_property_fields() == 0 &&
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001714 properties()->length() > MaxFastProperties()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00001715 Object* obj;
1716 { MaybeObject* maybe_obj =
1717 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
1718 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1719 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001720 return ReplaceSlowProperty(name, new_value, attributes);
1721 }
1722
1723 int index = map()->NextFreePropertyIndex();
1724 FieldDescriptor new_field(name, index, attributes);
1725 // Make a new DescriptorArray replacing an entry with FieldDescriptor.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001726 Object* descriptors_unchecked;
1727 { MaybeObject* maybe_descriptors_unchecked = map()->instance_descriptors()->
1728 CopyInsert(&new_field, REMOVE_TRANSITIONS);
1729 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
1730 return maybe_descriptors_unchecked;
1731 }
1732 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001733 DescriptorArray* new_descriptors =
1734 DescriptorArray::cast(descriptors_unchecked);
1735
1736 // Make a new map for the object.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001737 Object* new_map_unchecked;
1738 { MaybeObject* maybe_new_map_unchecked = map()->CopyDropDescriptors();
1739 if (!maybe_new_map_unchecked->ToObject(&new_map_unchecked)) {
1740 return maybe_new_map_unchecked;
1741 }
1742 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001743 Map* new_map = Map::cast(new_map_unchecked);
1744 new_map->set_instance_descriptors(new_descriptors);
1745
1746 // Make new properties array if necessary.
1747 FixedArray* new_properties = 0; // Will always be NULL or a valid pointer.
1748 int new_unused_property_fields = map()->unused_property_fields() - 1;
1749 if (map()->unused_property_fields() == 0) {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00001750 new_unused_property_fields = kFieldsAdded - 1;
lrn@chromium.org303ada72010-10-27 09:33:13 +00001751 Object* new_properties_object;
1752 { MaybeObject* maybe_new_properties_object =
1753 properties()->CopySize(properties()->length() + kFieldsAdded);
1754 if (!maybe_new_properties_object->ToObject(&new_properties_object)) {
1755 return maybe_new_properties_object;
1756 }
1757 }
1758 new_properties = FixedArray::cast(new_properties_object);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001759 }
1760
1761 // Update pointers to commit changes.
1762 // Object points to the new map.
1763 new_map->set_unused_property_fields(new_unused_property_fields);
1764 set_map(new_map);
1765 if (new_properties) {
1766 set_properties(FixedArray::cast(new_properties));
1767 }
1768 return FastPropertyAtPut(index, new_value);
1769}
1770
1771
1772
lrn@chromium.org303ada72010-10-27 09:33:13 +00001773MaybeObject* JSObject::SetPropertyWithInterceptor(
1774 String* name,
1775 Object* value,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001776 PropertyAttributes attributes,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001777 StrictModeFlag strict_mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001778 Isolate* isolate = GetIsolate();
1779 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001780 Handle<JSObject> this_handle(this);
1781 Handle<String> name_handle(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001782 Handle<Object> value_handle(value, isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001783 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
1784 if (!interceptor->setter()->IsUndefined()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001785 LOG(isolate, ApiNamedPropertyAccess("interceptor-named-set", this, name));
1786 CustomArguments args(isolate, interceptor->data(), this, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00001787 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001788 v8::NamedPropertySetter setter =
1789 v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
1790 v8::Handle<v8::Value> result;
1791 {
1792 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001793 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001794 Handle<Object> value_unhole(value->IsTheHole() ?
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001795 isolate->heap()->undefined_value() :
1796 value,
1797 isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001798 result = setter(v8::Utils::ToLocal(name_handle),
1799 v8::Utils::ToLocal(value_unhole),
1800 info);
1801 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001802 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001803 if (!result.IsEmpty()) return *value_handle;
1804 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00001805 MaybeObject* raw_result =
1806 this_handle->SetPropertyPostInterceptor(*name_handle,
1807 *value_handle,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001808 attributes,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001809 strict_mode);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001810 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001811 return raw_result;
1812}
1813
1814
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001815MaybeObject* JSReceiver::SetProperty(String* name,
1816 Object* value,
1817 PropertyAttributes attributes,
1818 StrictModeFlag strict_mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001819 LookupResult result;
1820 LocalLookup(name, &result);
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001821 return SetProperty(&result, name, value, attributes, strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001822}
1823
1824
lrn@chromium.org303ada72010-10-27 09:33:13 +00001825MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
1826 String* name,
1827 Object* value,
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00001828 JSObject* holder,
1829 StrictModeFlag strict_mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001830 Isolate* isolate = GetIsolate();
1831 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001832
1833 // We should never get here to initialize a const with the hole
1834 // value since a const declaration would conflict with the setter.
1835 ASSERT(!value->IsTheHole());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001836 Handle<Object> value_handle(value, isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001837
1838 // To accommodate both the old and the new api we switch on the
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001839 // data structure used to store the callbacks. Eventually foreign
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001840 // callbacks should be phased out.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001841 if (structure->IsForeign()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001842 AccessorDescriptor* callback =
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001843 reinterpret_cast<AccessorDescriptor*>(
1844 Foreign::cast(structure)->address());
lrn@chromium.org303ada72010-10-27 09:33:13 +00001845 MaybeObject* obj = (callback->setter)(this, value, callback->data);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001846 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001847 if (obj->IsFailure()) return obj;
1848 return *value_handle;
1849 }
1850
1851 if (structure->IsAccessorInfo()) {
1852 // api style callbacks
1853 AccessorInfo* data = AccessorInfo::cast(structure);
1854 Object* call_obj = data->setter();
1855 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
1856 if (call_fun == NULL) return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001857 Handle<String> key(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001858 LOG(isolate, ApiNamedPropertyAccess("store", this, name));
1859 CustomArguments args(isolate, data->data(), this, JSObject::cast(holder));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00001860 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001861 {
1862 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001863 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001864 call_fun(v8::Utils::ToLocal(key),
1865 v8::Utils::ToLocal(value_handle),
1866 info);
1867 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001868 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001869 return *value_handle;
1870 }
1871
1872 if (structure->IsFixedArray()) {
1873 Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
1874 if (setter->IsJSFunction()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001875 return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001876 } else {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00001877 if (strict_mode == kNonStrictMode) {
1878 return value;
1879 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001880 Handle<String> key(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001881 Handle<Object> holder_handle(holder, isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001882 Handle<Object> args[2] = { key, holder_handle };
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001883 return isolate->Throw(
1884 *isolate->factory()->NewTypeError("no_setter_in_callback",
1885 HandleVector(args, 2)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001886 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001887 }
1888
1889 UNREACHABLE();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00001890 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001891}
1892
1893
lrn@chromium.org303ada72010-10-27 09:33:13 +00001894MaybeObject* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter,
1895 Object* value) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001896 Isolate* isolate = GetIsolate();
1897 Handle<Object> value_handle(value, isolate);
1898 Handle<JSFunction> fun(JSFunction::cast(setter), isolate);
1899 Handle<JSObject> self(this, isolate);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001900#ifdef ENABLE_DEBUGGER_SUPPORT
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001901 Debug* debug = isolate->debug();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001902 // Handle stepping into a setter if step into is active.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001903 if (debug->StepInActive()) {
1904 debug->HandleStepIn(fun, Handle<Object>::null(), 0, false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001905 }
1906#endif
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001907 bool has_pending_exception;
1908 Object** argv[] = { value_handle.location() };
1909 Execution::Call(fun, self, 1, argv, &has_pending_exception);
1910 // Check for pending exception and return the result.
1911 if (has_pending_exception) return Failure::Exception();
1912 return *value_handle;
1913}
1914
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001915
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001916void JSObject::LookupCallbackSetterInPrototypes(String* name,
1917 LookupResult* result) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001918 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001919 for (Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001920 pt != heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001921 pt = pt->GetPrototype()) {
1922 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
ager@chromium.org5c838252010-02-19 08:53:10 +00001923 if (result->IsProperty()) {
whesse@chromium.org030d38e2011-07-13 13:23:34 +00001924 if (result->type() == CALLBACKS && !result->IsReadOnly()) return;
1925 // Found non-callback or read-only callback, stop looking.
1926 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001927 }
1928 }
1929 result->NotFound();
1930}
1931
1932
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00001933MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes(
1934 uint32_t index,
1935 Object* value,
1936 bool* found,
1937 StrictModeFlag strict_mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001938 Heap* heap = GetHeap();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001939 for (Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001940 pt != heap->null_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001941 pt = pt->GetPrototype()) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001942 if (!JSObject::cast(pt)->HasDictionaryElements()) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00001943 continue;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001944 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00001945 NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
1946 int entry = dictionary->FindEntry(index);
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001947 if (entry != NumberDictionary::kNotFound) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001948 PropertyDetails details = dictionary->DetailsAt(entry);
1949 if (details.type() == CALLBACKS) {
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00001950 *found = true;
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00001951 return SetElementWithCallback(dictionary->ValueAt(entry),
1952 index,
1953 value,
1954 JSObject::cast(pt),
1955 strict_mode);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001956 }
1957 }
1958 }
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00001959 *found = false;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001960 return heap->the_hole_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001961}
1962
1963
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001964void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
1965 DescriptorArray* descriptors = map()->instance_descriptors();
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001966 int number = descriptors->SearchWithCache(name);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001967 if (number != DescriptorArray::kNotFound) {
1968 result->DescriptorResult(this, descriptors->GetDetails(number), number);
1969 } else {
1970 result->NotFound();
1971 }
1972}
1973
1974
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001975void Map::LookupInDescriptors(JSObject* holder,
1976 String* name,
1977 LookupResult* result) {
1978 DescriptorArray* descriptors = instance_descriptors();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001979 DescriptorLookupCache* cache = heap()->isolate()->descriptor_lookup_cache();
1980 int number = cache->Lookup(descriptors, name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001981 if (number == DescriptorLookupCache::kAbsent) {
1982 number = descriptors->Search(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001983 cache->Update(descriptors, name, number);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001984 }
1985 if (number != DescriptorArray::kNotFound) {
1986 result->DescriptorResult(holder, descriptors->GetDetails(number), number);
1987 } else {
1988 result->NotFound();
1989 }
1990}
1991
1992
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00001993static JSObject::ElementsKind GetElementsKindFromExternalArrayType(
1994 ExternalArrayType array_type) {
1995 switch (array_type) {
1996 case kExternalByteArray:
1997 return JSObject::EXTERNAL_BYTE_ELEMENTS;
1998 break;
1999 case kExternalUnsignedByteArray:
2000 return JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS;
2001 break;
2002 case kExternalShortArray:
2003 return JSObject::EXTERNAL_SHORT_ELEMENTS;
2004 break;
2005 case kExternalUnsignedShortArray:
2006 return JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS;
2007 break;
2008 case kExternalIntArray:
2009 return JSObject::EXTERNAL_INT_ELEMENTS;
2010 break;
2011 case kExternalUnsignedIntArray:
2012 return JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS;
2013 break;
2014 case kExternalFloatArray:
2015 return JSObject::EXTERNAL_FLOAT_ELEMENTS;
2016 break;
2017 case kExternalDoubleArray:
2018 return JSObject::EXTERNAL_DOUBLE_ELEMENTS;
2019 break;
2020 case kExternalPixelArray:
2021 return JSObject::EXTERNAL_PIXEL_ELEMENTS;
2022 break;
2023 }
2024 UNREACHABLE();
2025 return JSObject::DICTIONARY_ELEMENTS;
2026}
2027
2028
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002029MaybeObject* Map::GetExternalArrayElementsMap(ExternalArrayType array_type,
2030 bool safe_to_add_transition) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002031 Heap* current_heap = heap();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002032 DescriptorArray* descriptors = instance_descriptors();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002033 String* external_array_sentinel_name = current_heap->empty_symbol();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002034
2035 if (safe_to_add_transition) {
2036 // It's only safe to manipulate the descriptor array if it would be
2037 // safe to add a transition.
2038
2039 ASSERT(!is_shared()); // no transitions can be added to shared maps.
2040 // Check if the external array transition already exists.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002041 DescriptorLookupCache* cache =
2042 current_heap->isolate()->descriptor_lookup_cache();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002043 int index = cache->Lookup(descriptors, external_array_sentinel_name);
2044 if (index == DescriptorLookupCache::kAbsent) {
2045 index = descriptors->Search(external_array_sentinel_name);
2046 cache->Update(descriptors,
2047 external_array_sentinel_name,
2048 index);
2049 }
2050
2051 // If the transition already exists, check the type. If there is a match,
2052 // return it.
2053 if (index != DescriptorArray::kNotFound) {
2054 PropertyDetails details(PropertyDetails(descriptors->GetDetails(index)));
2055 if (details.type() == EXTERNAL_ARRAY_TRANSITION &&
2056 details.array_type() == array_type) {
2057 return descriptors->GetValue(index);
2058 } else {
2059 safe_to_add_transition = false;
2060 }
2061 }
2062 }
2063
2064 // No transition to an existing external array map. Make a new one.
2065 Object* obj;
2066 { MaybeObject* maybe_map = CopyDropTransitions();
2067 if (!maybe_map->ToObject(&obj)) return maybe_map;
2068 }
2069 Map* new_map = Map::cast(obj);
2070
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00002071 new_map->set_elements_kind(GetElementsKindFromExternalArrayType(array_type));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002072 GetIsolate()->counters()->map_to_external_array_elements()->Increment();
2073
2074 // Only remember the map transition if the object's map is NOT equal to the
2075 // global object_function's map and there is not an already existing
2076 // non-matching external array transition.
2077 bool allow_map_transition =
2078 safe_to_add_transition &&
2079 (GetIsolate()->context()->global_context()->object_function()->map() !=
2080 map());
2081 if (allow_map_transition) {
2082 // Allocate new instance descriptors for the old map with map transition.
2083 ExternalArrayTransitionDescriptor desc(external_array_sentinel_name,
2084 Map::cast(new_map),
2085 array_type);
2086 Object* new_descriptors;
2087 MaybeObject* maybe_new_descriptors = descriptors->CopyInsert(
2088 &desc,
2089 KEEP_TRANSITIONS);
2090 if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
2091 return maybe_new_descriptors;
2092 }
2093 descriptors = DescriptorArray::cast(new_descriptors);
2094 set_instance_descriptors(descriptors);
2095 }
2096
2097 return new_map;
2098}
2099
2100
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002101void JSObject::LocalLookupRealNamedProperty(String* name,
2102 LookupResult* result) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002103 if (IsJSGlobalProxy()) {
2104 Object* proto = GetPrototype();
2105 if (proto->IsNull()) return result->NotFound();
2106 ASSERT(proto->IsJSGlobalObject());
2107 return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
2108 }
2109
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002110 if (HasFastProperties()) {
2111 LookupInDescriptor(name, result);
ager@chromium.org5c838252010-02-19 08:53:10 +00002112 if (result->IsFound()) {
2113 // A property, a map transition or a null descriptor was found.
2114 // We return all of these result types because
2115 // LocalLookupRealNamedProperty is used when setting properties
2116 // where map transitions and null descriptors are handled.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002117 ASSERT(result->holder() == this && result->type() != NORMAL);
2118 // Disallow caching for uninitialized constants. These can only
2119 // occur as fields.
2120 if (result->IsReadOnly() && result->type() == FIELD &&
ager@chromium.org7c537e22008-10-16 08:43:32 +00002121 FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002122 result->DisallowCaching();
2123 }
2124 return;
2125 }
2126 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002127 int entry = property_dictionary()->FindEntry(name);
2128 if (entry != StringDictionary::kNotFound) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002129 Object* value = property_dictionary()->ValueAt(entry);
2130 if (IsGlobalObject()) {
2131 PropertyDetails d = property_dictionary()->DetailsAt(entry);
2132 if (d.IsDeleted()) {
2133 result->NotFound();
2134 return;
2135 }
2136 value = JSGlobalPropertyCell::cast(value)->value();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002137 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002138 // Make sure to disallow caching for uninitialized constants
2139 // found in the dictionary-mode objects.
2140 if (value->IsTheHole()) result->DisallowCaching();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002141 result->DictionaryResult(this, entry);
2142 return;
2143 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002144 }
2145 result->NotFound();
2146}
2147
2148
2149void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) {
2150 LocalLookupRealNamedProperty(name, result);
2151 if (result->IsProperty()) return;
2152
2153 LookupRealNamedPropertyInPrototypes(name, result);
2154}
2155
2156
2157void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
2158 LookupResult* result) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002159 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002160 for (Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002161 pt != heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002162 pt = JSObject::cast(pt)->GetPrototype()) {
2163 JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
ager@chromium.org5c838252010-02-19 08:53:10 +00002164 if (result->IsProperty() && (result->type() != INTERCEPTOR)) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002165 }
2166 result->NotFound();
2167}
2168
2169
2170// We only need to deal with CALLBACKS and INTERCEPTORS
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002171MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(
2172 LookupResult* result,
2173 String* name,
2174 Object* value,
2175 bool check_prototype,
2176 StrictModeFlag strict_mode) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00002177 if (check_prototype && !result->IsProperty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002178 LookupCallbackSetterInPrototypes(name, result);
2179 }
2180
2181 if (result->IsProperty()) {
2182 if (!result->IsReadOnly()) {
2183 switch (result->type()) {
2184 case CALLBACKS: {
2185 Object* obj = result->GetCallbackObject();
2186 if (obj->IsAccessorInfo()) {
2187 AccessorInfo* info = AccessorInfo::cast(obj);
2188 if (info->all_can_write()) {
2189 return SetPropertyWithCallback(result->GetCallbackObject(),
2190 name,
2191 value,
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002192 result->holder(),
2193 strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002194 }
2195 }
2196 break;
2197 }
2198 case INTERCEPTOR: {
2199 // Try lookup real named properties. Note that only property can be
2200 // set is callbacks marked as ALL_CAN_WRITE on the prototype chain.
2201 LookupResult r;
2202 LookupRealNamedProperty(name, &r);
2203 if (r.IsProperty()) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002204 return SetPropertyWithFailedAccessCheck(&r,
2205 name,
2206 value,
2207 check_prototype,
2208 strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002209 }
2210 break;
2211 }
2212 default: {
2213 break;
2214 }
2215 }
2216 }
2217 }
2218
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002219 Heap* heap = GetHeap();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002220 HandleScope scope(heap->isolate());
2221 Handle<Object> value_handle(value);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002222 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00002223 return *value_handle;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002224}
2225
2226
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002227MaybeObject* JSReceiver::SetProperty(LookupResult* result,
2228 String* key,
2229 Object* value,
2230 PropertyAttributes attributes,
2231 StrictModeFlag strict_mode) {
2232 if (result->IsFound() && result->type() == HANDLER) {
2233 return JSProxy::cast(this)->SetPropertyWithHandler(
2234 key, value, attributes, strict_mode);
2235 } else {
2236 return JSObject::cast(this)->SetPropertyForResult(
2237 result, key, value, attributes, strict_mode);
2238 }
2239}
2240
2241
rossberg@chromium.org717967f2011-07-20 13:44:42 +00002242bool JSProxy::HasPropertyWithHandler(String* name_raw) {
2243 Isolate* isolate = GetIsolate();
2244 HandleScope scope(isolate);
2245 Handle<Object> receiver(this);
2246 Handle<Object> name(name_raw);
2247 Handle<Object> handler(this->handler());
2248
2249 // Extract trap function.
2250 Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("has");
2251 Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
2252 if (trap->IsUndefined()) {
2253 trap = isolate->derived_has_trap();
2254 }
2255
2256 // Call trap function.
2257 Object** args[] = { name.location() };
2258 bool has_exception;
2259 Handle<Object> result =
2260 Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
2261 if (has_exception) return Failure::Exception();
2262
2263 return result->ToBoolean()->IsTrue();
2264}
2265
2266
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002267MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler(
2268 String* name_raw,
2269 Object* value_raw,
2270 PropertyAttributes attributes,
2271 StrictModeFlag strict_mode) {
2272 Isolate* isolate = GetIsolate();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002273 HandleScope scope(isolate);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002274 Handle<Object> receiver(this);
2275 Handle<Object> name(name_raw);
2276 Handle<Object> value(value_raw);
2277 Handle<Object> handler(this->handler());
2278
2279 // Extract trap function.
2280 Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("set");
2281 Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
2282 if (trap->IsUndefined()) {
2283 trap = isolate->derived_set_trap();
2284 }
2285
2286 // Call trap function.
2287 Object** args[] = {
2288 receiver.location(), name.location(), value.location()
2289 };
2290 bool has_exception;
rossberg@chromium.org717967f2011-07-20 13:44:42 +00002291 Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002292 if (has_exception) return Failure::Exception();
2293
ager@chromium.org04921a82011-06-27 13:21:41 +00002294 return *value;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002295}
2296
2297
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002298MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
2299 String* name_raw, DeleteMode mode) {
2300 Isolate* isolate = GetIsolate();
2301 HandleScope scope(isolate);
2302 Handle<Object> receiver(this);
2303 Handle<Object> name(name_raw);
2304 Handle<Object> handler(this->handler());
2305
2306 // Extract trap function.
2307 Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete");
2308 Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
2309 if (trap->IsUndefined()) {
2310 Handle<Object> args[] = { handler, trap_name };
2311 Handle<Object> error = isolate->factory()->NewTypeError(
2312 "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args)));
2313 isolate->Throw(*error);
2314 return Failure::Exception();
2315 }
2316
2317 // Call trap function.
2318 Object** args[] = { name.location() };
2319 bool has_exception;
2320 Handle<Object> result =
2321 Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
2322 if (has_exception) return Failure::Exception();
2323
2324 Object* bool_result = result->ToBoolean();
danno@chromium.orgb6451162011-08-17 14:33:23 +00002325 if (mode == STRICT_DELETION &&
2326 bool_result == isolate->heap()->false_value()) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002327 Handle<Object> args[] = { handler, trap_name };
2328 Handle<Object> error = isolate->factory()->NewTypeError(
2329 "handler_failed", HandleVector(args, ARRAY_SIZE(args)));
2330 isolate->Throw(*error);
2331 return Failure::Exception();
2332 }
2333 return bool_result;
2334}
2335
2336
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002337MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler(
2338 JSReceiver* receiver_raw,
2339 String* name_raw,
2340 bool* has_exception) {
2341 Isolate* isolate = GetIsolate();
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002342 HandleScope scope(isolate);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002343 Handle<JSReceiver> receiver(receiver_raw);
2344 Handle<Object> name(name_raw);
2345 Handle<Object> handler(this->handler());
2346
2347 // Extract trap function.
2348 Handle<String> trap_name =
2349 isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor");
2350 Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
2351 if (trap->IsUndefined()) {
2352 Handle<Object> args[] = { handler, trap_name };
2353 Handle<Object> error = isolate->factory()->NewTypeError(
2354 "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args)));
2355 isolate->Throw(*error);
2356 *has_exception = true;
2357 return NONE;
2358 }
2359
2360 // Call trap function.
2361 Object** args[] = { name.location() };
2362 Handle<Object> result =
2363 Execution::Call(trap, handler, ARRAY_SIZE(args), args, has_exception);
2364 if (has_exception) return NONE;
2365
2366 // TODO(rossberg): convert result to PropertyAttributes
2367 USE(result);
2368 return NONE;
2369}
2370
2371
rossberg@chromium.org717967f2011-07-20 13:44:42 +00002372void JSProxy::Fix() {
2373 Isolate* isolate = GetIsolate();
2374 HandleScope scope(isolate);
2375 Handle<JSProxy> self(this);
2376
2377 isolate->factory()->BecomeJSObject(self);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00002378 ASSERT(self->IsJSObject());
rossberg@chromium.org717967f2011-07-20 13:44:42 +00002379 // TODO(rossberg): recognize function proxies.
2380}
2381
2382
2383
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002384MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
whesse@chromium.org030d38e2011-07-13 13:23:34 +00002385 String* name,
2386 Object* value,
2387 PropertyAttributes attributes,
2388 StrictModeFlag strict_mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002389 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002390 // Make sure that the top context does not change when doing callbacks or
2391 // interceptor calls.
2392 AssertNoContextChange ncc;
2393
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002394 // Optimization for 2-byte strings often used as keys in a decompression
2395 // dictionary. We make these short keys into symbols to avoid constantly
2396 // reallocating them.
2397 if (!name->IsSymbol() && name->length() <= 2) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00002398 Object* symbol_version;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002399 { MaybeObject* maybe_symbol_version = heap->LookupSymbol(name);
lrn@chromium.org303ada72010-10-27 09:33:13 +00002400 if (maybe_symbol_version->ToObject(&symbol_version)) {
2401 name = String::cast(symbol_version);
2402 }
2403 }
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002404 }
2405
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002406 // Check access rights if needed.
2407 if (IsAccessCheckNeeded()
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002408 && !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002409 return SetPropertyWithFailedAccessCheck(result,
2410 name,
2411 value,
2412 true,
2413 strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002414 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002415
2416 if (IsJSGlobalProxy()) {
2417 Object* proto = GetPrototype();
2418 if (proto->IsNull()) return value;
2419 ASSERT(proto->IsJSGlobalObject());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002420 return JSObject::cast(proto)->SetProperty(
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002421 result, name, value, attributes, strict_mode);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002422 }
2423
ager@chromium.org32912102009-01-16 10:38:43 +00002424 if (!result->IsProperty() && !IsJSContextExtensionObject()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002425 // We could not find a local property so let's check whether there is an
2426 // accessor that wants to handle the property.
2427 LookupResult accessor_result;
2428 LookupCallbackSetterInPrototypes(name, &accessor_result);
ager@chromium.org5c838252010-02-19 08:53:10 +00002429 if (accessor_result.IsProperty()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002430 return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
2431 name,
2432 value,
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002433 accessor_result.holder(),
2434 strict_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002435 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002436 }
ager@chromium.org5c838252010-02-19 08:53:10 +00002437 if (!result->IsFound()) {
2438 // Neither properties nor transitions found.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002439 return AddProperty(name, value, attributes, strict_mode);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002440 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002441 if (result->IsReadOnly() && result->IsProperty()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002442 if (strict_mode == kStrictMode) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002443 HandleScope scope(heap->isolate());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002444 Handle<String> key(name);
2445 Handle<Object> holder(this);
2446 Handle<Object> args[2] = { key, holder };
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002447 return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError(
2448 "strict_read_only_property", HandleVector(args, 2)));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002449 } else {
2450 return value;
2451 }
2452 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002453 // This is a real property that is not read-only, or it is a
2454 // transition or null descriptor and there are no setters in the prototypes.
2455 switch (result->type()) {
2456 case NORMAL:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002457 return SetNormalizedProperty(result, value);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002458 case FIELD:
ager@chromium.org7c537e22008-10-16 08:43:32 +00002459 return FastPropertyAtPut(result->GetFieldIndex(), value);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002460 case MAP_TRANSITION:
2461 if (attributes == result->GetAttributes()) {
2462 // Only use map transition if the attributes match.
2463 return AddFastPropertyUsingMap(result->GetTransitionMap(),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002464 name,
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002465 value);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002466 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002467 return ConvertDescriptorToField(name, value, attributes);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002468 case CONSTANT_FUNCTION:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002469 // Only replace the function if necessary.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002470 if (value == result->GetConstantFunction()) return value;
2471 // Preserve the attributes of this existing property.
2472 attributes = result->GetAttributes();
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002473 return ConvertDescriptorToField(name, value, attributes);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002474 case CALLBACKS:
2475 return SetPropertyWithCallback(result->GetCallbackObject(),
2476 name,
2477 value,
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002478 result->holder(),
2479 strict_mode);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002480 case INTERCEPTOR:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00002481 return SetPropertyWithInterceptor(name, value, attributes, strict_mode);
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00002482 case CONSTANT_TRANSITION: {
2483 // If the same constant function is being added we can simply
2484 // transition to the target map.
2485 Map* target_map = result->GetTransitionMap();
2486 DescriptorArray* target_descriptors = target_map->instance_descriptors();
2487 int number = target_descriptors->SearchWithCache(name);
2488 ASSERT(number != DescriptorArray::kNotFound);
2489 ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
2490 JSFunction* function =
2491 JSFunction::cast(target_descriptors->GetValue(number));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002492 ASSERT(!HEAP->InNewSpace(function));
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00002493 if (value == function) {
2494 set_map(target_map);
2495 return value;
2496 }
2497 // Otherwise, replace with a MAP_TRANSITION to a new map with a
2498 // FIELD, even if the value is a constant function.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002499 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00002500 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002501 case NULL_DESCRIPTOR:
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002502 case EXTERNAL_ARRAY_TRANSITION:
ager@chromium.org7c537e22008-10-16 08:43:32 +00002503 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002504 default:
2505 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002506 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002507 UNREACHABLE();
2508 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002509}
2510
2511
2512// Set a real local property, even if it is READ_ONLY. If the property is not
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002513// present, add it with attributes NONE. This code is an exact clone of
2514// SetProperty, with the check for IsReadOnly and the check for a
2515// callback setter removed. The two lines looking up the LookupResult
2516// result are also added. If one of the functions is changed, the other
2517// should be.
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002518// Note that this method cannot be used to set the prototype of a function
2519// because ConvertDescriptorToField() which is called in "case CALLBACKS:"
2520// doesn't handle function prototypes correctly.
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00002521MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002522 String* name,
2523 Object* value,
2524 PropertyAttributes attributes) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002525
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002526 // Make sure that the top context does not change when doing callbacks or
2527 // interceptor calls.
2528 AssertNoContextChange ncc;
ager@chromium.org5c838252010-02-19 08:53:10 +00002529 LookupResult result;
2530 LocalLookup(name, &result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002531 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002532 if (IsAccessCheckNeeded()) {
2533 Heap* heap = GetHeap();
2534 if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00002535 return SetPropertyWithFailedAccessCheck(&result,
2536 name,
2537 value,
2538 false,
2539 kNonStrictMode);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002540 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002541 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002542
2543 if (IsJSGlobalProxy()) {
2544 Object* proto = GetPrototype();
2545 if (proto->IsNull()) return value;
2546 ASSERT(proto->IsJSGlobalObject());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00002547 return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes(
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002548 name,
2549 value,
2550 attributes);
2551 }
2552
ager@chromium.org7c537e22008-10-16 08:43:32 +00002553 // Check for accessor in prototype chain removed here in clone.
ager@chromium.org5c838252010-02-19 08:53:10 +00002554 if (!result.IsFound()) {
2555 // Neither properties nor transitions found.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002556 return AddProperty(name, value, attributes, kNonStrictMode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002557 }
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002558
ager@chromium.org5c838252010-02-19 08:53:10 +00002559 PropertyDetails details = PropertyDetails(attributes, NORMAL);
2560
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002561 // Check of IsReadOnly removed from here in clone.
ager@chromium.org5c838252010-02-19 08:53:10 +00002562 switch (result.type()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002563 case NORMAL:
ager@chromium.org5c838252010-02-19 08:53:10 +00002564 return SetNormalizedProperty(name, value, details);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002565 case FIELD:
ager@chromium.org5c838252010-02-19 08:53:10 +00002566 return FastPropertyAtPut(result.GetFieldIndex(), value);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002567 case MAP_TRANSITION:
ager@chromium.org5c838252010-02-19 08:53:10 +00002568 if (attributes == result.GetAttributes()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002569 // Only use map transition if the attributes match.
ager@chromium.org5c838252010-02-19 08:53:10 +00002570 return AddFastPropertyUsingMap(result.GetTransitionMap(),
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002571 name,
2572 value);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002573 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002574 return ConvertDescriptorToField(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002575 case CONSTANT_FUNCTION:
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002576 // Only replace the function if necessary.
ager@chromium.org5c838252010-02-19 08:53:10 +00002577 if (value == result.GetConstantFunction()) return value;
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002578 // Preserve the attributes of this existing property.
ager@chromium.org5c838252010-02-19 08:53:10 +00002579 attributes = result.GetAttributes();
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002580 return ConvertDescriptorToField(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002581 case CALLBACKS:
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002582 case INTERCEPTOR:
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002583 // Override callback in clone
2584 return ConvertDescriptorToField(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002585 case CONSTANT_TRANSITION:
2586 // Replace with a MAP_TRANSITION to a new map with a FIELD, even
2587 // if the value is a function.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002588 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002589 case NULL_DESCRIPTOR:
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002590 case EXTERNAL_ARRAY_TRANSITION:
ager@chromium.org7c537e22008-10-16 08:43:32 +00002591 return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002592 default:
2593 UNREACHABLE();
2594 }
2595 UNREACHABLE();
2596 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002597}
2598
2599
2600PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
2601 JSObject* receiver,
2602 String* name,
2603 bool continue_search) {
2604 // Check local property, ignore interceptor.
2605 LookupResult result;
2606 LocalLookupRealNamedProperty(name, &result);
kasper.lund44510672008-07-25 07:37:58 +00002607 if (result.IsProperty()) return result.GetAttributes();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002608
2609 if (continue_search) {
2610 // Continue searching via the prototype chain.
2611 Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002612 if (!pt->IsNull()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002613 return JSObject::cast(pt)->
2614 GetPropertyAttributeWithReceiver(receiver, name);
2615 }
2616 }
2617 return ABSENT;
2618}
2619
2620
2621PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
2622 JSObject* receiver,
2623 String* name,
2624 bool continue_search) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002625 Isolate* isolate = GetIsolate();
2626
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002627 // Make sure that the top context does not change when doing
2628 // callbacks or interceptor calls.
2629 AssertNoContextChange ncc;
2630
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002631 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002632 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
2633 Handle<JSObject> receiver_handle(receiver);
2634 Handle<JSObject> holder_handle(this);
2635 Handle<String> name_handle(name);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002636 CustomArguments args(isolate, interceptor->data(), receiver, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00002637 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002638 if (!interceptor->query()->IsUndefined()) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00002639 v8::NamedPropertyQuery query =
2640 v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002641 LOG(isolate,
2642 ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00002643 v8::Handle<v8::Integer> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002644 {
2645 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002646 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002647 result = query(v8::Utils::ToLocal(name_handle), info);
2648 }
2649 if (!result.IsEmpty()) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00002650 ASSERT(result->IsInt32());
2651 return static_cast<PropertyAttributes>(result->Int32Value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002652 }
2653 } else if (!interceptor->getter()->IsUndefined()) {
2654 v8::NamedPropertyGetter getter =
2655 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002656 LOG(isolate,
2657 ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002658 v8::Handle<v8::Value> result;
2659 {
2660 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002661 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002662 result = getter(v8::Utils::ToLocal(name_handle), info);
2663 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002664 if (!result.IsEmpty()) return DONT_ENUM;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002665 }
2666 return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
2667 *name_handle,
2668 continue_search);
2669}
2670
2671
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002672PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver(
2673 JSReceiver* receiver,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002674 String* key) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002675 uint32_t index = 0;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002676 if (IsJSObject() && key->AsArrayIndex(&index)) {
2677 if (JSObject::cast(this)->HasElementWithReceiver(receiver, index))
2678 return NONE;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002679 return ABSENT;
2680 }
2681 // Named property.
2682 LookupResult result;
2683 Lookup(key, &result);
2684 return GetPropertyAttribute(receiver, &result, key, true);
2685}
2686
2687
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002688PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver,
2689 LookupResult* result,
2690 String* name,
2691 bool continue_search) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002692 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002693 if (IsAccessCheckNeeded()) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002694 JSObject* this_obj = JSObject::cast(this);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002695 Heap* heap = GetHeap();
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002696 if (!heap->isolate()->MayNamedAccess(this_obj, name, v8::ACCESS_HAS)) {
2697 return this_obj->GetPropertyAttributeWithFailedAccessCheck(
2698 receiver, result, name, continue_search);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002699 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002700 }
ager@chromium.org5c838252010-02-19 08:53:10 +00002701 if (result->IsProperty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002702 switch (result->type()) {
2703 case NORMAL: // fall through
2704 case FIELD:
2705 case CONSTANT_FUNCTION:
2706 case CALLBACKS:
2707 return result->GetAttributes();
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002708 case HANDLER: {
2709 // TODO(rossberg): propagate exceptions properly.
2710 bool has_exception = false;
2711 return JSProxy::cast(this)->GetPropertyAttributeWithHandler(
2712 receiver, name, &has_exception);
2713 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002714 case INTERCEPTOR:
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002715 return result->holder()->GetPropertyAttributeWithInterceptor(
2716 JSObject::cast(receiver), name, continue_search);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002717 default:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002718 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002719 }
2720 }
2721 return ABSENT;
2722}
2723
2724
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002725PropertyAttributes JSReceiver::GetLocalPropertyAttribute(String* name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002726 // Check whether the name is an array index.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002727 uint32_t index = 0;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002728 if (IsJSObject() && name->AsArrayIndex(&index)) {
2729 if (JSObject::cast(this)->HasLocalElement(index)) return NONE;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002730 return ABSENT;
2731 }
2732 // Named property.
2733 LookupResult result;
2734 LocalLookup(name, &result);
2735 return GetPropertyAttribute(this, &result, name, false);
2736}
2737
2738
lrn@chromium.org303ada72010-10-27 09:33:13 +00002739MaybeObject* NormalizedMapCache::Get(JSObject* obj,
2740 PropertyNormalizationMode mode) {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002741 Isolate* isolate = obj->GetIsolate();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002742 Map* fast = obj->map();
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00002743 int index = fast->Hash() % kEntries;
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002744 Object* result = get(index);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00002745 if (result->IsMap() &&
2746 Map::cast(result)->EquivalentToForNormalization(fast, mode)) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002747#ifdef DEBUG
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00002748 Map::cast(result)->SharedMapVerify();
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002749 if (FLAG_enable_slow_asserts) {
2750 // The cached map should match newly created normalized map bit-by-bit.
lrn@chromium.org303ada72010-10-27 09:33:13 +00002751 Object* fresh;
2752 { MaybeObject* maybe_fresh =
2753 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2754 if (maybe_fresh->ToObject(&fresh)) {
2755 ASSERT(memcmp(Map::cast(fresh)->address(),
2756 Map::cast(result)->address(),
2757 Map::kSize) == 0);
2758 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002759 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002760 }
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002761#endif
2762 return result;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002763 }
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002764
lrn@chromium.org303ada72010-10-27 09:33:13 +00002765 { MaybeObject* maybe_result =
2766 fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
2767 if (!maybe_result->ToObject(&result)) return maybe_result;
2768 }
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002769 set(index, result);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002770 isolate->counters()->normalized_maps()->Increment();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002771
2772 return result;
2773}
2774
2775
ricow@chromium.org65fae842010-08-25 15:26:24 +00002776void NormalizedMapCache::Clear() {
2777 int entries = length();
2778 for (int i = 0; i != entries; i++) {
2779 set_undefined(i);
2780 }
2781}
2782
2783
lrn@chromium.org303ada72010-10-27 09:33:13 +00002784MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00002785 if (map()->is_shared()) {
2786 // Fast case maps are never marked as shared.
2787 ASSERT(!HasFastProperties());
2788 // Replace the map with an identical copy that can be safely modified.
lrn@chromium.org303ada72010-10-27 09:33:13 +00002789 Object* obj;
2790 { MaybeObject* maybe_obj = map()->CopyNormalized(KEEP_INOBJECT_PROPERTIES,
2791 UNIQUE_NORMALIZED_MAP);
2792 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2793 }
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002794 GetIsolate()->counters()->normalized_maps()->Increment();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002795
2796 set_map(Map::cast(obj));
2797 }
2798 return map()->UpdateCodeCache(name, code);
2799}
2800
2801
lrn@chromium.org303ada72010-10-27 09:33:13 +00002802MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
2803 int expected_additional_properties) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002804 if (!HasFastProperties()) return this;
2805
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002806 // The global object is always normalized.
2807 ASSERT(!IsGlobalObject());
ricow@chromium.org83aa5492011-02-07 12:42:56 +00002808 // JSGlobalProxy must never be normalized
2809 ASSERT(!IsJSGlobalProxy());
2810
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002811 Map* map_of_this = map();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002812
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002813 // Allocate new content.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002814 int property_count = map_of_this->NumberOfDescribedProperties();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002815 if (expected_additional_properties > 0) {
2816 property_count += expected_additional_properties;
2817 } else {
2818 property_count += 2; // Make space for two more properties.
2819 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00002820 Object* obj;
2821 { MaybeObject* maybe_obj =
2822 StringDictionary::Allocate(property_count);
2823 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2824 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002825 StringDictionary* dictionary = StringDictionary::cast(obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002826
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002827 DescriptorArray* descs = map_of_this->instance_descriptors();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002828 for (int i = 0; i < descs->number_of_descriptors(); i++) {
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00002829 PropertyDetails details(descs->GetDetails(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002830 switch (details.type()) {
2831 case CONSTANT_FUNCTION: {
2832 PropertyDetails d =
2833 PropertyDetails(details.attributes(), NORMAL, details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002834 Object* value = descs->GetConstantFunction(i);
lrn@chromium.org303ada72010-10-27 09:33:13 +00002835 Object* result;
2836 { MaybeObject* maybe_result =
2837 dictionary->Add(descs->GetKey(i), value, d);
2838 if (!maybe_result->ToObject(&result)) return maybe_result;
2839 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002840 dictionary = StringDictionary::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002841 break;
2842 }
2843 case FIELD: {
2844 PropertyDetails d =
2845 PropertyDetails(details.attributes(), NORMAL, details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002846 Object* value = FastPropertyAt(descs->GetFieldIndex(i));
lrn@chromium.org303ada72010-10-27 09:33:13 +00002847 Object* result;
2848 { MaybeObject* maybe_result =
2849 dictionary->Add(descs->GetKey(i), value, d);
2850 if (!maybe_result->ToObject(&result)) return maybe_result;
2851 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002852 dictionary = StringDictionary::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002853 break;
2854 }
2855 case CALLBACKS: {
2856 PropertyDetails d =
2857 PropertyDetails(details.attributes(), CALLBACKS, details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002858 Object* value = descs->GetCallbacksObject(i);
lrn@chromium.org303ada72010-10-27 09:33:13 +00002859 Object* result;
2860 { MaybeObject* maybe_result =
2861 dictionary->Add(descs->GetKey(i), value, d);
2862 if (!maybe_result->ToObject(&result)) return maybe_result;
2863 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002864 dictionary = StringDictionary::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002865 break;
2866 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002867 case MAP_TRANSITION:
2868 case CONSTANT_TRANSITION:
2869 case NULL_DESCRIPTOR:
2870 case INTERCEPTOR:
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002871 case EXTERNAL_ARRAY_TRANSITION:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002872 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002873 default:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002874 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002875 }
2876 }
2877
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002878 Heap* current_heap = map_of_this->heap();
2879
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002880 // Copy the next enumeration index from instance descriptor.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002881 int index = map_of_this->instance_descriptors()->NextEnumerationIndex();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002882 dictionary->SetNextEnumerationIndex(index);
2883
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002884 { MaybeObject* maybe_obj =
2885 current_heap->isolate()->context()->global_context()->
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002886 normalized_map_cache()->Get(this, mode);
lrn@chromium.org303ada72010-10-27 09:33:13 +00002887 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
2888 }
ager@chromium.org32912102009-01-16 10:38:43 +00002889 Map* new_map = Map::cast(obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002890
ager@chromium.org32912102009-01-16 10:38:43 +00002891 // We have now successfully allocated all the necessary objects.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002892 // Changes can now be made with the guarantee that all of them take effect.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002893
2894 // Resize the object in the heap if necessary.
2895 int new_instance_size = new_map->instance_size();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002896 int instance_size_delta = map_of_this->instance_size() - new_instance_size;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002897 ASSERT(instance_size_delta >= 0);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002898 current_heap->CreateFillerObjectAt(this->address() + new_instance_size,
2899 instance_size_delta);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002900
ager@chromium.org32912102009-01-16 10:38:43 +00002901 set_map(new_map);
danno@chromium.org40cb8782011-05-25 07:58:50 +00002902 new_map->clear_instance_descriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002903
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002904 set_properties(dictionary);
2905
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002906 current_heap->isolate()->counters()->props_to_dictionary()->Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002907
2908#ifdef DEBUG
2909 if (FLAG_trace_normalization) {
2910 PrintF("Object properties have been normalized:\n");
2911 Print();
2912 }
2913#endif
2914 return this;
2915}
2916
2917
lrn@chromium.org303ada72010-10-27 09:33:13 +00002918MaybeObject* JSObject::TransformToFastProperties(int unused_property_fields) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002919 if (HasFastProperties()) return this;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002920 ASSERT(!IsGlobalObject());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002921 return property_dictionary()->
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002922 TransformPropertiesToFastFor(this, unused_property_fields);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002923}
2924
2925
lrn@chromium.org303ada72010-10-27 09:33:13 +00002926MaybeObject* JSObject::NormalizeElements() {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002927 ASSERT(!HasExternalArrayElements());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002928
whesse@chromium.org7b260152011-06-20 15:33:18 +00002929 // Find the backing store.
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002930 FixedArrayBase* array = FixedArrayBase::cast(elements());
whesse@chromium.org7b260152011-06-20 15:33:18 +00002931 Map* old_map = array->map();
2932 bool is_arguments =
2933 (old_map == old_map->heap()->non_strict_arguments_elements_map());
2934 if (is_arguments) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002935 array = FixedArrayBase::cast(FixedArray::cast(array)->get(1));
lrn@chromium.org303ada72010-10-27 09:33:13 +00002936 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002937 if (array->IsDictionary()) return array;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002938
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002939 ASSERT(HasFastElements() ||
2940 HasFastDoubleElements() ||
2941 HasFastArgumentsElements());
whesse@chromium.org7b260152011-06-20 15:33:18 +00002942 // Compute the effective length and allocate a new backing store.
2943 int length = IsJSArray()
2944 ? Smi::cast(JSArray::cast(this)->length())->value()
2945 : array->length();
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002946 int old_capacity = 0;
2947 int used_elements = 0;
2948 GetElementsCapacityAndUsage(&old_capacity, &used_elements);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002949 NumberDictionary* dictionary = NULL;
2950 { Object* object;
ricow@chromium.org2c99e282011-07-28 09:15:17 +00002951 MaybeObject* maybe = NumberDictionary::Allocate(used_elements);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002952 if (!maybe->ToObject(&object)) return maybe;
2953 dictionary = NumberDictionary::cast(object);
lrn@chromium.org303ada72010-10-27 09:33:13 +00002954 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002955
2956 // Copy the elements to the new backing store.
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002957 bool has_double_elements = array->IsFixedDoubleArray();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002958 for (int i = 0; i < length; i++) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002959 Object* value = NULL;
2960 if (has_double_elements) {
2961 FixedDoubleArray* double_array = FixedDoubleArray::cast(array);
2962 if (double_array->is_the_hole(i)) {
2963 value = GetIsolate()->heap()->the_hole_value();
2964 } else {
2965 // Objects must be allocated in the old object space, since the
2966 // overall number of HeapNumbers needed for the conversion might
2967 // exceed the capacity of new space, and we would fail repeatedly
2968 // trying to convert the FixedDoubleArray.
2969 MaybeObject* maybe_value_object =
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00002970 GetHeap()->AllocateHeapNumber(double_array->get_scalar(i), TENURED);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002971 if (!maybe_value_object->ToObject(&value)) return maybe_value_object;
lrn@chromium.org303ada72010-10-27 09:33:13 +00002972 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002973 } else {
2974 ASSERT(old_map->has_fast_elements());
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00002975 value = FixedArray::cast(array)->get(i);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00002976 }
2977 PropertyDetails details = PropertyDetails(NONE, NORMAL);
2978 if (!value->IsTheHole()) {
2979 Object* result;
2980 MaybeObject* maybe_result =
2981 dictionary->AddNumberEntry(i, value, details);
2982 if (!maybe_result->ToObject(&result)) return maybe_result;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002983 dictionary = NumberDictionary::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002984 }
2985 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002986
whesse@chromium.org7b260152011-06-20 15:33:18 +00002987 // Switch to using the dictionary as the backing storage for elements.
2988 if (is_arguments) {
2989 FixedArray::cast(elements())->set(1, dictionary);
2990 } else {
2991 // Set the new map first to satify the elements type assert in
2992 // set_elements().
2993 Object* new_map;
2994 MaybeObject* maybe = map()->GetSlowElementsMap();
2995 if (!maybe->ToObject(&new_map)) return maybe;
2996 set_map(Map::cast(new_map));
2997 set_elements(dictionary);
2998 }
2999
3000 old_map->isolate()->counters()->elements_to_dictionary()->Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003001
3002#ifdef DEBUG
3003 if (FLAG_trace_normalization) {
3004 PrintF("Object elements have been normalized:\n");
3005 Print();
3006 }
3007#endif
3008
whesse@chromium.org7b260152011-06-20 15:33:18 +00003009 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
3010 return dictionary;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003011}
3012
3013
vegorov@chromium.org7943d462011-08-01 11:41:52 +00003014MaybeObject* JSObject::GetHiddenProperties(HiddenPropertiesFlag flag) {
3015 Isolate* isolate = GetIsolate();
3016 Heap* heap = isolate->heap();
3017 Object* holder = BypassGlobalProxy();
3018 if (holder->IsUndefined()) return heap->undefined_value();
3019 JSObject* obj = JSObject::cast(holder);
3020 if (obj->HasFastProperties()) {
3021 // If the object has fast properties, check whether the first slot
3022 // in the descriptor array matches the hidden symbol. Since the
3023 // hidden symbols hash code is zero (and no other string has hash
3024 // code zero) it will always occupy the first entry if present.
3025 DescriptorArray* descriptors = obj->map()->instance_descriptors();
3026 if ((descriptors->number_of_descriptors() > 0) &&
3027 (descriptors->GetKey(0) == heap->hidden_symbol()) &&
3028 descriptors->IsProperty(0)) {
3029 ASSERT(descriptors->GetType(0) == FIELD);
3030 return obj->FastPropertyAt(descriptors->GetFieldIndex(0));
3031 }
3032 }
3033
3034 // Only attempt to find the hidden properties in the local object and not
3035 // in the prototype chain.
3036 if (!obj->HasHiddenPropertiesObject()) {
3037 // Hidden properties object not found. Allocate a new hidden properties
3038 // object if requested. Otherwise return the undefined value.
3039 if (flag == ALLOW_CREATION) {
3040 Object* hidden_obj;
3041 { MaybeObject* maybe_obj = heap->AllocateJSObject(
3042 isolate->context()->global_context()->object_function());
3043 if (!maybe_obj->ToObject(&hidden_obj)) return maybe_obj;
3044 }
3045 return obj->SetHiddenPropertiesObject(hidden_obj);
3046 } else {
3047 return heap->undefined_value();
3048 }
3049 }
3050 return obj->GetHiddenPropertiesObject();
3051}
3052
3053
3054MaybeObject* JSObject::GetIdentityHash(HiddenPropertiesFlag flag) {
3055 Isolate* isolate = GetIsolate();
3056 Object* hidden_props_obj;
3057 { MaybeObject* maybe_obj = GetHiddenProperties(flag);
3058 if (!maybe_obj->ToObject(&hidden_props_obj)) return maybe_obj;
3059 }
3060 if (!hidden_props_obj->IsJSObject()) {
3061 // We failed to create hidden properties. That's a detached
3062 // global proxy.
3063 ASSERT(hidden_props_obj->IsUndefined());
3064 return Smi::FromInt(0);
3065 }
3066 JSObject* hidden_props = JSObject::cast(hidden_props_obj);
3067 String* hash_symbol = isolate->heap()->identity_hash_symbol();
3068 {
3069 // Note that HasLocalProperty() can cause a GC in the general case in the
3070 // presence of interceptors.
3071 AssertNoAllocation no_alloc;
3072 if (hidden_props->HasLocalProperty(hash_symbol)) {
3073 MaybeObject* hash = hidden_props->GetProperty(hash_symbol);
3074 return Smi::cast(hash->ToObjectChecked());
3075 }
3076 }
3077
3078 int hash_value;
3079 int attempts = 0;
3080 do {
3081 // Generate a random 32-bit hash value but limit range to fit
3082 // within a smi.
3083 hash_value = V8::Random(isolate) & Smi::kMaxValue;
3084 attempts++;
3085 } while (hash_value == 0 && attempts < 30);
3086 hash_value = hash_value != 0 ? hash_value : 1; // never return 0
3087
3088 Smi* hash = Smi::FromInt(hash_value);
3089 { MaybeObject* result = hidden_props->SetLocalPropertyIgnoreAttributes(
3090 hash_symbol,
3091 hash,
3092 static_cast<PropertyAttributes>(None));
3093 if (result->IsFailure()) return result;
3094 }
3095 return hash;
3096}
3097
3098
lrn@chromium.org303ada72010-10-27 09:33:13 +00003099MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name,
3100 DeleteMode mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003101 // Check local property, ignore interceptor.
3102 LookupResult result;
3103 LocalLookupRealNamedProperty(name, &result);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003104 if (!result.IsProperty()) return GetHeap()->true_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003105
3106 // Normalize object if needed.
lrn@chromium.org303ada72010-10-27 09:33:13 +00003107 Object* obj;
3108 { MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3109 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3110 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003111
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003112 return DeleteNormalizedProperty(name, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003113}
3114
3115
lrn@chromium.org303ada72010-10-27 09:33:13 +00003116MaybeObject* JSObject::DeletePropertyWithInterceptor(String* name) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003117 Isolate* isolate = GetIsolate();
3118 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003119 Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
3120 Handle<String> name_handle(name);
3121 Handle<JSObject> this_handle(this);
3122 if (!interceptor->deleter()->IsUndefined()) {
3123 v8::NamedPropertyDeleter deleter =
3124 v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003125 LOG(isolate,
3126 ApiNamedPropertyAccess("interceptor-named-delete", *this_handle, name));
3127 CustomArguments args(isolate, interceptor->data(), this, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003128 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003129 v8::Handle<v8::Boolean> result;
3130 {
3131 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003132 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003133 result = deleter(v8::Utils::ToLocal(name_handle), info);
3134 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003135 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003136 if (!result.IsEmpty()) {
3137 ASSERT(result->IsBoolean());
3138 return *v8::Utils::OpenHandle(*result);
3139 }
3140 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00003141 MaybeObject* raw_result =
ager@chromium.orge2902be2009-06-08 12:21:35 +00003142 this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003143 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003144 return raw_result;
3145}
3146
3147
lrn@chromium.org303ada72010-10-27 09:33:13 +00003148MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003149 Isolate* isolate = GetIsolate();
3150 Heap* heap = isolate->heap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003151 // Make sure that the top context does not change when doing
3152 // callbacks or interceptor calls.
3153 AssertNoContextChange ncc;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003154 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003155 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003156 if (interceptor->deleter()->IsUndefined()) return heap->false_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003157 v8::IndexedPropertyDeleter deleter =
3158 v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
3159 Handle<JSObject> this_handle(this);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003160 LOG(isolate,
3161 ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
3162 CustomArguments args(isolate, interceptor->data(), this, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003163 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003164 v8::Handle<v8::Boolean> result;
3165 {
3166 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003167 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003168 result = deleter(index, info);
3169 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003170 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003171 if (!result.IsEmpty()) {
3172 ASSERT(result->IsBoolean());
3173 return *v8::Utils::OpenHandle(*result);
3174 }
danno@chromium.orgb6451162011-08-17 14:33:23 +00003175 MaybeObject* raw_result = this_handle->GetElementsAccessor()->Delete(
3176 *this_handle,
3177 index,
3178 NORMAL_DELETION);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003179 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003180 return raw_result;
3181}
3182
3183
lrn@chromium.org303ada72010-10-27 09:33:13 +00003184MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003185 Isolate* isolate = GetIsolate();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003186 // Check access rights if needed.
3187 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003188 !isolate->MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
3189 isolate->ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
3190 return isolate->heap()->false_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003191 }
3192
3193 if (IsJSGlobalProxy()) {
3194 Object* proto = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003195 if (proto->IsNull()) return isolate->heap()->false_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003196 ASSERT(proto->IsJSGlobalObject());
ager@chromium.orge2902be2009-06-08 12:21:35 +00003197 return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003198 }
3199
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003200 if (HasIndexedInterceptor()) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00003201 // Skip interceptor if forcing deletion.
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00003202 if (mode != FORCE_DELETION) {
3203 return DeleteElementWithInterceptor(index);
3204 }
3205 mode = JSReceiver::FORCE_DELETION;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003206 }
3207
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00003208 return GetElementsAccessor()->Delete(this, index, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003209}
3210
3211
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00003212MaybeObject* JSReceiver::DeleteProperty(String* name, DeleteMode mode) {
3213 if (IsJSProxy()) {
3214 return JSProxy::cast(this)->DeletePropertyWithHandler(name, mode);
3215 } else {
3216 return JSObject::cast(this)->DeleteProperty(name, mode);
3217 }
3218}
3219
3220
lrn@chromium.org303ada72010-10-27 09:33:13 +00003221MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003222 Isolate* isolate = GetIsolate();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003223 // ECMA-262, 3rd, 8.6.2.5
3224 ASSERT(name->IsString());
3225
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003226 // Check access rights if needed.
3227 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003228 !isolate->MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
3229 isolate->ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
3230 return isolate->heap()->false_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003231 }
3232
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003233 if (IsJSGlobalProxy()) {
3234 Object* proto = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003235 if (proto->IsNull()) return isolate->heap()->false_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003236 ASSERT(proto->IsJSGlobalObject());
ager@chromium.orge2902be2009-06-08 12:21:35 +00003237 return JSGlobalObject::cast(proto)->DeleteProperty(name, mode);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003238 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003239
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003240 uint32_t index = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003241 if (name->AsArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00003242 return DeleteElement(index, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003243 } else {
3244 LookupResult result;
3245 LocalLookup(name, &result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003246 if (!result.IsProperty()) return isolate->heap()->true_value();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003247 // Ignore attributes if forcing a deletion.
3248 if (result.IsDontDelete() && mode != FORCE_DELETION) {
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003249 if (mode == STRICT_DELETION) {
3250 // Deleting a non-configurable property in strict mode.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003251 HandleScope scope(isolate);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003252 Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) };
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003253 return isolate->Throw(*isolate->factory()->NewTypeError(
3254 "strict_delete_property", HandleVector(args, 2)));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003255 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003256 return isolate->heap()->false_value();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003257 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003258 // Check for interceptor.
3259 if (result.type() == INTERCEPTOR) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00003260 // Skip interceptor if forcing a deletion.
3261 if (mode == FORCE_DELETION) {
3262 return DeletePropertyPostInterceptor(name, mode);
3263 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003264 return DeletePropertyWithInterceptor(name);
3265 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003266 // Normalize object if needed.
lrn@chromium.org303ada72010-10-27 09:33:13 +00003267 Object* obj;
3268 { MaybeObject* maybe_obj =
3269 NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3270 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
3271 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003272 // Make sure the properties are normalized before removing the entry.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003273 return DeleteNormalizedProperty(name, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003274 }
3275}
3276
3277
whesse@chromium.org7b260152011-06-20 15:33:18 +00003278bool JSObject::ReferencesObjectFromElements(FixedArray* elements,
3279 ElementsKind kind,
3280 Object* object) {
3281 ASSERT(kind == FAST_ELEMENTS || kind == DICTIONARY_ELEMENTS);
3282 if (kind == FAST_ELEMENTS) {
3283 int length = IsJSArray()
3284 ? Smi::cast(JSArray::cast(this)->length())->value()
3285 : elements->length();
3286 for (int i = 0; i < length; ++i) {
3287 Object* element = elements->get(i);
3288 if (!element->IsTheHole() && element == object) return true;
3289 }
3290 } else {
3291 Object* key = NumberDictionary::cast(elements)->SlowReverseLookup(object);
3292 if (!key->IsUndefined()) return true;
3293 }
3294 return false;
3295}
3296
3297
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003298// Check whether this object references another object.
3299bool JSObject::ReferencesObject(Object* obj) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003300 Map* map_of_this = map();
3301 Heap* heap = map_of_this->heap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003302 AssertNoAllocation no_alloc;
3303
3304 // Is the object the constructor for this object?
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003305 if (map_of_this->constructor() == obj) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003306 return true;
3307 }
3308
3309 // Is the object the prototype for this object?
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003310 if (map_of_this->prototype() == obj) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003311 return true;
3312 }
3313
3314 // Check if the object is among the named properties.
3315 Object* key = SlowReverseLookup(obj);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003316 if (!key->IsUndefined()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003317 return true;
3318 }
3319
3320 // Check if the object is among the indexed properties.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003321 ElementsKind kind = GetElementsKind();
3322 switch (kind) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003323 case EXTERNAL_PIXEL_ELEMENTS:
ager@chromium.org3811b432009-10-28 14:53:37 +00003324 case EXTERNAL_BYTE_ELEMENTS:
3325 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3326 case EXTERNAL_SHORT_ELEMENTS:
3327 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3328 case EXTERNAL_INT_ELEMENTS:
3329 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3330 case EXTERNAL_FLOAT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003331 case EXTERNAL_DOUBLE_ELEMENTS:
whesse@chromium.org7b260152011-06-20 15:33:18 +00003332 case FAST_DOUBLE_ELEMENTS:
ager@chromium.org3811b432009-10-28 14:53:37 +00003333 // Raw pixels and external arrays do not reference other
3334 // objects.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003335 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003336 case FAST_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003337 case DICTIONARY_ELEMENTS: {
whesse@chromium.org7b260152011-06-20 15:33:18 +00003338 FixedArray* elements = FixedArray::cast(this->elements());
3339 if (ReferencesObjectFromElements(elements, kind, obj)) return true;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003340 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003341 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00003342 case NON_STRICT_ARGUMENTS_ELEMENTS: {
3343 FixedArray* parameter_map = FixedArray::cast(elements());
3344 // Check the mapped parameters.
3345 int length = parameter_map->length();
3346 for (int i = 2; i < length; ++i) {
3347 Object* value = parameter_map->get(i);
3348 if (!value->IsTheHole() && value == obj) return true;
3349 }
3350 // Check the arguments.
3351 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
3352 kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : FAST_ELEMENTS;
3353 if (ReferencesObjectFromElements(arguments, kind, obj)) return true;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003354 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003355 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003356 }
3357
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00003358 // For functions check the context.
3359 if (IsJSFunction()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003360 // Get the constructor function for arguments array.
3361 JSObject* arguments_boilerplate =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003362 heap->isolate()->context()->global_context()->
3363 arguments_boilerplate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003364 JSFunction* arguments_function =
3365 JSFunction::cast(arguments_boilerplate->map()->constructor());
3366
3367 // Get the context and don't check if it is the global context.
3368 JSFunction* f = JSFunction::cast(this);
3369 Context* context = f->context();
3370 if (context->IsGlobalContext()) {
3371 return false;
3372 }
3373
3374 // Check the non-special context slots.
3375 for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
3376 // Only check JS objects.
3377 if (context->get(i)->IsJSObject()) {
3378 JSObject* ctxobj = JSObject::cast(context->get(i));
3379 // If it is an arguments array check the content.
3380 if (ctxobj->map()->constructor() == arguments_function) {
3381 if (ctxobj->ReferencesObject(obj)) {
3382 return true;
3383 }
3384 } else if (ctxobj == obj) {
3385 return true;
3386 }
3387 }
3388 }
3389
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00003390 // Check the context extension (if any) if it can have references.
3391 if (context->has_extension() && !context->IsCatchContext()) {
3392 return JSObject::cast(context->extension())->ReferencesObject(obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003393 }
3394 }
3395
3396 // No references to object.
3397 return false;
3398}
3399
3400
lrn@chromium.org303ada72010-10-27 09:33:13 +00003401MaybeObject* JSObject::PreventExtensions() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003402 Isolate* isolate = GetIsolate();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003403 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003404 !isolate->MayNamedAccess(this,
3405 isolate->heap()->undefined_value(),
3406 v8::ACCESS_KEYS)) {
3407 isolate->ReportFailedAccessCheck(this, v8::ACCESS_KEYS);
3408 return isolate->heap()->false_value();
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00003409 }
3410
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00003411 if (IsJSGlobalProxy()) {
3412 Object* proto = GetPrototype();
3413 if (proto->IsNull()) return this;
3414 ASSERT(proto->IsJSGlobalObject());
3415 return JSObject::cast(proto)->PreventExtensions();
3416 }
3417
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003418 // If there are fast elements we normalize.
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00003419 NumberDictionary* dictionary = NULL;
3420 { MaybeObject* maybe = NormalizeElements();
3421 if (!maybe->To<NumberDictionary>(&dictionary)) return maybe;
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003422 }
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00003423 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003424 // Make sure that we never go back to fast case.
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00003425 dictionary->set_requires_slow_elements();
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003426
3427 // Do a map transition, other objects with this map may still
3428 // be extensible.
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00003429 Map* new_map;
3430 { MaybeObject* maybe = map()->CopyDropTransitions();
3431 if (!maybe->To<Map>(&new_map)) return maybe;
lrn@chromium.org303ada72010-10-27 09:33:13 +00003432 }
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00003433 new_map->set_is_extensible(false);
3434 set_map(new_map);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00003435 ASSERT(!map()->is_extensible());
3436 return new_map;
3437}
3438
3439
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003440// Tests for the fast common case for property enumeration:
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003441// - This object and all prototypes has an enum cache (which means that it has
3442// no interceptors and needs no access checks).
3443// - This object has no elements.
3444// - No prototype has enumerable properties/elements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003445bool JSObject::IsSimpleEnum() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003446 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003447 for (Object* o = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003448 o != heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003449 o = JSObject::cast(o)->GetPrototype()) {
3450 JSObject* curr = JSObject::cast(o);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003451 if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003452 ASSERT(!curr->HasNamedInterceptor());
3453 ASSERT(!curr->HasIndexedInterceptor());
3454 ASSERT(!curr->IsAccessCheckNeeded());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003455 if (curr->NumberOfEnumElements() > 0) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003456 if (curr != this) {
3457 FixedArray* curr_fixed_array =
3458 FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003459 if (curr_fixed_array->length() > 0) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003460 }
3461 }
3462 return true;
3463}
3464
3465
3466int Map::NumberOfDescribedProperties() {
3467 int result = 0;
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003468 DescriptorArray* descs = instance_descriptors();
3469 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3470 if (descs->IsProperty(i)) result++;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003471 }
3472 return result;
3473}
3474
3475
3476int Map::PropertyIndexFor(String* name) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003477 DescriptorArray* descs = instance_descriptors();
3478 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3479 if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
3480 return descs->GetFieldIndex(i);
3481 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003482 }
3483 return -1;
3484}
3485
3486
3487int Map::NextFreePropertyIndex() {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003488 int max_index = -1;
3489 DescriptorArray* descs = instance_descriptors();
3490 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3491 if (descs->GetType(i) == FIELD) {
3492 int current_index = descs->GetFieldIndex(i);
3493 if (current_index > max_index) max_index = current_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003494 }
3495 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003496 return max_index + 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003497}
3498
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003499
3500AccessorDescriptor* Map::FindAccessor(String* name) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003501 DescriptorArray* descs = instance_descriptors();
3502 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3503 if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
3504 return descs->GetCallbacks(i);
3505 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003506 }
3507 return NULL;
3508}
3509
3510
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003511void JSReceiver::LocalLookup(String* name, LookupResult* result) {
3512 if (IsJSProxy()) {
3513 result->HandlerResult();
3514 } else {
3515 JSObject::cast(this)->LocalLookup(name, result);
3516 }
3517}
3518
3519
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003520void JSObject::LocalLookup(String* name, LookupResult* result) {
3521 ASSERT(name->IsString());
3522
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003523 Heap* heap = GetHeap();
3524
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003525 if (IsJSGlobalProxy()) {
3526 Object* proto = GetPrototype();
3527 if (proto->IsNull()) return result->NotFound();
3528 ASSERT(proto->IsJSGlobalObject());
3529 return JSObject::cast(proto)->LocalLookup(name, result);
3530 }
3531
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003532 // Do not use inline caching if the object is a non-global object
3533 // that requires access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003534 if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003535 result->DisallowCaching();
3536 }
3537
3538 // Check __proto__ before interceptor.
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00003539 if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003540 result->ConstantResult(this);
3541 return;
3542 }
3543
3544 // Check for lookup interceptor except when bootstrapping.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003545 if (HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003546 result->InterceptorResult(this);
3547 return;
3548 }
3549
3550 LocalLookupRealNamedProperty(name, result);
3551}
3552
3553
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003554void JSReceiver::Lookup(String* name, LookupResult* result) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003555 // Ecma-262 3rd 8.6.2.4
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003556 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003557 for (Object* current = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003558 current != heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003559 current = JSObject::cast(current)->GetPrototype()) {
3560 JSObject::cast(current)->LocalLookup(name, result);
ager@chromium.org5c838252010-02-19 08:53:10 +00003561 if (result->IsProperty()) return;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003562 }
3563 result->NotFound();
3564}
3565
3566
3567// Search object and it's prototype chain for callback properties.
3568void JSObject::LookupCallback(String* name, LookupResult* result) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003569 Heap* heap = GetHeap();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003570 for (Object* current = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003571 current != heap->null_value();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003572 current = JSObject::cast(current)->GetPrototype()) {
3573 JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
ager@chromium.org5c838252010-02-19 08:53:10 +00003574 if (result->IsProperty() && result->type() == CALLBACKS) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003575 }
3576 result->NotFound();
3577}
3578
3579
whesse@chromium.org7b260152011-06-20 15:33:18 +00003580// Search for a getter or setter in an elements dictionary. Returns either
3581// undefined if the element is read-only, or the getter/setter pair (fixed
3582// array) if there is an existing one, or the hole value if the element does
3583// not exist or is a normal non-getter/setter data element.
3584static Object* FindGetterSetterInDictionary(NumberDictionary* dictionary,
3585 uint32_t index,
3586 Heap* heap) {
3587 int entry = dictionary->FindEntry(index);
3588 if (entry != NumberDictionary::kNotFound) {
3589 Object* result = dictionary->ValueAt(entry);
3590 PropertyDetails details = dictionary->DetailsAt(entry);
3591 if (details.IsReadOnly()) return heap->undefined_value();
3592 if (details.type() == CALLBACKS && result->IsFixedArray()) return result;
3593 }
3594 return heap->the_hole_value();
3595}
3596
3597
lrn@chromium.org303ada72010-10-27 09:33:13 +00003598MaybeObject* JSObject::DefineGetterSetter(String* name,
3599 PropertyAttributes attributes) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003600 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003601 // Make sure that the top context does not change when doing callbacks or
3602 // interceptor calls.
3603 AssertNoContextChange ncc;
3604
ager@chromium.orgddb913d2009-01-27 10:01:48 +00003605 // Try to flatten before operating on the string.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003606 name->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003607
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003608 if (!CanSetCallback(name)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003609 return heap->undefined_value();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003610 }
3611
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003612 uint32_t index = 0;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003613 bool is_element = name->AsArrayIndex(&index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003614
3615 if (is_element) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003616 switch (GetElementsKind()) {
3617 case FAST_ELEMENTS:
ricow@chromium.org9fa09672011-07-25 11:05:35 +00003618 case FAST_DOUBLE_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003619 break;
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003620 case EXTERNAL_PIXEL_ELEMENTS:
ager@chromium.org3811b432009-10-28 14:53:37 +00003621 case EXTERNAL_BYTE_ELEMENTS:
3622 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3623 case EXTERNAL_SHORT_ELEMENTS:
3624 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3625 case EXTERNAL_INT_ELEMENTS:
3626 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3627 case EXTERNAL_FLOAT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003628 case EXTERNAL_DOUBLE_ELEMENTS:
ager@chromium.org3811b432009-10-28 14:53:37 +00003629 // Ignore getters and setters on pixel and external array
3630 // elements.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003631 return heap->undefined_value();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003632 case DICTIONARY_ELEMENTS: {
whesse@chromium.org7b260152011-06-20 15:33:18 +00003633 Object* probe =
3634 FindGetterSetterInDictionary(element_dictionary(), index, heap);
3635 if (!probe->IsTheHole()) return probe;
3636 // Otherwise allow to override it.
3637 break;
3638 }
3639 case NON_STRICT_ARGUMENTS_ELEMENTS: {
3640 // Ascertain whether we have read-only properties or an existing
3641 // getter/setter pair in an arguments elements dictionary backing
3642 // store.
3643 FixedArray* parameter_map = FixedArray::cast(elements());
3644 uint32_t length = parameter_map->length();
3645 Object* probe =
whesse@chromium.orgdf8c03c2011-06-21 14:36:03 +00003646 index < (length - 2) ? parameter_map->get(index + 2) : NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003647 if (probe == NULL || probe->IsTheHole()) {
3648 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
3649 if (arguments->IsDictionary()) {
3650 NumberDictionary* dictionary = NumberDictionary::cast(arguments);
3651 probe = FindGetterSetterInDictionary(dictionary, index, heap);
3652 if (!probe->IsTheHole()) return probe;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003653 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003654 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003655 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003656 }
3657 }
3658 } else {
3659 // Lookup the name.
3660 LookupResult result;
3661 LocalLookup(name, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00003662 if (result.IsProperty()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003663 if (result.IsReadOnly()) return heap->undefined_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003664 if (result.type() == CALLBACKS) {
3665 Object* obj = result.GetCallbackObject();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003666 // Need to preserve old getters/setters.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003667 if (obj->IsFixedArray()) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003668 // Use set to update attributes.
3669 return SetPropertyCallback(name, obj, attributes);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003670 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003671 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003672 }
3673 }
3674
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003675 // Allocate the fixed array to hold getter and setter.
lrn@chromium.org303ada72010-10-27 09:33:13 +00003676 Object* structure;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003677 { MaybeObject* maybe_structure = heap->AllocateFixedArray(2, TENURED);
lrn@chromium.org303ada72010-10-27 09:33:13 +00003678 if (!maybe_structure->ToObject(&structure)) return maybe_structure;
3679 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003680
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003681 if (is_element) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003682 return SetElementCallback(index, structure, attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003683 } else {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003684 return SetPropertyCallback(name, structure, attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003685 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003686}
3687
3688
3689bool JSObject::CanSetCallback(String* name) {
3690 ASSERT(!IsAccessCheckNeeded()
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003691 || Isolate::Current()->MayNamedAccess(this, name, v8::ACCESS_SET));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003692
3693 // Check if there is an API defined callback object which prohibits
3694 // callback overwriting in this object or it's prototype chain.
3695 // This mechanism is needed for instance in a browser setting, where
3696 // certain accessors such as window.location should not be allowed
3697 // to be overwritten because allowing overwriting could potentially
3698 // cause security problems.
3699 LookupResult callback_result;
3700 LookupCallback(name, &callback_result);
3701 if (callback_result.IsProperty()) {
3702 Object* obj = callback_result.GetCallbackObject();
3703 if (obj->IsAccessorInfo() &&
3704 AccessorInfo::cast(obj)->prohibits_overwriting()) {
3705 return false;
3706 }
3707 }
3708
3709 return true;
3710}
3711
3712
lrn@chromium.org303ada72010-10-27 09:33:13 +00003713MaybeObject* JSObject::SetElementCallback(uint32_t index,
3714 Object* structure,
3715 PropertyAttributes attributes) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003716 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3717
3718 // Normalize elements to make this operation simple.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003719 NumberDictionary* dictionary = NULL;
3720 { Object* result;
3721 MaybeObject* maybe = NormalizeElements();
3722 if (!maybe->ToObject(&result)) return maybe;
3723 dictionary = NumberDictionary::cast(result);
lrn@chromium.org303ada72010-10-27 09:33:13 +00003724 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00003725 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003726
3727 // Update the dictionary with the new CALLBACKS property.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003728 { Object* result;
3729 MaybeObject* maybe = dictionary->Set(index, structure, details);
3730 if (!maybe->ToObject(&result)) return maybe;
3731 dictionary = NumberDictionary::cast(result);
lrn@chromium.org303ada72010-10-27 09:33:13 +00003732 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003733
whesse@chromium.org7b260152011-06-20 15:33:18 +00003734 dictionary->set_requires_slow_elements();
3735 // Update the dictionary backing store on the object.
3736 if (elements()->map() == GetHeap()->non_strict_arguments_elements_map()) {
3737 // Also delete any parameter alias.
3738 //
3739 // TODO(kmillikin): when deleting the last parameter alias we could
3740 // switch to a direct backing store without the parameter map. This
3741 // would allow GC of the context.
3742 FixedArray* parameter_map = FixedArray::cast(elements());
3743 uint32_t length = parameter_map->length();
whesse@chromium.orgdf8c03c2011-06-21 14:36:03 +00003744 if (index < length - 2) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00003745 parameter_map->set(index + 2, GetHeap()->the_hole_value());
3746 }
3747 parameter_map->set(1, dictionary);
3748 } else {
3749 set_elements(dictionary);
3750 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003751
3752 return structure;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003753}
3754
3755
lrn@chromium.org303ada72010-10-27 09:33:13 +00003756MaybeObject* JSObject::SetPropertyCallback(String* name,
3757 Object* structure,
3758 PropertyAttributes attributes) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003759 PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
3760
3761 bool convert_back_to_fast = HasFastProperties() &&
3762 (map()->instance_descriptors()->number_of_descriptors()
3763 < DescriptorArray::kMaxNumberOfDescriptors);
3764
3765 // Normalize object to make this operation simple.
lrn@chromium.org303ada72010-10-27 09:33:13 +00003766 Object* ok;
3767 { MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3768 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3769 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003770
3771 // For the global object allocate a new map to invalidate the global inline
3772 // caches which have a global property cell reference directly in the code.
3773 if (IsGlobalObject()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00003774 Object* new_map;
3775 { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
3776 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
3777 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003778 set_map(Map::cast(new_map));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003779 // When running crankshaft, changing the map is not enough. We
3780 // need to deoptimize all functions that rely on this global
3781 // object.
3782 Deoptimizer::DeoptimizeGlobalObject(this);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003783 }
3784
3785 // Update the dictionary with the new CALLBACKS property.
lrn@chromium.org303ada72010-10-27 09:33:13 +00003786 Object* result;
3787 { MaybeObject* maybe_result = SetNormalizedProperty(name, structure, details);
3788 if (!maybe_result->ToObject(&result)) return maybe_result;
3789 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003790
3791 if (convert_back_to_fast) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00003792 { MaybeObject* maybe_ok = TransformToFastProperties(0);
3793 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3794 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003795 }
3796 return result;
3797}
3798
lrn@chromium.org303ada72010-10-27 09:33:13 +00003799MaybeObject* JSObject::DefineAccessor(String* name,
3800 bool is_getter,
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003801 Object* fun,
lrn@chromium.org303ada72010-10-27 09:33:13 +00003802 PropertyAttributes attributes) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003803 ASSERT(fun->IsJSFunction() || fun->IsUndefined());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003804 Isolate* isolate = GetIsolate();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003805 // Check access rights if needed.
3806 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003807 !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
3808 isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
3809 return isolate->heap()->undefined_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003810 }
3811
3812 if (IsJSGlobalProxy()) {
3813 Object* proto = GetPrototype();
3814 if (proto->IsNull()) return this;
3815 ASSERT(proto->IsJSGlobalObject());
3816 return JSObject::cast(proto)->DefineAccessor(name, is_getter,
3817 fun, attributes);
3818 }
3819
lrn@chromium.org303ada72010-10-27 09:33:13 +00003820 Object* array;
3821 { MaybeObject* maybe_array = DefineGetterSetter(name, attributes);
3822 if (!maybe_array->ToObject(&array)) return maybe_array;
3823 }
3824 if (array->IsUndefined()) return array;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003825 FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
3826 return this;
3827}
3828
3829
lrn@chromium.org303ada72010-10-27 09:33:13 +00003830MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003831 Isolate* isolate = GetIsolate();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003832 String* name = String::cast(info->name());
3833 // Check access rights if needed.
3834 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003835 !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
3836 isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
3837 return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003838 }
3839
3840 if (IsJSGlobalProxy()) {
3841 Object* proto = GetPrototype();
3842 if (proto->IsNull()) return this;
3843 ASSERT(proto->IsJSGlobalObject());
3844 return JSObject::cast(proto)->DefineAccessor(info);
3845 }
3846
3847 // Make sure that the top context does not change when doing callbacks or
3848 // interceptor calls.
3849 AssertNoContextChange ncc;
3850
3851 // Try to flatten before operating on the string.
3852 name->TryFlatten();
3853
3854 if (!CanSetCallback(name)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003855 return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003856 }
3857
3858 uint32_t index = 0;
3859 bool is_element = name->AsArrayIndex(&index);
3860
3861 if (is_element) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003862 if (IsJSArray()) return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003863
3864 // Accessors overwrite previous callbacks (cf. with getters/setters).
3865 switch (GetElementsKind()) {
3866 case FAST_ELEMENTS:
ricow@chromium.org9fa09672011-07-25 11:05:35 +00003867 case FAST_DOUBLE_ELEMENTS:
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003868 break;
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00003869 case EXTERNAL_PIXEL_ELEMENTS:
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003870 case EXTERNAL_BYTE_ELEMENTS:
3871 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3872 case EXTERNAL_SHORT_ELEMENTS:
3873 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3874 case EXTERNAL_INT_ELEMENTS:
3875 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3876 case EXTERNAL_FLOAT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00003877 case EXTERNAL_DOUBLE_ELEMENTS:
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003878 // Ignore getters and setters on pixel and external array
3879 // elements.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003880 return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003881 case DICTIONARY_ELEMENTS:
3882 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003883 case NON_STRICT_ARGUMENTS_ELEMENTS:
3884 UNIMPLEMENTED();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003885 break;
3886 }
3887
lrn@chromium.org303ada72010-10-27 09:33:13 +00003888 Object* ok;
3889 { MaybeObject* maybe_ok =
3890 SetElementCallback(index, info, info->property_attributes());
3891 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3892 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003893 } else {
3894 // Lookup the name.
3895 LookupResult result;
3896 LocalLookup(name, &result);
3897 // ES5 forbids turning a property into an accessor if it's not
3898 // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
3899 if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003900 return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003901 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00003902 Object* ok;
3903 { MaybeObject* maybe_ok =
3904 SetPropertyCallback(name, info, info->property_attributes());
3905 if (!maybe_ok->ToObject(&ok)) return maybe_ok;
3906 }
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003907 }
3908
3909 return this;
3910}
3911
3912
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003913Object* JSObject::LookupAccessor(String* name, bool is_getter) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003914 Heap* heap = GetHeap();
3915
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003916 // Make sure that the top context does not change when doing callbacks or
3917 // interceptor calls.
3918 AssertNoContextChange ncc;
3919
3920 // Check access rights if needed.
3921 if (IsAccessCheckNeeded() &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003922 !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_HAS)) {
3923 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
3924 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003925 }
3926
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003927 // Make the lookup and include prototypes.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003928 int accessor_index = is_getter ? kGetterIndex : kSetterIndex;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00003929 uint32_t index = 0;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003930 if (name->AsArrayIndex(&index)) {
3931 for (Object* obj = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003932 obj != heap->null_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003933 obj = JSObject::cast(obj)->GetPrototype()) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003934 JSObject* js_object = JSObject::cast(obj);
3935 if (js_object->HasDictionaryElements()) {
3936 NumberDictionary* dictionary = js_object->element_dictionary();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003937 int entry = dictionary->FindEntry(index);
3938 if (entry != NumberDictionary::kNotFound) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003939 Object* element = dictionary->ValueAt(entry);
3940 PropertyDetails details = dictionary->DetailsAt(entry);
3941 if (details.type() == CALLBACKS) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00003942 if (element->IsFixedArray()) {
3943 return FixedArray::cast(element)->get(accessor_index);
3944 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003945 }
3946 }
3947 }
3948 }
3949 } else {
3950 for (Object* obj = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003951 obj != heap->null_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003952 obj = JSObject::cast(obj)->GetPrototype()) {
3953 LookupResult result;
3954 JSObject::cast(obj)->LocalLookup(name, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00003955 if (result.IsProperty()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003956 if (result.IsReadOnly()) return heap->undefined_value();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003957 if (result.type() == CALLBACKS) {
3958 Object* obj = result.GetCallbackObject();
3959 if (obj->IsFixedArray()) {
3960 return FixedArray::cast(obj)->get(accessor_index);
3961 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003962 }
3963 }
3964 }
3965 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003966 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003967}
3968
3969
3970Object* JSObject::SlowReverseLookup(Object* value) {
3971 if (HasFastProperties()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003972 DescriptorArray* descs = map()->instance_descriptors();
3973 for (int i = 0; i < descs->number_of_descriptors(); i++) {
3974 if (descs->GetType(i) == FIELD) {
3975 if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
3976 return descs->GetKey(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003977 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00003978 } else if (descs->GetType(i) == CONSTANT_FUNCTION) {
3979 if (descs->GetConstantFunction(i) == value) {
3980 return descs->GetKey(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003981 }
3982 }
3983 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003984 return GetHeap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003985 } else {
3986 return property_dictionary()->SlowReverseLookup(value);
3987 }
3988}
3989
3990
lrn@chromium.org303ada72010-10-27 09:33:13 +00003991MaybeObject* Map::CopyDropDescriptors() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003992 Heap* heap = GetHeap();
lrn@chromium.org303ada72010-10-27 09:33:13 +00003993 Object* result;
3994 { MaybeObject* maybe_result =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003995 heap->AllocateMap(instance_type(), instance_size());
lrn@chromium.org303ada72010-10-27 09:33:13 +00003996 if (!maybe_result->ToObject(&result)) return maybe_result;
3997 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003998 Map::cast(result)->set_prototype(prototype());
3999 Map::cast(result)->set_constructor(constructor());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004000 // Don't copy descriptors, so map transitions always remain a forest.
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004001 // If we retained the same descriptors we would have two maps
4002 // pointing to the same transition which is bad because the garbage
4003 // collector relies on being able to reverse pointers from transitions
4004 // to maps. If properties need to be retained use CopyDropTransitions.
danno@chromium.org40cb8782011-05-25 07:58:50 +00004005 Map::cast(result)->clear_instance_descriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004006 // Please note instance_type and instance_size are set when allocated.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004007 Map::cast(result)->set_inobject_properties(inobject_properties());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004008 Map::cast(result)->set_unused_property_fields(unused_property_fields());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004009
4010 // If the map has pre-allocated properties always start out with a descriptor
4011 // array describing these properties.
4012 if (pre_allocated_property_fields() > 0) {
4013 ASSERT(constructor()->IsJSFunction());
4014 JSFunction* ctor = JSFunction::cast(constructor());
lrn@chromium.org303ada72010-10-27 09:33:13 +00004015 Object* descriptors;
4016 { MaybeObject* maybe_descriptors =
4017 ctor->initial_map()->instance_descriptors()->RemoveTransitions();
4018 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
4019 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004020 Map::cast(result)->set_instance_descriptors(
4021 DescriptorArray::cast(descriptors));
4022 Map::cast(result)->set_pre_allocated_property_fields(
4023 pre_allocated_property_fields());
4024 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004025 Map::cast(result)->set_bit_field(bit_field());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004026 Map::cast(result)->set_bit_field2(bit_field2());
danno@chromium.org40cb8782011-05-25 07:58:50 +00004027 Map::cast(result)->set_bit_field3(bit_field3());
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004028 Map::cast(result)->set_is_shared(false);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004029 Map::cast(result)->ClearCodeCache(heap);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004030 return result;
4031}
4032
4033
lrn@chromium.org303ada72010-10-27 09:33:13 +00004034MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
4035 NormalizedMapSharingMode sharing) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004036 int new_instance_size = instance_size();
4037 if (mode == CLEAR_INOBJECT_PROPERTIES) {
4038 new_instance_size -= inobject_properties() * kPointerSize;
4039 }
4040
lrn@chromium.org303ada72010-10-27 09:33:13 +00004041 Object* result;
4042 { MaybeObject* maybe_result =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004043 GetHeap()->AllocateMap(instance_type(), new_instance_size);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004044 if (!maybe_result->ToObject(&result)) return maybe_result;
4045 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004046
4047 if (mode != CLEAR_INOBJECT_PROPERTIES) {
4048 Map::cast(result)->set_inobject_properties(inobject_properties());
4049 }
4050
4051 Map::cast(result)->set_prototype(prototype());
4052 Map::cast(result)->set_constructor(constructor());
4053
4054 Map::cast(result)->set_bit_field(bit_field());
4055 Map::cast(result)->set_bit_field2(bit_field2());
danno@chromium.org40cb8782011-05-25 07:58:50 +00004056 Map::cast(result)->set_bit_field3(bit_field3());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004057
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004058 Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
4059
ricow@chromium.org65fae842010-08-25 15:26:24 +00004060#ifdef DEBUG
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004061 if (Map::cast(result)->is_shared()) {
4062 Map::cast(result)->SharedMapVerify();
4063 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004064#endif
4065
4066 return result;
4067}
4068
4069
lrn@chromium.org303ada72010-10-27 09:33:13 +00004070MaybeObject* Map::CopyDropTransitions() {
4071 Object* new_map;
4072 { MaybeObject* maybe_new_map = CopyDropDescriptors();
4073 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
4074 }
4075 Object* descriptors;
4076 { MaybeObject* maybe_descriptors =
4077 instance_descriptors()->RemoveTransitions();
4078 if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
4079 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004080 cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00004081 return new_map;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004082}
4083
4084
lrn@chromium.org303ada72010-10-27 09:33:13 +00004085MaybeObject* Map::UpdateCodeCache(String* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004086 // Allocate the code cache if not present.
4087 if (code_cache()->IsFixedArray()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00004088 Object* result;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004089 { MaybeObject* maybe_result = code->heap()->AllocateCodeCache();
lrn@chromium.org303ada72010-10-27 09:33:13 +00004090 if (!maybe_result->ToObject(&result)) return maybe_result;
4091 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004092 set_code_cache(result);
4093 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004094
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004095 // Update the code cache.
4096 return CodeCache::cast(code_cache())->Update(name, code);
4097}
4098
4099
4100Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
4101 // Do a lookup if a code cache exists.
4102 if (!code_cache()->IsFixedArray()) {
4103 return CodeCache::cast(code_cache())->Lookup(name, flags);
4104 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004105 return GetHeap()->undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004106 }
4107}
4108
4109
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +00004110int Map::IndexInCodeCache(Object* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004111 // Get the internal index if a code cache exists.
4112 if (!code_cache()->IsFixedArray()) {
4113 return CodeCache::cast(code_cache())->GetIndex(name, code);
4114 }
4115 return -1;
4116}
4117
4118
4119void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
4120 // No GC is supposed to happen between a call to IndexInCodeCache and
4121 // RemoveFromCodeCache so the code cache must be there.
4122 ASSERT(!code_cache()->IsFixedArray());
4123 CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
4124}
4125
4126
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004127void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004128 // Traverse the transition tree without using a stack. We do this by
4129 // reversing the pointers in the maps and descriptor arrays.
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004130 Map* current = this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004131 Map* meta_map = heap()->meta_map();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004132 Object** map_or_index_field = NULL;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004133 while (current != meta_map) {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004134 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
danno@chromium.org40cb8782011-05-25 07:58:50 +00004135 *RawField(current, Map::kInstanceDescriptorsOrBitField3Offset));
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004136 if (!d->IsEmpty()) {
4137 FixedArray* contents = reinterpret_cast<FixedArray*>(
4138 d->get(DescriptorArray::kContentArrayIndex));
4139 map_or_index_field = RawField(contents, HeapObject::kMapOffset);
4140 Object* map_or_index = *map_or_index_field;
4141 bool map_done = true; // Controls a nested continue statement.
4142 for (int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : 0;
4143 i < contents->length();
4144 i += 2) {
4145 PropertyDetails details(Smi::cast(contents->get(i + 1)));
4146 if (details.IsTransition()) {
4147 // Found a map in the transition array. We record our progress in
4148 // the transition array by recording the current map in the map field
4149 // of the next map and recording the index in the transition array in
4150 // the map field of the array.
4151 Map* next = Map::cast(contents->get(i));
4152 next->set_map(current);
4153 *map_or_index_field = Smi::FromInt(i + 2);
4154 current = next;
4155 map_done = false;
4156 break;
4157 }
4158 }
4159 if (!map_done) continue;
whesse@chromium.org030d38e2011-07-13 13:23:34 +00004160 } else {
4161 map_or_index_field = NULL;
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004162 }
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004163 // That was the regular transitions, now for the prototype transitions.
4164 FixedArray* prototype_transitions =
4165 current->unchecked_prototype_transitions();
4166 Object** proto_map_or_index_field =
4167 RawField(prototype_transitions, HeapObject::kMapOffset);
4168 Object* map_or_index = *proto_map_or_index_field;
4169 const int start = kProtoTransitionHeaderSize + kProtoTransitionMapOffset;
4170 int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : start;
4171 if (i < prototype_transitions->length()) {
4172 // Found a map in the prototype transition array. Record progress in
4173 // an analogous way to the regular transitions array above.
4174 Object* perhaps_map = prototype_transitions->get(i);
4175 if (perhaps_map->IsMap()) {
4176 Map* next = Map::cast(perhaps_map);
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004177 next->set_map(current);
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004178 *proto_map_or_index_field =
4179 Smi::FromInt(i + kProtoTransitionElementsPerEntry);
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004180 current = next;
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004181 continue;
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004182 }
4183 }
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00004184 *proto_map_or_index_field = heap()->fixed_array_map();
4185 if (map_or_index_field != NULL) {
4186 *map_or_index_field = heap()->fixed_array_map();
4187 }
4188
4189 // The callback expects a map to have a real map as its map, so we save
4190 // the map field, which is being used to track the traversal and put the
4191 // correct map (the meta_map) in place while we do the callback.
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004192 Map* prev = current->map();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004193 current->set_map(meta_map);
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00004194 callback(current, data);
4195 current = prev;
4196 }
4197}
4198
4199
lrn@chromium.org303ada72010-10-27 09:33:13 +00004200MaybeObject* CodeCache::Update(String* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004201 // The number of monomorphic stubs for normal load/store/call IC's can grow to
4202 // a large number and therefore they need to go into a hash table. They are
4203 // used to load global properties from cells.
4204 if (code->type() == NORMAL) {
4205 // Make sure that a hash table is allocated for the normal load code cache.
4206 if (normal_type_cache()->IsUndefined()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00004207 Object* result;
4208 { MaybeObject* maybe_result =
4209 CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize);
4210 if (!maybe_result->ToObject(&result)) return maybe_result;
4211 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004212 set_normal_type_cache(result);
4213 }
4214 return UpdateNormalTypeCache(name, code);
4215 } else {
4216 ASSERT(default_cache()->IsFixedArray());
4217 return UpdateDefaultCache(name, code);
4218 }
4219}
4220
4221
lrn@chromium.org303ada72010-10-27 09:33:13 +00004222MaybeObject* CodeCache::UpdateDefaultCache(String* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004223 // When updating the default code cache we disregard the type encoded in the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004224 // flags. This allows call constant stubs to overwrite call field
4225 // stubs, etc.
4226 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
4227
4228 // First check whether we can update existing code cache without
4229 // extending it.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004230 FixedArray* cache = default_cache();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004231 int length = cache->length();
ager@chromium.org236ad962008-09-25 09:45:57 +00004232 int deleted_index = -1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004233 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004234 Object* key = cache->get(i);
ager@chromium.org236ad962008-09-25 09:45:57 +00004235 if (key->IsNull()) {
4236 if (deleted_index < 0) deleted_index = i;
4237 continue;
4238 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004239 if (key->IsUndefined()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00004240 if (deleted_index >= 0) i = deleted_index;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004241 cache->set(i + kCodeCacheEntryNameOffset, name);
4242 cache->set(i + kCodeCacheEntryCodeOffset, code);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004243 return this;
4244 }
4245 if (name->Equals(String::cast(key))) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004246 Code::Flags found =
4247 Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004248 if (Code::RemoveTypeFromFlags(found) == flags) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004249 cache->set(i + kCodeCacheEntryCodeOffset, code);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004250 return this;
4251 }
4252 }
4253 }
4254
ager@chromium.org236ad962008-09-25 09:45:57 +00004255 // Reached the end of the code cache. If there were deleted
4256 // elements, reuse the space for the first of them.
4257 if (deleted_index >= 0) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004258 cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
4259 cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
ager@chromium.org236ad962008-09-25 09:45:57 +00004260 return this;
4261 }
4262
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004263 // Extend the code cache with some new entries (at least one). Must be a
4264 // multiple of the entry size.
4265 int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
4266 new_length = new_length - new_length % kCodeCacheEntrySize;
4267 ASSERT((new_length % kCodeCacheEntrySize) == 0);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004268 Object* result;
4269 { MaybeObject* maybe_result = cache->CopySize(new_length);
4270 if (!maybe_result->ToObject(&result)) return maybe_result;
4271 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004272
4273 // Add the (name, code) pair to the new cache.
4274 cache = FixedArray::cast(result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004275 cache->set(length + kCodeCacheEntryNameOffset, name);
4276 cache->set(length + kCodeCacheEntryCodeOffset, code);
4277 set_default_cache(cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004278 return this;
4279}
4280
4281
lrn@chromium.org303ada72010-10-27 09:33:13 +00004282MaybeObject* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004283 // Adding a new entry can cause a new cache to be allocated.
4284 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
lrn@chromium.org303ada72010-10-27 09:33:13 +00004285 Object* new_cache;
4286 { MaybeObject* maybe_new_cache = cache->Put(name, code);
4287 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache;
4288 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004289 set_normal_type_cache(new_cache);
4290 return this;
4291}
4292
4293
4294Object* CodeCache::Lookup(String* name, Code::Flags flags) {
4295 if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
4296 return LookupNormalTypeCache(name, flags);
4297 } else {
4298 return LookupDefaultCache(name, flags);
4299 }
4300}
4301
4302
4303Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
4304 FixedArray* cache = default_cache();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004305 int length = cache->length();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004306 for (int i = 0; i < length; i += kCodeCacheEntrySize) {
4307 Object* key = cache->get(i + kCodeCacheEntryNameOffset);
ager@chromium.org236ad962008-09-25 09:45:57 +00004308 // Skip deleted elements.
4309 if (key->IsNull()) continue;
4310 if (key->IsUndefined()) return key;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004311 if (name->Equals(String::cast(key))) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004312 Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
4313 if (code->flags() == flags) {
4314 return code;
4315 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004316 }
4317 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004318 return GetHeap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004319}
4320
4321
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004322Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
4323 if (!normal_type_cache()->IsUndefined()) {
4324 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
4325 return cache->Lookup(name, flags);
4326 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004327 return GetHeap()->undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004328 }
4329}
4330
4331
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +00004332int CodeCache::GetIndex(Object* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004333 if (code->type() == NORMAL) {
4334 if (normal_type_cache()->IsUndefined()) return -1;
4335 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +00004336 return cache->GetIndex(String::cast(name), code->flags());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004337 }
4338
4339 FixedArray* array = default_cache();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004340 int len = array->length();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004341 for (int i = 0; i < len; i += kCodeCacheEntrySize) {
4342 if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004343 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004344 return -1;
4345}
4346
4347
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +00004348void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004349 if (code->type() == NORMAL) {
4350 ASSERT(!normal_type_cache()->IsUndefined());
4351 CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +00004352 ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004353 cache->RemoveByIndex(index);
4354 } else {
4355 FixedArray* array = default_cache();
4356 ASSERT(array->length() >= index && array->get(index)->IsCode());
4357 // Use null instead of undefined for deleted elements to distinguish
4358 // deleted elements from unused elements. This distinction is used
4359 // when looking up in the cache and when updating the cache.
4360 ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
4361 array->set_null(index - 1); // Name.
4362 array->set_null(index); // Code.
4363 }
4364}
4365
4366
4367// The key in the code cache hash table consists of the property name and the
4368// code object. The actual match is on the name and the code flags. If a key
4369// is created using the flags and not a code object it can only be used for
4370// lookup not to create a new entry.
4371class CodeCacheHashTableKey : public HashTableKey {
4372 public:
4373 CodeCacheHashTableKey(String* name, Code::Flags flags)
4374 : name_(name), flags_(flags), code_(NULL) { }
4375
4376 CodeCacheHashTableKey(String* name, Code* code)
4377 : name_(name),
4378 flags_(code->flags()),
4379 code_(code) { }
4380
4381
4382 bool IsMatch(Object* other) {
4383 if (!other->IsFixedArray()) return false;
4384 FixedArray* pair = FixedArray::cast(other);
4385 String* name = String::cast(pair->get(0));
4386 Code::Flags flags = Code::cast(pair->get(1))->flags();
4387 if (flags != flags_) {
4388 return false;
4389 }
4390 return name_->Equals(name);
4391 }
4392
4393 static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) {
4394 return name->Hash() ^ flags;
4395 }
4396
4397 uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
4398
4399 uint32_t HashForObject(Object* obj) {
4400 FixedArray* pair = FixedArray::cast(obj);
4401 String* name = String::cast(pair->get(0));
4402 Code* code = Code::cast(pair->get(1));
4403 return NameFlagsHashHelper(name, code->flags());
4404 }
4405
lrn@chromium.org303ada72010-10-27 09:33:13 +00004406 MUST_USE_RESULT MaybeObject* AsObject() {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004407 ASSERT(code_ != NULL);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004408 Object* obj;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004409 { MaybeObject* maybe_obj = code_->heap()->AllocateFixedArray(2);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004410 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4411 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004412 FixedArray* pair = FixedArray::cast(obj);
4413 pair->set(0, name_);
4414 pair->set(1, code_);
4415 return pair;
4416 }
4417
4418 private:
4419 String* name_;
4420 Code::Flags flags_;
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004421 // TODO(jkummerow): We should be able to get by without this.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004422 Code* code_;
4423};
4424
4425
4426Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) {
4427 CodeCacheHashTableKey key(name, flags);
4428 int entry = FindEntry(&key);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004429 if (entry == kNotFound) return GetHeap()->undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004430 return get(EntryToIndex(entry) + 1);
4431}
4432
4433
lrn@chromium.org303ada72010-10-27 09:33:13 +00004434MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004435 CodeCacheHashTableKey key(name, code);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004436 Object* obj;
4437 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
4438 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4439 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004440
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004441 // Don't use |this|, as the table might have grown.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004442 CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
4443
4444 int entry = cache->FindInsertionEntry(key.Hash());
lrn@chromium.org303ada72010-10-27 09:33:13 +00004445 Object* k;
4446 { MaybeObject* maybe_k = key.AsObject();
4447 if (!maybe_k->ToObject(&k)) return maybe_k;
4448 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004449
4450 cache->set(EntryToIndex(entry), k);
4451 cache->set(EntryToIndex(entry) + 1, code);
4452 cache->ElementAdded();
4453 return cache;
4454}
4455
4456
4457int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) {
4458 CodeCacheHashTableKey key(name, flags);
4459 int entry = FindEntry(&key);
4460 return (entry == kNotFound) ? -1 : entry;
4461}
4462
4463
4464void CodeCacheHashTable::RemoveByIndex(int index) {
4465 ASSERT(index >= 0);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004466 Heap* heap = GetHeap();
4467 set(EntryToIndex(index), heap->null_value());
4468 set(EntryToIndex(index) + 1, heap->null_value());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004469 ElementRemoved();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004470}
4471
4472
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004473MaybeObject* PolymorphicCodeCache::Update(MapList* maps,
4474 Code::Flags flags,
4475 Code* code) {
4476 // Initialize cache if necessary.
4477 if (cache()->IsUndefined()) {
4478 Object* result;
4479 { MaybeObject* maybe_result =
4480 PolymorphicCodeCacheHashTable::Allocate(
4481 PolymorphicCodeCacheHashTable::kInitialSize);
4482 if (!maybe_result->ToObject(&result)) return maybe_result;
4483 }
4484 set_cache(result);
4485 } else {
4486 // This entry shouldn't be contained in the cache yet.
4487 ASSERT(PolymorphicCodeCacheHashTable::cast(cache())
4488 ->Lookup(maps, flags)->IsUndefined());
4489 }
4490 PolymorphicCodeCacheHashTable* hash_table =
4491 PolymorphicCodeCacheHashTable::cast(cache());
4492 Object* new_cache;
4493 { MaybeObject* maybe_new_cache = hash_table->Put(maps, flags, code);
4494 if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache;
4495 }
4496 set_cache(new_cache);
4497 return this;
4498}
4499
4500
4501Object* PolymorphicCodeCache::Lookup(MapList* maps, Code::Flags flags) {
4502 if (!cache()->IsUndefined()) {
4503 PolymorphicCodeCacheHashTable* hash_table =
4504 PolymorphicCodeCacheHashTable::cast(cache());
4505 return hash_table->Lookup(maps, flags);
4506 } else {
4507 return GetHeap()->undefined_value();
4508 }
4509}
4510
4511
4512// Despite their name, object of this class are not stored in the actual
4513// hash table; instead they're temporarily used for lookups. It is therefore
4514// safe to have a weak (non-owning) pointer to a MapList as a member field.
4515class PolymorphicCodeCacheHashTableKey : public HashTableKey {
4516 public:
4517 // Callers must ensure that |maps| outlives the newly constructed object.
4518 PolymorphicCodeCacheHashTableKey(MapList* maps, int code_flags)
4519 : maps_(maps),
4520 code_flags_(code_flags) {}
4521
4522 bool IsMatch(Object* other) {
4523 MapList other_maps(kDefaultListAllocationSize);
4524 int other_flags;
4525 FromObject(other, &other_flags, &other_maps);
4526 if (code_flags_ != other_flags) return false;
4527 if (maps_->length() != other_maps.length()) return false;
4528 // Compare just the hashes first because it's faster.
4529 int this_hash = MapsHashHelper(maps_, code_flags_);
4530 int other_hash = MapsHashHelper(&other_maps, other_flags);
4531 if (this_hash != other_hash) return false;
4532
4533 // Full comparison: for each map in maps_, look for an equivalent map in
4534 // other_maps. This implementation is slow, but probably good enough for
4535 // now because the lists are short (<= 4 elements currently).
4536 for (int i = 0; i < maps_->length(); ++i) {
4537 bool match_found = false;
4538 for (int j = 0; j < other_maps.length(); ++j) {
4539 if (maps_->at(i)->EquivalentTo(other_maps.at(j))) {
4540 match_found = true;
4541 break;
4542 }
4543 }
4544 if (!match_found) return false;
4545 }
4546 return true;
4547 }
4548
4549 static uint32_t MapsHashHelper(MapList* maps, int code_flags) {
4550 uint32_t hash = code_flags;
4551 for (int i = 0; i < maps->length(); ++i) {
4552 hash ^= maps->at(i)->Hash();
4553 }
4554 return hash;
4555 }
4556
4557 uint32_t Hash() {
4558 return MapsHashHelper(maps_, code_flags_);
4559 }
4560
4561 uint32_t HashForObject(Object* obj) {
4562 MapList other_maps(kDefaultListAllocationSize);
4563 int other_flags;
4564 FromObject(obj, &other_flags, &other_maps);
4565 return MapsHashHelper(&other_maps, other_flags);
4566 }
4567
4568 MUST_USE_RESULT MaybeObject* AsObject() {
4569 Object* obj;
4570 // The maps in |maps_| must be copied to a newly allocated FixedArray,
4571 // both because the referenced MapList is short-lived, and because C++
4572 // objects can't be stored in the heap anyway.
4573 { MaybeObject* maybe_obj =
4574 HEAP->AllocateUninitializedFixedArray(maps_->length() + 1);
4575 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4576 }
4577 FixedArray* list = FixedArray::cast(obj);
4578 list->set(0, Smi::FromInt(code_flags_));
4579 for (int i = 0; i < maps_->length(); ++i) {
4580 list->set(i + 1, maps_->at(i));
4581 }
4582 return list;
4583 }
4584
4585 private:
4586 static MapList* FromObject(Object* obj, int* code_flags, MapList* maps) {
4587 FixedArray* list = FixedArray::cast(obj);
4588 maps->Rewind(0);
4589 *code_flags = Smi::cast(list->get(0))->value();
4590 for (int i = 1; i < list->length(); ++i) {
4591 maps->Add(Map::cast(list->get(i)));
4592 }
4593 return maps;
4594 }
4595
4596 MapList* maps_; // weak.
4597 int code_flags_;
whesse@chromium.org7b260152011-06-20 15:33:18 +00004598 static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1;
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00004599};
4600
4601
4602Object* PolymorphicCodeCacheHashTable::Lookup(MapList* maps, int code_flags) {
4603 PolymorphicCodeCacheHashTableKey key(maps, code_flags);
4604 int entry = FindEntry(&key);
4605 if (entry == kNotFound) return GetHeap()->undefined_value();
4606 return get(EntryToIndex(entry) + 1);
4607}
4608
4609
4610MaybeObject* PolymorphicCodeCacheHashTable::Put(MapList* maps,
4611 int code_flags,
4612 Code* code) {
4613 PolymorphicCodeCacheHashTableKey key(maps, code_flags);
4614 Object* obj;
4615 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
4616 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4617 }
4618 PolymorphicCodeCacheHashTable* cache =
4619 reinterpret_cast<PolymorphicCodeCacheHashTable*>(obj);
4620 int entry = cache->FindInsertionEntry(key.Hash());
4621 { MaybeObject* maybe_obj = key.AsObject();
4622 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4623 }
4624 cache->set(EntryToIndex(entry), obj);
4625 cache->set(EntryToIndex(entry) + 1, code);
4626 cache->ElementAdded();
4627 return cache;
4628}
4629
4630
lrn@chromium.org303ada72010-10-27 09:33:13 +00004631MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004632 ElementsAccessor* accessor = array->GetElementsAccessor();
4633 MaybeObject* maybe_result =
rossberg@chromium.org28a37082011-08-22 11:03:23 +00004634 accessor->AddElementsToFixedArray(array->elements(), this, array, array);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00004635 FixedArray* result;
4636 if (!maybe_result->To<FixedArray>(&result)) return maybe_result;
4637#ifdef DEBUG
4638 if (FLAG_enable_slow_asserts) {
4639 for (int i = 0; i < result->length(); i++) {
4640 Object* current = result->get(i);
4641 ASSERT(current->IsNumber() || current->IsString());
4642 }
4643 }
4644#endif
4645 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004646}
4647
4648
lrn@chromium.org303ada72010-10-27 09:33:13 +00004649MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
rossberg@chromium.org28a37082011-08-22 11:03:23 +00004650 ElementsAccessor* accessor = ElementsAccessor::ForArray(other);
4651 MaybeObject* maybe_result =
4652 accessor->AddElementsToFixedArray(other, this, NULL, NULL);
4653 FixedArray* result;
4654 if (!maybe_result->To<FixedArray>(&result)) return maybe_result;
ager@chromium.orgb61a0d12010-10-13 08:35:23 +00004655#ifdef DEBUG
4656 if (FLAG_enable_slow_asserts) {
rossberg@chromium.org28a37082011-08-22 11:03:23 +00004657 for (int i = 0; i < result->length(); i++) {
4658 Object* current = result->get(i);
4659 ASSERT(current->IsNumber() || current->IsString());
ager@chromium.orgb61a0d12010-10-13 08:35:23 +00004660 }
4661 }
4662#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004663 return result;
4664}
4665
4666
lrn@chromium.org303ada72010-10-27 09:33:13 +00004667MaybeObject* FixedArray::CopySize(int new_length) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004668 Heap* heap = GetHeap();
4669 if (new_length == 0) return heap->empty_fixed_array();
lrn@chromium.org303ada72010-10-27 09:33:13 +00004670 Object* obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004671 { MaybeObject* maybe_obj = heap->AllocateFixedArray(new_length);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004672 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
4673 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004674 FixedArray* result = FixedArray::cast(obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004675 // Copy the content
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004676 AssertNoAllocation no_gc;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004677 int len = length();
4678 if (new_length < len) len = new_length;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004679 result->set_map(map());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004680 WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004681 for (int i = 0; i < len; i++) {
4682 result->set(i, get(i), mode);
4683 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004684 return result;
4685}
4686
4687
4688void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004689 AssertNoAllocation no_gc;
4690 WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004691 for (int index = 0; index < len; index++) {
4692 dest->set(dest_pos+index, get(pos+index), mode);
4693 }
4694}
4695
4696
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004697#ifdef DEBUG
4698bool FixedArray::IsEqualTo(FixedArray* other) {
4699 if (length() != other->length()) return false;
4700 for (int i = 0 ; i < length(); ++i) {
4701 if (get(i) != other->get(i)) return false;
4702 }
4703 return true;
4704}
4705#endif
4706
4707
lrn@chromium.org303ada72010-10-27 09:33:13 +00004708MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004709 Heap* heap = Isolate::Current()->heap();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004710 if (number_of_descriptors == 0) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004711 return heap->empty_descriptor_array();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004712 }
4713 // Allocate the array of keys.
lrn@chromium.org303ada72010-10-27 09:33:13 +00004714 Object* array;
4715 { MaybeObject* maybe_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004716 heap->AllocateFixedArray(ToKeyIndex(number_of_descriptors));
lrn@chromium.org303ada72010-10-27 09:33:13 +00004717 if (!maybe_array->ToObject(&array)) return maybe_array;
4718 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004719 // Do not use DescriptorArray::cast on incomplete object.
4720 FixedArray* result = FixedArray::cast(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004721
4722 // Allocate the content array and set it in the descriptor array.
lrn@chromium.org303ada72010-10-27 09:33:13 +00004723 { MaybeObject* maybe_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004724 heap->AllocateFixedArray(number_of_descriptors << 1);
lrn@chromium.org303ada72010-10-27 09:33:13 +00004725 if (!maybe_array->ToObject(&array)) return maybe_array;
4726 }
danno@chromium.org40cb8782011-05-25 07:58:50 +00004727 result->set(kBitField3StorageIndex, Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004728 result->set(kContentArrayIndex, array);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004729 result->set(kEnumerationIndexIndex,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004730 Smi::FromInt(PropertyDetails::kInitialIndex));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004731 return result;
4732}
4733
4734
4735void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
4736 FixedArray* new_cache) {
4737 ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
4738 if (HasEnumCache()) {
4739 FixedArray::cast(get(kEnumerationIndexIndex))->
4740 set(kEnumCacheBridgeCacheIndex, new_cache);
4741 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004742 if (IsEmpty()) return; // Do nothing for empty descriptor array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004743 FixedArray::cast(bridge_storage)->
4744 set(kEnumCacheBridgeCacheIndex, new_cache);
4745 fast_set(FixedArray::cast(bridge_storage),
4746 kEnumCacheBridgeEnumIndex,
4747 get(kEnumerationIndexIndex));
4748 set(kEnumerationIndexIndex, bridge_storage);
4749 }
4750}
4751
4752
lrn@chromium.org303ada72010-10-27 09:33:13 +00004753MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
4754 TransitionFlag transition_flag) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004755 // Transitions are only kept when inserting another transition.
4756 // This precondition is not required by this function's implementation, but
4757 // is currently required by the semantics of maps, so we check it.
4758 // Conversely, we filter after replacing, so replacing a transition and
4759 // removing all other transitions is not supported.
4760 bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
4761 ASSERT(remove_transitions == !descriptor->GetDetails().IsTransition());
4762 ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004763
4764 // Ensure the key is a symbol.
lrn@chromium.org303ada72010-10-27 09:33:13 +00004765 Object* result;
4766 { MaybeObject* maybe_result = descriptor->KeyToSymbol();
4767 if (!maybe_result->ToObject(&result)) return maybe_result;
4768 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004769
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004770 int transitions = 0;
4771 int null_descriptors = 0;
4772 if (remove_transitions) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004773 for (int i = 0; i < number_of_descriptors(); i++) {
4774 if (IsTransition(i)) transitions++;
4775 if (IsNullDescriptor(i)) null_descriptors++;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004776 }
4777 } else {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004778 for (int i = 0; i < number_of_descriptors(); i++) {
4779 if (IsNullDescriptor(i)) null_descriptors++;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004780 }
4781 }
4782 int new_size = number_of_descriptors() - transitions - null_descriptors;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004783
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004784 // If key is in descriptor, we replace it in-place when filtering.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004785 // Count a null descriptor for key as inserted, not replaced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004786 int index = Search(descriptor->GetKey());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004787 const bool inserting = (index == kNotFound);
4788 const bool replacing = !inserting;
4789 bool keep_enumeration_index = false;
4790 if (inserting) {
4791 ++new_size;
4792 }
4793 if (replacing) {
4794 // We are replacing an existing descriptor. We keep the enumeration
4795 // index of a visible property.
4796 PropertyType t = PropertyDetails(GetDetails(index)).type();
4797 if (t == CONSTANT_FUNCTION ||
4798 t == FIELD ||
4799 t == CALLBACKS ||
4800 t == INTERCEPTOR) {
4801 keep_enumeration_index = true;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004802 } else if (remove_transitions) {
4803 // Replaced descriptor has been counted as removed if it is
4804 // a transition that will be replaced. Adjust count in this case.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004805 ++new_size;
4806 }
4807 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00004808 { MaybeObject* maybe_result = Allocate(new_size);
4809 if (!maybe_result->ToObject(&result)) return maybe_result;
4810 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004811 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004812 // Set the enumeration index in the descriptors and set the enumeration index
4813 // in the result.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004814 int enumeration_index = NextEnumerationIndex();
4815 if (!descriptor->GetDetails().IsTransition()) {
4816 if (keep_enumeration_index) {
4817 descriptor->SetEnumerationIndex(
4818 PropertyDetails(GetDetails(index)).index());
4819 } else {
4820 descriptor->SetEnumerationIndex(enumeration_index);
4821 ++enumeration_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004822 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004823 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004824 new_descriptors->SetNextEnumerationIndex(enumeration_index);
4825
4826 // Copy the descriptors, filtering out transitions and null descriptors,
4827 // and inserting or replacing a descriptor.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004828 uint32_t descriptor_hash = descriptor->GetKey()->Hash();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004829 int from_index = 0;
4830 int to_index = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004831
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004832 for (; from_index < number_of_descriptors(); from_index++) {
4833 String* key = GetKey(from_index);
4834 if (key->Hash() > descriptor_hash || key == descriptor->GetKey()) {
4835 break;
4836 }
4837 if (IsNullDescriptor(from_index)) continue;
4838 if (remove_transitions && IsTransition(from_index)) continue;
4839 new_descriptors->CopyFrom(to_index++, this, from_index);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004840 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004841
4842 new_descriptors->Set(to_index++, descriptor);
4843 if (replacing) from_index++;
4844
4845 for (; from_index < number_of_descriptors(); from_index++) {
4846 if (IsNullDescriptor(from_index)) continue;
4847 if (remove_transitions && IsTransition(from_index)) continue;
4848 new_descriptors->CopyFrom(to_index++, this, from_index);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004849 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004850
4851 ASSERT(to_index == new_descriptors->number_of_descriptors());
4852 SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004853
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004854 return new_descriptors;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004855}
4856
4857
lrn@chromium.org303ada72010-10-27 09:33:13 +00004858MaybeObject* DescriptorArray::RemoveTransitions() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004859 // Remove all transitions and null descriptors. Return a copy of the array
4860 // with all transitions removed, or a Failure object if the new array could
4861 // not be allocated.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004862
4863 // Compute the size of the map transition entries to be removed.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004864 int num_removed = 0;
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004865 for (int i = 0; i < number_of_descriptors(); i++) {
4866 if (!IsProperty(i)) num_removed++;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004867 }
4868
4869 // Allocate the new descriptor array.
lrn@chromium.org303ada72010-10-27 09:33:13 +00004870 Object* result;
4871 { MaybeObject* maybe_result = Allocate(number_of_descriptors() - num_removed);
4872 if (!maybe_result->ToObject(&result)) return maybe_result;
4873 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004874 DescriptorArray* new_descriptors = DescriptorArray::cast(result);
4875
4876 // Copy the content.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004877 int next_descriptor = 0;
4878 for (int i = 0; i < number_of_descriptors(); i++) {
4879 if (IsProperty(i)) new_descriptors->CopyFrom(next_descriptor++, this, i);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004880 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004881 ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004882
4883 return new_descriptors;
4884}
4885
4886
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +00004887void DescriptorArray::SortUnchecked() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004888 // In-place heap sort.
4889 int len = number_of_descriptors();
4890
4891 // Bottom-up max-heap construction.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004892 // Index of the last node with children
4893 const int max_parent_index = (len / 2) - 1;
4894 for (int i = max_parent_index; i >= 0; --i) {
4895 int parent_index = i;
4896 const uint32_t parent_hash = GetKey(i)->Hash();
4897 while (parent_index <= max_parent_index) {
4898 int child_index = 2 * parent_index + 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004899 uint32_t child_hash = GetKey(child_index)->Hash();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004900 if (child_index + 1 < len) {
4901 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4902 if (right_child_hash > child_hash) {
4903 child_index++;
4904 child_hash = right_child_hash;
4905 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004906 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004907 if (child_hash <= parent_hash) break;
4908 Swap(parent_index, child_index);
4909 // Now element at child_index could be < its children.
4910 parent_index = child_index; // parent_hash remains correct.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004911 }
4912 }
4913
4914 // Extract elements and create sorted array.
4915 for (int i = len - 1; i > 0; --i) {
4916 // Put max element at the back of the array.
4917 Swap(0, i);
4918 // Sift down the new top element.
4919 int parent_index = 0;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004920 const uint32_t parent_hash = GetKey(parent_index)->Hash();
4921 const int max_parent_index = (i / 2) - 1;
4922 while (parent_index <= max_parent_index) {
4923 int child_index = parent_index * 2 + 1;
4924 uint32_t child_hash = GetKey(child_index)->Hash();
4925 if (child_index + 1 < i) {
4926 uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
4927 if (right_child_hash > child_hash) {
4928 child_index++;
4929 child_hash = right_child_hash;
4930 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004931 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004932 if (child_hash <= parent_hash) break;
4933 Swap(parent_index, child_index);
4934 parent_index = child_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004935 }
4936 }
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +00004937}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004938
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +00004939
4940void DescriptorArray::Sort() {
4941 SortUnchecked();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004942 SLOW_ASSERT(IsSortedNoDuplicates());
4943}
4944
4945
4946int DescriptorArray::BinarySearch(String* name, int low, int high) {
4947 uint32_t hash = name->Hash();
4948
4949 while (low <= high) {
4950 int mid = (low + high) / 2;
4951 String* mid_name = GetKey(mid);
4952 uint32_t mid_hash = mid_name->Hash();
4953
4954 if (mid_hash > hash) {
4955 high = mid - 1;
4956 continue;
4957 }
4958 if (mid_hash < hash) {
4959 low = mid + 1;
4960 continue;
4961 }
4962 // Found an element with the same hash-code.
4963 ASSERT(hash == mid_hash);
4964 // There might be more, so we find the first one and
4965 // check them all to see if we have a match.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004966 if (name == mid_name && !is_null_descriptor(mid)) return mid;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004967 while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
4968 for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004969 if (GetKey(mid)->Equals(name) && !is_null_descriptor(mid)) return mid;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004970 }
4971 break;
4972 }
4973 return kNotFound;
4974}
4975
4976
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004977int DescriptorArray::LinearSearch(String* name, int len) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00004978 uint32_t hash = name->Hash();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004979 for (int number = 0; number < len; number++) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00004980 String* entry = GetKey(number);
4981 if ((entry->Hash() == hash) &&
4982 name->Equals(entry) &&
4983 !is_null_descriptor(number)) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004984 return number;
4985 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004986 }
4987 return kNotFound;
4988}
4989
4990
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004991MaybeObject* DeoptimizationInputData::Allocate(int deopt_entry_count,
4992 PretenureFlag pretenure) {
4993 ASSERT(deopt_entry_count > 0);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004994 return HEAP->AllocateFixedArray(LengthFor(deopt_entry_count),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004995 pretenure);
4996}
4997
4998
4999MaybeObject* DeoptimizationOutputData::Allocate(int number_of_deopt_points,
5000 PretenureFlag pretenure) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005001 if (number_of_deopt_points == 0) return HEAP->empty_fixed_array();
5002 return HEAP->AllocateFixedArray(LengthOfFixedArray(number_of_deopt_points),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005003 pretenure);
5004}
5005
5006
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005007#ifdef DEBUG
5008bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
5009 if (IsEmpty()) return other->IsEmpty();
5010 if (other->IsEmpty()) return false;
5011 if (length() != other->length()) return false;
5012 for (int i = 0; i < length(); ++i) {
5013 if (get(i) != other->get(i) && i != kContentArrayIndex) return false;
5014 }
5015 return GetContentArray()->IsEqualTo(other->GetContentArray());
5016}
5017#endif
5018
5019
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005020bool String::LooksValid() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005021 if (!Isolate::Current()->heap()->Contains(this)) return false;
ager@chromium.org870a0b62008-11-04 11:43:05 +00005022 return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005023}
5024
5025
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005026int String::Utf8Length() {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005027 if (IsAsciiRepresentation()) return length();
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005028 // Attempt to flatten before accessing the string. It probably
5029 // doesn't make Utf8Length faster, but it is very likely that
5030 // the string will be accessed later (for example by WriteUtf8)
5031 // so it's still a good idea.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005032 Heap* heap = GetHeap();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005033 TryFlatten();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005034 Access<StringInputBuffer> buffer(
5035 heap->isolate()->objects_string_input_buffer());
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005036 buffer->Reset(0, this);
5037 int result = 0;
5038 while (buffer->has_more())
5039 result += unibrow::Utf8::Length(buffer->GetNext());
5040 return result;
5041}
5042
5043
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005044String::FlatContent String::GetFlatContent() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005045 int length = this->length();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005046 StringShape shape(this);
ager@chromium.org7c537e22008-10-16 08:43:32 +00005047 String* string = this;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005048 int offset = 0;
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005049 if (shape.representation_tag() == kConsStringTag) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005050 ConsString* cons = ConsString::cast(string);
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005051 if (cons->second()->length() != 0) {
5052 return FlatContent();
5053 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005054 string = cons->first();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005055 shape = StringShape(string);
ager@chromium.org7c537e22008-10-16 08:43:32 +00005056 }
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005057 if (shape.representation_tag() == kSlicedStringTag) {
5058 SlicedString* slice = SlicedString::cast(string);
5059 offset = slice->offset();
5060 string = slice->parent();
5061 shape = StringShape(string);
5062 ASSERT(shape.representation_tag() != kConsStringTag &&
5063 shape.representation_tag() != kSlicedStringTag);
5064 }
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005065 if (shape.encoding_tag() == kAsciiStringTag) {
5066 const char* start;
5067 if (shape.representation_tag() == kSeqStringTag) {
5068 start = SeqAsciiString::cast(string)->GetChars();
5069 } else {
5070 start = ExternalAsciiString::cast(string)->resource()->data();
5071 }
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005072 return FlatContent(Vector<const char>(start + offset, length));
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005073 } else {
5074 ASSERT(shape.encoding_tag() == kTwoByteStringTag);
5075 const uc16* start;
5076 if (shape.representation_tag() == kSeqStringTag) {
5077 start = SeqTwoByteString::cast(string)->GetChars();
5078 } else {
5079 start = ExternalTwoByteString::cast(string)->resource()->data();
5080 }
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005081 return FlatContent(Vector<const uc16>(start + offset, length));
ager@chromium.org7c537e22008-10-16 08:43:32 +00005082 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00005083}
5084
5085
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005086SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
5087 RobustnessFlag robust_flag,
5088 int offset,
5089 int length,
5090 int* length_return) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005091 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
5092 return SmartPointer<char>(NULL);
5093 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005094 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005095
5096 // Negative length means the to the end of the string.
5097 if (length < 0) length = kMaxInt - offset;
5098
5099 // Compute the size of the UTF-8 string. Start at the specified offset.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005100 Access<StringInputBuffer> buffer(
5101 heap->isolate()->objects_string_input_buffer());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005102 buffer->Reset(offset, this);
5103 int character_position = offset;
5104 int utf8_bytes = 0;
5105 while (buffer->has_more()) {
5106 uint16_t character = buffer->GetNext();
5107 if (character_position < offset + length) {
5108 utf8_bytes += unibrow::Utf8::Length(character);
5109 }
5110 character_position++;
5111 }
5112
5113 if (length_return) {
5114 *length_return = utf8_bytes;
5115 }
5116
5117 char* result = NewArray<char>(utf8_bytes + 1);
5118
5119 // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
5120 buffer->Rewind();
5121 buffer->Seek(offset);
5122 character_position = offset;
5123 int utf8_byte_position = 0;
5124 while (buffer->has_more()) {
5125 uint16_t character = buffer->GetNext();
5126 if (character_position < offset + length) {
5127 if (allow_nulls == DISALLOW_NULLS && character == 0) {
5128 character = ' ';
5129 }
5130 utf8_byte_position +=
5131 unibrow::Utf8::Encode(result + utf8_byte_position, character);
5132 }
5133 character_position++;
5134 }
5135 result[utf8_byte_position] = 0;
5136 return SmartPointer<char>(result);
5137}
5138
5139
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00005140SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
5141 RobustnessFlag robust_flag,
5142 int* length_return) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005143 return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
5144}
5145
5146
5147const uc16* String::GetTwoByteData() {
5148 return GetTwoByteData(0);
5149}
5150
5151
5152const uc16* String::GetTwoByteData(unsigned start) {
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005153 ASSERT(!IsAsciiRepresentationUnderneath());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005154 switch (StringShape(this).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005155 case kSeqStringTag:
ager@chromium.org7c537e22008-10-16 08:43:32 +00005156 return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005157 case kExternalStringTag:
5158 return ExternalTwoByteString::cast(this)->
5159 ExternalTwoByteStringGetData(start);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005160 case kSlicedStringTag: {
5161 SlicedString* slice = SlicedString::cast(this);
5162 return slice->parent()->GetTwoByteData(start + slice->offset());
5163 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005164 case kConsStringTag:
5165 UNREACHABLE();
5166 return NULL;
5167 }
5168 UNREACHABLE();
5169 return NULL;
5170}
5171
5172
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005173SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005174 if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005175 return SmartPointer<uc16>();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005176 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005177 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005178
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005179 Access<StringInputBuffer> buffer(
5180 heap->isolate()->objects_string_input_buffer());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005181 buffer->Reset(this);
5182
5183 uc16* result = NewArray<uc16>(length() + 1);
5184
5185 int i = 0;
5186 while (buffer->has_more()) {
5187 uint16_t character = buffer->GetNext();
5188 result[i++] = character;
5189 }
5190 result[i] = 0;
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005191 return SmartPointer<uc16>(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005192}
5193
5194
ager@chromium.org7c537e22008-10-16 08:43:32 +00005195const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005196 return reinterpret_cast<uc16*>(
5197 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
5198}
5199
5200
ager@chromium.org7c537e22008-10-16 08:43:32 +00005201void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00005202 unsigned* offset_ptr,
5203 unsigned max_chars) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005204 unsigned chars_read = 0;
5205 unsigned offset = *offset_ptr;
5206 while (chars_read < max_chars) {
5207 uint16_t c = *reinterpret_cast<uint16_t*>(
5208 reinterpret_cast<char*>(this) -
5209 kHeapObjectTag + kHeaderSize + offset * kShortSize);
5210 if (c <= kMaxAsciiCharCode) {
5211 // Fast case for ASCII characters. Cursor is an input output argument.
5212 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
5213 rbb->util_buffer,
5214 rbb->capacity,
5215 rbb->cursor)) {
5216 break;
5217 }
5218 } else {
5219 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
5220 rbb->util_buffer,
5221 rbb->capacity,
5222 rbb->cursor)) {
5223 break;
5224 }
5225 }
5226 offset++;
5227 chars_read++;
5228 }
5229 *offset_ptr = offset;
5230 rbb->remaining += chars_read;
5231}
5232
5233
ager@chromium.org7c537e22008-10-16 08:43:32 +00005234const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
5235 unsigned* remaining,
5236 unsigned* offset_ptr,
5237 unsigned max_chars) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005238 const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
5239 kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
5240 *remaining = max_chars;
5241 *offset_ptr += max_chars;
5242 return b;
5243}
5244
5245
5246// This will iterate unless the block of string data spans two 'halves' of
5247// a ConsString, in which case it will recurse. Since the block of string
5248// data to be read has a maximum size this limits the maximum recursion
5249// depth to something sane. Since C++ does not have tail call recursion
5250// elimination, the iteration must be explicit. Since this is not an
5251// -IntoBuffer method it can delegate to one of the efficient
5252// *AsciiStringReadBlock routines.
5253const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
5254 unsigned* offset_ptr,
5255 unsigned max_chars) {
5256 ConsString* current = this;
5257 unsigned offset = *offset_ptr;
5258 int offset_correction = 0;
5259
5260 while (true) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00005261 String* left = current->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005262 unsigned left_length = (unsigned)left->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005263 if (left_length > offset &&
5264 (max_chars <= left_length - offset ||
5265 (rbb->capacity <= left_length - offset &&
5266 (max_chars = left_length - offset, true)))) { // comma operator!
5267 // Left hand side only - iterate unless we have reached the bottom of
5268 // the cons tree. The assignment on the left of the comma operator is
5269 // in order to make use of the fact that the -IntoBuffer routines can
5270 // produce at most 'capacity' characters. This enables us to postpone
5271 // the point where we switch to the -IntoBuffer routines (below) in order
5272 // to maximize the chances of delegating a big chunk of work to the
5273 // efficient *AsciiStringReadBlock routines.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005274 if (StringShape(left).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005275 current = ConsString::cast(left);
5276 continue;
5277 } else {
5278 const unibrow::byte* answer =
5279 String::ReadBlock(left, rbb, &offset, max_chars);
5280 *offset_ptr = offset + offset_correction;
5281 return answer;
5282 }
5283 } else if (left_length <= offset) {
5284 // Right hand side only - iterate unless we have reached the bottom of
5285 // the cons tree.
ager@chromium.org870a0b62008-11-04 11:43:05 +00005286 String* right = current->second();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005287 offset -= left_length;
5288 offset_correction += left_length;
ager@chromium.org870a0b62008-11-04 11:43:05 +00005289 if (StringShape(right).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005290 current = ConsString::cast(right);
5291 continue;
5292 } else {
5293 const unibrow::byte* answer =
5294 String::ReadBlock(right, rbb, &offset, max_chars);
5295 *offset_ptr = offset + offset_correction;
5296 return answer;
5297 }
5298 } else {
5299 // The block to be read spans two sides of the ConsString, so we call the
5300 // -IntoBuffer version, which will recurse. The -IntoBuffer methods
5301 // are able to assemble data from several part strings because they use
5302 // the util_buffer to store their data and never return direct pointers
5303 // to their storage. We don't try to read more than the buffer capacity
5304 // here or we can get too much recursion.
5305 ASSERT(rbb->remaining == 0);
5306 ASSERT(rbb->cursor == 0);
5307 current->ConsStringReadBlockIntoBuffer(
5308 rbb,
5309 &offset,
5310 max_chars > rbb->capacity ? rbb->capacity : max_chars);
5311 *offset_ptr = offset + offset_correction;
5312 return rbb->util_buffer;
5313 }
5314 }
5315}
5316
5317
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005318uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
5319 ASSERT(index >= 0 && index < length());
5320 return resource()->data()[index];
5321}
5322
5323
5324const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
5325 unsigned* remaining,
5326 unsigned* offset_ptr,
5327 unsigned max_chars) {
5328 // Cast const char* to unibrow::byte* (signedness difference).
5329 const unibrow::byte* b =
5330 reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
5331 *remaining = max_chars;
5332 *offset_ptr += max_chars;
5333 return b;
5334}
5335
5336
5337const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
5338 unsigned start) {
5339 return resource()->data() + start;
5340}
5341
5342
5343uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
5344 ASSERT(index >= 0 && index < length());
5345 return resource()->data()[index];
5346}
5347
5348
5349void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
5350 ReadBlockBuffer* rbb,
5351 unsigned* offset_ptr,
5352 unsigned max_chars) {
5353 unsigned chars_read = 0;
5354 unsigned offset = *offset_ptr;
5355 const uint16_t* data = resource()->data();
5356 while (chars_read < max_chars) {
5357 uint16_t c = data[offset];
5358 if (c <= kMaxAsciiCharCode) {
ager@chromium.org80787b72009-04-17 10:24:24 +00005359 // Fast case for ASCII characters. Cursor is an input output argument.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005360 if (!unibrow::CharacterStream::EncodeAsciiCharacter(c,
5361 rbb->util_buffer,
5362 rbb->capacity,
5363 rbb->cursor))
5364 break;
5365 } else {
5366 if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c,
5367 rbb->util_buffer,
5368 rbb->capacity,
5369 rbb->cursor))
5370 break;
5371 }
5372 offset++;
5373 chars_read++;
5374 }
5375 *offset_ptr = offset;
5376 rbb->remaining += chars_read;
5377}
5378
5379
ager@chromium.org7c537e22008-10-16 08:43:32 +00005380void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005381 unsigned* offset_ptr,
5382 unsigned max_chars) {
5383 unsigned capacity = rbb->capacity - rbb->cursor;
5384 if (max_chars > capacity) max_chars = capacity;
5385 memcpy(rbb->util_buffer + rbb->cursor,
5386 reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize +
5387 *offset_ptr * kCharSize,
5388 max_chars);
5389 rbb->remaining += max_chars;
5390 *offset_ptr += max_chars;
5391 rbb->cursor += max_chars;
5392}
5393
5394
5395void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
5396 ReadBlockBuffer* rbb,
5397 unsigned* offset_ptr,
5398 unsigned max_chars) {
5399 unsigned capacity = rbb->capacity - rbb->cursor;
5400 if (max_chars > capacity) max_chars = capacity;
5401 memcpy(rbb->util_buffer + rbb->cursor,
5402 resource()->data() + *offset_ptr,
5403 max_chars);
5404 rbb->remaining += max_chars;
5405 *offset_ptr += max_chars;
5406 rbb->cursor += max_chars;
5407}
5408
5409
5410// This method determines the type of string involved and then copies
5411// a whole chunk of characters into a buffer, or returns a pointer to a buffer
5412// where they can be found. The pointer is not necessarily valid across a GC
5413// (see AsciiStringReadBlock).
5414const unibrow::byte* String::ReadBlock(String* input,
5415 ReadBlockBuffer* rbb,
5416 unsigned* offset_ptr,
5417 unsigned max_chars) {
5418 ASSERT(*offset_ptr <= static_cast<unsigned>(input->length()));
5419 if (max_chars == 0) {
5420 rbb->remaining = 0;
5421 return NULL;
5422 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005423 switch (StringShape(input).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005424 case kSeqStringTag:
ager@chromium.org5ec48922009-05-05 07:25:34 +00005425 if (input->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005426 SeqAsciiString* str = SeqAsciiString::cast(input);
5427 return str->SeqAsciiStringReadBlock(&rbb->remaining,
5428 offset_ptr,
5429 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005430 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005431 SeqTwoByteString* str = SeqTwoByteString::cast(input);
5432 str->SeqTwoByteStringReadBlockIntoBuffer(rbb,
5433 offset_ptr,
5434 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005435 return rbb->util_buffer;
5436 }
5437 case kConsStringTag:
5438 return ConsString::cast(input)->ConsStringReadBlock(rbb,
5439 offset_ptr,
5440 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005441 case kExternalStringTag:
ager@chromium.org5ec48922009-05-05 07:25:34 +00005442 if (input->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005443 return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
5444 &rbb->remaining,
5445 offset_ptr,
5446 max_chars);
5447 } else {
5448 ExternalTwoByteString::cast(input)->
5449 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
5450 offset_ptr,
5451 max_chars);
5452 return rbb->util_buffer;
5453 }
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005454 case kSlicedStringTag:
5455 return SlicedString::cast(input)->SlicedStringReadBlock(rbb,
5456 offset_ptr,
5457 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005458 default:
5459 break;
5460 }
5461
5462 UNREACHABLE();
5463 return 0;
5464}
5465
5466
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005467void Relocatable::PostGarbageCollectionProcessing() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005468 Isolate* isolate = Isolate::Current();
5469 Relocatable* current = isolate->relocatable_top();
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005470 while (current != NULL) {
5471 current->PostGarbageCollection();
5472 current = current->prev_;
5473 }
5474}
5475
5476
5477// Reserve space for statics needing saving and restoring.
5478int Relocatable::ArchiveSpacePerThread() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005479 return sizeof(Isolate::Current()->relocatable_top());
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005480}
5481
5482
5483// Archive statics that are thread local.
lrn@chromium.org1c092762011-05-09 09:42:16 +00005484char* Relocatable::ArchiveState(Isolate* isolate, char* to) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005485 *reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top();
5486 isolate->set_relocatable_top(NULL);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005487 return to + ArchiveSpacePerThread();
5488}
5489
5490
5491// Restore statics that are thread local.
lrn@chromium.org1c092762011-05-09 09:42:16 +00005492char* Relocatable::RestoreState(Isolate* isolate, char* from) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005493 isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005494 return from + ArchiveSpacePerThread();
5495}
5496
5497
5498char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
5499 Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
5500 Iterate(v, top);
5501 return thread_storage + ArchiveSpacePerThread();
5502}
5503
5504
5505void Relocatable::Iterate(ObjectVisitor* v) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005506 Isolate* isolate = Isolate::Current();
5507 Iterate(v, isolate->relocatable_top());
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005508}
5509
5510
5511void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
5512 Relocatable* current = top;
5513 while (current != NULL) {
5514 current->IterateInstance(v);
5515 current = current->prev_;
5516 }
5517}
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005518
5519
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005520FlatStringReader::FlatStringReader(Isolate* isolate, Handle<String> str)
5521 : Relocatable(isolate),
5522 str_(str.location()),
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005523 length_(str->length()) {
5524 PostGarbageCollection();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005525}
5526
5527
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005528FlatStringReader::FlatStringReader(Isolate* isolate, Vector<const char> input)
5529 : Relocatable(isolate),
5530 str_(0),
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005531 is_ascii_(true),
5532 length_(input.length()),
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005533 start_(input.start()) { }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005534
5535
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005536void FlatStringReader::PostGarbageCollection() {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005537 if (str_ == NULL) return;
5538 Handle<String> str(str_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005539 ASSERT(str->IsFlat());
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005540 String::FlatContent content = str->GetFlatContent();
5541 ASSERT(content.IsFlat());
5542 is_ascii_ = content.IsAscii();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005543 if (is_ascii_) {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005544 start_ = content.ToAsciiVector().start();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005545 } else {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005546 start_ = content.ToUC16Vector().start();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005547 }
5548}
5549
5550
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005551void StringInputBuffer::Seek(unsigned pos) {
5552 Reset(pos, input_);
5553}
5554
5555
5556void SafeStringInputBuffer::Seek(unsigned pos) {
5557 Reset(pos, input_);
5558}
5559
5560
5561// This method determines the type of string involved and then copies
5562// a whole chunk of characters into a buffer. It can be used with strings
5563// that have been glued together to form a ConsString and which must cooperate
5564// to fill up a buffer.
5565void String::ReadBlockIntoBuffer(String* input,
5566 ReadBlockBuffer* rbb,
5567 unsigned* offset_ptr,
5568 unsigned max_chars) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005569 ASSERT(*offset_ptr <= (unsigned)input->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005570 if (max_chars == 0) return;
5571
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005572 switch (StringShape(input).representation_tag()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005573 case kSeqStringTag:
ager@chromium.org5ec48922009-05-05 07:25:34 +00005574 if (input->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005575 SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005576 offset_ptr,
5577 max_chars);
5578 return;
5579 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005580 SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005581 offset_ptr,
5582 max_chars);
5583 return;
5584 }
5585 case kConsStringTag:
5586 ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb,
5587 offset_ptr,
5588 max_chars);
5589 return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005590 case kExternalStringTag:
ager@chromium.org5ec48922009-05-05 07:25:34 +00005591 if (input->IsAsciiRepresentation()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005592 ExternalAsciiString::cast(input)->
5593 ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
5594 } else {
5595 ExternalTwoByteString::cast(input)->
5596 ExternalTwoByteStringReadBlockIntoBuffer(rbb,
5597 offset_ptr,
5598 max_chars);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005599 }
5600 return;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005601 case kSlicedStringTag:
5602 SlicedString::cast(input)->SlicedStringReadBlockIntoBuffer(rbb,
5603 offset_ptr,
5604 max_chars);
5605 return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005606 default:
5607 break;
5608 }
5609
5610 UNREACHABLE();
5611 return;
5612}
5613
5614
5615const unibrow::byte* String::ReadBlock(String* input,
5616 unibrow::byte* util_buffer,
5617 unsigned capacity,
5618 unsigned* remaining,
5619 unsigned* offset_ptr) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005620 ASSERT(*offset_ptr <= (unsigned)input->length());
5621 unsigned chars = input->length() - *offset_ptr;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005622 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
5623 const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005624 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005625 *remaining = rbb.remaining;
5626 return answer;
5627}
5628
5629
5630const unibrow::byte* String::ReadBlock(String** raw_input,
5631 unibrow::byte* util_buffer,
5632 unsigned capacity,
5633 unsigned* remaining,
5634 unsigned* offset_ptr) {
5635 Handle<String> input(raw_input);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005636 ASSERT(*offset_ptr <= (unsigned)input->length());
5637 unsigned chars = input->length() - *offset_ptr;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005638 if (chars > capacity) chars = capacity;
5639 ReadBlockBuffer rbb(util_buffer, 0, capacity, 0);
5640 ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005641 ASSERT(rbb.remaining <= static_cast<unsigned>(input->length()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005642 *remaining = rbb.remaining;
5643 return rbb.util_buffer;
5644}
5645
5646
5647// This will iterate unless the block of string data spans two 'halves' of
5648// a ConsString, in which case it will recurse. Since the block of string
5649// data to be read has a maximum size this limits the maximum recursion
5650// depth to something sane. Since C++ does not have tail call recursion
5651// elimination, the iteration must be explicit.
5652void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
5653 unsigned* offset_ptr,
5654 unsigned max_chars) {
5655 ConsString* current = this;
5656 unsigned offset = *offset_ptr;
5657 int offset_correction = 0;
5658
5659 while (true) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00005660 String* left = current->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005661 unsigned left_length = (unsigned)left->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005662 if (left_length > offset &&
5663 max_chars <= left_length - offset) {
5664 // Left hand side only - iterate unless we have reached the bottom of
5665 // the cons tree.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005666 if (StringShape(left).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005667 current = ConsString::cast(left);
5668 continue;
5669 } else {
5670 String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars);
5671 *offset_ptr = offset + offset_correction;
5672 return;
5673 }
5674 } else if (left_length <= offset) {
5675 // Right hand side only - iterate unless we have reached the bottom of
5676 // the cons tree.
5677 offset -= left_length;
5678 offset_correction += left_length;
ager@chromium.org870a0b62008-11-04 11:43:05 +00005679 String* right = current->second();
5680 if (StringShape(right).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005681 current = ConsString::cast(right);
5682 continue;
5683 } else {
5684 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
5685 *offset_ptr = offset + offset_correction;
5686 return;
5687 }
5688 } else {
5689 // The block to be read spans two sides of the ConsString, so we recurse.
5690 // First recurse on the left.
5691 max_chars -= left_length - offset;
5692 String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset);
5693 // We may have reached the max or there may not have been enough space
5694 // in the buffer for the characters in the left hand side.
5695 if (offset == left_length) {
5696 // Recurse on the right.
5697 String* right = String::cast(current->second());
5698 offset -= left_length;
5699 offset_correction += left_length;
5700 String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars);
5701 }
5702 *offset_ptr = offset + offset_correction;
5703 return;
5704 }
5705 }
5706}
5707
5708
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005709uint16_t ConsString::ConsStringGet(int index) {
5710 ASSERT(index >= 0 && index < this->length());
5711
5712 // Check for a flattened cons string
ager@chromium.org870a0b62008-11-04 11:43:05 +00005713 if (second()->length() == 0) {
5714 String* left = first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005715 return left->Get(index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005716 }
5717
5718 String* string = String::cast(this);
5719
5720 while (true) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005721 if (StringShape(string).IsCons()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005722 ConsString* cons_string = ConsString::cast(string);
ager@chromium.org870a0b62008-11-04 11:43:05 +00005723 String* left = cons_string->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005724 if (left->length() > index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005725 string = left;
5726 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005727 index -= left->length();
ager@chromium.org870a0b62008-11-04 11:43:05 +00005728 string = cons_string->second();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005729 }
5730 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005731 return string->Get(index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005732 }
5733 }
5734
5735 UNREACHABLE();
5736 return 0;
5737}
5738
5739
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005740uint16_t SlicedString::SlicedStringGet(int index) {
5741 return parent()->Get(offset() + index);
5742}
5743
5744
5745const unibrow::byte* SlicedString::SlicedStringReadBlock(
5746 ReadBlockBuffer* buffer, unsigned* offset_ptr, unsigned chars) {
5747 unsigned offset = this->offset();
5748 *offset_ptr += offset;
5749 const unibrow::byte* answer = String::ReadBlock(String::cast(parent()),
5750 buffer, offset_ptr, chars);
5751 *offset_ptr -= offset;
5752 return answer;
5753}
5754
5755
5756void SlicedString::SlicedStringReadBlockIntoBuffer(
5757 ReadBlockBuffer* buffer, unsigned* offset_ptr, unsigned chars) {
5758 unsigned offset = this->offset();
5759 *offset_ptr += offset;
5760 String::ReadBlockIntoBuffer(String::cast(parent()),
5761 buffer, offset_ptr, chars);
5762 *offset_ptr -= offset;
5763}
5764
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005765template <typename sinkchar>
5766void String::WriteToFlat(String* src,
5767 sinkchar* sink,
5768 int f,
5769 int t) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005770 String* source = src;
5771 int from = f;
5772 int to = t;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005773 while (true) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005774 ASSERT(0 <= from && from <= to && to <= source->length());
5775 switch (StringShape(source).full_representation_tag()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005776 case kAsciiStringTag | kExternalStringTag: {
5777 CopyChars(sink,
5778 ExternalAsciiString::cast(source)->resource()->data() + from,
5779 to - from);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005780 return;
5781 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005782 case kTwoByteStringTag | kExternalStringTag: {
5783 const uc16* data =
5784 ExternalTwoByteString::cast(source)->resource()->data();
5785 CopyChars(sink,
5786 data + from,
5787 to - from);
5788 return;
5789 }
5790 case kAsciiStringTag | kSeqStringTag: {
5791 CopyChars(sink,
5792 SeqAsciiString::cast(source)->GetChars() + from,
5793 to - from);
5794 return;
5795 }
5796 case kTwoByteStringTag | kSeqStringTag: {
5797 CopyChars(sink,
5798 SeqTwoByteString::cast(source)->GetChars() + from,
5799 to - from);
5800 return;
5801 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005802 case kAsciiStringTag | kConsStringTag:
5803 case kTwoByteStringTag | kConsStringTag: {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005804 ConsString* cons_string = ConsString::cast(source);
ager@chromium.org870a0b62008-11-04 11:43:05 +00005805 String* first = cons_string->first();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005806 int boundary = first->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00005807 if (to - boundary >= boundary - from) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005808 // Right hand side is longer. Recurse over left.
5809 if (from < boundary) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005810 WriteToFlat(first, sink, from, boundary);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005811 sink += boundary - from;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005812 from = 0;
5813 } else {
5814 from -= boundary;
5815 }
5816 to -= boundary;
ager@chromium.org870a0b62008-11-04 11:43:05 +00005817 source = cons_string->second();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005818 } else {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005819 // Left hand side is longer. Recurse over right.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005820 if (to > boundary) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00005821 String* second = cons_string->second();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005822 WriteToFlat(second,
5823 sink + boundary - from,
5824 0,
5825 to - boundary);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005826 to = boundary;
5827 }
5828 source = first;
5829 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005830 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005831 }
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005832 case kAsciiStringTag | kSlicedStringTag:
5833 case kTwoByteStringTag | kSlicedStringTag: {
5834 SlicedString* slice = SlicedString::cast(source);
5835 unsigned offset = slice->offset();
5836 WriteToFlat(slice->parent(), sink, from + offset, to + offset);
5837 return;
5838 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005839 }
5840 }
5841}
5842
5843
ager@chromium.org7c537e22008-10-16 08:43:32 +00005844template <typename IteratorA, typename IteratorB>
5845static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
5846 // General slow case check. We know that the ia and ib iterators
5847 // have the same length.
5848 while (ia->has_more()) {
5849 uc32 ca = ia->GetNext();
5850 uc32 cb = ib->GetNext();
5851 if (ca != cb)
5852 return false;
5853 }
5854 return true;
5855}
5856
5857
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005858// Compares the contents of two strings by reading and comparing
5859// int-sized blocks of characters.
5860template <typename Char>
5861static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
5862 int length = a.length();
5863 ASSERT_EQ(length, b.length());
5864 const Char* pa = a.start();
5865 const Char* pb = b.start();
5866 int i = 0;
ager@chromium.org9085a012009-05-11 19:22:57 +00005867#ifndef V8_HOST_CAN_READ_UNALIGNED
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005868 // If this architecture isn't comfortable reading unaligned ints
5869 // then we have to check that the strings are aligned before
5870 // comparing them blockwise.
5871 const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
5872 uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
5873 uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005874 if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005875#endif
5876 const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
5877 int endpoint = length - kStepSize;
5878 // Compare blocks until we reach near the end of the string.
5879 for (; i <= endpoint; i += kStepSize) {
5880 uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
5881 uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
5882 if (wa != wb) {
5883 return false;
5884 }
5885 }
ager@chromium.org9085a012009-05-11 19:22:57 +00005886#ifndef V8_HOST_CAN_READ_UNALIGNED
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005887 }
5888#endif
5889 // Compare the remaining characters that didn't fit into a block.
5890 for (; i < length; i++) {
5891 if (a[i] != b[i]) {
5892 return false;
5893 }
5894 }
5895 return true;
5896}
5897
5898
ager@chromium.org7c537e22008-10-16 08:43:32 +00005899template <typename IteratorA>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005900static inline bool CompareStringContentsPartial(Isolate* isolate,
5901 IteratorA* ia,
5902 String* b) {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005903 String::FlatContent content = b->GetFlatContent();
5904 if (content.IsFlat()) {
5905 if (content.IsAscii()) {
5906 VectorIterator<char> ib(content.ToAsciiVector());
ager@chromium.org7c537e22008-10-16 08:43:32 +00005907 return CompareStringContents(ia, &ib);
5908 } else {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005909 VectorIterator<uc16> ib(content.ToUC16Vector());
ager@chromium.org7c537e22008-10-16 08:43:32 +00005910 return CompareStringContents(ia, &ib);
5911 }
5912 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005913 isolate->objects_string_compare_buffer_b()->Reset(0, b);
5914 return CompareStringContents(ia,
5915 isolate->objects_string_compare_buffer_b());
ager@chromium.org7c537e22008-10-16 08:43:32 +00005916 }
5917}
5918
5919
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005920bool String::SlowEquals(String* other) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005921 // Fast check: negative check with lengths.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005922 int len = length();
5923 if (len != other->length()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005924 if (len == 0) return true;
5925
5926 // Fast check: if hash code is computed for both strings
5927 // a fast negative check can be performed.
5928 if (HasHashCode() && other->HasHashCode()) {
5929 if (Hash() != other->Hash()) return false;
5930 }
5931
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005932 // We know the strings are both non-empty. Compare the first chars
5933 // before we try to flatten the strings.
5934 if (this->Get(0) != other->Get(0)) return false;
5935
5936 String* lhs = this->TryFlattenGetString();
5937 String* rhs = other->TryFlattenGetString();
5938
5939 if (StringShape(lhs).IsSequentialAscii() &&
5940 StringShape(rhs).IsSequentialAscii()) {
5941 const char* str1 = SeqAsciiString::cast(lhs)->GetChars();
5942 const char* str2 = SeqAsciiString::cast(rhs)->GetChars();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005943 return CompareRawStringContents(Vector<const char>(str1, len),
5944 Vector<const char>(str2, len));
5945 }
5946
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005947 Isolate* isolate = GetIsolate();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005948 String::FlatContent lhs_content = lhs->GetFlatContent();
5949 String::FlatContent rhs_content = rhs->GetFlatContent();
5950 if (lhs_content.IsFlat()) {
5951 if (lhs_content.IsAscii()) {
5952 Vector<const char> vec1 = lhs_content.ToAsciiVector();
5953 if (rhs_content.IsFlat()) {
5954 if (rhs_content.IsAscii()) {
5955 Vector<const char> vec2 = rhs_content.ToAsciiVector();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005956 return CompareRawStringContents(vec1, vec2);
5957 } else {
5958 VectorIterator<char> buf1(vec1);
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005959 VectorIterator<uc16> ib(rhs_content.ToUC16Vector());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005960 return CompareStringContents(&buf1, &ib);
5961 }
5962 } else {
5963 VectorIterator<char> buf1(vec1);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005964 isolate->objects_string_compare_buffer_b()->Reset(0, rhs);
5965 return CompareStringContents(&buf1,
5966 isolate->objects_string_compare_buffer_b());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005967 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00005968 } else {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005969 Vector<const uc16> vec1 = lhs_content.ToUC16Vector();
5970 if (rhs_content.IsFlat()) {
5971 if (rhs_content.IsAscii()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005972 VectorIterator<uc16> buf1(vec1);
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005973 VectorIterator<char> ib(rhs_content.ToAsciiVector());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005974 return CompareStringContents(&buf1, &ib);
5975 } else {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00005976 Vector<const uc16> vec2(rhs_content.ToUC16Vector());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005977 return CompareRawStringContents(vec1, vec2);
5978 }
5979 } else {
5980 VectorIterator<uc16> buf1(vec1);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005981 isolate->objects_string_compare_buffer_b()->Reset(0, rhs);
5982 return CompareStringContents(&buf1,
5983 isolate->objects_string_compare_buffer_b());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005984 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005985 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00005986 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005987 isolate->objects_string_compare_buffer_a()->Reset(0, lhs);
5988 return CompareStringContentsPartial(isolate,
5989 isolate->objects_string_compare_buffer_a(), rhs);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005990 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005991}
5992
5993
5994bool String::MarkAsUndetectable() {
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005995 if (StringShape(this).IsSymbol()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005996
5997 Map* map = this->map();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00005998 Heap* heap = map->heap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005999 if (map == heap->string_map()) {
6000 this->set_map(heap->undetectable_string_map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006001 return true;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006002 } else if (map == heap->ascii_string_map()) {
6003 this->set_map(heap->undetectable_ascii_string_map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006004 return true;
6005 }
6006 // Rest cannot be marked as undetectable
6007 return false;
6008}
6009
6010
6011bool String::IsEqualTo(Vector<const char> str) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006012 Isolate* isolate = GetIsolate();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006013 int slen = length();
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +00006014 Access<UnicodeCache::Utf8Decoder>
6015 decoder(isolate->unicode_cache()->utf8_decoder());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006016 decoder->Reset(str.start(), str.length());
6017 int i;
6018 for (i = 0; i < slen && decoder->has_more(); i++) {
6019 uc32 r = decoder->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006020 if (Get(i) != r) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006021 }
6022 return i == slen && !decoder->has_more();
6023}
6024
6025
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00006026bool String::IsAsciiEqualTo(Vector<const char> str) {
6027 int slen = length();
6028 if (str.length() != slen) return false;
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00006029 FlatContent content = GetFlatContent();
6030 if (content.IsAscii()) {
6031 return CompareChars(content.ToAsciiVector().start(),
6032 str.start(), slen) == 0;
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00006033 }
6034 for (int i = 0; i < slen; i++) {
6035 if (Get(i) != static_cast<uint16_t>(str[i])) return false;
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00006036 }
6037 return true;
6038}
6039
6040
6041bool String::IsTwoByteEqualTo(Vector<const uc16> str) {
6042 int slen = length();
6043 if (str.length() != slen) return false;
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00006044 FlatContent content = GetFlatContent();
6045 if (content.IsTwoByte()) {
6046 return CompareChars(content.ToUC16Vector().start(), str.start(), slen) == 0;
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00006047 }
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00006048 for (int i = 0; i < slen; i++) {
6049 if (Get(i) != str[i]) return false;
6050 }
6051 return true;
6052}
6053
6054
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006055uint32_t String::ComputeAndSetHash() {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00006056 // Should only be called if hash code has not yet been computed.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006057 ASSERT(!HasHashCode());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006058
ricow@chromium.orgaa1b6162010-03-29 07:44:58 +00006059 const int len = length();
6060
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006061 // Compute the hash code.
ricow@chromium.orgaa1b6162010-03-29 07:44:58 +00006062 uint32_t field = 0;
6063 if (StringShape(this).IsSequentialAscii()) {
6064 field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len);
6065 } else if (StringShape(this).IsSequentialTwoByte()) {
6066 field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len);
6067 } else {
6068 StringInputBuffer buffer(this);
6069 field = ComputeHashField(&buffer, len);
6070 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006071
6072 // Store the hash code in the object.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006073 set_hash_field(field);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006074
6075 // Check the hash code is there.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006076 ASSERT(HasHashCode());
ager@chromium.org3b45ab52009-03-19 22:21:34 +00006077 uint32_t result = field >> kHashShift;
6078 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
6079 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006080}
6081
6082
6083bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
6084 uint32_t* index,
6085 int length) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006086 if (length == 0 || length > kMaxArrayIndexSize) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006087 uc32 ch = buffer->GetNext();
6088
6089 // If the string begins with a '0' character, it must only consist
6090 // of it to be a legal array index.
6091 if (ch == '0') {
6092 *index = 0;
6093 return length == 1;
6094 }
6095
6096 // Convert string to uint32 array index; character by character.
6097 int d = ch - '0';
6098 if (d < 0 || d > 9) return false;
6099 uint32_t result = d;
6100 while (buffer->has_more()) {
6101 d = buffer->GetNext() - '0';
6102 if (d < 0 || d > 9) return false;
6103 // Check that the new result is below the 32 bit limit.
6104 if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
6105 result = (result * 10) + d;
6106 }
6107
6108 *index = result;
6109 return true;
6110}
6111
6112
6113bool String::SlowAsArrayIndex(uint32_t* index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006114 if (length() <= kMaxCachedArrayIndexLength) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006115 Hash(); // force computation of hash code
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006116 uint32_t field = hash_field();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00006117 if ((field & kIsNotArrayIndexMask) != 0) return false;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006118 // Isolate the array index form the full hash field.
6119 *index = (kArrayIndexHashMask & field) >> kHashShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006120 return true;
6121 } else {
6122 StringInputBuffer buffer(this);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006123 return ComputeArrayIndex(&buffer, index, length());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006124 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006125}
6126
6127
ager@chromium.org5b2fbee2010-09-08 06:38:15 +00006128uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00006129 // For array indexes mix the length into the hash as an array index could
6130 // be zero.
6131 ASSERT(length > 0);
6132 ASSERT(length <= String::kMaxArrayIndexSize);
6133 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
6134 (1 << String::kArrayIndexValueBits));
ager@chromium.org5b2fbee2010-09-08 06:38:15 +00006135
6136 value <<= String::kHashShift;
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00006137 value |= length << String::kArrayIndexHashLengthShift;
ager@chromium.org5b2fbee2010-09-08 06:38:15 +00006138
6139 ASSERT((value & String::kIsNotArrayIndexMask) == 0);
6140 ASSERT((length > String::kMaxCachedArrayIndexLength) ||
6141 (value & String::kContainsCachedArrayIndexMask) == 0);
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00006142 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006143}
6144
6145
ager@chromium.org7c537e22008-10-16 08:43:32 +00006146uint32_t StringHasher::GetHashField() {
6147 ASSERT(is_valid());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006148 if (length_ <= String::kMaxHashCalcLength) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006149 if (is_array_index()) {
ager@chromium.org5b2fbee2010-09-08 06:38:15 +00006150 return MakeArrayIndexHash(array_index(), length_);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006151 }
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00006152 return (GetHash() << String::kHashShift) | String::kIsNotArrayIndexMask;
ager@chromium.org7c537e22008-10-16 08:43:32 +00006153 } else {
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00006154 return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006155 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006156}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006157
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006158
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006159uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
6160 int length) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006161 StringHasher hasher(length);
6162
6163 // Very long strings have a trivial hash that doesn't inspect the
6164 // string contents.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006165 if (hasher.has_trivial_hash()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006166 return hasher.GetHashField();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006167 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006168
6169 // Do the iterative array index computation as long as there is a
6170 // chance this is an array index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006171 while (buffer->has_more() && hasher.is_array_index()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006172 hasher.AddCharacter(buffer->GetNext());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006173 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006174
6175 // Process the remaining characters without updating the array
6176 // index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006177 while (buffer->has_more()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006178 hasher.AddCharacterNoIndex(buffer->GetNext());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006179 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006180
6181 return hasher.GetHashField();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006182}
6183
6184
lrn@chromium.org303ada72010-10-27 09:33:13 +00006185MaybeObject* String::SubString(int start, int end, PretenureFlag pretenure) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006186 Heap* heap = GetHeap();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006187 if (start == 0 && end == length()) return this;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006188 MaybeObject* result = heap->AllocateSubString(this, start, end, pretenure);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006189 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006190}
6191
6192
6193void String::PrintOn(FILE* file) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006194 int length = this->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006195 for (int i = 0; i < length; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006196 fprintf(file, "%c", Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006197 }
6198}
6199
6200
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006201void Map::CreateBackPointers() {
6202 DescriptorArray* descriptors = instance_descriptors();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006203 for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00006204 if (descriptors->GetType(i) == MAP_TRANSITION ||
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006205 descriptors->GetType(i) == EXTERNAL_ARRAY_TRANSITION ||
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00006206 descriptors->GetType(i) == CONSTANT_TRANSITION) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006207 // Get target.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006208 Map* target = Map::cast(descriptors->GetValue(i));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006209#ifdef DEBUG
6210 // Verify target.
6211 Object* source_prototype = prototype();
6212 Object* target_prototype = target->prototype();
6213 ASSERT(source_prototype->IsJSObject() ||
6214 source_prototype->IsMap() ||
6215 source_prototype->IsNull());
6216 ASSERT(target_prototype->IsJSObject() ||
6217 target_prototype->IsNull());
6218 ASSERT(source_prototype->IsMap() ||
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006219 source_prototype == target_prototype);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006220#endif
6221 // Point target back to source. set_prototype() will not let us set
6222 // the prototype to a map, as we do here.
6223 *RawField(target, kPrototypeOffset) = this;
6224 }
6225 }
6226}
6227
6228
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006229void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006230 // Live DescriptorArray objects will be marked, so we must use
6231 // low-level accessors to get and modify their data.
6232 DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
danno@chromium.org40cb8782011-05-25 07:58:50 +00006233 *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset));
6234 if (d->IsEmpty()) return;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006235 Smi* NullDescriptorDetails =
6236 PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
6237 FixedArray* contents = reinterpret_cast<FixedArray*>(
6238 d->get(DescriptorArray::kContentArrayIndex));
6239 ASSERT(contents->length() >= 2);
6240 for (int i = 0; i < contents->length(); i += 2) {
6241 // If the pair (value, details) is a map transition,
6242 // check if the target is live. If not, null the descriptor.
6243 // Also drop the back pointer for that map transition, so that this
6244 // map is not reached again by following a back pointer from a
6245 // non-live object.
6246 PropertyDetails details(Smi::cast(contents->get(i + 1)));
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00006247 if (details.type() == MAP_TRANSITION ||
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006248 details.type() == EXTERNAL_ARRAY_TRANSITION ||
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00006249 details.type() == CONSTANT_TRANSITION) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006250 Map* target = reinterpret_cast<Map*>(contents->get(i));
6251 ASSERT(target->IsHeapObject());
6252 if (!target->IsMarked()) {
6253 ASSERT(target->IsMap());
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00006254 contents->set_unchecked(i + 1, NullDescriptorDetails);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006255 contents->set_null_unchecked(heap, i);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006256 ASSERT(target->prototype() == this ||
6257 target->prototype() == real_prototype);
6258 // Getter prototype() is read-only, set_prototype() has side effects.
6259 *RawField(target, Map::kPrototypeOffset) = real_prototype;
6260 }
6261 }
6262 }
6263}
6264
6265
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00006266int Map::Hash() {
6267 // For performance reasons we only hash the 3 most variable fields of a map:
6268 // constructor, prototype and bit_field2.
6269
6270 // Shift away the tag.
6271 int hash = (static_cast<uint32_t>(
6272 reinterpret_cast<uintptr_t>(constructor())) >> 2);
6273
6274 // XOR-ing the prototype and constructor directly yields too many zero bits
6275 // when the two pointers are close (which is fairly common).
6276 // To avoid this we shift the prototype 4 bits relatively to the constructor.
6277 hash ^= (static_cast<uint32_t>(
6278 reinterpret_cast<uintptr_t>(prototype())) << 2);
6279
6280 return hash ^ (hash >> 16) ^ bit_field2();
6281}
6282
6283
6284bool Map::EquivalentToForNormalization(Map* other,
6285 PropertyNormalizationMode mode) {
6286 return
6287 constructor() == other->constructor() &&
6288 prototype() == other->prototype() &&
6289 inobject_properties() == ((mode == CLEAR_INOBJECT_PROPERTIES) ?
6290 0 :
6291 other->inobject_properties()) &&
6292 instance_type() == other->instance_type() &&
6293 bit_field() == other->bit_field() &&
6294 bit_field2() == other->bit_field2() &&
6295 (bit_field3() & ~(1<<Map::kIsShared)) ==
6296 (other->bit_field3() & ~(1<<Map::kIsShared));
6297}
6298
6299
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00006300void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) {
6301 // Iterate over all fields in the body but take care in dealing with
6302 // the code entry.
6303 IteratePointers(v, kPropertiesOffset, kCodeEntryOffset);
6304 v->VisitCodeEntry(this->address() + kCodeEntryOffset);
6305 IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size);
6306}
6307
6308
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006309void JSFunction::MarkForLazyRecompilation() {
6310 ASSERT(is_compiled() && !IsOptimized());
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006311 ASSERT(shared()->allows_lazy_compilation() ||
6312 code()->optimizable());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006313 Builtins* builtins = GetIsolate()->builtins();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006314 ReplaceCode(builtins->builtin(Builtins::kLazyRecompile));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006315}
6316
6317
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006318bool JSFunction::IsInlineable() {
6319 if (IsBuiltin()) return false;
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00006320 SharedFunctionInfo* shared_info = shared();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006321 // Check that the function has a script associated with it.
vegorov@chromium.org5d6c1f52011-02-28 13:13:38 +00006322 if (!shared_info->script()->IsScript()) return false;
6323 if (shared_info->optimization_disabled()) return false;
6324 Code* code = shared_info->code();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006325 if (code->kind() == Code::OPTIMIZED_FUNCTION) return true;
6326 // If we never ran this (unlikely) then lets try to optimize it.
6327 if (code->kind() != Code::FUNCTION) return true;
6328 return code->optimizable();
6329}
6330
6331
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006332Object* JSFunction::SetInstancePrototype(Object* value) {
6333 ASSERT(value->IsJSObject());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006334 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006335 if (has_initial_map()) {
6336 initial_map()->set_prototype(value);
6337 } else {
6338 // Put the value in the initial map field until an initial map is
6339 // needed. At that point, a new initial map is created and the
6340 // prototype is put into the initial map where it belongs.
6341 set_prototype_or_initial_map(value);
6342 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006343 heap->ClearInstanceofCache();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006344 return value;
6345}
6346
6347
lrn@chromium.org303ada72010-10-27 09:33:13 +00006348MaybeObject* JSFunction::SetPrototype(Object* value) {
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006349 ASSERT(should_have_prototype());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006350 Object* construct_prototype = value;
6351
6352 // If the value is not a JSObject, store the value in the map's
6353 // constructor field so it can be accessed. Also, set the prototype
6354 // used for constructing objects to the original object prototype.
6355 // See ECMA-262 13.2.2.
6356 if (!value->IsJSObject()) {
6357 // Copy the map so this does not affect unrelated functions.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006358 // Remove map transitions because they point to maps with a
6359 // different prototype.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006360 Object* new_object;
lrn@chromium.org303ada72010-10-27 09:33:13 +00006361 { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006362 if (!maybe_new_map->ToObject(&new_object)) return maybe_new_map;
lrn@chromium.org303ada72010-10-27 09:33:13 +00006363 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006364 Map* new_map = Map::cast(new_object);
6365 Heap* heap = new_map->heap();
6366 set_map(new_map);
6367 new_map->set_constructor(value);
6368 new_map->set_non_instance_prototype(true);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006369 construct_prototype =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006370 heap->isolate()->context()->global_context()->
6371 initial_object_prototype();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006372 } else {
6373 map()->set_non_instance_prototype(false);
6374 }
6375
6376 return SetInstancePrototype(construct_prototype);
6377}
6378
6379
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006380Object* JSFunction::RemovePrototype() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006381 Context* global_context = context()->global_context();
6382 Map* no_prototype_map = shared()->strict_mode()
6383 ? global_context->strict_mode_function_without_prototype_map()
6384 : global_context->function_without_prototype_map();
6385
6386 if (map() == no_prototype_map) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00006387 // Be idempotent.
6388 return this;
6389 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006390
6391 ASSERT(!shared()->strict_mode() ||
6392 map() == global_context->strict_mode_function_map());
6393 ASSERT(shared()->strict_mode() || map() == global_context->function_map());
6394
6395 set_map(no_prototype_map);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006396 set_prototype_or_initial_map(no_prototype_map->heap()->the_hole_value());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006397 return this;
6398}
6399
6400
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006401Object* JSFunction::SetInstanceClassName(String* name) {
6402 shared()->set_instance_class_name(name);
6403 return this;
6404}
6405
6406
whesse@chromium.org023421e2010-12-21 12:19:12 +00006407void JSFunction::PrintName(FILE* out) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006408 SmartPointer<char> name = shared()->DebugName()->ToCString();
whesse@chromium.org023421e2010-12-21 12:19:12 +00006409 PrintF(out, "%s", *name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006410}
6411
6412
ager@chromium.org236ad962008-09-25 09:45:57 +00006413Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
6414 return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
6415}
6416
6417
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006418MaybeObject* Oddball::Initialize(const char* to_string,
6419 Object* to_number,
6420 byte kind) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00006421 Object* symbol;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006422 { MaybeObject* maybe_symbol =
6423 Isolate::Current()->heap()->LookupAsciiSymbol(to_string);
lrn@chromium.org303ada72010-10-27 09:33:13 +00006424 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
6425 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006426 set_to_string(String::cast(symbol));
6427 set_to_number(to_number);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006428 set_kind(kind);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006429 return this;
6430}
6431
6432
ager@chromium.orgb61a0d12010-10-13 08:35:23 +00006433String* SharedFunctionInfo::DebugName() {
6434 Object* n = name();
6435 if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name();
6436 return String::cast(n);
6437}
6438
6439
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006440bool SharedFunctionInfo::HasSourceCode() {
6441 return !script()->IsUndefined() &&
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00006442 !reinterpret_cast<Script*>(script())->source()->IsUndefined();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006443}
6444
6445
6446Object* SharedFunctionInfo::GetSourceCode() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006447 Isolate* isolate = GetIsolate();
6448 if (!HasSourceCode()) return isolate->heap()->undefined_value();
6449 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006450 Object* source = Script::cast(script())->source();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006451 return *SubString(Handle<String>(String::cast(source), isolate),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006452 start_position(), end_position());
6453}
6454
6455
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006456int SharedFunctionInfo::SourceSize() {
6457 return end_position() - start_position();
6458}
6459
6460
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006461int SharedFunctionInfo::CalculateInstanceSize() {
6462 int instance_size =
6463 JSObject::kHeaderSize +
6464 expected_nof_properties() * kPointerSize;
6465 if (instance_size > JSObject::kMaxInstanceSize) {
6466 instance_size = JSObject::kMaxInstanceSize;
6467 }
6468 return instance_size;
6469}
6470
6471
6472int SharedFunctionInfo::CalculateInObjectProperties() {
6473 return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
6474}
6475
6476
ager@chromium.org5c838252010-02-19 08:53:10 +00006477bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
6478 // Check the basic conditions for generating inline constructor code.
6479 if (!FLAG_inline_new
6480 || !has_only_simple_this_property_assignments()
6481 || this_property_assignments_count() == 0) {
6482 return false;
6483 }
6484
6485 // If the prototype is null inline constructors cause no problems.
6486 if (!prototype->IsJSObject()) {
6487 ASSERT(prototype->IsNull());
6488 return true;
6489 }
6490
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006491 Heap* heap = GetHeap();
6492
ager@chromium.org5c838252010-02-19 08:53:10 +00006493 // Traverse the proposed prototype chain looking for setters for properties of
6494 // the same names as are set by the inline constructor.
6495 for (Object* obj = prototype;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006496 obj != heap->null_value();
ager@chromium.org5c838252010-02-19 08:53:10 +00006497 obj = obj->GetPrototype()) {
6498 JSObject* js_object = JSObject::cast(obj);
6499 for (int i = 0; i < this_property_assignments_count(); i++) {
6500 LookupResult result;
6501 String* name = GetThisPropertyAssignmentName(i);
6502 js_object->LocalLookupRealNamedProperty(name, &result);
6503 if (result.IsProperty() && result.type() == CALLBACKS) {
6504 return false;
6505 }
6506 }
6507 }
6508
6509 return true;
6510}
6511
6512
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +00006513void SharedFunctionInfo::ForbidInlineConstructor() {
6514 set_compiler_hints(BooleanBit::set(compiler_hints(),
6515 kHasOnlySimpleThisPropertyAssignments,
6516 false));
6517}
6518
6519
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006520void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006521 bool only_simple_this_property_assignments,
6522 FixedArray* assignments) {
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006523 set_compiler_hints(BooleanBit::set(compiler_hints(),
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006524 kHasOnlySimpleThisPropertyAssignments,
6525 only_simple_this_property_assignments));
6526 set_this_property_assignments(assignments);
6527 set_this_property_assignments_count(assignments->length() / 3);
6528}
6529
6530
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006531void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006532 Heap* heap = GetHeap();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006533 set_compiler_hints(BooleanBit::set(compiler_hints(),
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006534 kHasOnlySimpleThisPropertyAssignments,
6535 false));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006536 set_this_property_assignments(heap->undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006537 set_this_property_assignments_count(0);
6538}
6539
6540
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006541String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
6542 Object* obj = this_property_assignments();
6543 ASSERT(obj->IsFixedArray());
6544 ASSERT(index < this_property_assignments_count());
6545 obj = FixedArray::cast(obj)->get(index * 3);
6546 ASSERT(obj->IsString());
6547 return String::cast(obj);
6548}
6549
6550
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006551bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
6552 Object* obj = this_property_assignments();
6553 ASSERT(obj->IsFixedArray());
6554 ASSERT(index < this_property_assignments_count());
6555 obj = FixedArray::cast(obj)->get(index * 3 + 1);
6556 return Smi::cast(obj)->value() != -1;
6557}
6558
6559
6560int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
6561 ASSERT(IsThisPropertyAssignmentArgument(index));
6562 Object* obj =
6563 FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
6564 return Smi::cast(obj)->value();
6565}
6566
6567
6568Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
6569 ASSERT(!IsThisPropertyAssignmentArgument(index));
6570 Object* obj =
6571 FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
6572 return obj;
6573}
6574
6575
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006576// Support function for printing the source code to a StringStream
6577// without any allocation in the heap.
6578void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
6579 int max_length) {
6580 // For some native functions there is no source.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006581 if (!HasSourceCode()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006582 accumulator->Add("<No Source>");
6583 return;
6584 }
6585
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006586 // Get the source for the script which this function came from.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006587 // Don't use String::cast because we don't want more assertion errors while
6588 // we are already creating a stack dump.
6589 String* script_source =
6590 reinterpret_cast<String*>(Script::cast(script())->source());
6591
6592 if (!script_source->LooksValid()) {
6593 accumulator->Add("<Invalid Source>");
6594 return;
6595 }
6596
6597 if (!is_toplevel()) {
6598 accumulator->Add("function ");
6599 Object* name = this->name();
6600 if (name->IsString() && String::cast(name)->length() > 0) {
6601 accumulator->PrintName(name);
6602 }
6603 }
6604
6605 int len = end_position() - start_position();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006606 if (len <= max_length || max_length < 0) {
6607 accumulator->Put(script_source, start_position(), end_position());
6608 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006609 accumulator->Put(script_source,
6610 start_position(),
6611 start_position() + max_length);
6612 accumulator->Add("...\n");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006613 }
6614}
6615
6616
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006617static bool IsCodeEquivalent(Code* code, Code* recompiled) {
6618 if (code->instruction_size() != recompiled->instruction_size()) return false;
6619 ByteArray* code_relocation = code->relocation_info();
6620 ByteArray* recompiled_relocation = recompiled->relocation_info();
6621 int length = code_relocation->length();
6622 if (length != recompiled_relocation->length()) return false;
6623 int compare = memcmp(code_relocation->GetDataStartAddress(),
6624 recompiled_relocation->GetDataStartAddress(),
6625 length);
6626 return compare == 0;
6627}
6628
6629
6630void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) {
6631 ASSERT(!has_deoptimization_support());
6632 AssertNoAllocation no_allocation;
6633 Code* code = this->code();
6634 if (IsCodeEquivalent(code, recompiled)) {
6635 // Copy the deoptimization data from the recompiled code.
6636 code->set_deoptimization_data(recompiled->deoptimization_data());
6637 code->set_has_deoptimization_support(true);
6638 } else {
6639 // TODO(3025757): In case the recompiled isn't equivalent to the
6640 // old code, we have to replace it. We should try to avoid this
6641 // altogether because it flushes valuable type feedback by
6642 // effectively resetting all IC state.
6643 set_code(recompiled);
6644 }
6645 ASSERT(has_deoptimization_support());
6646}
6647
6648
ager@chromium.orgea91cc52011-05-23 06:06:11 +00006649void SharedFunctionInfo::DisableOptimization(JSFunction* function) {
6650 // Disable optimization for the shared function info and mark the
6651 // code as non-optimizable. The marker on the shared function info
6652 // is there because we flush non-optimized code thereby loosing the
6653 // non-optimizable information for the code. When the code is
6654 // regenerated and set on the shared function info it is marked as
6655 // non-optimizable if optimization is disabled for the shared
6656 // function info.
6657 set_optimization_disabled(true);
6658 // Code should be the lazy compilation stub or else unoptimized. If the
6659 // latter, disable optimization for the code too.
6660 ASSERT(code()->kind() == Code::FUNCTION || code()->kind() == Code::BUILTIN);
6661 if (code()->kind() == Code::FUNCTION) {
6662 code()->set_optimizable(false);
6663 }
6664 if (FLAG_trace_opt) {
6665 PrintF("[disabled optimization for: ");
6666 function->PrintName();
6667 PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function));
6668 }
6669}
6670
6671
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006672bool SharedFunctionInfo::VerifyBailoutId(int id) {
6673 // TODO(srdjan): debugging ARM crashes in hydrogen. OK to disable while
6674 // we are always bailing out on ARM.
6675
6676 ASSERT(id != AstNode::kNoNumber);
6677 Code* unoptimized = code();
6678 DeoptimizationOutputData* data =
6679 DeoptimizationOutputData::cast(unoptimized->deoptimization_data());
6680 unsigned ignore = Deoptimizer::GetOutputInfo(data, id, this);
6681 USE(ignore);
6682 return true; // Return true if there was no ASSERT.
6683}
6684
6685
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006686void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) {
6687 ASSERT(!IsInobjectSlackTrackingInProgress());
6688
6689 // Only initiate the tracking the first time.
6690 if (live_objects_may_exist()) return;
6691 set_live_objects_may_exist(true);
6692
6693 // No tracking during the snapshot construction phase.
6694 if (Serializer::enabled()) return;
6695
6696 if (map->unused_property_fields() == 0) return;
6697
6698 // Nonzero counter is a leftover from the previous attempt interrupted
6699 // by GC, keep it.
6700 if (construction_count() == 0) {
6701 set_construction_count(kGenerousAllocationCount);
6702 }
6703 set_initial_map(map);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006704 Builtins* builtins = map->heap()->isolate()->builtins();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006705 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric),
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006706 construct_stub());
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006707 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown));
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006708}
6709
6710
6711// Called from GC, hence reinterpret_cast and unchecked accessors.
6712void SharedFunctionInfo::DetachInitialMap() {
6713 Map* map = reinterpret_cast<Map*>(initial_map());
6714
6715 // Make the map remember to restore the link if it survives the GC.
6716 map->set_bit_field2(
6717 map->bit_field2() | (1 << Map::kAttachedToSharedFunctionInfo));
6718
6719 // Undo state changes made by StartInobjectTracking (except the
6720 // construction_count). This way if the initial map does not survive the GC
6721 // then StartInobjectTracking will be called again the next time the
6722 // constructor is called. The countdown will continue and (possibly after
6723 // several more GCs) CompleteInobjectSlackTracking will eventually be called.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006724 set_initial_map(map->heap()->raw_unchecked_undefined_value());
6725 Builtins* builtins = map->heap()->isolate()->builtins();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006726 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown),
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006727 *RawField(this, kConstructStubOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006728 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric));
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006729 // It is safe to clear the flag: it will be set again if the map is live.
6730 set_live_objects_may_exist(false);
6731}
6732
6733
6734// Called from GC, hence reinterpret_cast and unchecked accessors.
6735void SharedFunctionInfo::AttachInitialMap(Map* map) {
6736 map->set_bit_field2(
6737 map->bit_field2() & ~(1 << Map::kAttachedToSharedFunctionInfo));
6738
6739 // Resume inobject slack tracking.
6740 set_initial_map(map);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006741 Builtins* builtins = map->heap()->isolate()->builtins();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006742 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric),
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006743 *RawField(this, kConstructStubOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006744 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown));
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006745 // The map survived the gc, so there may be objects referencing it.
6746 set_live_objects_may_exist(true);
6747}
6748
6749
6750static void GetMinInobjectSlack(Map* map, void* data) {
6751 int slack = map->unused_property_fields();
6752 if (*reinterpret_cast<int*>(data) > slack) {
6753 *reinterpret_cast<int*>(data) = slack;
6754 }
6755}
6756
6757
6758static void ShrinkInstanceSize(Map* map, void* data) {
6759 int slack = *reinterpret_cast<int*>(data);
6760 map->set_inobject_properties(map->inobject_properties() - slack);
6761 map->set_unused_property_fields(map->unused_property_fields() - slack);
6762 map->set_instance_size(map->instance_size() - slack * kPointerSize);
6763
6764 // Visitor id might depend on the instance size, recalculate it.
6765 map->set_visitor_id(StaticVisitorBase::GetVisitorId(map));
6766}
6767
6768
6769void SharedFunctionInfo::CompleteInobjectSlackTracking() {
6770 ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress());
6771 Map* map = Map::cast(initial_map());
6772
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00006773 Heap* heap = map->heap();
6774 set_initial_map(heap->undefined_value());
6775 Builtins* builtins = heap->isolate()->builtins();
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006776 ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown),
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006777 construct_stub());
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006778 set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric));
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006779
6780 int slack = map->unused_property_fields();
6781 map->TraverseTransitionTree(&GetMinInobjectSlack, &slack);
6782 if (slack != 0) {
6783 // Resize the initial map and all maps in its transition tree.
6784 map->TraverseTransitionTree(&ShrinkInstanceSize, &slack);
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00006785
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00006786 // Give the correct expected_nof_properties to initial maps created later.
6787 ASSERT(expected_nof_properties() >= slack);
6788 set_expected_nof_properties(expected_nof_properties() - slack);
6789 }
6790}
6791
6792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006793void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
ager@chromium.org236ad962008-09-25 09:45:57 +00006794 ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006795 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
6796 Object* old_target = target;
6797 VisitPointer(&target);
6798 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006799}
6800
6801
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00006802void ObjectVisitor::VisitCodeEntry(Address entry_address) {
6803 Object* code = Code::GetObjectFromEntryAddress(entry_address);
6804 Object* old_code = code;
6805 VisitPointer(&code);
6806 if (code != old_code) {
6807 Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry();
6808 }
6809}
6810
6811
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006812void ObjectVisitor::VisitGlobalPropertyCell(RelocInfo* rinfo) {
6813 ASSERT(rinfo->rmode() == RelocInfo::GLOBAL_PROPERTY_CELL);
6814 Object* cell = rinfo->target_cell();
6815 Object* old_cell = cell;
6816 VisitPointer(&cell);
6817 if (cell != old_cell) {
6818 rinfo->set_target_cell(reinterpret_cast<JSGlobalPropertyCell*>(cell));
6819 }
6820}
6821
6822
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006823void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +00006824 ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
6825 rinfo->IsPatchedReturnSequence()) ||
6826 (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
6827 rinfo->IsPatchedDebugBreakSlotSequence()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006828 Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
6829 Object* old_target = target;
6830 VisitPointer(&target);
6831 CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006832}
6833
6834
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006835void Code::InvalidateRelocation() {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006836 set_relocation_info(heap()->empty_byte_array());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006837}
6838
6839
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006840void Code::Relocate(intptr_t delta) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006841 for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
6842 it.rinfo()->apply(delta);
6843 }
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006844 CPU::FlushICache(instruction_start(), instruction_size());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006845}
6846
6847
6848void Code::CopyFrom(const CodeDesc& desc) {
6849 // copy code
6850 memmove(instruction_start(), desc.buffer, desc.instr_size);
6851
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006852 // copy reloc info
6853 memmove(relocation_start(),
6854 desc.buffer + desc.buffer_size - desc.reloc_size,
6855 desc.reloc_size);
6856
6857 // unbox handles and relocate
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00006858 intptr_t delta = instruction_start() - desc.buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006859 int mode_mask = RelocInfo::kCodeTargetMask |
ager@chromium.org236ad962008-09-25 09:45:57 +00006860 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006861 RelocInfo::ModeMask(RelocInfo::GLOBAL_PROPERTY_CELL) |
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006862 RelocInfo::kApplyMask;
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00006863 Assembler* origin = desc.origin; // Needed to find target_object on X64.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006864 for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00006865 RelocInfo::Mode mode = it.rinfo()->rmode();
6866 if (mode == RelocInfo::EMBEDDED_OBJECT) {
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00006867 Handle<Object> p = it.rinfo()->target_object_handle(origin);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006868 it.rinfo()->set_target_object(*p);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006869 } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00006870 Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006871 it.rinfo()->set_target_cell(*cell);
ager@chromium.org236ad962008-09-25 09:45:57 +00006872 } else if (RelocInfo::IsCodeTarget(mode)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006873 // rewrite code handles in inline cache targets to direct
6874 // pointers to the first instruction in the code object
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00006875 Handle<Object> p = it.rinfo()->target_object_handle(origin);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006876 Code* code = Code::cast(*p);
6877 it.rinfo()->set_target_address(code->instruction_start());
6878 } else {
6879 it.rinfo()->apply(delta);
6880 }
6881 }
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006882 CPU::FlushICache(instruction_start(), instruction_size());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006883}
6884
6885
6886// Locate the source position which is closest to the address in the code. This
6887// is using the source position information embedded in the relocation info.
6888// The position returned is relative to the beginning of the script where the
6889// source for this function is found.
6890int Code::SourcePosition(Address pc) {
6891 int distance = kMaxInt;
ager@chromium.org236ad962008-09-25 09:45:57 +00006892 int position = RelocInfo::kNoPosition; // Initially no position found.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006893 // Run through all the relocation info to find the best matching source
6894 // position. All the code needs to be considered as the sequence of the
6895 // instructions in the code does not necessarily follow the same order as the
6896 // source.
6897 RelocIterator it(this, RelocInfo::kPositionMask);
6898 while (!it.done()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00006899 // Only look at positions after the current pc.
6900 if (it.rinfo()->pc() < pc) {
6901 // Get position and distance.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006902
6903 int dist = static_cast<int>(pc - it.rinfo()->pc());
6904 int pos = static_cast<int>(it.rinfo()->data());
ager@chromium.org236ad962008-09-25 09:45:57 +00006905 // If this position is closer than the current candidate or if it has the
6906 // same distance as the current candidate and the position is higher then
6907 // this position is the new candidate.
6908 if ((dist < distance) ||
6909 (dist == distance && pos > position)) {
6910 position = pos;
6911 distance = dist;
6912 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006913 }
6914 it.next();
6915 }
6916 return position;
6917}
6918
6919
6920// Same as Code::SourcePosition above except it only looks for statement
6921// positions.
6922int Code::SourceStatementPosition(Address pc) {
6923 // First find the position as close as possible using all position
6924 // information.
6925 int position = SourcePosition(pc);
6926 // Now find the closest statement position before the position.
6927 int statement_position = 0;
6928 RelocIterator it(this, RelocInfo::kPositionMask);
6929 while (!it.done()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00006930 if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006931 int p = static_cast<int>(it.rinfo()->data());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006932 if (statement_position < p && p <= position) {
6933 statement_position = p;
6934 }
6935 }
6936 it.next();
6937 }
6938 return statement_position;
6939}
6940
6941
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006942SafepointEntry Code::GetSafepointEntry(Address pc) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006943 SafepointTable table(this);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00006944 return table.FindEntry(pc);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006945}
6946
6947
6948void Code::SetNoStackCheckTable() {
6949 // Indicate the absence of a stack-check table by a table start after the
6950 // end of the instructions. Table start must be aligned, so round up.
ricow@chromium.org83aa5492011-02-07 12:42:56 +00006951 set_stack_check_table_offset(RoundUp(instruction_size(), kIntSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006952}
6953
6954
6955Map* Code::FindFirstMap() {
6956 ASSERT(is_inline_cache_stub());
6957 AssertNoAllocation no_allocation;
6958 int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
6959 for (RelocIterator it(this, mask); !it.done(); it.next()) {
6960 RelocInfo* info = it.rinfo();
6961 Object* object = info->target_object();
6962 if (object->IsMap()) return Map::cast(object);
6963 }
6964 return NULL;
6965}
6966
6967
ricow@chromium.org4f693d62011-07-04 14:01:31 +00006968#ifdef ENABLE_DISASSEMBLER
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006969
whesse@chromium.org023421e2010-12-21 12:19:12 +00006970void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006971 disasm::NameConverter converter;
6972 int deopt_count = DeoptCount();
whesse@chromium.org023421e2010-12-21 12:19:12 +00006973 PrintF(out, "Deoptimization Input Data (deopt points = %d)\n", deopt_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006974 if (0 == deopt_count) return;
6975
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00006976 PrintF(out, "%6s %6s %6s %12s\n", "index", "ast id", "argc",
6977 FLAG_print_code_verbose ? "commands" : "");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006978 for (int i = 0; i < deopt_count; i++) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00006979 PrintF(out, "%6d %6d %6d",
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006980 i, AstId(i)->value(), ArgumentsStackHeight(i)->value());
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00006981
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00006982 if (!FLAG_print_code_verbose) {
6983 PrintF(out, "\n");
6984 continue;
6985 }
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00006986 // Print details of the frame translation.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006987 int translation_index = TranslationIndex(i)->value();
6988 TranslationIterator iterator(TranslationByteArray(), translation_index);
6989 Translation::Opcode opcode =
6990 static_cast<Translation::Opcode>(iterator.Next());
6991 ASSERT(Translation::BEGIN == opcode);
6992 int frame_count = iterator.Next();
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00006993 PrintF(out, " %s {count=%d}\n", Translation::StringFor(opcode),
6994 frame_count);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006995
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00006996 while (iterator.HasNext() &&
6997 Translation::BEGIN !=
6998 (opcode = static_cast<Translation::Opcode>(iterator.Next()))) {
6999 PrintF(out, "%24s %s ", "", Translation::StringFor(opcode));
7000
7001 switch (opcode) {
7002 case Translation::BEGIN:
7003 UNREACHABLE();
7004 break;
7005
7006 case Translation::FRAME: {
7007 int ast_id = iterator.Next();
7008 int function_id = iterator.Next();
7009 JSFunction* function =
7010 JSFunction::cast(LiteralArray()->get(function_id));
7011 unsigned height = iterator.Next();
fschneider@chromium.org1805e212011-09-05 10:49:12 +00007012 PrintF(out, "{ast_id=%d, function=", ast_id);
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00007013 function->PrintName(out);
7014 PrintF(out, ", height=%u}", height);
7015 break;
7016 }
7017
7018 case Translation::DUPLICATE:
7019 break;
7020
7021 case Translation::REGISTER: {
7022 int reg_code = iterator.Next();
7023 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
7024 break;
7025 }
7026
7027 case Translation::INT32_REGISTER: {
7028 int reg_code = iterator.Next();
7029 PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
7030 break;
7031 }
7032
7033 case Translation::DOUBLE_REGISTER: {
7034 int reg_code = iterator.Next();
7035 PrintF(out, "{input=%s}",
7036 DoubleRegister::AllocationIndexToString(reg_code));
7037 break;
7038 }
7039
7040 case Translation::STACK_SLOT: {
7041 int input_slot_index = iterator.Next();
7042 PrintF(out, "{input=%d}", input_slot_index);
7043 break;
7044 }
7045
7046 case Translation::INT32_STACK_SLOT: {
7047 int input_slot_index = iterator.Next();
7048 PrintF(out, "{input=%d}", input_slot_index);
7049 break;
7050 }
7051
7052 case Translation::DOUBLE_STACK_SLOT: {
7053 int input_slot_index = iterator.Next();
7054 PrintF(out, "{input=%d}", input_slot_index);
7055 break;
7056 }
7057
7058 case Translation::LITERAL: {
7059 unsigned literal_index = iterator.Next();
7060 PrintF(out, "{literal_id=%u}", literal_index);
7061 break;
7062 }
7063
7064 case Translation::ARGUMENTS_OBJECT:
7065 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007066 }
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00007067 PrintF(out, "\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007068 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007069 }
7070}
7071
7072
whesse@chromium.org023421e2010-12-21 12:19:12 +00007073void DeoptimizationOutputData::DeoptimizationOutputDataPrint(FILE* out) {
7074 PrintF(out, "Deoptimization Output Data (deopt points = %d)\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007075 this->DeoptPoints());
7076 if (this->DeoptPoints() == 0) return;
7077
7078 PrintF("%6s %8s %s\n", "ast id", "pc", "state");
7079 for (int i = 0; i < this->DeoptPoints(); i++) {
7080 int pc_and_state = this->PcAndState(i)->value();
7081 PrintF("%6d %8d %s\n",
7082 this->AstId(i)->value(),
7083 FullCodeGenerator::PcField::decode(pc_and_state),
7084 FullCodeGenerator::State2String(
7085 FullCodeGenerator::StateField::decode(pc_and_state)));
7086 }
7087}
7088
whesse@chromium.org7b260152011-06-20 15:33:18 +00007089
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00007090// Identify kind of code.
7091const char* Code::Kind2String(Kind kind) {
7092 switch (kind) {
7093 case FUNCTION: return "FUNCTION";
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007094 case OPTIMIZED_FUNCTION: return "OPTIMIZED_FUNCTION";
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00007095 case STUB: return "STUB";
7096 case BUILTIN: return "BUILTIN";
7097 case LOAD_IC: return "LOAD_IC";
7098 case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
7099 case STORE_IC: return "STORE_IC";
7100 case KEYED_STORE_IC: return "KEYED_STORE_IC";
7101 case CALL_IC: return "CALL_IC";
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007102 case KEYED_CALL_IC: return "KEYED_CALL_IC";
danno@chromium.org40cb8782011-05-25 07:58:50 +00007103 case UNARY_OP_IC: return "UNARY_OP_IC";
7104 case BINARY_OP_IC: return "BINARY_OP_IC";
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007105 case COMPARE_IC: return "COMPARE_IC";
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007106 case TO_BOOLEAN_IC: return "TO_BOOLEAN_IC";
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00007107 }
7108 UNREACHABLE();
7109 return NULL;
7110}
mads.s.ager31e71382008-08-13 09:32:07 +00007111
7112
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007113const char* Code::ICState2String(InlineCacheState state) {
7114 switch (state) {
7115 case UNINITIALIZED: return "UNINITIALIZED";
7116 case PREMONOMORPHIC: return "PREMONOMORPHIC";
7117 case MONOMORPHIC: return "MONOMORPHIC";
7118 case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
7119 case MEGAMORPHIC: return "MEGAMORPHIC";
7120 case DEBUG_BREAK: return "DEBUG_BREAK";
7121 case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN";
7122 }
7123 UNREACHABLE();
7124 return NULL;
7125}
7126
7127
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007128const char* Code::PropertyType2String(PropertyType type) {
7129 switch (type) {
7130 case NORMAL: return "NORMAL";
7131 case FIELD: return "FIELD";
7132 case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
7133 case CALLBACKS: return "CALLBACKS";
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00007134 case HANDLER: return "HANDLER";
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007135 case INTERCEPTOR: return "INTERCEPTOR";
7136 case MAP_TRANSITION: return "MAP_TRANSITION";
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00007137 case EXTERNAL_ARRAY_TRANSITION: return "EXTERNAL_ARRAY_TRANSITION";
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007138 case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
7139 case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
7140 }
7141 UNREACHABLE();
7142 return NULL;
7143}
7144
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007145
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007146void Code::PrintExtraICState(FILE* out, Kind kind, ExtraICState extra) {
7147 const char* name = NULL;
7148 switch (kind) {
7149 case CALL_IC:
7150 if (extra == STRING_INDEX_OUT_OF_BOUNDS) {
7151 name = "STRING_INDEX_OUT_OF_BOUNDS";
7152 }
7153 break;
7154 case STORE_IC:
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00007155 case KEYED_STORE_IC:
7156 if (extra == kStrictMode) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007157 name = "STRICT";
7158 }
7159 break;
7160 default:
7161 break;
7162 }
7163 if (name != NULL) {
7164 PrintF(out, "extra_ic_state = %s\n", name);
7165 } else {
whesse@chromium.org7b260152011-06-20 15:33:18 +00007166 PrintF(out, "extra_ic_state = %d\n", extra);
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007167 }
7168}
7169
7170
whesse@chromium.org023421e2010-12-21 12:19:12 +00007171void Code::Disassemble(const char* name, FILE* out) {
7172 PrintF(out, "kind = %s\n", Kind2String(kind()));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007173 if (is_inline_cache_stub()) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00007174 PrintF(out, "ic_state = %s\n", ICState2String(ic_state()));
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00007175 PrintExtraICState(out, kind(), extra_ic_state());
whesse@chromium.org023421e2010-12-21 12:19:12 +00007176 PrintF(out, "ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007177 if (ic_state() == MONOMORPHIC) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00007178 PrintF(out, "type = %s\n", PropertyType2String(type()));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007179 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00007180 if (is_call_stub() || is_keyed_call_stub()) {
7181 PrintF(out, "argc = %d\n", arguments_count());
7182 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007183 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007184 if ((name != NULL) && (name[0] != '\0')) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00007185 PrintF(out, "name = %s\n", name);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007186 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007187 if (kind() == OPTIMIZED_FUNCTION) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00007188 PrintF(out, "stack_slots = %d\n", stack_slots());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007189 }
mads.s.ager31e71382008-08-13 09:32:07 +00007190
whesse@chromium.org023421e2010-12-21 12:19:12 +00007191 PrintF(out, "Instructions (size = %d)\n", instruction_size());
7192 Disassembler::Decode(out, this);
7193 PrintF(out, "\n");
mads.s.ager31e71382008-08-13 09:32:07 +00007194
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007195 if (kind() == FUNCTION) {
7196 DeoptimizationOutputData* data =
7197 DeoptimizationOutputData::cast(this->deoptimization_data());
whesse@chromium.org023421e2010-12-21 12:19:12 +00007198 data->DeoptimizationOutputDataPrint(out);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007199 } else if (kind() == OPTIMIZED_FUNCTION) {
7200 DeoptimizationInputData* data =
7201 DeoptimizationInputData::cast(this->deoptimization_data());
whesse@chromium.org023421e2010-12-21 12:19:12 +00007202 data->DeoptimizationInputDataPrint(out);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007203 }
7204 PrintF("\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007205
7206 if (kind() == OPTIMIZED_FUNCTION) {
7207 SafepointTable table(this);
whesse@chromium.org023421e2010-12-21 12:19:12 +00007208 PrintF(out, "Safepoints (size = %u)\n", table.size());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007209 for (unsigned i = 0; i < table.length(); i++) {
7210 unsigned pc_offset = table.GetPcOffset(i);
whesse@chromium.org023421e2010-12-21 12:19:12 +00007211 PrintF(out, "%p %4d ", (instruction_start() + pc_offset), pc_offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007212 table.PrintEntry(i);
whesse@chromium.org023421e2010-12-21 12:19:12 +00007213 PrintF(out, " (sp -> fp)");
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007214 SafepointEntry entry = table.GetEntry(i);
7215 if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) {
7216 PrintF(out, " %6d", entry.deoptimization_index());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007217 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +00007218 PrintF(out, " <none>");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007219 }
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00007220 if (entry.argument_count() > 0) {
7221 PrintF(out, " argc: %d", entry.argument_count());
7222 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00007223 PrintF(out, "\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007224 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00007225 PrintF(out, "\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007226 } else if (kind() == FUNCTION) {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007227 unsigned offset = stack_check_table_offset();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007228 // If there is no stack check table, the "table start" will at or after
7229 // (due to alignment) the end of the instruction stream.
7230 if (static_cast<int>(offset) < instruction_size()) {
7231 unsigned* address =
7232 reinterpret_cast<unsigned*>(instruction_start() + offset);
7233 unsigned length = address[0];
whesse@chromium.org023421e2010-12-21 12:19:12 +00007234 PrintF(out, "Stack checks (size = %u)\n", length);
7235 PrintF(out, "ast_id pc_offset\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007236 for (unsigned i = 0; i < length; ++i) {
7237 unsigned index = (2 * i) + 1;
whesse@chromium.org023421e2010-12-21 12:19:12 +00007238 PrintF(out, "%6u %9u\n", address[index], address[index + 1]);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007239 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00007240 PrintF(out, "\n");
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007241 }
7242 }
7243
mads.s.ager31e71382008-08-13 09:32:07 +00007244 PrintF("RelocInfo (size = %d)\n", relocation_size());
whesse@chromium.org023421e2010-12-21 12:19:12 +00007245 for (RelocIterator it(this); !it.done(); it.next()) it.rinfo()->Print(out);
7246 PrintF(out, "\n");
mads.s.ager31e71382008-08-13 09:32:07 +00007247}
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00007248#endif // ENABLE_DISASSEMBLER
7249
7250
whesse@chromium.org7b260152011-06-20 15:33:18 +00007251static void CopyFastElementsToFast(FixedArray* source,
7252 FixedArray* destination,
7253 WriteBarrierMode mode) {
7254 uint32_t count = static_cast<uint32_t>(source->length());
7255 for (uint32_t i = 0; i < count; ++i) {
7256 destination->set(i, source->get(i), mode);
7257 }
7258}
7259
7260
7261static void CopySlowElementsToFast(NumberDictionary* source,
7262 FixedArray* destination,
7263 WriteBarrierMode mode) {
7264 for (int i = 0; i < source->Capacity(); ++i) {
7265 Object* key = source->KeyAt(i);
7266 if (key->IsNumber()) {
7267 uint32_t entry = static_cast<uint32_t>(key->Number());
7268 destination->set(entry, source->ValueAt(i), mode);
7269 }
7270 }
7271}
7272
7273
lrn@chromium.org303ada72010-10-27 09:33:13 +00007274MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
7275 int length) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007276 Heap* heap = GetHeap();
ager@chromium.org3811b432009-10-28 14:53:37 +00007277 // We should never end in here with a pixel or external array.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007278 ASSERT(!HasExternalArrayElements());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007279
whesse@chromium.org7b260152011-06-20 15:33:18 +00007280 // Allocate a new fast elements backing store.
7281 FixedArray* new_elements = NULL;
7282 { Object* object;
7283 MaybeObject* maybe = heap->AllocateFixedArrayWithHoles(capacity);
7284 if (!maybe->ToObject(&object)) return maybe;
7285 new_elements = FixedArray::cast(object);
lrn@chromium.org303ada72010-10-27 09:33:13 +00007286 }
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007287
whesse@chromium.org7b260152011-06-20 15:33:18 +00007288 // Find the new map to use for this object if there is a map change.
7289 Map* new_map = NULL;
7290 if (elements()->map() != heap->non_strict_arguments_elements_map()) {
7291 Object* object;
7292 MaybeObject* maybe = map()->GetFastElementsMap();
7293 if (!maybe->ToObject(&object)) return maybe;
7294 new_map = Map::cast(object);
lrn@chromium.org303ada72010-10-27 09:33:13 +00007295 }
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007296
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007297 switch (GetElementsKind()) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007298 case FAST_ELEMENTS: {
7299 AssertNoAllocation no_gc;
7300 WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007301 CopyFastElementsToFast(FixedArray::cast(elements()), new_elements, mode);
7302 set_map(new_map);
7303 set_elements(new_elements);
7304 break;
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007305 }
7306 case DICTIONARY_ELEMENTS: {
7307 AssertNoAllocation no_gc;
7308 WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007309 CopySlowElementsToFast(NumberDictionary::cast(elements()),
7310 new_elements,
7311 mode);
7312 set_map(new_map);
7313 set_elements(new_elements);
7314 break;
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007315 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007316 case NON_STRICT_ARGUMENTS_ELEMENTS: {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007317 AssertNoAllocation no_gc;
7318 WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007319 // The object's map and the parameter map are unchanged, the unaliased
7320 // arguments are copied to the new backing store.
7321 FixedArray* parameter_map = FixedArray::cast(elements());
7322 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
7323 if (arguments->IsDictionary()) {
7324 CopySlowElementsToFast(NumberDictionary::cast(arguments),
7325 new_elements,
7326 mode);
7327 } else {
7328 CopyFastElementsToFast(arguments, new_elements, mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007329 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007330 parameter_map->set(1, new_elements);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007331 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007332 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007333 case FAST_DOUBLE_ELEMENTS: {
7334 FixedDoubleArray* old_elements = FixedDoubleArray::cast(elements());
7335 uint32_t old_length = static_cast<uint32_t>(old_elements->length());
7336 // Fill out the new array with this content and array holes.
7337 for (uint32_t i = 0; i < old_length; i++) {
7338 if (!old_elements->is_the_hole(i)) {
7339 Object* obj;
7340 // Objects must be allocated in the old object space, since the
7341 // overall number of HeapNumbers needed for the conversion might
7342 // exceed the capacity of new space, and we would fail repeatedly
7343 // trying to convert the FixedDoubleArray.
7344 MaybeObject* maybe_value_object =
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00007345 GetHeap()->AllocateHeapNumber(old_elements->get_scalar(i),
7346 TENURED);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007347 if (!maybe_value_object->ToObject(&obj)) return maybe_value_object;
7348 // Force write barrier. It's not worth trying to exploit
7349 // elems->GetWriteBarrierMode(), since it requires an
7350 // AssertNoAllocation stack object that would have to be positioned
7351 // after the HeapNumber allocation anyway.
whesse@chromium.org7b260152011-06-20 15:33:18 +00007352 new_elements->set(i, obj, UPDATE_WRITE_BARRIER);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007353 }
7354 }
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007355 set_map(new_map);
7356 set_elements(new_elements);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007357 break;
7358 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007359 case EXTERNAL_BYTE_ELEMENTS:
7360 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7361 case EXTERNAL_SHORT_ELEMENTS:
7362 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7363 case EXTERNAL_INT_ELEMENTS:
7364 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7365 case EXTERNAL_FLOAT_ELEMENTS:
7366 case EXTERNAL_DOUBLE_ELEMENTS:
7367 case EXTERNAL_PIXEL_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007368 UNREACHABLE();
7369 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007370 }
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007371
whesse@chromium.org7b260152011-06-20 15:33:18 +00007372 // Update the length if necessary.
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007373 if (IsJSArray()) {
7374 JSArray::cast(this)->set_length(Smi::FromInt(length));
7375 }
7376
whesse@chromium.org7b260152011-06-20 15:33:18 +00007377 return new_elements;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007378}
7379
7380
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007381MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
7382 int capacity,
7383 int length) {
7384 Heap* heap = GetHeap();
7385 // We should never end in here with a pixel or external array.
7386 ASSERT(!HasExternalArrayElements());
7387
7388 Object* obj;
7389 { MaybeObject* maybe_obj =
7390 heap->AllocateUninitializedFixedDoubleArray(capacity);
7391 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7392 }
7393 FixedDoubleArray* elems = FixedDoubleArray::cast(obj);
7394
7395 { MaybeObject* maybe_obj = map()->GetFastDoubleElementsMap();
7396 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7397 }
7398 Map* new_map = Map::cast(obj);
7399
7400 AssertNoAllocation no_gc;
7401 switch (GetElementsKind()) {
7402 case FAST_ELEMENTS: {
7403 elems->Initialize(FixedArray::cast(elements()));
7404 break;
7405 }
7406 case FAST_DOUBLE_ELEMENTS: {
7407 elems->Initialize(FixedDoubleArray::cast(elements()));
7408 break;
7409 }
7410 case DICTIONARY_ELEMENTS: {
7411 elems->Initialize(NumberDictionary::cast(elements()));
7412 break;
7413 }
7414 default:
7415 UNREACHABLE();
7416 break;
7417 }
7418
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007419 ASSERT(new_map->has_fast_double_elements());
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007420 set_map(new_map);
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007421 ASSERT(elems->IsFixedDoubleArray());
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00007422 set_elements(elems);
7423
7424 if (IsJSArray()) {
7425 JSArray::cast(this)->set_length(Smi::FromInt(length));
7426 }
7427
7428 return this;
7429}
7430
7431
lrn@chromium.org303ada72010-10-27 09:33:13 +00007432MaybeObject* JSObject::SetSlowElements(Object* len) {
ager@chromium.org3811b432009-10-28 14:53:37 +00007433 // We should never end in here with a pixel or external array.
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007434 ASSERT(!HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007435
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007436 uint32_t new_length = static_cast<uint32_t>(len->Number());
7437
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007438 switch (GetElementsKind()) {
7439 case FAST_ELEMENTS: {
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007440 case FAST_DOUBLE_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007441 // Make sure we never try to shrink dense arrays into sparse arrays.
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007442 ASSERT(static_cast<uint32_t>(
7443 FixedArrayBase::cast(elements())->length()) <= new_length);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007444 MaybeObject* result = NormalizeElements();
7445 if (result->IsFailure()) return result;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007446
7447 // Update length for JSArrays.
7448 if (IsJSArray()) JSArray::cast(this)->set_length(len);
7449 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007450 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007451 case DICTIONARY_ELEMENTS: {
7452 if (IsJSArray()) {
7453 uint32_t old_length =
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007454 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007455 element_dictionary()->RemoveNumberEntries(new_length, old_length),
7456 JSArray::cast(this)->set_length(len);
7457 }
7458 break;
7459 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007460 case NON_STRICT_ARGUMENTS_ELEMENTS:
7461 UNIMPLEMENTED();
7462 break;
7463 case EXTERNAL_BYTE_ELEMENTS:
7464 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7465 case EXTERNAL_SHORT_ELEMENTS:
7466 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7467 case EXTERNAL_INT_ELEMENTS:
7468 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7469 case EXTERNAL_FLOAT_ELEMENTS:
7470 case EXTERNAL_DOUBLE_ELEMENTS:
7471 case EXTERNAL_PIXEL_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007472 UNREACHABLE();
7473 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007474 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007475 return this;
7476}
7477
7478
lrn@chromium.org303ada72010-10-27 09:33:13 +00007479MaybeObject* JSArray::Initialize(int capacity) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007480 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007481 ASSERT(capacity >= 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007482 set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007483 FixedArray* new_elements;
7484 if (capacity == 0) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007485 new_elements = heap->empty_fixed_array();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007486 } else {
lrn@chromium.org303ada72010-10-27 09:33:13 +00007487 Object* obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007488 { MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(capacity);
lrn@chromium.org303ada72010-10-27 09:33:13 +00007489 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7490 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007491 new_elements = FixedArray::cast(obj);
7492 }
7493 set_elements(new_elements);
7494 return this;
7495}
7496
7497
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007498void JSArray::Expand(int required_size) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007499 Handle<JSArray> self(this);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007500 Handle<FixedArray> old_backing(FixedArray::cast(elements()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007501 int old_size = old_backing->length();
ager@chromium.org6141cbe2009-11-20 12:14:52 +00007502 int new_size = required_size > old_size ? required_size : old_size;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007503 Handle<FixedArray> new_backing = FACTORY->NewFixedArray(new_size);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007504 // Can't use this any more now because we may have had a GC!
7505 for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
7506 self->SetContent(*new_backing);
7507}
7508
7509
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007510static Failure* ArrayLengthRangeError(Heap* heap) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007511 HandleScope scope(heap->isolate());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007512 return heap->isolate()->Throw(
7513 *FACTORY->NewRangeError("invalid_array_length",
7514 HandleVector<Object>(NULL, 0)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007515}
7516
7517
lrn@chromium.org303ada72010-10-27 09:33:13 +00007518MaybeObject* JSObject::SetElementsLength(Object* len) {
ager@chromium.org3811b432009-10-28 14:53:37 +00007519 // We should never end in here with a pixel or external array.
ager@chromium.org5c838252010-02-19 08:53:10 +00007520 ASSERT(AllowsSetElementsLength());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007521
lrn@chromium.org303ada72010-10-27 09:33:13 +00007522 MaybeObject* maybe_smi_length = len->ToSmi();
7523 Object* smi_length = Smi::FromInt(0);
7524 if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007525 const int value = Smi::cast(smi_length)->value();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007526 if (value < 0) return ArrayLengthRangeError(GetHeap());
rossberg@chromium.org717967f2011-07-20 13:44:42 +00007527 JSObject::ElementsKind elements_kind = GetElementsKind();
7528 switch (elements_kind) {
7529 case FAST_ELEMENTS:
7530 case FAST_DOUBLE_ELEMENTS: {
7531 int old_capacity = FixedArrayBase::cast(elements())->length();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007532 if (value <= old_capacity) {
7533 if (IsJSArray()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00007534 Object* obj;
rossberg@chromium.org717967f2011-07-20 13:44:42 +00007535 if (elements_kind == FAST_ELEMENTS) {
7536 MaybeObject* maybe_obj = EnsureWritableFastElements();
lrn@chromium.org303ada72010-10-27 09:33:13 +00007537 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7538 }
ager@chromium.org04921a82011-06-27 13:21:41 +00007539 if (2 * value <= old_capacity) {
7540 // If more than half the elements won't be used, trim the array.
7541 if (value == 0) {
7542 initialize_elements();
7543 } else {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00007544 Address filler_start;
7545 int filler_size;
7546 if (GetElementsKind() == FAST_ELEMENTS) {
7547 FixedArray* fast_elements = FixedArray::cast(elements());
7548 fast_elements->set_length(value);
7549 filler_start = fast_elements->address() +
7550 FixedArray::OffsetOfElementAt(value);
7551 filler_size = (old_capacity - value) * kPointerSize;
7552 } else {
7553 ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS);
7554 FixedDoubleArray* fast_double_elements =
7555 FixedDoubleArray::cast(elements());
7556 fast_double_elements->set_length(value);
7557 filler_start = fast_double_elements->address() +
7558 FixedDoubleArray::OffsetOfElementAt(value);
7559 filler_size = (old_capacity - value) * kDoubleSize;
7560 }
ager@chromium.org04921a82011-06-27 13:21:41 +00007561 GetHeap()->CreateFillerObjectAt(filler_start, filler_size);
7562 }
7563 } else {
7564 // Otherwise, fill the unused tail with holes.
7565 int old_length = FastD2I(JSArray::cast(this)->length()->Number());
rossberg@chromium.org717967f2011-07-20 13:44:42 +00007566 if (GetElementsKind() == FAST_ELEMENTS) {
7567 FixedArray* fast_elements = FixedArray::cast(elements());
7568 for (int i = value; i < old_length; i++) {
7569 fast_elements->set_the_hole(i);
7570 }
7571 } else {
7572 ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS);
7573 FixedDoubleArray* fast_double_elements =
7574 FixedDoubleArray::cast(elements());
7575 for (int i = value; i < old_length; i++) {
7576 fast_double_elements->set_the_hole(i);
7577 }
ager@chromium.org04921a82011-06-27 13:21:41 +00007578 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007579 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007580 JSArray::cast(this)->set_length(Smi::cast(smi_length));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007581 }
7582 return this;
7583 }
7584 int min = NewElementsCapacity(old_capacity);
7585 int new_capacity = value > min ? value : min;
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007586 if (!ShouldConvertToSlowElements(new_capacity)) {
rossberg@chromium.org717967f2011-07-20 13:44:42 +00007587 MaybeObject* result;
7588 if (GetElementsKind() == FAST_ELEMENTS) {
7589 result = SetFastElementsCapacityAndLength(new_capacity, value);
7590 } else {
7591 ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS);
7592 result = SetFastDoubleElementsCapacityAndLength(new_capacity,
7593 value);
7594 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007595 if (result->IsFailure()) return result;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007596 return this;
7597 }
7598 break;
7599 }
7600 case DICTIONARY_ELEMENTS: {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007601 if (IsJSArray()) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007602 if (value == 0) {
7603 // If the length of a slow array is reset to zero, we clear
7604 // the array and flush backing storage. This has the added
7605 // benefit that the array returns to fast mode.
lrn@chromium.org303ada72010-10-27 09:33:13 +00007606 Object* obj;
7607 { MaybeObject* maybe_obj = ResetElements();
7608 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7609 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007610 } else {
7611 // Remove deleted elements.
7612 uint32_t old_length =
7613 static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
7614 element_dictionary()->RemoveNumberEntries(value, old_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007615 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007616 JSArray::cast(this)->set_length(Smi::cast(smi_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007617 }
7618 return this;
7619 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007620 case NON_STRICT_ARGUMENTS_ELEMENTS:
7621 case EXTERNAL_BYTE_ELEMENTS:
7622 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7623 case EXTERNAL_SHORT_ELEMENTS:
7624 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7625 case EXTERNAL_INT_ELEMENTS:
7626 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
7627 case EXTERNAL_FLOAT_ELEMENTS:
7628 case EXTERNAL_DOUBLE_ELEMENTS:
7629 case EXTERNAL_PIXEL_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007630 UNREACHABLE();
7631 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007632 }
7633 }
7634
7635 // General slow case.
7636 if (len->IsNumber()) {
7637 uint32_t length;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007638 if (len->ToArrayIndex(&length)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007639 return SetSlowElements(len);
7640 } else {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007641 return ArrayLengthRangeError(GetHeap());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007642 }
7643 }
7644
7645 // len is not a number so make the array size one and
7646 // set only element to len.
lrn@chromium.org303ada72010-10-27 09:33:13 +00007647 Object* obj;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007648 { MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1);
lrn@chromium.org303ada72010-10-27 09:33:13 +00007649 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
7650 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007651 FixedArray::cast(obj)->set(0, len);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007652 if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007653 set_elements(FixedArray::cast(obj));
7654 return this;
7655}
7656
7657
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007658Object* Map::GetPrototypeTransition(Object* prototype) {
7659 FixedArray* cache = prototype_transitions();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007660 int number_of_transitions = NumberOfProtoTransitions();
7661 const int proto_offset =
7662 kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset;
7663 const int map_offset = kProtoTransitionHeaderSize + kProtoTransitionMapOffset;
7664 const int step = kProtoTransitionElementsPerEntry;
7665 for (int i = 0; i < number_of_transitions; i++) {
7666 if (cache->get(proto_offset + i * step) == prototype) {
7667 Object* map = cache->get(map_offset + i * step);
7668 ASSERT(map->IsMap());
7669 return map;
7670 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007671 }
7672 return NULL;
7673}
7674
7675
7676MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007677 ASSERT(map->IsMap());
7678 ASSERT(HeapObject::cast(prototype)->map()->IsMap());
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007679 // Don't cache prototype transition if this map is shared.
7680 if (is_shared() || !FLAG_cache_prototype_transitions) return this;
7681
7682 FixedArray* cache = prototype_transitions();
7683
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007684 const int step = kProtoTransitionElementsPerEntry;
7685 const int header = kProtoTransitionHeaderSize;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007686
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007687 int capacity = (cache->length() - header) / step;
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007688
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007689 int transitions = NumberOfProtoTransitions() + 1;
7690
7691 if (transitions > capacity) {
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007692 if (capacity > kMaxCachedPrototypeTransitions) return this;
7693
7694 FixedArray* new_cache;
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007695 // Grow array by factor 2 over and above what we need.
7696 { MaybeObject* maybe_cache =
7697 heap()->AllocateFixedArray(transitions * 2 * step + header);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007698 if (!maybe_cache->To<FixedArray>(&new_cache)) return maybe_cache;
7699 }
7700
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007701 for (int i = 0; i < capacity * step; i++) {
7702 new_cache->set(i + header, cache->get(i + header));
7703 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007704 cache = new_cache;
7705 set_prototype_transitions(cache);
7706 }
7707
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007708 int last = transitions - 1;
7709
7710 cache->set(header + last * step + kProtoTransitionPrototypeOffset, prototype);
7711 cache->set(header + last * step + kProtoTransitionMapOffset, map);
7712 SetNumberOfProtoTransitions(transitions);
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007713
7714 return cache;
7715}
7716
7717
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007718MaybeObject* JSReceiver::SetPrototype(Object* value,
7719 bool skip_hidden_prototypes) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007720#ifdef DEBUG
7721 int size = Size();
7722#endif
7723
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007724 Heap* heap = GetHeap();
ager@chromium.org5c838252010-02-19 08:53:10 +00007725 // Silently ignore the change if value is not a JSObject or null.
7726 // SpiderMonkey behaves this way.
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007727 if (!value->IsJSReceiver() && !value->IsNull()) return value;
ager@chromium.org5c838252010-02-19 08:53:10 +00007728
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007729 // From 8.6.2 Object Internal Methods
7730 // ...
7731 // In addition, if [[Extensible]] is false the value of the [[Class]] and
7732 // [[Prototype]] internal properties of the object may not be modified.
7733 // ...
7734 // Implementation specific extensions that modify [[Class]], [[Prototype]]
7735 // or [[Extensible]] must not violate the invariants defined in the preceding
7736 // paragraph.
7737 if (!this->map()->is_extensible()) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007738 HandleScope scope(heap->isolate());
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00007739 Handle<Object> handle(this, heap->isolate());
7740 return heap->isolate()->Throw(
7741 *FACTORY->NewTypeError("non_extensible_proto",
7742 HandleVector<Object>(&handle, 1)));
7743 }
7744
ager@chromium.org5c838252010-02-19 08:53:10 +00007745 // Before we can set the prototype we need to be sure
7746 // prototype cycles are prevented.
7747 // It is sufficient to validate that the receiver is not in the new prototype
7748 // chain.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007749 for (Object* pt = value; pt != heap->null_value(); pt = pt->GetPrototype()) {
ager@chromium.org5c838252010-02-19 08:53:10 +00007750 if (JSObject::cast(pt) == this) {
7751 // Cycle detected.
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00007752 HandleScope scope(heap->isolate());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007753 return heap->isolate()->Throw(
7754 *FACTORY->NewError("cyclic_proto", HandleVector<Object>(NULL, 0)));
ager@chromium.org5c838252010-02-19 08:53:10 +00007755 }
7756 }
7757
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007758 JSReceiver* real_receiver = this;
ager@chromium.org5c838252010-02-19 08:53:10 +00007759
7760 if (skip_hidden_prototypes) {
7761 // Find the first object in the chain whose prototype object is not
7762 // hidden and set the new prototype on that object.
7763 Object* current_proto = real_receiver->GetPrototype();
7764 while (current_proto->IsJSObject() &&
7765 JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
7766 real_receiver = JSObject::cast(current_proto);
7767 current_proto = current_proto->GetPrototype();
7768 }
7769 }
7770
7771 // Set the new prototype of the object.
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007772 Map* map = real_receiver->map();
7773
7774 // Nothing to do if prototype is already set.
7775 if (map->prototype() == value) return value;
7776
7777 Object* new_map = map->GetPrototypeTransition(value);
7778 if (new_map == NULL) {
7779 { MaybeObject* maybe_new_map = map->CopyDropTransitions();
7780 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
7781 }
7782
7783 { MaybeObject* maybe_new_cache =
7784 map->PutPrototypeTransition(value, Map::cast(new_map));
7785 if (maybe_new_cache->IsFailure()) return maybe_new_cache;
7786 }
7787
7788 Map::cast(new_map)->set_prototype(value);
lrn@chromium.org303ada72010-10-27 09:33:13 +00007789 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007790 ASSERT(Map::cast(new_map)->prototype() == value);
ager@chromium.org5c838252010-02-19 08:53:10 +00007791 real_receiver->set_map(Map::cast(new_map));
7792
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007793 heap->ClearInstanceofCache();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00007794 ASSERT(size == Size());
ager@chromium.org5c838252010-02-19 08:53:10 +00007795 return value;
7796}
7797
7798
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007799bool JSObject::HasElementPostInterceptor(JSReceiver* receiver, uint32_t index) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007800 switch (GetElementsKind()) {
7801 case FAST_ELEMENTS: {
7802 uint32_t length = IsJSArray() ?
7803 static_cast<uint32_t>
7804 (Smi::cast(JSArray::cast(this)->length())->value()) :
7805 static_cast<uint32_t>(FixedArray::cast(elements())->length());
7806 if ((index < length) &&
7807 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
7808 return true;
7809 }
7810 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007811 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007812 case FAST_DOUBLE_ELEMENTS: {
7813 uint32_t length = IsJSArray() ?
7814 static_cast<uint32_t>
7815 (Smi::cast(JSArray::cast(this)->length())->value()) :
7816 static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
7817 if ((index < length) &&
7818 !FixedDoubleArray::cast(elements())->is_the_hole(index)) {
7819 return true;
7820 }
7821 break;
7822 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007823 case EXTERNAL_PIXEL_ELEMENTS: {
7824 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007825 if (index < static_cast<uint32_t>(pixels->length())) {
7826 return true;
7827 }
7828 break;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007829 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007830 case EXTERNAL_BYTE_ELEMENTS:
7831 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7832 case EXTERNAL_SHORT_ELEMENTS:
7833 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7834 case EXTERNAL_INT_ELEMENTS:
7835 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007836 case EXTERNAL_FLOAT_ELEMENTS:
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007837 case EXTERNAL_DOUBLE_ELEMENTS: {
ager@chromium.org3811b432009-10-28 14:53:37 +00007838 ExternalArray* array = ExternalArray::cast(elements());
7839 if (index < static_cast<uint32_t>(array->length())) {
7840 return true;
7841 }
7842 break;
7843 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007844 case DICTIONARY_ELEMENTS: {
7845 if (element_dictionary()->FindEntry(index)
7846 != NumberDictionary::kNotFound) {
7847 return true;
7848 }
7849 break;
7850 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007851 case NON_STRICT_ARGUMENTS_ELEMENTS:
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007852 UNREACHABLE();
7853 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007854 }
7855
7856 // Handle [] on String objects.
7857 if (this->IsStringObjectWithCharacterAt(index)) return true;
7858
7859 Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007860 if (pt->IsNull()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007861 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
7862}
7863
7864
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007865bool JSObject::HasElementWithInterceptor(JSReceiver* receiver, uint32_t index) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007866 Isolate* isolate = GetIsolate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007867 // Make sure that the top context does not change when doing
7868 // callbacks or interceptor calls.
7869 AssertNoContextChange ncc;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007870 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007871 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00007872 Handle<JSReceiver> receiver_handle(receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007873 Handle<JSObject> holder_handle(this);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007874 CustomArguments args(isolate, interceptor->data(), receiver, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007875 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007876 if (!interceptor->query()->IsUndefined()) {
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00007877 v8::IndexedPropertyQuery query =
7878 v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007879 LOG(isolate,
7880 ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00007881 v8::Handle<v8::Integer> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007882 {
7883 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007884 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007885 result = query(index, info);
7886 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00007887 if (!result.IsEmpty()) {
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00007888 ASSERT(result->IsInt32());
7889 return true; // absence of property is signaled by empty handle.
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00007890 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007891 } else if (!interceptor->getter()->IsUndefined()) {
7892 v8::IndexedPropertyGetter getter =
7893 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007894 LOG(isolate,
7895 ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007896 v8::Handle<v8::Value> result;
7897 {
7898 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00007899 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007900 result = getter(index, info);
7901 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007902 if (!result.IsEmpty()) return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007903 }
7904 return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
7905}
7906
7907
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007908JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007909 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00007910 if (IsAccessCheckNeeded()) {
7911 Heap* heap = GetHeap();
7912 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
7913 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
7914 return UNDEFINED_ELEMENT;
7915 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007916 }
7917
ricow@chromium.org83aa5492011-02-07 12:42:56 +00007918 if (IsJSGlobalProxy()) {
7919 Object* proto = GetPrototype();
7920 if (proto->IsNull()) return UNDEFINED_ELEMENT;
7921 ASSERT(proto->IsJSGlobalObject());
7922 return JSObject::cast(proto)->HasLocalElement(index);
7923 }
7924
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007925 // Check for lookup interceptor
7926 if (HasIndexedInterceptor()) {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007927 return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT
7928 : UNDEFINED_ELEMENT;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007929 }
7930
7931 // Handle [] on String objects.
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007932 if (this->IsStringObjectWithCharacterAt(index)) {
7933 return STRING_CHARACTER_ELEMENT;
7934 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007935
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007936 switch (GetElementsKind()) {
7937 case FAST_ELEMENTS: {
7938 uint32_t length = IsJSArray() ?
7939 static_cast<uint32_t>
7940 (Smi::cast(JSArray::cast(this)->length())->value()) :
7941 static_cast<uint32_t>(FixedArray::cast(elements())->length());
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007942 if ((index < length) &&
7943 !FixedArray::cast(elements())->get(index)->IsTheHole()) {
7944 return FAST_ELEMENT;
7945 }
7946 break;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007947 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +00007948 case FAST_DOUBLE_ELEMENTS: {
7949 uint32_t length = IsJSArray() ?
7950 static_cast<uint32_t>
7951 (Smi::cast(JSArray::cast(this)->length())->value()) :
7952 static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
7953 if ((index < length) &&
7954 !FixedDoubleArray::cast(elements())->is_the_hole(index)) {
7955 return FAST_ELEMENT;
7956 }
7957 break;
7958 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00007959 case EXTERNAL_PIXEL_ELEMENTS: {
7960 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007961 if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT;
7962 break;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007963 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007964 case EXTERNAL_BYTE_ELEMENTS:
7965 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
7966 case EXTERNAL_SHORT_ELEMENTS:
7967 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
7968 case EXTERNAL_INT_ELEMENTS:
7969 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00007970 case EXTERNAL_FLOAT_ELEMENTS:
7971 case EXTERNAL_DOUBLE_ELEMENTS: {
ager@chromium.org3811b432009-10-28 14:53:37 +00007972 ExternalArray* array = ExternalArray::cast(elements());
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007973 if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT;
7974 break;
ager@chromium.org3811b432009-10-28 14:53:37 +00007975 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007976 case DICTIONARY_ELEMENTS: {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007977 if (element_dictionary()->FindEntry(index) !=
whesse@chromium.org7b260152011-06-20 15:33:18 +00007978 NumberDictionary::kNotFound) {
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00007979 return DICTIONARY_ELEMENT;
7980 }
7981 break;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007982 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00007983 case NON_STRICT_ARGUMENTS_ELEMENTS: {
7984 // Aliased parameters and non-aliased elements in a fast backing store
7985 // behave as FAST_ELEMENT. Non-aliased elements in a dictionary
7986 // backing store behave as DICTIONARY_ELEMENT.
7987 FixedArray* parameter_map = FixedArray::cast(elements());
7988 uint32_t length = parameter_map->length();
7989 Object* probe =
whesse@chromium.orgdf8c03c2011-06-21 14:36:03 +00007990 index < (length - 2) ? parameter_map->get(index + 2) : NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00007991 if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT;
7992 // If not aliased, check the arguments.
7993 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
7994 if (arguments->IsDictionary()) {
7995 NumberDictionary* dictionary = NumberDictionary::cast(arguments);
7996 if (dictionary->FindEntry(index) != NumberDictionary::kNotFound) {
7997 return DICTIONARY_ELEMENT;
7998 }
7999 } else {
8000 length = arguments->length();
8001 probe = (index < length) ? arguments->get(index) : NULL;
8002 if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT;
8003 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008004 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008005 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008006 }
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +00008007
8008 return UNDEFINED_ELEMENT;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008009}
8010
8011
whesse@chromium.org7b260152011-06-20 15:33:18 +00008012bool JSObject::HasElementInElements(FixedArray* elements,
8013 ElementsKind kind,
8014 uint32_t index) {
8015 ASSERT(kind == FAST_ELEMENTS || kind == DICTIONARY_ELEMENTS);
8016 if (kind == FAST_ELEMENTS) {
8017 int length = IsJSArray()
8018 ? Smi::cast(JSArray::cast(this)->length())->value()
8019 : elements->length();
8020 if (index < static_cast<uint32_t>(length) &&
8021 !elements->get(index)->IsTheHole()) {
8022 return true;
8023 }
8024 } else {
8025 if (NumberDictionary::cast(elements)->FindEntry(index) !=
8026 NumberDictionary::kNotFound) {
8027 return true;
8028 }
8029 }
8030 return false;
8031}
8032
8033
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008034bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008035 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008036 if (IsAccessCheckNeeded()) {
8037 Heap* heap = GetHeap();
8038 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
8039 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
8040 return false;
8041 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008042 }
8043
8044 // Check for lookup interceptor
8045 if (HasIndexedInterceptor()) {
8046 return HasElementWithInterceptor(receiver, index);
8047 }
8048
whesse@chromium.org7b260152011-06-20 15:33:18 +00008049 ElementsKind kind = GetElementsKind();
8050 switch (kind) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008051 case FAST_ELEMENTS: {
8052 uint32_t length = IsJSArray() ?
8053 static_cast<uint32_t>
8054 (Smi::cast(JSArray::cast(this)->length())->value()) :
8055 static_cast<uint32_t>(FixedArray::cast(elements())->length());
8056 if ((index < length) &&
8057 !FixedArray::cast(elements())->get(index)->IsTheHole()) return true;
8058 break;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008059 }
rossberg@chromium.org717967f2011-07-20 13:44:42 +00008060 case FAST_DOUBLE_ELEMENTS: {
8061 uint32_t length = IsJSArray() ?
8062 static_cast<uint32_t>
8063 (Smi::cast(JSArray::cast(this)->length())->value()) :
8064 static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
8065 if ((index < length) &&
8066 !FixedDoubleArray::cast(elements())->is_the_hole(index)) return true;
8067 break;
8068 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008069 case EXTERNAL_PIXEL_ELEMENTS: {
8070 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008071 if (index < static_cast<uint32_t>(pixels->length())) {
8072 return true;
8073 }
8074 break;
8075 }
ager@chromium.org3811b432009-10-28 14:53:37 +00008076 case EXTERNAL_BYTE_ELEMENTS:
8077 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
8078 case EXTERNAL_SHORT_ELEMENTS:
8079 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
8080 case EXTERNAL_INT_ELEMENTS:
8081 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00008082 case EXTERNAL_FLOAT_ELEMENTS:
8083 case EXTERNAL_DOUBLE_ELEMENTS: {
ager@chromium.org3811b432009-10-28 14:53:37 +00008084 ExternalArray* array = ExternalArray::cast(elements());
8085 if (index < static_cast<uint32_t>(array->length())) {
8086 return true;
8087 }
8088 break;
8089 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008090 case DICTIONARY_ELEMENTS: {
8091 if (element_dictionary()->FindEntry(index)
8092 != NumberDictionary::kNotFound) {
8093 return true;
8094 }
8095 break;
8096 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008097 case NON_STRICT_ARGUMENTS_ELEMENTS: {
8098 FixedArray* parameter_map = FixedArray::cast(elements());
8099 uint32_t length = parameter_map->length();
8100 Object* probe =
whesse@chromium.orgdf8c03c2011-06-21 14:36:03 +00008101 (index < length - 2) ? parameter_map->get(index + 2) : NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008102 if (probe != NULL && !probe->IsTheHole()) return true;
8103
8104 // Not a mapped parameter, check the arguments.
8105 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
8106 kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS : FAST_ELEMENTS;
8107 if (HasElementInElements(arguments, kind, index)) return true;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008108 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008109 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008110 }
8111
8112 // Handle [] on String objects.
8113 if (this->IsStringObjectWithCharacterAt(index)) return true;
8114
8115 Object* pt = GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008116 if (pt->IsNull()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008117 return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
8118}
8119
8120
lrn@chromium.org303ada72010-10-27 09:33:13 +00008121MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008122 Object* value,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008123 StrictModeFlag strict_mode,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008124 bool check_prototype) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008125 Isolate* isolate = GetIsolate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008126 // Make sure that the top context does not change when doing
8127 // callbacks or interceptor calls.
8128 AssertNoContextChange ncc;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008129 HandleScope scope(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008130 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
8131 Handle<JSObject> this_handle(this);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008132 Handle<Object> value_handle(value, isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008133 if (!interceptor->setter()->IsUndefined()) {
8134 v8::IndexedPropertySetter setter =
8135 v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008136 LOG(isolate,
8137 ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
8138 CustomArguments args(isolate, interceptor->data(), this, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00008139 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008140 v8::Handle<v8::Value> result;
8141 {
8142 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008143 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008144 result = setter(index, v8::Utils::ToLocal(value_handle), info);
8145 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008146 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008147 if (!result.IsEmpty()) return *value_handle;
8148 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00008149 MaybeObject* raw_result =
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008150 this_handle->SetElementWithoutInterceptor(index,
8151 *value_handle,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008152 strict_mode,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008153 check_prototype);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008154 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008155 return raw_result;
8156}
8157
8158
lrn@chromium.org303ada72010-10-27 09:33:13 +00008159MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
8160 Object* structure,
8161 uint32_t index,
8162 Object* holder) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008163 Isolate* isolate = GetIsolate();
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008164 ASSERT(!structure->IsForeign());
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008165
8166 // api style callbacks.
8167 if (structure->IsAccessorInfo()) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008168 Handle<AccessorInfo> data(AccessorInfo::cast(structure));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008169 Object* fun_obj = data->getter();
8170 v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008171 HandleScope scope(isolate);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008172 Handle<JSObject> self(JSObject::cast(receiver));
8173 Handle<JSObject> holder_handle(JSObject::cast(holder));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008174 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008175 Handle<String> key = isolate->factory()->NumberToString(number);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008176 LOG(isolate, ApiNamedPropertyAccess("load", *self, *key));
8177 CustomArguments args(isolate, data->data(), *self, *holder_handle);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008178 v8::AccessorInfo info(args.end());
8179 v8::Handle<v8::Value> result;
8180 {
8181 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008182 VMState state(isolate, EXTERNAL);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008183 result = call_fun(v8::Utils::ToLocal(key), info);
8184 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008185 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
8186 if (result.IsEmpty()) return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008187 return *v8::Utils::OpenHandle(*result);
8188 }
8189
8190 // __defineGetter__ callback
8191 if (structure->IsFixedArray()) {
8192 Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
8193 if (getter->IsJSFunction()) {
8194 return Object::GetPropertyWithDefinedGetter(receiver,
8195 JSFunction::cast(getter));
8196 }
8197 // Getter is not a function.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008198 return isolate->heap()->undefined_value();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008199 }
8200
8201 UNREACHABLE();
8202 return NULL;
8203}
8204
8205
lrn@chromium.org303ada72010-10-27 09:33:13 +00008206MaybeObject* JSObject::SetElementWithCallback(Object* structure,
8207 uint32_t index,
8208 Object* value,
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008209 JSObject* holder,
8210 StrictModeFlag strict_mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008211 Isolate* isolate = GetIsolate();
8212 HandleScope scope(isolate);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008213
8214 // We should never get here to initialize a const with the hole
8215 // value since a const declaration would conflict with the setter.
8216 ASSERT(!value->IsTheHole());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008217 Handle<Object> value_handle(value, isolate);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008218
8219 // To accommodate both the old and the new api we switch on the
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008220 // data structure used to store the callbacks. Eventually foreign
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008221 // callbacks should be phased out.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00008222 ASSERT(!structure->IsForeign());
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008223
8224 if (structure->IsAccessorInfo()) {
8225 // api style callbacks
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008226 Handle<JSObject> self(this);
8227 Handle<JSObject> holder_handle(JSObject::cast(holder));
8228 Handle<AccessorInfo> data(AccessorInfo::cast(structure));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008229 Object* call_obj = data->setter();
8230 v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
8231 if (call_fun == NULL) return value;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008232 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
8233 Handle<String> key(isolate->factory()->NumberToString(number));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00008234 LOG(isolate, ApiNamedPropertyAccess("store", *self, *key));
8235 CustomArguments args(isolate, data->data(), *self, *holder_handle);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008236 v8::AccessorInfo info(args.end());
8237 {
8238 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008239 VMState state(isolate, EXTERNAL);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008240 call_fun(v8::Utils::ToLocal(key),
8241 v8::Utils::ToLocal(value_handle),
8242 info);
8243 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008244 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008245 return *value_handle;
8246 }
8247
8248 if (structure->IsFixedArray()) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008249 Handle<Object> setter(FixedArray::cast(structure)->get(kSetterIndex));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008250 if (setter->IsJSFunction()) {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008251 return SetPropertyWithDefinedSetter(JSFunction::cast(*setter), value);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008252 } else {
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008253 if (strict_mode == kNonStrictMode) {
8254 return value;
8255 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008256 Handle<Object> holder_handle(holder, isolate);
8257 Handle<Object> key(isolate->factory()->NewNumberFromUint(index));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008258 Handle<Object> args[2] = { key, holder_handle };
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008259 return isolate->Throw(
8260 *isolate->factory()->NewTypeError("no_setter_in_callback",
8261 HandleVector(args, 2)));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00008262 }
8263 }
8264
8265 UNREACHABLE();
8266 return NULL;
8267}
8268
8269
whesse@chromium.org7b260152011-06-20 15:33:18 +00008270bool JSObject::HasFastArgumentsElements() {
8271 Heap* heap = GetHeap();
8272 if (!elements()->IsFixedArray()) return false;
8273 FixedArray* elements = FixedArray::cast(this->elements());
8274 if (elements->map() != heap->non_strict_arguments_elements_map()) {
8275 return false;
8276 }
8277 FixedArray* arguments = FixedArray::cast(elements->get(1));
8278 return !arguments->IsDictionary();
8279}
8280
8281
8282bool JSObject::HasDictionaryArgumentsElements() {
8283 Heap* heap = GetHeap();
8284 if (!elements()->IsFixedArray()) return false;
8285 FixedArray* elements = FixedArray::cast(this->elements());
8286 if (elements->map() != heap->non_strict_arguments_elements_map()) {
8287 return false;
8288 }
8289 FixedArray* arguments = FixedArray::cast(elements->get(1));
8290 return arguments->IsDictionary();
8291}
8292
8293
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008294// Adding n elements in fast case is O(n*n).
8295// Note: revisit design to have dual undefined values to capture absent
8296// elements.
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008297MaybeObject* JSObject::SetFastElement(uint32_t index,
8298 Object* value,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008299 StrictModeFlag strict_mode,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008300 bool check_prototype) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008301 ASSERT(HasFastElements() || HasFastArgumentsElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008302
whesse@chromium.org7b260152011-06-20 15:33:18 +00008303 FixedArray* backing_store = FixedArray::cast(elements());
8304 if (backing_store->map() == GetHeap()->non_strict_arguments_elements_map()) {
8305 backing_store = FixedArray::cast(backing_store->get(1));
8306 } else {
8307 Object* writable;
8308 MaybeObject* maybe = EnsureWritableFastElements();
8309 if (!maybe->ToObject(&writable)) return maybe;
8310 backing_store = FixedArray::cast(writable);
lrn@chromium.org303ada72010-10-27 09:33:13 +00008311 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008312 uint32_t length = static_cast<uint32_t>(backing_store->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008313
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008314 if (check_prototype &&
whesse@chromium.org7b260152011-06-20 15:33:18 +00008315 (index >= length || backing_store->get(index)->IsTheHole())) {
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00008316 bool found;
ricow@chromium.orgc54d3652011-05-30 09:20:16 +00008317 MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index,
8318 value,
8319 &found,
8320 strict_mode);
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00008321 if (found) return result;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008322 }
8323
ager@chromium.org04921a82011-06-27 13:21:41 +00008324 // Check whether there is extra space in fixed array.
whesse@chromium.org7b260152011-06-20 15:33:18 +00008325 if (index < length) {
8326 backing_store->set(index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008327 if (IsJSArray()) {
8328 // Update the length of the array if needed.
8329 uint32_t array_length = 0;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008330 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008331 if (index >= array_length) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008332 JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008333 }
8334 }
ager@chromium.orgbdf2f942008-10-17 07:23:00 +00008335 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008336 }
8337
8338 // Allow gap in fast case.
whesse@chromium.org7b260152011-06-20 15:33:18 +00008339 if ((index - length) < kMaxGap) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008340 // Try allocating extra space.
whesse@chromium.org7b260152011-06-20 15:33:18 +00008341 int new_capacity = NewElementsCapacity(index + 1);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00008342 if (!ShouldConvertToSlowElements(new_capacity)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008343 ASSERT(static_cast<uint32_t>(new_capacity) > index);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008344 Object* new_elements;
8345 MaybeObject* maybe =
8346 SetFastElementsCapacityAndLength(new_capacity, index + 1);
8347 if (!maybe->ToObject(&new_elements)) return maybe;
8348 FixedArray::cast(new_elements)->set(index, value);
ager@chromium.orgbdf2f942008-10-17 07:23:00 +00008349 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008350 }
8351 }
8352
8353 // Otherwise default to slow case.
whesse@chromium.org7b260152011-06-20 15:33:18 +00008354 MaybeObject* result = NormalizeElements();
8355 if (result->IsFailure()) return result;
8356 return SetDictionaryElement(index, value, strict_mode, check_prototype);
8357}
8358
8359
8360MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
8361 Object* value,
8362 StrictModeFlag strict_mode,
8363 bool check_prototype) {
8364 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
8365 Isolate* isolate = GetIsolate();
8366 Heap* heap = isolate->heap();
8367
8368 // Insert element in the dictionary.
8369 FixedArray* elements = FixedArray::cast(this->elements());
8370 bool is_arguments =
8371 (elements->map() == heap->non_strict_arguments_elements_map());
8372 NumberDictionary* dictionary = NULL;
8373 if (is_arguments) {
8374 dictionary = NumberDictionary::cast(elements->get(1));
8375 } else {
8376 dictionary = NumberDictionary::cast(elements);
lrn@chromium.org303ada72010-10-27 09:33:13 +00008377 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008378
8379 int entry = dictionary->FindEntry(index);
8380 if (entry != NumberDictionary::kNotFound) {
8381 Object* element = dictionary->ValueAt(entry);
8382 PropertyDetails details = dictionary->DetailsAt(entry);
8383 if (details.type() == CALLBACKS) {
8384 return SetElementWithCallback(element, index, value, this, strict_mode);
8385 } else {
8386 dictionary->UpdateMaxNumberKey(index);
8387 // If put fails in strict mode, throw an exception.
8388 if (!dictionary->ValueAtPut(entry, value) && strict_mode == kStrictMode) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008389 Handle<Object> holder(this);
ager@chromium.org04921a82011-06-27 13:21:41 +00008390 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008391 Handle<Object> args[2] = { number, holder };
8392 Handle<Object> error =
8393 isolate->factory()->NewTypeError("strict_read_only_property",
8394 HandleVector(args, 2));
8395 return isolate->Throw(*error);
8396 }
8397 }
8398 } else {
8399 // Index not already used. Look for an accessor in the prototype chain.
8400 if (check_prototype) {
8401 bool found;
8402 MaybeObject* result =
8403 SetElementWithCallbackSetterInPrototypes(
8404 index, value, &found, strict_mode);
8405 if (found) return result;
8406 }
8407 // When we set the is_extensible flag to false we always force the
8408 // element into dictionary mode (and force them to stay there).
8409 if (!map()->is_extensible()) {
8410 if (strict_mode == kNonStrictMode) {
8411 return isolate->heap()->undefined_value();
8412 } else {
8413 Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
8414 Handle<String> name = isolate->factory()->NumberToString(number);
8415 Handle<Object> args[1] = { name };
8416 Handle<Object> error =
8417 isolate->factory()->NewTypeError("object_not_extensible",
8418 HandleVector(args, 1));
8419 return isolate->Throw(*error);
8420 }
8421 }
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00008422 FixedArrayBase* new_dictionary;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008423 MaybeObject* maybe = dictionary->AtNumberPut(index, value);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00008424 if (!maybe->To<FixedArrayBase>(&new_dictionary)) return maybe;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008425 if (dictionary != NumberDictionary::cast(new_dictionary)) {
8426 if (is_arguments) {
8427 elements->set(1, new_dictionary);
8428 } else {
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +00008429 set_elements(new_dictionary);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008430 }
8431 dictionary = NumberDictionary::cast(new_dictionary);
8432 }
8433 }
8434
8435 // Update the array length if this JSObject is an array.
8436 if (IsJSArray()) {
8437 MaybeObject* result =
8438 JSArray::cast(this)->JSArrayUpdateLengthFromIndex(index, value);
8439 if (result->IsFailure()) return result;
8440 }
8441
8442 // Attempt to put this object back in fast case.
8443 if (ShouldConvertToFastElements()) {
8444 uint32_t new_length = 0;
8445 if (IsJSArray()) {
8446 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length));
8447 } else {
8448 new_length = dictionary->max_number_key() + 1;
8449 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008450 MaybeObject* result = CanConvertToFastDoubleElements()
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008451 ? SetFastDoubleElementsCapacityAndLength(new_length, new_length)
8452 : SetFastElementsCapacityAndLength(new_length, new_length);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008453 if (result->IsFailure()) return result;
8454#ifdef DEBUG
8455 if (FLAG_trace_normalization) {
8456 PrintF("Object elements are fast case again:\n");
8457 Print();
8458 }
8459#endif
8460 }
8461 return value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008462}
8463
ricow@chromium.org0b9f8502010-08-18 07:45:01 +00008464
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008465MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement(
8466 uint32_t index,
8467 Object* value,
8468 StrictModeFlag strict_mode,
8469 bool check_prototype) {
8470 ASSERT(HasFastDoubleElements());
8471
8472 FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
8473 uint32_t elms_length = static_cast<uint32_t>(elms->length());
8474
8475 // If storing to an element that isn't in the array, pass the store request
8476 // up the prototype chain before storing in the receiver's elements.
8477 if (check_prototype &&
8478 (index >= elms_length || elms->is_the_hole(index))) {
8479 bool found;
8480 MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index,
8481 value,
8482 &found,
8483 strict_mode);
8484 if (found) return result;
8485 }
8486
8487 // If the value object is not a heap number, switch to fast elements and try
8488 // again.
8489 bool value_is_smi = value->IsSmi();
8490 if (!value->IsNumber()) {
8491 Object* obj;
8492 uint32_t length = elms_length;
8493 if (IsJSArray()) {
8494 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
8495 }
8496 MaybeObject* maybe_obj =
8497 SetFastElementsCapacityAndLength(elms_length, length);
8498 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8499 return SetFastElement(index, value, strict_mode, check_prototype);
8500 }
8501
8502 double double_value = value_is_smi
8503 ? static_cast<double>(Smi::cast(value)->value())
8504 : HeapNumber::cast(value)->value();
8505
8506 // Check whether there is extra space in the fixed array.
8507 if (index < elms_length) {
8508 elms->set(index, double_value);
8509 if (IsJSArray()) {
8510 // Update the length of the array if needed.
8511 uint32_t array_length = 0;
8512 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
8513 if (index >= array_length) {
8514 JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
8515 }
8516 }
8517 return value;
8518 }
8519
8520 // Allow gap in fast case.
8521 if ((index - elms_length) < kMaxGap) {
8522 // Try allocating extra space.
8523 int new_capacity = NewElementsCapacity(index+1);
ricow@chromium.org9fa09672011-07-25 11:05:35 +00008524 if (!ShouldConvertToSlowElements(new_capacity)) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008525 ASSERT(static_cast<uint32_t>(new_capacity) > index);
8526 Object* obj;
8527 { MaybeObject* maybe_obj =
8528 SetFastDoubleElementsCapacityAndLength(new_capacity,
8529 index + 1);
8530 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8531 }
8532 FixedDoubleArray::cast(elements())->set(index, double_value);
8533 return value;
8534 }
8535 }
8536
8537 // Otherwise default to slow case.
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008538 ASSERT(HasFastDoubleElements());
8539 ASSERT(map()->has_fast_double_elements());
8540 ASSERT(elements()->IsFixedDoubleArray());
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008541 Object* obj;
8542 { MaybeObject* maybe_obj = NormalizeElements();
8543 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
8544 }
8545 ASSERT(HasDictionaryElements());
8546 return SetElement(index, value, strict_mode, check_prototype);
8547}
8548
8549
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008550MaybeObject* JSObject::SetElement(uint32_t index,
8551 Object* value,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008552 StrictModeFlag strict_mode,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008553 bool check_prototype) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008554 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008555 if (IsAccessCheckNeeded()) {
8556 Heap* heap = GetHeap();
8557 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008558 HandleScope scope(heap->isolate());
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008559 Handle<Object> value_handle(value);
8560 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
8561 return *value_handle;
8562 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008563 }
8564
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008565 if (IsJSGlobalProxy()) {
8566 Object* proto = GetPrototype();
8567 if (proto->IsNull()) return value;
8568 ASSERT(proto->IsJSGlobalObject());
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008569 return JSObject::cast(proto)->SetElement(index,
8570 value,
8571 strict_mode,
8572 check_prototype);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008573 }
8574
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008575 // Check for lookup interceptor
8576 if (HasIndexedInterceptor()) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008577 return SetElementWithInterceptor(index,
8578 value,
8579 strict_mode,
8580 check_prototype);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008581 }
8582
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008583 return SetElementWithoutInterceptor(index,
8584 value,
8585 strict_mode,
8586 check_prototype);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008587}
8588
8589
lrn@chromium.org303ada72010-10-27 09:33:13 +00008590MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008591 Object* value,
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008592 StrictModeFlag strict_mode,
lrn@chromium.org5d00b602011-01-05 09:51:43 +00008593 bool check_prototype) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008594 Isolate* isolate = GetIsolate();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008595 switch (GetElementsKind()) {
8596 case FAST_ELEMENTS:
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00008597 return SetFastElement(index, value, strict_mode, check_prototype);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008598 case FAST_DOUBLE_ELEMENTS:
8599 return SetFastDoubleElement(index, value, strict_mode, check_prototype);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00008600 case EXTERNAL_PIXEL_ELEMENTS: {
8601 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008602 return pixels->SetValue(index, value);
8603 }
ager@chromium.org3811b432009-10-28 14:53:37 +00008604 case EXTERNAL_BYTE_ELEMENTS: {
8605 ExternalByteArray* array = ExternalByteArray::cast(elements());
8606 return array->SetValue(index, value);
8607 }
8608 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
8609 ExternalUnsignedByteArray* array =
8610 ExternalUnsignedByteArray::cast(elements());
8611 return array->SetValue(index, value);
8612 }
8613 case EXTERNAL_SHORT_ELEMENTS: {
8614 ExternalShortArray* array = ExternalShortArray::cast(elements());
8615 return array->SetValue(index, value);
8616 }
8617 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
8618 ExternalUnsignedShortArray* array =
8619 ExternalUnsignedShortArray::cast(elements());
8620 return array->SetValue(index, value);
8621 }
8622 case EXTERNAL_INT_ELEMENTS: {
8623 ExternalIntArray* array = ExternalIntArray::cast(elements());
8624 return array->SetValue(index, value);
8625 }
8626 case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
8627 ExternalUnsignedIntArray* array =
8628 ExternalUnsignedIntArray::cast(elements());
8629 return array->SetValue(index, value);
8630 }
8631 case EXTERNAL_FLOAT_ELEMENTS: {
8632 ExternalFloatArray* array = ExternalFloatArray::cast(elements());
8633 return array->SetValue(index, value);
8634 }
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00008635 case EXTERNAL_DOUBLE_ELEMENTS: {
8636 ExternalDoubleArray* array = ExternalDoubleArray::cast(elements());
8637 return array->SetValue(index, value);
8638 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008639 case DICTIONARY_ELEMENTS:
8640 return SetDictionaryElement(index, value, strict_mode, check_prototype);
8641 case NON_STRICT_ARGUMENTS_ELEMENTS: {
8642 FixedArray* parameter_map = FixedArray::cast(elements());
8643 uint32_t length = parameter_map->length();
8644 Object* probe =
whesse@chromium.orgdf8c03c2011-06-21 14:36:03 +00008645 (index < length - 2) ? parameter_map->get(index + 2) : NULL;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008646 if (probe != NULL && !probe->IsTheHole()) {
8647 Context* context = Context::cast(parameter_map->get(0));
8648 int context_index = Smi::cast(probe)->value();
8649 ASSERT(!context->get(context_index)->IsTheHole());
8650 context->set(context_index, value);
8651 return value;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008652 } else {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008653 // Object is not mapped, defer to the arguments.
8654 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
8655 if (arguments->IsDictionary()) {
8656 return SetDictionaryElement(index, value, strict_mode,
8657 check_prototype);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008658 } else {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008659 return SetFastElement(index, value, strict_mode, check_prototype);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008660 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008661 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008662 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008663 }
8664 // All possible cases have been handled above. Add a return to avoid the
8665 // complaints from the compiler.
8666 UNREACHABLE();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008667 return isolate->heap()->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008668}
8669
8670
lrn@chromium.org303ada72010-10-27 09:33:13 +00008671MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index,
8672 Object* value) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008673 uint32_t old_len = 0;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008674 CHECK(length()->ToArrayIndex(&old_len));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008675 // Check to see if we need to update the length. For now, we make
8676 // sure that the length stays within 32-bits (unsigned).
8677 if (index >= old_len && index != 0xffffffff) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00008678 Object* len;
8679 { MaybeObject* maybe_len =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008680 GetHeap()->NumberFromDouble(static_cast<double>(index) + 1);
lrn@chromium.org303ada72010-10-27 09:33:13 +00008681 if (!maybe_len->ToObject(&len)) return maybe_len;
8682 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008683 set_length(len);
8684 }
8685 return value;
8686}
8687
8688
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00008689MaybeObject* JSObject::GetElementWithInterceptor(Object* receiver,
lrn@chromium.org303ada72010-10-27 09:33:13 +00008690 uint32_t index) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008691 Isolate* isolate = GetIsolate();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008692 // Make sure that the top context does not change when doing
8693 // callbacks or interceptor calls.
8694 AssertNoContextChange ncc;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008695 HandleScope scope(isolate);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00008696 Handle<InterceptorInfo> interceptor(GetIndexedInterceptor(), isolate);
8697 Handle<Object> this_handle(receiver, isolate);
8698 Handle<JSObject> holder_handle(this, isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008699 if (!interceptor->getter()->IsUndefined()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008700 v8::IndexedPropertyGetter getter =
8701 v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008702 LOG(isolate,
8703 ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
8704 CustomArguments args(isolate, interceptor->data(), receiver, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00008705 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008706 v8::Handle<v8::Value> result;
8707 {
8708 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008709 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008710 result = getter(index, info);
8711 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008712 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008713 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
8714 }
8715
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00008716 Heap* heap = holder_handle->GetHeap();
8717 ElementsAccessor* handler = holder_handle->GetElementsAccessor();
rossberg@chromium.org28a37082011-08-22 11:03:23 +00008718 MaybeObject* raw_result = handler->Get(holder_handle->elements(),
8719 index,
8720 *holder_handle,
8721 *this_handle);
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00008722 if (raw_result != heap->the_hole_value()) return raw_result;
8723
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008724 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008725
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00008726 Object* pt = holder_handle->GetPrototype();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008727 if (pt == heap->null_value()) return heap->undefined_value();
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00008728 return pt->GetElementWithReceiver(*this_handle, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008729}
8730
8731
8732bool JSObject::HasDenseElements() {
8733 int capacity = 0;
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008734 int used = 0;
8735 GetElementsCapacityAndUsage(&capacity, &used);
8736 return (capacity == 0) || (used > (capacity / 2));
8737}
8738
8739
8740void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
8741 *capacity = 0;
8742 *used = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008743
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008744 FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements());
8745 FixedArray* backing_store = NULL;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008746 switch (GetElementsKind()) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008747 case NON_STRICT_ARGUMENTS_ELEMENTS:
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008748 backing_store_base =
8749 FixedArray::cast(FixedArray::cast(backing_store_base)->get(1));
8750 backing_store = FixedArray::cast(backing_store_base);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008751 if (backing_store->IsDictionary()) {
8752 NumberDictionary* dictionary = NumberDictionary::cast(backing_store);
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008753 *capacity = dictionary->Capacity();
8754 *used = dictionary->NumberOfElements();
whesse@chromium.org7b260152011-06-20 15:33:18 +00008755 break;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008756 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00008757 // Fall through.
8758 case FAST_ELEMENTS:
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008759 backing_store = FixedArray::cast(backing_store_base);
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008760 *capacity = backing_store->length();
8761 for (int i = 0; i < *capacity; ++i) {
8762 if (!backing_store->get(i)->IsTheHole()) ++(*used);
whesse@chromium.org7b260152011-06-20 15:33:18 +00008763 }
8764 break;
8765 case DICTIONARY_ELEMENTS: {
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +00008766 NumberDictionary* dictionary =
8767 NumberDictionary::cast(FixedArray::cast(elements()));
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008768 *capacity = dictionary->Capacity();
8769 *used = dictionary->NumberOfElements();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008770 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008771 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008772 case FAST_DOUBLE_ELEMENTS: {
8773 FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008774 *capacity = elms->length();
8775 for (int i = 0; i < *capacity; i++) {
8776 if (!elms->is_the_hole(i)) ++(*used);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008777 }
8778 break;
8779 }
ager@chromium.org3811b432009-10-28 14:53:37 +00008780 case EXTERNAL_BYTE_ELEMENTS:
8781 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
8782 case EXTERNAL_SHORT_ELEMENTS:
8783 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
8784 case EXTERNAL_INT_ELEMENTS:
8785 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00008786 case EXTERNAL_FLOAT_ELEMENTS:
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008787 case EXTERNAL_DOUBLE_ELEMENTS:
8788 case EXTERNAL_PIXEL_ELEMENTS:
8789 // External arrays are considered 100% used.
8790 ExternalArray* external_array = ExternalArray::cast(elements());
8791 *capacity = external_array->length();
8792 *used = external_array->length();
8793 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008794 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008795}
8796
8797
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008798bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008799 STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <=
8800 kMaxUncheckedFastElementsLength);
8801 if (new_capacity <= kMaxUncheckedOldFastElementsLength ||
8802 (new_capacity <= kMaxUncheckedFastElementsLength &&
8803 GetHeap()->InNewSpace(this))) {
8804 return false;
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008805 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008806 // If the fast-case backing storage takes up roughly three times as
8807 // much space (in machine words) as a dictionary backing storage
8808 // would, the object should have slow elements.
8809 int old_capacity = 0;
8810 int used_elements = 0;
8811 GetElementsCapacityAndUsage(&old_capacity, &used_elements);
8812 int dictionary_size = NumberDictionary::ComputeCapacity(used_elements) *
8813 NumberDictionary::kEntrySize;
8814 return 3 * dictionary_size <= new_capacity;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008815}
8816
8817
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008818bool JSObject::ShouldConvertToFastElements() {
whesse@chromium.org7b260152011-06-20 15:33:18 +00008819 ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008820 // If the elements are sparse, we should not go back to fast case.
8821 if (!HasDenseElements()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008822 // An object requiring access checks is never allowed to have fast
8823 // elements. If it had fast elements we would skip security checks.
8824 if (IsAccessCheckNeeded()) return false;
whesse@chromium.org7b260152011-06-20 15:33:18 +00008825
8826 FixedArray* elements = FixedArray::cast(this->elements());
8827 NumberDictionary* dictionary = NULL;
8828 if (elements->map() == GetHeap()->non_strict_arguments_elements_map()) {
8829 dictionary = NumberDictionary::cast(elements->get(1));
8830 } else {
8831 dictionary = NumberDictionary::cast(elements);
8832 }
8833 // If an element has been added at a very high index in the elements
8834 // dictionary, we cannot go back to fast case.
8835 if (dictionary->requires_slow_elements()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008836 // If the dictionary backing storage takes up roughly half as much
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008837 // space (in machine words) as a fast-case backing storage would,
8838 // the object should have fast elements.
8839 uint32_t array_size = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008840 if (IsJSArray()) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008841 CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_size));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008842 } else {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008843 array_size = dictionary->max_number_key();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008844 }
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008845 uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
8846 NumberDictionary::kEntrySize;
8847 return 2 * dictionary_size >= array_size;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008848}
8849
8850
ricow@chromium.org2c99e282011-07-28 09:15:17 +00008851bool JSObject::CanConvertToFastDoubleElements() {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00008852 if (FLAG_unbox_double_arrays) {
8853 ASSERT(HasDictionaryElements());
8854 NumberDictionary* dictionary = NumberDictionary::cast(elements());
8855 for (int i = 0; i < dictionary->Capacity(); i++) {
8856 Object* key = dictionary->KeyAt(i);
8857 if (key->IsNumber()) {
8858 if (!dictionary->ValueAt(i)->IsNumber()) return false;
8859 }
8860 }
8861 return true;
8862 } else {
8863 return false;
8864 }
8865}
8866
8867
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008868// Certain compilers request function template instantiation when they
8869// see the definition of the other template functions in the
8870// class. This requires us to have the template functions put
8871// together, so even though this function belongs in objects-debug.cc,
8872// we keep it here instead to satisfy certain compilers.
whesse@chromium.org023421e2010-12-21 12:19:12 +00008873#ifdef OBJECT_PRINT
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008874template<typename Shape, typename Key>
whesse@chromium.org023421e2010-12-21 12:19:12 +00008875void Dictionary<Shape, Key>::Print(FILE* out) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008876 int capacity = HashTable<Shape, Key>::Capacity();
8877 for (int i = 0; i < capacity; i++) {
8878 Object* k = HashTable<Shape, Key>::KeyAt(i);
8879 if (HashTable<Shape, Key>::IsKey(k)) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00008880 PrintF(out, " ");
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008881 if (k->IsString()) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00008882 String::cast(k)->StringPrint(out);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008883 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +00008884 k->ShortPrint(out);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008885 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00008886 PrintF(out, ": ");
8887 ValueAt(i)->ShortPrint(out);
8888 PrintF(out, "\n");
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008889 }
8890 }
8891}
8892#endif
8893
8894
8895template<typename Shape, typename Key>
8896void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008897 int pos = 0;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008898 int capacity = HashTable<Shape, Key>::Capacity();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008899 AssertNoAllocation no_gc;
8900 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008901 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008902 Object* k = Dictionary<Shape, Key>::KeyAt(i);
8903 if (Dictionary<Shape, Key>::IsKey(k)) {
8904 elements->set(pos++, ValueAt(i), mode);
8905 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008906 }
8907 ASSERT(pos == elements->length());
8908}
8909
8910
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008911InterceptorInfo* JSObject::GetNamedInterceptor() {
8912 ASSERT(map()->has_named_interceptor());
8913 JSFunction* constructor = JSFunction::cast(map()->constructor());
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00008914 ASSERT(constructor->shared()->IsApiFunction());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008915 Object* result =
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00008916 constructor->shared()->get_api_func_data()->named_property_handler();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008917 return InterceptorInfo::cast(result);
8918}
8919
8920
8921InterceptorInfo* JSObject::GetIndexedInterceptor() {
8922 ASSERT(map()->has_indexed_interceptor());
8923 JSFunction* constructor = JSFunction::cast(map()->constructor());
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00008924 ASSERT(constructor->shared()->IsApiFunction());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008925 Object* result =
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00008926 constructor->shared()->get_api_func_data()->indexed_property_handler();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008927 return InterceptorInfo::cast(result);
8928}
8929
8930
lrn@chromium.org303ada72010-10-27 09:33:13 +00008931MaybeObject* JSObject::GetPropertyPostInterceptor(
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008932 JSReceiver* receiver,
lrn@chromium.org303ada72010-10-27 09:33:13 +00008933 String* name,
8934 PropertyAttributes* attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008935 // Check local property in holder, ignore interceptor.
8936 LookupResult result;
8937 LocalLookupRealNamedProperty(name, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00008938 if (result.IsProperty()) {
8939 return GetProperty(receiver, &result, name, attributes);
8940 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008941 // Continue searching via the prototype chain.
8942 Object* pt = GetPrototype();
8943 *attributes = ABSENT;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008944 if (pt->IsNull()) return GetHeap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008945 return pt->GetPropertyWithReceiver(receiver, name, attributes);
8946}
8947
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008948
lrn@chromium.org303ada72010-10-27 09:33:13 +00008949MaybeObject* JSObject::GetLocalPropertyPostInterceptor(
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008950 JSReceiver* receiver,
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008951 String* name,
8952 PropertyAttributes* attributes) {
8953 // Check local property in holder, ignore interceptor.
8954 LookupResult result;
8955 LocalLookupRealNamedProperty(name, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00008956 if (result.IsProperty()) {
8957 return GetProperty(receiver, &result, name, attributes);
8958 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00008959 return GetHeap()->undefined_value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008960}
8961
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008962
lrn@chromium.org303ada72010-10-27 09:33:13 +00008963MaybeObject* JSObject::GetPropertyWithInterceptor(
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008964 JSReceiver* receiver,
ager@chromium.orge2902be2009-06-08 12:21:35 +00008965 String* name,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00008966 PropertyAttributes* attributes) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008967 Isolate* isolate = GetIsolate();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008968 InterceptorInfo* interceptor = GetNamedInterceptor();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008969 HandleScope scope(isolate);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00008970 Handle<JSReceiver> receiver_handle(receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008971 Handle<JSObject> holder_handle(this);
8972 Handle<String> name_handle(name);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008973
8974 if (!interceptor->getter()->IsUndefined()) {
8975 v8::NamedPropertyGetter getter =
8976 v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008977 LOG(isolate,
8978 ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
8979 CustomArguments args(isolate, interceptor->data(), receiver, this);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00008980 v8::AccessorInfo info(args.end());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008981 v8::Handle<v8::Value> result;
8982 {
8983 // Leaving JavaScript.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008984 VMState state(isolate, EXTERNAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008985 result = getter(v8::Utils::ToLocal(name_handle), info);
8986 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008987 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008988 if (!result.IsEmpty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008989 *attributes = NONE;
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00008990 return *v8::Utils::OpenHandle(*result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008991 }
8992 }
8993
lrn@chromium.org303ada72010-10-27 09:33:13 +00008994 MaybeObject* result = holder_handle->GetPropertyPostInterceptor(
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008995 *receiver_handle,
8996 *name_handle,
8997 attributes);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00008998 RETURN_IF_SCHEDULED_EXCEPTION(isolate);
ager@chromium.orge2902be2009-06-08 12:21:35 +00008999 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009000}
9001
9002
9003bool JSObject::HasRealNamedProperty(String* key) {
9004 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009005 if (IsAccessCheckNeeded()) {
9006 Heap* heap = GetHeap();
9007 if (!heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
9008 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
9009 return false;
9010 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009011 }
9012
9013 LookupResult result;
9014 LocalLookupRealNamedProperty(key, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00009015 return result.IsProperty() && (result.type() != INTERCEPTOR);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009016}
9017
9018
9019bool JSObject::HasRealElementProperty(uint32_t index) {
9020 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009021 if (IsAccessCheckNeeded()) {
9022 Heap* heap = GetHeap();
9023 if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
9024 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
9025 return false;
9026 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009027 }
9028
9029 // Handle [] on String objects.
9030 if (this->IsStringObjectWithCharacterAt(index)) return true;
9031
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009032 switch (GetElementsKind()) {
9033 case FAST_ELEMENTS: {
9034 uint32_t length = IsJSArray() ?
9035 static_cast<uint32_t>(
9036 Smi::cast(JSArray::cast(this)->length())->value()) :
9037 static_cast<uint32_t>(FixedArray::cast(elements())->length());
9038 return (index < length) &&
9039 !FixedArray::cast(elements())->get(index)->IsTheHole();
9040 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +00009041 case FAST_DOUBLE_ELEMENTS: {
9042 uint32_t length = IsJSArray() ?
9043 static_cast<uint32_t>(
9044 Smi::cast(JSArray::cast(this)->length())->value()) :
9045 static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
9046 return (index < length) &&
9047 !FixedDoubleArray::cast(elements())->is_the_hole(index);
9048 break;
9049 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00009050 case EXTERNAL_PIXEL_ELEMENTS: {
9051 ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009052 return index < static_cast<uint32_t>(pixels->length());
9053 }
ager@chromium.org3811b432009-10-28 14:53:37 +00009054 case EXTERNAL_BYTE_ELEMENTS:
9055 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
9056 case EXTERNAL_SHORT_ELEMENTS:
9057 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
9058 case EXTERNAL_INT_ELEMENTS:
9059 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00009060 case EXTERNAL_FLOAT_ELEMENTS:
9061 case EXTERNAL_DOUBLE_ELEMENTS: {
ager@chromium.org3811b432009-10-28 14:53:37 +00009062 ExternalArray* array = ExternalArray::cast(elements());
9063 return index < static_cast<uint32_t>(array->length());
9064 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009065 case DICTIONARY_ELEMENTS: {
9066 return element_dictionary()->FindEntry(index)
9067 != NumberDictionary::kNotFound;
9068 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00009069 case NON_STRICT_ARGUMENTS_ELEMENTS:
9070 UNIMPLEMENTED();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009071 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009072 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009073 // All possibilities have been handled above already.
9074 UNREACHABLE();
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009075 return GetHeap()->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009076}
9077
9078
9079bool JSObject::HasRealNamedCallbackProperty(String* key) {
9080 // Check access rights if needed.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009081 if (IsAccessCheckNeeded()) {
9082 Heap* heap = GetHeap();
9083 if (!heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
9084 heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
9085 return false;
9086 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009087 }
9088
9089 LookupResult result;
9090 LocalLookupRealNamedProperty(key, &result);
ager@chromium.org5c838252010-02-19 08:53:10 +00009091 return result.IsProperty() && (result.type() == CALLBACKS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009092}
9093
9094
9095int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
9096 if (HasFastProperties()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00009097 DescriptorArray* descs = map()->instance_descriptors();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009098 int result = 0;
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00009099 for (int i = 0; i < descs->number_of_descriptors(); i++) {
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00009100 PropertyDetails details(descs->GetDetails(i));
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00009101 if (details.IsProperty() && (details.attributes() & filter) == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009102 result++;
9103 }
9104 }
9105 return result;
9106 } else {
9107 return property_dictionary()->NumberOfElementsFilterAttributes(filter);
9108 }
9109}
9110
9111
9112int JSObject::NumberOfEnumProperties() {
9113 return NumberOfLocalProperties(static_cast<PropertyAttributes>(DONT_ENUM));
9114}
9115
9116
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009117void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009118 Object* temp = get(i);
9119 set(i, get(j));
9120 set(j, temp);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009121 if (this != numbers) {
9122 temp = numbers->get(i);
9123 numbers->set(i, numbers->get(j));
9124 numbers->set(j, temp);
9125 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009126}
9127
9128
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009129static void InsertionSortPairs(FixedArray* content,
9130 FixedArray* numbers,
9131 int len) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009132 for (int i = 1; i < len; i++) {
9133 int j = i;
9134 while (j > 0 &&
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009135 (NumberToUint32(numbers->get(j - 1)) >
9136 NumberToUint32(numbers->get(j)))) {
9137 content->SwapPairs(numbers, j - 1, j);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009138 j--;
9139 }
9140 }
9141}
9142
9143
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009144void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009145 // In-place heap sort.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009146 ASSERT(content->length() == numbers->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009147
9148 // Bottom-up max-heap construction.
9149 for (int i = 1; i < len; ++i) {
9150 int child_index = i;
9151 while (child_index > 0) {
9152 int parent_index = ((child_index + 1) >> 1) - 1;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009153 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
9154 uint32_t child_value = NumberToUint32(numbers->get(child_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009155 if (parent_value < child_value) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009156 content->SwapPairs(numbers, parent_index, child_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009157 } else {
9158 break;
9159 }
9160 child_index = parent_index;
9161 }
9162 }
9163
9164 // Extract elements and create sorted array.
9165 for (int i = len - 1; i > 0; --i) {
9166 // Put max element at the back of the array.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009167 content->SwapPairs(numbers, 0, i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009168 // Sift down the new top element.
9169 int parent_index = 0;
9170 while (true) {
9171 int child_index = ((parent_index + 1) << 1) - 1;
9172 if (child_index >= i) break;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009173 uint32_t child1_value = NumberToUint32(numbers->get(child_index));
9174 uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
9175 uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009176 if (child_index + 1 >= i || child1_value > child2_value) {
9177 if (parent_value > child1_value) break;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009178 content->SwapPairs(numbers, parent_index, child_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009179 parent_index = child_index;
9180 } else {
9181 if (parent_value > child2_value) break;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009182 content->SwapPairs(numbers, parent_index, child_index + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009183 parent_index = child_index + 1;
9184 }
9185 }
9186 }
9187}
9188
9189
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009190// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
9191void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
9192 ASSERT(this->length() == numbers->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009193 // For small arrays, simply use insertion sort.
9194 if (len <= 10) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009195 InsertionSortPairs(this, numbers, len);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009196 return;
9197 }
9198 // Check the range of indices.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009199 uint32_t min_index = NumberToUint32(numbers->get(0));
9200 uint32_t max_index = min_index;
9201 uint32_t i;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009202 for (i = 1; i < len; i++) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009203 if (NumberToUint32(numbers->get(i)) < min_index) {
9204 min_index = NumberToUint32(numbers->get(i));
9205 } else if (NumberToUint32(numbers->get(i)) > max_index) {
9206 max_index = NumberToUint32(numbers->get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009207 }
9208 }
9209 if (max_index - min_index + 1 == len) {
9210 // Indices form a contiguous range, unless there are duplicates.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009211 // Do an in-place linear time sort assuming distinct numbers, but
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009212 // avoid hanging in case they are not.
9213 for (i = 0; i < len; i++) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009214 uint32_t p;
9215 uint32_t j = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009216 // While the current element at i is not at its correct position p,
9217 // swap the elements at these two positions.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009218 while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009219 j++ < len) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009220 SwapPairs(numbers, i, p);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009221 }
9222 }
9223 } else {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009224 HeapSortPairs(this, numbers, len);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009225 return;
9226 }
9227}
9228
9229
9230// Fill in the names of local properties into the supplied storage. The main
9231// purpose of this function is to provide reflection information for the object
9232// mirrors.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009233void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00009234 ASSERT(storage->length() >= (NumberOfLocalProperties(NONE) - index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009235 if (HasFastProperties()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00009236 DescriptorArray* descs = map()->instance_descriptors();
9237 for (int i = 0; i < descs->number_of_descriptors(); i++) {
9238 if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009239 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009240 ASSERT(storage->length() >= index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009241 } else {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00009242 property_dictionary()->CopyKeysTo(storage,
9243 index,
9244 StringDictionary::UNSORTED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009245 }
9246}
9247
9248
9249int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
9250 return GetLocalElementKeys(NULL, filter);
9251}
9252
9253
9254int JSObject::NumberOfEnumElements() {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009255 // Fast case for objects with no elements.
9256 if (!IsJSValue() && HasFastElements()) {
9257 uint32_t length = IsJSArray() ?
9258 static_cast<uint32_t>(
9259 Smi::cast(JSArray::cast(this)->length())->value()) :
9260 static_cast<uint32_t>(FixedArray::cast(elements())->length());
9261 if (length == 0) return 0;
9262 }
9263 // Compute the number of enumerable elements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009264 return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
9265}
9266
9267
9268int JSObject::GetLocalElementKeys(FixedArray* storage,
9269 PropertyAttributes filter) {
9270 int counter = 0;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009271 switch (GetElementsKind()) {
9272 case FAST_ELEMENTS: {
9273 int length = IsJSArray() ?
9274 Smi::cast(JSArray::cast(this)->length())->value() :
9275 FixedArray::cast(elements())->length();
9276 for (int i = 0; i < length; i++) {
9277 if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
9278 if (storage != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009279 storage->set(counter, Smi::FromInt(i));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009280 }
9281 counter++;
9282 }
9283 }
9284 ASSERT(!storage || storage->length() >= counter);
9285 break;
9286 }
rossberg@chromium.org717967f2011-07-20 13:44:42 +00009287 case FAST_DOUBLE_ELEMENTS: {
9288 int length = IsJSArray() ?
9289 Smi::cast(JSArray::cast(this)->length())->value() :
9290 FixedDoubleArray::cast(elements())->length();
9291 for (int i = 0; i < length; i++) {
9292 if (!FixedDoubleArray::cast(elements())->is_the_hole(i)) {
9293 if (storage != NULL) {
9294 storage->set(counter, Smi::FromInt(i));
9295 }
9296 counter++;
9297 }
9298 }
9299 ASSERT(!storage || storage->length() >= counter);
9300 break;
9301 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00009302 case EXTERNAL_PIXEL_ELEMENTS: {
9303 int length = ExternalPixelArray::cast(elements())->length();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009304 while (counter < length) {
9305 if (storage != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009306 storage->set(counter, Smi::FromInt(counter));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009307 }
9308 counter++;
9309 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009310 ASSERT(!storage || storage->length() >= counter);
9311 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009312 }
ager@chromium.org3811b432009-10-28 14:53:37 +00009313 case EXTERNAL_BYTE_ELEMENTS:
9314 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
9315 case EXTERNAL_SHORT_ELEMENTS:
9316 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
9317 case EXTERNAL_INT_ELEMENTS:
9318 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00009319 case EXTERNAL_FLOAT_ELEMENTS:
9320 case EXTERNAL_DOUBLE_ELEMENTS: {
ager@chromium.org3811b432009-10-28 14:53:37 +00009321 int length = ExternalArray::cast(elements())->length();
9322 while (counter < length) {
9323 if (storage != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009324 storage->set(counter, Smi::FromInt(counter));
ager@chromium.org3811b432009-10-28 14:53:37 +00009325 }
9326 counter++;
9327 }
9328 ASSERT(!storage || storage->length() >= counter);
9329 break;
9330 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009331 case DICTIONARY_ELEMENTS: {
9332 if (storage != NULL) {
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009333 element_dictionary()->CopyKeysTo(storage,
9334 filter,
9335 NumberDictionary::SORTED);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009336 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00009337 counter += element_dictionary()->NumberOfElementsFilterAttributes(filter);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009338 break;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009339 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00009340 case NON_STRICT_ARGUMENTS_ELEMENTS: {
9341 FixedArray* parameter_map = FixedArray::cast(elements());
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009342 int mapped_length = parameter_map->length() - 2;
whesse@chromium.org7b260152011-06-20 15:33:18 +00009343 FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
9344 if (arguments->IsDictionary()) {
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009345 // Copy the keys from arguments first, because Dictionary::CopyKeysTo
9346 // will insert in storage starting at index 0.
whesse@chromium.org7b260152011-06-20 15:33:18 +00009347 NumberDictionary* dictionary = NumberDictionary::cast(arguments);
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009348 if (storage != NULL) {
9349 dictionary->CopyKeysTo(storage, filter, NumberDictionary::UNSORTED);
9350 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00009351 counter += dictionary->NumberOfElementsFilterAttributes(filter);
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009352 for (int i = 0; i < mapped_length; ++i) {
9353 if (!parameter_map->get(i + 2)->IsTheHole()) {
9354 if (storage != NULL) storage->set(counter, Smi::FromInt(i));
whesse@chromium.org7b260152011-06-20 15:33:18 +00009355 ++counter;
9356 }
9357 }
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009358 if (storage != NULL) storage->SortPairs(storage, counter);
9359
9360 } else {
9361 int backing_length = arguments->length();
9362 int i = 0;
9363 for (; i < mapped_length; ++i) {
9364 if (!parameter_map->get(i + 2)->IsTheHole()) {
9365 if (storage != NULL) storage->set(counter, Smi::FromInt(i));
9366 ++counter;
9367 } else if (i < backing_length && !arguments->get(i)->IsTheHole()) {
9368 if (storage != NULL) storage->set(counter, Smi::FromInt(i));
9369 ++counter;
9370 }
9371 }
9372 for (; i < backing_length; ++i) {
9373 if (storage != NULL) storage->set(counter, Smi::FromInt(i));
9374 ++counter;
9375 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00009376 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009377 break;
whesse@chromium.org7b260152011-06-20 15:33:18 +00009378 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009379 }
9380
9381 if (this->IsJSValue()) {
9382 Object* val = JSValue::cast(this)->value();
9383 if (val->IsString()) {
9384 String* str = String::cast(val);
9385 if (storage) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009386 for (int i = 0; i < str->length(); i++) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009387 storage->set(counter + i, Smi::FromInt(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009388 }
9389 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00009390 counter += str->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009391 }
9392 }
9393 ASSERT(!storage || storage->length() == counter);
9394 return counter;
9395}
9396
9397
9398int JSObject::GetEnumElementKeys(FixedArray* storage) {
9399 return GetLocalElementKeys(storage,
9400 static_cast<PropertyAttributes>(DONT_ENUM));
9401}
9402
9403
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009404// StringKey simply carries a string object as key.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00009405class StringKey : public HashTableKey {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009406 public:
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009407 explicit StringKey(String* string) :
9408 string_(string),
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009409 hash_(HashForObject(string)) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009410
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009411 bool IsMatch(Object* string) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009412 // We know that all entries in a hash table had their hash keys created.
9413 // Use that knowledge to have fast failure.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009414 if (hash_ != HashForObject(string)) {
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009415 return false;
9416 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009417 return string_->Equals(String::cast(string));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009418 }
9419
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009420 uint32_t Hash() { return hash_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009421
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009422 uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009423
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009424 Object* AsObject() { return string_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009425
9426 String* string_;
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009427 uint32_t hash_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009428};
9429
ager@chromium.org381abbb2009-02-25 13:23:22 +00009430
9431// StringSharedKeys are used as keys in the eval cache.
9432class StringSharedKey : public HashTableKey {
9433 public:
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009434 StringSharedKey(String* source,
9435 SharedFunctionInfo* shared,
9436 StrictModeFlag strict_mode)
9437 : source_(source),
9438 shared_(shared),
9439 strict_mode_(strict_mode) { }
ager@chromium.org381abbb2009-02-25 13:23:22 +00009440
9441 bool IsMatch(Object* other) {
9442 if (!other->IsFixedArray()) return false;
9443 FixedArray* pair = FixedArray::cast(other);
9444 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
9445 if (shared != shared_) return false;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009446 StrictModeFlag strict_mode = static_cast<StrictModeFlag>(
9447 Smi::cast(pair->get(2))->value());
9448 if (strict_mode != strict_mode_) return false;
ager@chromium.org381abbb2009-02-25 13:23:22 +00009449 String* source = String::cast(pair->get(1));
9450 return source->Equals(source_);
9451 }
9452
ager@chromium.org381abbb2009-02-25 13:23:22 +00009453 static uint32_t StringSharedHashHelper(String* source,
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009454 SharedFunctionInfo* shared,
9455 StrictModeFlag strict_mode) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00009456 uint32_t hash = source->Hash();
9457 if (shared->HasSourceCode()) {
9458 // Instead of using the SharedFunctionInfo pointer in the hash
9459 // code computation, we use a combination of the hash of the
9460 // script source code and the start and end positions. We do
9461 // this to ensure that the cache entries can survive garbage
9462 // collection.
9463 Script* script = Script::cast(shared->script());
9464 hash ^= String::cast(script->source())->Hash();
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009465 if (strict_mode == kStrictMode) hash ^= 0x8000;
ager@chromium.org381abbb2009-02-25 13:23:22 +00009466 hash += shared->start_position();
9467 }
9468 return hash;
9469 }
9470
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009471 uint32_t Hash() {
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009472 return StringSharedHashHelper(source_, shared_, strict_mode_);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009473 }
9474
9475 uint32_t HashForObject(Object* obj) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00009476 FixedArray* pair = FixedArray::cast(obj);
9477 SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
9478 String* source = String::cast(pair->get(1));
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009479 StrictModeFlag strict_mode = static_cast<StrictModeFlag>(
9480 Smi::cast(pair->get(2))->value());
9481 return StringSharedHashHelper(source, shared, strict_mode);
ager@chromium.org381abbb2009-02-25 13:23:22 +00009482 }
9483
lrn@chromium.org303ada72010-10-27 09:33:13 +00009484 MUST_USE_RESULT MaybeObject* AsObject() {
9485 Object* obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009486 { MaybeObject* maybe_obj = source_->GetHeap()->AllocateFixedArray(3);
lrn@chromium.org303ada72010-10-27 09:33:13 +00009487 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9488 }
ager@chromium.org381abbb2009-02-25 13:23:22 +00009489 FixedArray* pair = FixedArray::cast(obj);
9490 pair->set(0, shared_);
9491 pair->set(1, source_);
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009492 pair->set(2, Smi::FromInt(strict_mode_));
ager@chromium.org381abbb2009-02-25 13:23:22 +00009493 return pair;
9494 }
9495
ager@chromium.org381abbb2009-02-25 13:23:22 +00009496 private:
9497 String* source_;
9498 SharedFunctionInfo* shared_;
ricow@chromium.org83aa5492011-02-07 12:42:56 +00009499 StrictModeFlag strict_mode_;
ager@chromium.org381abbb2009-02-25 13:23:22 +00009500};
9501
9502
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009503// RegExpKey carries the source and flags of a regular expression as key.
9504class RegExpKey : public HashTableKey {
9505 public:
9506 RegExpKey(String* string, JSRegExp::Flags flags)
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009507 : string_(string),
9508 flags_(Smi::FromInt(flags.value())) { }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009509
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00009510 // Rather than storing the key in the hash table, a pointer to the
9511 // stored value is stored where the key should be. IsMatch then
9512 // compares the search key to the found object, rather than comparing
9513 // a key to a key.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009514 bool IsMatch(Object* obj) {
9515 FixedArray* val = FixedArray::cast(obj);
9516 return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
9517 && (flags_ == val->get(JSRegExp::kFlagsIndex));
9518 }
9519
9520 uint32_t Hash() { return RegExpHash(string_, flags_); }
9521
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009522 Object* AsObject() {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009523 // Plain hash maps, which is where regexp keys are used, don't
9524 // use this function.
9525 UNREACHABLE();
9526 return NULL;
9527 }
9528
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009529 uint32_t HashForObject(Object* obj) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009530 FixedArray* val = FixedArray::cast(obj);
9531 return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
9532 Smi::cast(val->get(JSRegExp::kFlagsIndex)));
9533 }
9534
9535 static uint32_t RegExpHash(String* string, Smi* flags) {
9536 return string->Hash() + flags->value();
9537 }
9538
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009539 String* string_;
9540 Smi* flags_;
9541};
9542
ager@chromium.org9258b6b2008-09-11 09:11:10 +00009543// Utf8SymbolKey carries a vector of chars as key.
9544class Utf8SymbolKey : public HashTableKey {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009545 public:
ager@chromium.org9258b6b2008-09-11 09:11:10 +00009546 explicit Utf8SymbolKey(Vector<const char> string)
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009547 : string_(string), hash_field_(0) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009548
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009549 bool IsMatch(Object* string) {
9550 return String::cast(string)->IsEqualTo(string_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009551 }
9552
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009553 uint32_t Hash() {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009554 if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009555 unibrow::Utf8InputBuffer<> buffer(string_.start(),
9556 static_cast<unsigned>(string_.length()));
9557 chars_ = buffer.Length();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009558 hash_field_ = String::ComputeHashField(&buffer, chars_);
9559 uint32_t result = hash_field_ >> String::kHashShift;
ager@chromium.org3b45ab52009-03-19 22:21:34 +00009560 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
9561 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009562 }
9563
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009564 uint32_t HashForObject(Object* other) {
9565 return String::cast(other)->Hash();
9566 }
9567
lrn@chromium.org303ada72010-10-27 09:33:13 +00009568 MaybeObject* AsObject() {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009569 if (hash_field_ == 0) Hash();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009570 return Isolate::Current()->heap()->AllocateSymbol(
9571 string_, chars_, hash_field_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009572 }
9573
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009574 Vector<const char> string_;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009575 uint32_t hash_field_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009576 int chars_; // Caches the number of characters when computing the hash code.
9577};
9578
9579
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00009580template <typename Char>
9581class SequentialSymbolKey : public HashTableKey {
9582 public:
9583 explicit SequentialSymbolKey(Vector<const Char> string)
9584 : string_(string), hash_field_(0) { }
9585
9586 uint32_t Hash() {
9587 StringHasher hasher(string_.length());
9588
9589 // Very long strings have a trivial hash that doesn't inspect the
9590 // string contents.
9591 if (hasher.has_trivial_hash()) {
9592 hash_field_ = hasher.GetHashField();
9593 } else {
9594 int i = 0;
9595 // Do the iterative array index computation as long as there is a
9596 // chance this is an array index.
9597 while (i < string_.length() && hasher.is_array_index()) {
9598 hasher.AddCharacter(static_cast<uc32>(string_[i]));
9599 i++;
9600 }
9601
9602 // Process the remaining characters without updating the array
9603 // index.
9604 while (i < string_.length()) {
9605 hasher.AddCharacterNoIndex(static_cast<uc32>(string_[i]));
9606 i++;
9607 }
9608 hash_field_ = hasher.GetHashField();
9609 }
9610
9611 uint32_t result = hash_field_ >> String::kHashShift;
9612 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
9613 return result;
9614 }
9615
9616
9617 uint32_t HashForObject(Object* other) {
9618 return String::cast(other)->Hash();
9619 }
9620
9621 Vector<const Char> string_;
9622 uint32_t hash_field_;
9623};
9624
9625
9626
9627class AsciiSymbolKey : public SequentialSymbolKey<char> {
9628 public:
9629 explicit AsciiSymbolKey(Vector<const char> str)
9630 : SequentialSymbolKey<char>(str) { }
9631
9632 bool IsMatch(Object* string) {
9633 return String::cast(string)->IsAsciiEqualTo(string_);
9634 }
9635
9636 MaybeObject* AsObject() {
9637 if (hash_field_ == 0) Hash();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009638 return HEAP->AllocateAsciiSymbol(string_, hash_field_);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00009639 }
9640};
9641
9642
danno@chromium.org40cb8782011-05-25 07:58:50 +00009643class SubStringAsciiSymbolKey : public HashTableKey {
9644 public:
9645 explicit SubStringAsciiSymbolKey(Handle<SeqAsciiString> string,
9646 int from,
9647 int length)
9648 : string_(string), from_(from), length_(length) { }
9649
9650 uint32_t Hash() {
9651 ASSERT(length_ >= 0);
9652 ASSERT(from_ + length_ <= string_->length());
9653 StringHasher hasher(length_);
9654
9655 // Very long strings have a trivial hash that doesn't inspect the
9656 // string contents.
9657 if (hasher.has_trivial_hash()) {
9658 hash_field_ = hasher.GetHashField();
9659 } else {
9660 int i = 0;
9661 // Do the iterative array index computation as long as there is a
9662 // chance this is an array index.
9663 while (i < length_ && hasher.is_array_index()) {
9664 hasher.AddCharacter(static_cast<uc32>(
9665 string_->SeqAsciiStringGet(i + from_)));
9666 i++;
9667 }
9668
9669 // Process the remaining characters without updating the array
9670 // index.
9671 while (i < length_) {
9672 hasher.AddCharacterNoIndex(static_cast<uc32>(
9673 string_->SeqAsciiStringGet(i + from_)));
9674 i++;
9675 }
9676 hash_field_ = hasher.GetHashField();
9677 }
9678
9679 uint32_t result = hash_field_ >> String::kHashShift;
9680 ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
9681 return result;
9682 }
9683
9684
9685 uint32_t HashForObject(Object* other) {
9686 return String::cast(other)->Hash();
9687 }
9688
9689 bool IsMatch(Object* string) {
9690 Vector<const char> chars(string_->GetChars() + from_, length_);
9691 return String::cast(string)->IsAsciiEqualTo(chars);
9692 }
9693
9694 MaybeObject* AsObject() {
9695 if (hash_field_ == 0) Hash();
9696 Vector<const char> chars(string_->GetChars() + from_, length_);
9697 return HEAP->AllocateAsciiSymbol(chars, hash_field_);
9698 }
9699
9700 private:
9701 Handle<SeqAsciiString> string_;
9702 int from_;
9703 int length_;
9704 uint32_t hash_field_;
9705};
9706
9707
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00009708class TwoByteSymbolKey : public SequentialSymbolKey<uc16> {
9709 public:
9710 explicit TwoByteSymbolKey(Vector<const uc16> str)
9711 : SequentialSymbolKey<uc16>(str) { }
9712
9713 bool IsMatch(Object* string) {
9714 return String::cast(string)->IsTwoByteEqualTo(string_);
9715 }
9716
9717 MaybeObject* AsObject() {
9718 if (hash_field_ == 0) Hash();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009719 return HEAP->AllocateTwoByteSymbol(string_, hash_field_);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +00009720 }
9721};
9722
9723
ager@chromium.org9258b6b2008-09-11 09:11:10 +00009724// SymbolKey carries a string/symbol object as key.
9725class SymbolKey : public HashTableKey {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009726 public:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009727 explicit SymbolKey(String* string)
9728 : string_(string) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009729
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009730 bool IsMatch(Object* string) {
9731 return String::cast(string)->Equals(string_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009732 }
9733
9734 uint32_t Hash() { return string_->Hash(); }
9735
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009736 uint32_t HashForObject(Object* other) {
9737 return String::cast(other)->Hash();
9738 }
9739
lrn@chromium.org303ada72010-10-27 09:33:13 +00009740 MaybeObject* AsObject() {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00009741 // Attempt to flatten the string, so that symbols will most often
9742 // be flat strings.
9743 string_ = string_->TryFlattenGetString();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009744 Heap* heap = string_->GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009745 // Transform string to symbol if possible.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009746 Map* map = heap->SymbolMapForString(string_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009747 if (map != NULL) {
9748 string_->set_map(map);
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009749 ASSERT(string_->IsSymbol());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009750 return string_;
9751 }
9752 // Otherwise allocate a new symbol.
9753 StringInputBuffer buffer(string_);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009754 return heap->AllocateInternalSymbol(&buffer,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009755 string_->length(),
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009756 string_->hash_field());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009757 }
9758
9759 static uint32_t StringHash(Object* obj) {
9760 return String::cast(obj)->Hash();
9761 }
9762
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009763 String* string_;
9764};
9765
9766
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009767template<typename Shape, typename Key>
9768void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009769 IteratePointers(v, 0, kElementsStartOffset);
9770}
9771
9772
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009773template<typename Shape, typename Key>
9774void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009775 IteratePointers(v,
9776 kElementsStartOffset,
9777 kHeaderSize + length() * kPointerSize);
9778}
9779
9780
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009781template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +00009782MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for,
9783 PretenureFlag pretenure) {
ricow@chromium.org2c99e282011-07-28 09:15:17 +00009784 int capacity = ComputeCapacity(at_least_space_for);
9785 if (capacity > HashTable::kMaxCapacity) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009786 return Failure::OutOfMemoryException();
9787 }
9788
lrn@chromium.org303ada72010-10-27 09:33:13 +00009789 Object* obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00009790 { MaybeObject* maybe_obj = Isolate::Current()->heap()->
9791 AllocateHashTable(EntryToIndex(capacity), pretenure);
lrn@chromium.org303ada72010-10-27 09:33:13 +00009792 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009793 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00009794 HashTable::cast(obj)->SetNumberOfElements(0);
9795 HashTable::cast(obj)->SetNumberOfDeletedElements(0);
9796 HashTable::cast(obj)->SetCapacity(capacity);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009797 return obj;
9798}
9799
9800
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009801// Find entry for key otherwise return kNotFound.
ricow@chromium.org4980dff2010-07-19 08:33:45 +00009802int StringDictionary::FindEntry(String* key) {
9803 if (!key->IsSymbol()) {
9804 return HashTable<StringDictionaryShape, String*>::FindEntry(key);
9805 }
9806
9807 // Optimized for symbol key. Knowledge of the key type allows:
9808 // 1. Move the check if the key is a symbol out of the loop.
9809 // 2. Avoid comparing hash codes in symbol to symbol comparision.
9810 // 3. Detect a case when a dictionary key is not a symbol but the key is.
9811 // In case of positive result the dictionary key may be replaced by
9812 // the symbol with minimal performance penalty. It gives a chance to
9813 // perform further lookups in code stubs (and significant performance boost
9814 // a certain style of code).
9815
9816 // EnsureCapacity will guarantee the hash table is never full.
9817 uint32_t capacity = Capacity();
9818 uint32_t entry = FirstProbe(key->Hash(), capacity);
9819 uint32_t count = 1;
9820
9821 while (true) {
9822 int index = EntryToIndex(entry);
9823 Object* element = get(index);
9824 if (element->IsUndefined()) break; // Empty entry.
9825 if (key == element) return entry;
9826 if (!element->IsSymbol() &&
9827 !element->IsNull() &&
9828 String::cast(element)->Equals(key)) {
9829 // Replace a non-symbol key by the equivalent symbol for faster further
9830 // lookups.
9831 set(index, key);
9832 return entry;
9833 }
9834 ASSERT(element->IsNull() || !String::cast(element)->Equals(key));
9835 entry = NextProbe(entry, count++, capacity);
9836 }
9837 return kNotFound;
9838}
9839
9840
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009841template<typename Shape, typename Key>
ager@chromium.org04921a82011-06-27 13:21:41 +00009842MaybeObject* HashTable<Shape, Key>::Rehash(HashTable* new_table, Key key) {
9843 ASSERT(NumberOfElements() < new_table->Capacity());
9844
9845 AssertNoAllocation no_gc;
9846 WriteBarrierMode mode = new_table->GetWriteBarrierMode(no_gc);
9847
9848 // Copy prefix to new array.
9849 for (int i = kPrefixStartIndex;
9850 i < kPrefixStartIndex + Shape::kPrefixSize;
9851 i++) {
9852 new_table->set(i, get(i), mode);
9853 }
9854
9855 // Rehash the elements.
9856 int capacity = Capacity();
9857 for (int i = 0; i < capacity; i++) {
9858 uint32_t from_index = EntryToIndex(i);
9859 Object* k = get(from_index);
9860 if (IsKey(k)) {
9861 uint32_t hash = Shape::HashForObject(key, k);
9862 uint32_t insertion_index =
9863 EntryToIndex(new_table->FindInsertionEntry(hash));
9864 for (int j = 0; j < Shape::kEntrySize; j++) {
9865 new_table->set(insertion_index + j, get(from_index + j), mode);
9866 }
9867 }
9868 }
9869 new_table->SetNumberOfElements(NumberOfElements());
9870 new_table->SetNumberOfDeletedElements(0);
9871 return new_table;
9872}
9873
9874
9875template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +00009876MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009877 int capacity = Capacity();
9878 int nof = NumberOfElements() + n;
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00009879 int nod = NumberOfDeletedElements();
9880 // Return if:
9881 // 50% is still free after adding n elements and
9882 // at most 50% of the free elements are deleted elements.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009883 if (nod <= (capacity - nof) >> 1) {
9884 int needed_free = nof >> 1;
9885 if (nof + needed_free <= capacity) return this;
9886 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009887
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009888 const int kMinCapacityForPretenure = 256;
9889 bool pretenure =
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00009890 (capacity > kMinCapacityForPretenure) && !GetHeap()->InNewSpace(this);
lrn@chromium.org303ada72010-10-27 09:33:13 +00009891 Object* obj;
9892 { MaybeObject* maybe_obj =
9893 Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
9894 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
9895 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009896
ager@chromium.org04921a82011-06-27 13:21:41 +00009897 return Rehash(HashTable::cast(obj), key);
9898}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009899
ager@chromium.org04921a82011-06-27 13:21:41 +00009900
9901template<typename Shape, typename Key>
9902MaybeObject* HashTable<Shape, Key>::Shrink(Key key) {
9903 int capacity = Capacity();
9904 int nof = NumberOfElements();
9905
9906 // Shrink to fit the number of elements if only a quarter of the
9907 // capacity is filled with elements.
9908 if (nof > (capacity >> 2)) return this;
9909 // Allocate a new dictionary with room for at least the current
9910 // number of elements. The allocation method will make sure that
9911 // there is extra room in the dictionary for additions. Don't go
9912 // lower than room for 16 elements.
9913 int at_least_room_for = nof;
9914 if (at_least_room_for < 16) return this;
9915
9916 const int kMinCapacityForPretenure = 256;
9917 bool pretenure =
9918 (at_least_room_for > kMinCapacityForPretenure) &&
9919 !GetHeap()->InNewSpace(this);
9920 Object* obj;
9921 { MaybeObject* maybe_obj =
9922 Allocate(at_least_room_for, pretenure ? TENURED : NOT_TENURED);
9923 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009924 }
ager@chromium.org04921a82011-06-27 13:21:41 +00009925
9926 return Rehash(HashTable::cast(obj), key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009927}
9928
9929
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009930template<typename Shape, typename Key>
9931uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009932 uint32_t capacity = Capacity();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009933 uint32_t entry = FirstProbe(hash, capacity);
9934 uint32_t count = 1;
9935 // EnsureCapacity will guarantee the hash table is never full.
9936 while (true) {
9937 Object* element = KeyAt(entry);
9938 if (element->IsUndefined() || element->IsNull()) break;
9939 entry = NextProbe(entry, count++, capacity);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009940 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009941 return entry;
9942}
9943
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009944// Force instantiation of template instances class.
9945// Please note this list is compiler dependent.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009946
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009947template class HashTable<SymbolTableShape, HashTableKey*>;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009948
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009949template class HashTable<CompilationCacheShape, HashTableKey*>;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009950
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009951template class HashTable<MapCacheShape, HashTableKey*>;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009952
vegorov@chromium.org7943d462011-08-01 11:41:52 +00009953template class HashTable<ObjectHashTableShape, JSObject*>;
9954
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009955template class Dictionary<StringDictionaryShape, String*>;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009956
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009957template class Dictionary<NumberDictionaryShape, uint32_t>;
ager@chromium.org9258b6b2008-09-11 09:11:10 +00009958
lrn@chromium.org303ada72010-10-27 09:33:13 +00009959template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Allocate(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009960 int);
9961
lrn@chromium.org303ada72010-10-27 09:33:13 +00009962template MaybeObject* Dictionary<StringDictionaryShape, String*>::Allocate(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009963 int);
9964
lrn@chromium.org303ada72010-10-27 09:33:13 +00009965template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AtPut(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009966 uint32_t, Object*);
9967
9968template Object* Dictionary<NumberDictionaryShape, uint32_t>::SlowReverseLookup(
9969 Object*);
9970
9971template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup(
9972 Object*);
9973
9974template void Dictionary<NumberDictionaryShape, uint32_t>::CopyKeysTo(
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009975 FixedArray*,
9976 PropertyAttributes,
9977 Dictionary<NumberDictionaryShape, uint32_t>::SortMode);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009978
9979template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty(
9980 int, JSObject::DeleteMode);
9981
9982template Object* Dictionary<NumberDictionaryShape, uint32_t>::DeleteProperty(
9983 int, JSObject::DeleteMode);
9984
ager@chromium.org04921a82011-06-27 13:21:41 +00009985template MaybeObject* Dictionary<StringDictionaryShape, String*>::Shrink(
9986 String*);
9987
9988template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Shrink(
9989 uint32_t);
9990
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009991template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo(
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009992 FixedArray*,
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +00009993 int,
sgjesse@chromium.org6db88712011-07-11 11:41:22 +00009994 Dictionary<StringDictionaryShape, String*>::SortMode);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009995
9996template int
9997Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes(
9998 PropertyAttributes);
9999
lrn@chromium.org303ada72010-10-27 09:33:13 +000010000template MaybeObject* Dictionary<StringDictionaryShape, String*>::Add(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010001 String*, Object*, PropertyDetails);
10002
lrn@chromium.org303ada72010-10-27 09:33:13 +000010003template MaybeObject*
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010004Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices();
10005
10006template int
10007Dictionary<NumberDictionaryShape, uint32_t>::NumberOfElementsFilterAttributes(
10008 PropertyAttributes);
10009
lrn@chromium.org303ada72010-10-27 09:33:13 +000010010template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::Add(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010011 uint32_t, Object*, PropertyDetails);
10012
lrn@chromium.org303ada72010-10-27 09:33:13 +000010013template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::
10014 EnsureCapacity(int, uint32_t);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010015
lrn@chromium.org303ada72010-10-27 09:33:13 +000010016template MaybeObject* Dictionary<StringDictionaryShape, String*>::
10017 EnsureCapacity(int, String*);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010018
lrn@chromium.org303ada72010-10-27 09:33:13 +000010019template MaybeObject* Dictionary<NumberDictionaryShape, uint32_t>::AddEntry(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010020 uint32_t, Object*, PropertyDetails, uint32_t);
10021
lrn@chromium.org303ada72010-10-27 09:33:13 +000010022template MaybeObject* Dictionary<StringDictionaryShape, String*>::AddEntry(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010023 String*, Object*, PropertyDetails, uint32_t);
10024
10025template
10026int Dictionary<NumberDictionaryShape, uint32_t>::NumberOfEnumElements();
10027
10028template
10029int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements();
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010030
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000010031template
10032int HashTable<NumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
10033
10034
ager@chromium.org5ec48922009-05-05 07:25:34 +000010035// Collates undefined and unexisting elements below limit from position
10036// zero of the elements. The object stays in Dictionary mode.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010037MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010038 ASSERT(HasDictionaryElements());
ager@chromium.org5ec48922009-05-05 07:25:34 +000010039 // Must stay in dictionary mode, either because of requires_slow_elements,
10040 // or because we are not going to sort (and therefore compact) all of the
10041 // elements.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010042 NumberDictionary* dict = element_dictionary();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010043 HeapNumber* result_double = NULL;
10044 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
10045 // Allocate space for result before we start mutating the object.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010046 Object* new_double;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010047 { MaybeObject* maybe_new_double = GetHeap()->AllocateHeapNumber(0.0);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010048 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
10049 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010050 result_double = HeapNumber::cast(new_double);
10051 }
10052
lrn@chromium.org303ada72010-10-27 09:33:13 +000010053 Object* obj;
10054 { MaybeObject* maybe_obj =
10055 NumberDictionary::Allocate(dict->NumberOfElements());
10056 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10057 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010058 NumberDictionary* new_dict = NumberDictionary::cast(obj);
ager@chromium.org5ec48922009-05-05 07:25:34 +000010059
10060 AssertNoAllocation no_alloc;
10061
ager@chromium.org5ec48922009-05-05 07:25:34 +000010062 uint32_t pos = 0;
10063 uint32_t undefs = 0;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010064 int capacity = dict->Capacity();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010065 for (int i = 0; i < capacity; i++) {
10066 Object* k = dict->KeyAt(i);
10067 if (dict->IsKey(k)) {
10068 ASSERT(k->IsNumber());
10069 ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
10070 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
10071 ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
10072 Object* value = dict->ValueAt(i);
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010073 PropertyDetails details = dict->DetailsAt(i);
10074 if (details.type() == CALLBACKS) {
10075 // Bail out and do the sorting of undefineds and array holes in JS.
10076 return Smi::FromInt(-1);
10077 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010078 uint32_t key = NumberToUint32(k);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010079 // In the following we assert that adding the entry to the new dictionary
10080 // does not cause GC. This is the case because we made sure to allocate
10081 // the dictionary big enough above, so it need not grow.
ager@chromium.org5ec48922009-05-05 07:25:34 +000010082 if (key < limit) {
10083 if (value->IsUndefined()) {
10084 undefs++;
10085 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +000010086 if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
10087 // Adding an entry with the key beyond smi-range requires
10088 // allocation. Bailout.
10089 return Smi::FromInt(-1);
10090 }
lrn@chromium.org303ada72010-10-27 09:33:13 +000010091 new_dict->AddNumberEntry(pos, value, details)->ToObjectUnchecked();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010092 pos++;
10093 }
10094 } else {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +000010095 if (key > static_cast<uint32_t>(Smi::kMaxValue)) {
10096 // Adding an entry with the key beyond smi-range requires
10097 // allocation. Bailout.
10098 return Smi::FromInt(-1);
10099 }
lrn@chromium.org303ada72010-10-27 09:33:13 +000010100 new_dict->AddNumberEntry(key, value, details)->ToObjectUnchecked();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010101 }
10102 }
10103 }
10104
10105 uint32_t result = pos;
ager@chromium.orgeadaf222009-06-16 09:43:10 +000010106 PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010107 Heap* heap = GetHeap();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010108 while (undefs > 0) {
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +000010109 if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
10110 // Adding an entry with the key beyond smi-range requires
10111 // allocation. Bailout.
10112 return Smi::FromInt(-1);
10113 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010114 new_dict->AddNumberEntry(pos, heap->undefined_value(), no_details)->
lrn@chromium.org303ada72010-10-27 09:33:13 +000010115 ToObjectUnchecked();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010116 pos++;
10117 undefs--;
10118 }
10119
10120 set_elements(new_dict);
10121
10122 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
10123 return Smi::FromInt(static_cast<int>(result));
10124 }
10125
10126 ASSERT_NE(NULL, result_double);
10127 result_double->set_value(static_cast<double>(result));
10128 return result_double;
10129}
10130
10131
10132// Collects all defined (non-hole) and non-undefined (array) elements at
10133// the start of the elements array.
10134// If the object is in dictionary mode, it is converted to fast elements
10135// mode.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010136MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +000010137 ASSERT(!HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010138
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010139 Heap* heap = GetHeap();
10140
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010141 if (HasDictionaryElements()) {
ager@chromium.org5ec48922009-05-05 07:25:34 +000010142 // Convert to fast elements containing only the existing properties.
10143 // Ordering is irrelevant, since we are going to sort anyway.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010144 NumberDictionary* dict = element_dictionary();
ager@chromium.org5ec48922009-05-05 07:25:34 +000010145 if (IsJSArray() || dict->requires_slow_elements() ||
10146 dict->max_number_key() >= limit) {
10147 return PrepareSlowElementsForSort(limit);
10148 }
10149 // Convert to fast elements.
10150
lrn@chromium.org303ada72010-10-27 09:33:13 +000010151 Object* obj;
10152 { MaybeObject* maybe_obj = map()->GetFastElementsMap();
10153 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10154 }
fschneider@chromium.org40b9da32010-06-28 11:29:21 +000010155 Map* new_map = Map::cast(obj);
10156
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010157 PretenureFlag tenure = heap->InNewSpace(this) ? NOT_TENURED: TENURED;
lrn@chromium.org303ada72010-10-27 09:33:13 +000010158 Object* new_array;
10159 { MaybeObject* maybe_new_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010160 heap->AllocateFixedArray(dict->NumberOfElements(), tenure);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010161 if (!maybe_new_array->ToObject(&new_array)) return maybe_new_array;
10162 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010163 FixedArray* fast_elements = FixedArray::cast(new_array);
10164 dict->CopyValuesTo(fast_elements);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +000010165
10166 set_map(new_map);
ager@chromium.org5ec48922009-05-05 07:25:34 +000010167 set_elements(fast_elements);
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010168 } else if (!HasFastDoubleElements()) {
lrn@chromium.org303ada72010-10-27 09:33:13 +000010169 Object* obj;
10170 { MaybeObject* maybe_obj = EnsureWritableFastElements();
10171 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10172 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010173 }
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010174 ASSERT(HasFastElements() || HasFastDoubleElements());
ager@chromium.org5ec48922009-05-05 07:25:34 +000010175
10176 // Collect holes at the end, undefined before that and the rest at the
10177 // start, and return the number of non-hole, non-undefined values.
10178
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010179 FixedArrayBase* elements_base = FixedArrayBase::cast(this->elements());
10180 uint32_t elements_length = static_cast<uint32_t>(elements_base->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +000010181 if (limit > elements_length) {
10182 limit = elements_length ;
10183 }
10184 if (limit == 0) {
10185 return Smi::FromInt(0);
10186 }
10187
10188 HeapNumber* result_double = NULL;
10189 if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
10190 // Pessimistically allocate space for return value before
10191 // we start mutating the array.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010192 Object* new_double;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010193 { MaybeObject* maybe_new_double = heap->AllocateHeapNumber(0.0);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010194 if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
10195 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010196 result_double = HeapNumber::cast(new_double);
10197 }
10198
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010199 uint32_t result = 0;
10200 if (elements_base->map() == heap->fixed_double_array_map()) {
10201 FixedDoubleArray* elements = FixedDoubleArray::cast(elements_base);
10202 // Split elements into defined and the_hole, in that order.
10203 unsigned int holes = limit;
10204 // Assume most arrays contain no holes and undefined values, so minimize the
10205 // number of stores of non-undefined, non-the-hole values.
10206 for (unsigned int i = 0; i < holes; i++) {
10207 if (elements->is_the_hole(i)) {
10208 holes--;
10209 } else {
10210 continue;
10211 }
10212 // Position i needs to be filled.
10213 while (holes > i) {
10214 if (elements->is_the_hole(holes)) {
10215 holes--;
10216 } else {
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000010217 elements->set(i, elements->get_scalar(holes));
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010218 break;
10219 }
10220 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010221 }
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010222 result = holes;
10223 while (holes < limit) {
10224 elements->set_the_hole(holes);
10225 holes++;
10226 }
10227 } else {
10228 FixedArray* elements = FixedArray::cast(elements_base);
10229 AssertNoAllocation no_alloc;
10230
10231 // Split elements into defined, undefined and the_hole, in that order. Only
10232 // count locations for undefined and the hole, and fill them afterwards.
10233 WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
10234 unsigned int undefs = limit;
10235 unsigned int holes = limit;
10236 // Assume most arrays contain no holes and undefined values, so minimize the
10237 // number of stores of non-undefined, non-the-hole values.
10238 for (unsigned int i = 0; i < undefs; i++) {
10239 Object* current = elements->get(i);
ager@chromium.org5ec48922009-05-05 07:25:34 +000010240 if (current->IsTheHole()) {
10241 holes--;
10242 undefs--;
10243 } else if (current->IsUndefined()) {
10244 undefs--;
10245 } else {
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010246 continue;
10247 }
10248 // Position i needs to be filled.
10249 while (undefs > i) {
10250 current = elements->get(undefs);
10251 if (current->IsTheHole()) {
10252 holes--;
10253 undefs--;
10254 } else if (current->IsUndefined()) {
10255 undefs--;
10256 } else {
10257 elements->set(i, current, write_barrier);
10258 break;
10259 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010260 }
10261 }
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +000010262 result = undefs;
10263 while (undefs < holes) {
10264 elements->set_undefined(undefs);
10265 undefs++;
10266 }
10267 while (holes < limit) {
10268 elements->set_the_hole(holes);
10269 holes++;
10270 }
ager@chromium.org5ec48922009-05-05 07:25:34 +000010271 }
10272
10273 if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
10274 return Smi::FromInt(static_cast<int>(result));
10275 }
10276 ASSERT_NE(NULL, result_double);
10277 result_double->set_value(static_cast<double>(result));
10278 return result_double;
10279}
10280
10281
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +000010282Object* ExternalPixelArray::SetValue(uint32_t index, Object* value) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010283 uint8_t clamped_value = 0;
10284 if (index < static_cast<uint32_t>(length())) {
10285 if (value->IsSmi()) {
10286 int int_value = Smi::cast(value)->value();
10287 if (int_value < 0) {
10288 clamped_value = 0;
10289 } else if (int_value > 255) {
10290 clamped_value = 255;
10291 } else {
10292 clamped_value = static_cast<uint8_t>(int_value);
10293 }
10294 } else if (value->IsHeapNumber()) {
10295 double double_value = HeapNumber::cast(value)->value();
10296 if (!(double_value > 0)) {
10297 // NaN and less than zero clamp to zero.
10298 clamped_value = 0;
10299 } else if (double_value > 255) {
10300 // Greater than 255 clamp to 255.
10301 clamped_value = 255;
10302 } else {
10303 // Other doubles are rounded to the nearest integer.
10304 clamped_value = static_cast<uint8_t>(double_value + 0.5);
10305 }
10306 } else {
10307 // Clamp undefined to zero (default). All other types have been
10308 // converted to a number type further up in the call chain.
10309 ASSERT(value->IsUndefined());
10310 }
10311 set(index, clamped_value);
10312 }
10313 return Smi::FromInt(clamped_value);
10314}
10315
10316
ager@chromium.org3811b432009-10-28 14:53:37 +000010317template<typename ExternalArrayClass, typename ValueType>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010318static MaybeObject* ExternalArrayIntSetter(Heap* heap,
10319 ExternalArrayClass* receiver,
lrn@chromium.org303ada72010-10-27 09:33:13 +000010320 uint32_t index,
10321 Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010322 ValueType cast_value = 0;
10323 if (index < static_cast<uint32_t>(receiver->length())) {
10324 if (value->IsSmi()) {
10325 int int_value = Smi::cast(value)->value();
10326 cast_value = static_cast<ValueType>(int_value);
10327 } else if (value->IsHeapNumber()) {
10328 double double_value = HeapNumber::cast(value)->value();
10329 cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
10330 } else {
10331 // Clamp undefined to zero (default). All other types have been
10332 // converted to a number type further up in the call chain.
10333 ASSERT(value->IsUndefined());
10334 }
10335 receiver->set(index, cast_value);
10336 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010337 return heap->NumberFromInt32(cast_value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010338}
10339
10340
lrn@chromium.org303ada72010-10-27 09:33:13 +000010341MaybeObject* ExternalByteArray::SetValue(uint32_t index, Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010342 return ExternalArrayIntSetter<ExternalByteArray, int8_t>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010343 (GetHeap(), this, index, value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010344}
10345
10346
lrn@chromium.org303ada72010-10-27 09:33:13 +000010347MaybeObject* ExternalUnsignedByteArray::SetValue(uint32_t index,
10348 Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010349 return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010350 (GetHeap(), this, index, value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010351}
10352
10353
lrn@chromium.org303ada72010-10-27 09:33:13 +000010354MaybeObject* ExternalShortArray::SetValue(uint32_t index,
10355 Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010356 return ExternalArrayIntSetter<ExternalShortArray, int16_t>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010357 (GetHeap(), this, index, value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010358}
10359
10360
lrn@chromium.org303ada72010-10-27 09:33:13 +000010361MaybeObject* ExternalUnsignedShortArray::SetValue(uint32_t index,
10362 Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010363 return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010364 (GetHeap(), this, index, value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010365}
10366
10367
lrn@chromium.org303ada72010-10-27 09:33:13 +000010368MaybeObject* ExternalIntArray::SetValue(uint32_t index, Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010369 return ExternalArrayIntSetter<ExternalIntArray, int32_t>
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010370 (GetHeap(), this, index, value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010371}
10372
10373
lrn@chromium.org303ada72010-10-27 09:33:13 +000010374MaybeObject* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010375 uint32_t cast_value = 0;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010376 Heap* heap = GetHeap();
ager@chromium.org3811b432009-10-28 14:53:37 +000010377 if (index < static_cast<uint32_t>(length())) {
10378 if (value->IsSmi()) {
10379 int int_value = Smi::cast(value)->value();
10380 cast_value = static_cast<uint32_t>(int_value);
10381 } else if (value->IsHeapNumber()) {
10382 double double_value = HeapNumber::cast(value)->value();
10383 cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
10384 } else {
10385 // Clamp undefined to zero (default). All other types have been
10386 // converted to a number type further up in the call chain.
10387 ASSERT(value->IsUndefined());
10388 }
10389 set(index, cast_value);
10390 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010391 return heap->NumberFromUint32(cast_value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010392}
10393
10394
lrn@chromium.org303ada72010-10-27 09:33:13 +000010395MaybeObject* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
ager@chromium.org3811b432009-10-28 14:53:37 +000010396 float cast_value = 0;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010397 Heap* heap = GetHeap();
ager@chromium.org3811b432009-10-28 14:53:37 +000010398 if (index < static_cast<uint32_t>(length())) {
10399 if (value->IsSmi()) {
10400 int int_value = Smi::cast(value)->value();
10401 cast_value = static_cast<float>(int_value);
10402 } else if (value->IsHeapNumber()) {
10403 double double_value = HeapNumber::cast(value)->value();
10404 cast_value = static_cast<float>(double_value);
10405 } else {
10406 // Clamp undefined to zero (default). All other types have been
10407 // converted to a number type further up in the call chain.
10408 ASSERT(value->IsUndefined());
10409 }
10410 set(index, cast_value);
10411 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010412 return heap->AllocateHeapNumber(cast_value);
ager@chromium.org3811b432009-10-28 14:53:37 +000010413}
10414
10415
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +000010416MaybeObject* ExternalDoubleArray::SetValue(uint32_t index, Object* value) {
10417 double double_value = 0;
10418 Heap* heap = GetHeap();
10419 if (index < static_cast<uint32_t>(length())) {
10420 if (value->IsSmi()) {
10421 int int_value = Smi::cast(value)->value();
10422 double_value = static_cast<double>(int_value);
10423 } else if (value->IsHeapNumber()) {
10424 double_value = HeapNumber::cast(value)->value();
10425 } else {
10426 // Clamp undefined to zero (default). All other types have been
10427 // converted to a number type further up in the call chain.
10428 ASSERT(value->IsUndefined());
10429 }
10430 set(index, double_value);
10431 }
10432 return heap->AllocateHeapNumber(double_value);
10433}
10434
10435
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010436JSGlobalPropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010437 ASSERT(!HasFastProperties());
10438 Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010439 return JSGlobalPropertyCell::cast(value);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010440}
10441
10442
lrn@chromium.org303ada72010-10-27 09:33:13 +000010443MaybeObject* GlobalObject::EnsurePropertyCell(String* name) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000010444 ASSERT(!HasFastProperties());
10445 int entry = property_dictionary()->FindEntry(name);
10446 if (entry == StringDictionary::kNotFound) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010447 Heap* heap = GetHeap();
lrn@chromium.org303ada72010-10-27 09:33:13 +000010448 Object* cell;
10449 { MaybeObject* maybe_cell =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010450 heap->AllocateJSGlobalPropertyCell(heap->the_hole_value());
lrn@chromium.org303ada72010-10-27 09:33:13 +000010451 if (!maybe_cell->ToObject(&cell)) return maybe_cell;
10452 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000010453 PropertyDetails details(NONE, NORMAL);
10454 details = details.AsDeleted();
lrn@chromium.org303ada72010-10-27 09:33:13 +000010455 Object* dictionary;
10456 { MaybeObject* maybe_dictionary =
10457 property_dictionary()->Add(name, cell, details);
10458 if (!maybe_dictionary->ToObject(&dictionary)) return maybe_dictionary;
10459 }
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000010460 set_properties(StringDictionary::cast(dictionary));
10461 return cell;
10462 } else {
10463 Object* value = property_dictionary()->ValueAt(entry);
10464 ASSERT(value->IsJSGlobalPropertyCell());
10465 return value;
10466 }
10467}
10468
10469
lrn@chromium.org303ada72010-10-27 09:33:13 +000010470MaybeObject* SymbolTable::LookupString(String* string, Object** s) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010471 SymbolKey key(string);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010472 return LookupKey(&key, s);
10473}
10474
10475
ager@chromium.org6141cbe2009-11-20 12:14:52 +000010476// This class is used for looking up two character strings in the symbol table.
10477// If we don't have a hit we don't want to waste much time so we unroll the
10478// string hash calculation loop here for speed. Doesn't work if the two
10479// characters form a decimal integer, since such strings have a different hash
10480// algorithm.
10481class TwoCharHashTableKey : public HashTableKey {
10482 public:
10483 TwoCharHashTableKey(uint32_t c1, uint32_t c2)
10484 : c1_(c1), c2_(c2) {
10485 // Char 1.
10486 uint32_t hash = c1 + (c1 << 10);
10487 hash ^= hash >> 6;
10488 // Char 2.
10489 hash += c2;
10490 hash += hash << 10;
10491 hash ^= hash >> 6;
10492 // GetHash.
10493 hash += hash << 3;
10494 hash ^= hash >> 11;
10495 hash += hash << 15;
10496 if (hash == 0) hash = 27;
10497#ifdef DEBUG
10498 StringHasher hasher(2);
10499 hasher.AddCharacter(c1);
10500 hasher.AddCharacter(c2);
10501 // If this assert fails then we failed to reproduce the two-character
10502 // version of the string hashing algorithm above. One reason could be
10503 // that we were passed two digits as characters, since the hash
10504 // algorithm is different in that case.
10505 ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
10506#endif
10507 hash_ = hash;
10508 }
10509
10510 bool IsMatch(Object* o) {
10511 if (!o->IsString()) return false;
10512 String* other = String::cast(o);
10513 if (other->length() != 2) return false;
10514 if (other->Get(0) != c1_) return false;
10515 return other->Get(1) == c2_;
10516 }
10517
10518 uint32_t Hash() { return hash_; }
10519 uint32_t HashForObject(Object* key) {
10520 if (!key->IsString()) return 0;
10521 return String::cast(key)->Hash();
10522 }
10523
10524 Object* AsObject() {
10525 // The TwoCharHashTableKey is only used for looking in the symbol
10526 // table, not for adding to it.
10527 UNREACHABLE();
10528 return NULL;
10529 }
jkummerow@chromium.orge297f592011-06-08 10:05:15 +000010530
ager@chromium.org6141cbe2009-11-20 12:14:52 +000010531 private:
10532 uint32_t c1_;
10533 uint32_t c2_;
10534 uint32_t hash_;
10535};
10536
10537
ager@chromium.org7c537e22008-10-16 08:43:32 +000010538bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
10539 SymbolKey key(string);
10540 int entry = FindEntry(&key);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010541 if (entry == kNotFound) {
ager@chromium.org7c537e22008-10-16 08:43:32 +000010542 return false;
10543 } else {
10544 String* result = String::cast(KeyAt(entry));
ager@chromium.org870a0b62008-11-04 11:43:05 +000010545 ASSERT(StringShape(result).IsSymbol());
ager@chromium.org7c537e22008-10-16 08:43:32 +000010546 *symbol = result;
10547 return true;
10548 }
10549}
10550
10551
ager@chromium.org6141cbe2009-11-20 12:14:52 +000010552bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
10553 uint32_t c2,
10554 String** symbol) {
10555 TwoCharHashTableKey key(c1, c2);
10556 int entry = FindEntry(&key);
10557 if (entry == kNotFound) {
10558 return false;
10559 } else {
10560 String* result = String::cast(KeyAt(entry));
10561 ASSERT(StringShape(result).IsSymbol());
10562 *symbol = result;
10563 return true;
10564 }
10565}
10566
10567
lrn@chromium.org303ada72010-10-27 09:33:13 +000010568MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010569 Utf8SymbolKey key(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010570 return LookupKey(&key, s);
10571}
10572
10573
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +000010574MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str,
10575 Object** s) {
10576 AsciiSymbolKey key(str);
10577 return LookupKey(&key, s);
10578}
10579
10580
danno@chromium.org40cb8782011-05-25 07:58:50 +000010581MaybeObject* SymbolTable::LookupSubStringAsciiSymbol(Handle<SeqAsciiString> str,
10582 int from,
10583 int length,
10584 Object** s) {
10585 SubStringAsciiSymbolKey key(str, from, length);
10586 return LookupKey(&key, s);
10587}
10588
10589
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +000010590MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector<const uc16> str,
10591 Object** s) {
10592 TwoByteSymbolKey key(str);
10593 return LookupKey(&key, s);
10594}
10595
lrn@chromium.org303ada72010-10-27 09:33:13 +000010596MaybeObject* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010597 int entry = FindEntry(key);
10598
10599 // Symbol already in table.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010600 if (entry != kNotFound) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010601 *s = KeyAt(entry);
10602 return this;
10603 }
10604
10605 // Adding new symbol. Grow table if needed.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010606 Object* obj;
10607 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
10608 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10609 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010610
10611 // Create symbol object.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010612 Object* symbol;
10613 { MaybeObject* maybe_symbol = key->AsObject();
10614 if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol;
10615 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010616
10617 // If the symbol table grew as part of EnsureCapacity, obj is not
10618 // the current symbol table and therefore we cannot use
10619 // SymbolTable::cast here.
10620 SymbolTable* table = reinterpret_cast<SymbolTable*>(obj);
10621
10622 // Add the new symbol and return it along with the symbol table.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010623 entry = table->FindInsertionEntry(key->Hash());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010624 table->set(EntryToIndex(entry), symbol);
10625 table->ElementAdded();
10626 *s = symbol;
10627 return table;
10628}
10629
10630
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000010631Object* CompilationCacheTable::Lookup(String* src) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010632 StringKey key(src);
10633 int entry = FindEntry(&key);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010634 if (entry == kNotFound) return GetHeap()->undefined_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010635 return get(EntryToIndex(entry) + 1);
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010636}
10637
10638
ricow@chromium.org83aa5492011-02-07 12:42:56 +000010639Object* CompilationCacheTable::LookupEval(String* src,
10640 Context* context,
10641 StrictModeFlag strict_mode) {
10642 StringSharedKey key(src, context->closure()->shared(), strict_mode);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010643 int entry = FindEntry(&key);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010644 if (entry == kNotFound) return GetHeap()->undefined_value();
ager@chromium.org381abbb2009-02-25 13:23:22 +000010645 return get(EntryToIndex(entry) + 1);
10646}
10647
10648
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +000010649Object* CompilationCacheTable::LookupRegExp(String* src,
10650 JSRegExp::Flags flags) {
10651 RegExpKey key(src, flags);
10652 int entry = FindEntry(&key);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010653 if (entry == kNotFound) return GetHeap()->undefined_value();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +000010654 return get(EntryToIndex(entry) + 1);
10655}
10656
10657
lrn@chromium.org303ada72010-10-27 09:33:13 +000010658MaybeObject* CompilationCacheTable::Put(String* src, Object* value) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010659 StringKey key(src);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010660 Object* obj;
10661 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
10662 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10663 }
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010664
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000010665 CompilationCacheTable* cache =
10666 reinterpret_cast<CompilationCacheTable*>(obj);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010667 int entry = cache->FindInsertionEntry(key.Hash());
ager@chromium.org9258b6b2008-09-11 09:11:10 +000010668 cache->set(EntryToIndex(entry), src);
10669 cache->set(EntryToIndex(entry) + 1, value);
10670 cache->ElementAdded();
10671 return cache;
10672}
10673
10674
lrn@chromium.org303ada72010-10-27 09:33:13 +000010675MaybeObject* CompilationCacheTable::PutEval(String* src,
10676 Context* context,
ricow@chromium.org83aa5492011-02-07 12:42:56 +000010677 SharedFunctionInfo* value) {
10678 StringSharedKey key(src,
10679 context->closure()->shared(),
10680 value->strict_mode() ? kStrictMode : kNonStrictMode);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010681 Object* obj;
10682 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
10683 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10684 }
ager@chromium.org381abbb2009-02-25 13:23:22 +000010685
10686 CompilationCacheTable* cache =
10687 reinterpret_cast<CompilationCacheTable*>(obj);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010688 int entry = cache->FindInsertionEntry(key.Hash());
ager@chromium.org381abbb2009-02-25 13:23:22 +000010689
lrn@chromium.org303ada72010-10-27 09:33:13 +000010690 Object* k;
10691 { MaybeObject* maybe_k = key.AsObject();
10692 if (!maybe_k->ToObject(&k)) return maybe_k;
10693 }
ager@chromium.org381abbb2009-02-25 13:23:22 +000010694
10695 cache->set(EntryToIndex(entry), k);
10696 cache->set(EntryToIndex(entry) + 1, value);
10697 cache->ElementAdded();
10698 return cache;
10699}
10700
10701
lrn@chromium.org303ada72010-10-27 09:33:13 +000010702MaybeObject* CompilationCacheTable::PutRegExp(String* src,
10703 JSRegExp::Flags flags,
10704 FixedArray* value) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +000010705 RegExpKey key(src, flags);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010706 Object* obj;
10707 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
10708 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10709 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +000010710
10711 CompilationCacheTable* cache =
10712 reinterpret_cast<CompilationCacheTable*>(obj);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010713 int entry = cache->FindInsertionEntry(key.Hash());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000010714 // We store the value in the key slot, and compare the search key
10715 // to the stored value with a custon IsMatch function during lookups.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +000010716 cache->set(EntryToIndex(entry), value);
10717 cache->set(EntryToIndex(entry) + 1, value);
10718 cache->ElementAdded();
10719 return cache;
10720}
10721
10722
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010723void CompilationCacheTable::Remove(Object* value) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010724 Object* null_value = GetHeap()->null_value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010725 for (int entry = 0, size = Capacity(); entry < size; entry++) {
10726 int entry_index = EntryToIndex(entry);
10727 int value_index = entry_index + 1;
10728 if (get(value_index) == value) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010729 fast_set(this, entry_index, null_value);
10730 fast_set(this, value_index, null_value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000010731 ElementRemoved();
10732 }
10733 }
10734 return;
10735}
10736
10737
ager@chromium.org236ad962008-09-25 09:45:57 +000010738// SymbolsKey used for HashTable where key is array of symbols.
10739class SymbolsKey : public HashTableKey {
10740 public:
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010741 explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
ager@chromium.org236ad962008-09-25 09:45:57 +000010742
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010743 bool IsMatch(Object* symbols) {
10744 FixedArray* o = FixedArray::cast(symbols);
ager@chromium.org236ad962008-09-25 09:45:57 +000010745 int len = symbols_->length();
10746 if (o->length() != len) return false;
10747 for (int i = 0; i < len; i++) {
10748 if (o->get(i) != symbols_->get(i)) return false;
10749 }
10750 return true;
10751 }
10752
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010753 uint32_t Hash() { return HashForObject(symbols_); }
ager@chromium.org236ad962008-09-25 09:45:57 +000010754
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010755 uint32_t HashForObject(Object* obj) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010756 FixedArray* symbols = FixedArray::cast(obj);
10757 int len = symbols->length();
10758 uint32_t hash = 0;
ager@chromium.org236ad962008-09-25 09:45:57 +000010759 for (int i = 0; i < len; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010760 hash ^= String::cast(symbols->get(i))->Hash();
ager@chromium.org236ad962008-09-25 09:45:57 +000010761 }
10762 return hash;
10763 }
10764
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010765 Object* AsObject() { return symbols_; }
ager@chromium.org236ad962008-09-25 09:45:57 +000010766
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010767 private:
ager@chromium.org236ad962008-09-25 09:45:57 +000010768 FixedArray* symbols_;
10769};
10770
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010771
ager@chromium.org236ad962008-09-25 09:45:57 +000010772Object* MapCache::Lookup(FixedArray* array) {
10773 SymbolsKey key(array);
10774 int entry = FindEntry(&key);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000010775 if (entry == kNotFound) return GetHeap()->undefined_value();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010776 return get(EntryToIndex(entry) + 1);
ager@chromium.org236ad962008-09-25 09:45:57 +000010777}
10778
10779
lrn@chromium.org303ada72010-10-27 09:33:13 +000010780MaybeObject* MapCache::Put(FixedArray* array, Map* value) {
ager@chromium.org236ad962008-09-25 09:45:57 +000010781 SymbolsKey key(array);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010782 Object* obj;
10783 { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
10784 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10785 }
ager@chromium.org236ad962008-09-25 09:45:57 +000010786
10787 MapCache* cache = reinterpret_cast<MapCache*>(obj);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010788 int entry = cache->FindInsertionEntry(key.Hash());
ager@chromium.org236ad962008-09-25 09:45:57 +000010789 cache->set(EntryToIndex(entry), array);
10790 cache->set(EntryToIndex(entry) + 1, value);
10791 cache->ElementAdded();
10792 return cache;
10793}
10794
10795
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010796template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010797MaybeObject* Dictionary<Shape, Key>::Allocate(int at_least_space_for) {
10798 Object* obj;
10799 { MaybeObject* maybe_obj =
10800 HashTable<Shape, Key>::Allocate(at_least_space_for);
10801 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010802 }
lrn@chromium.org303ada72010-10-27 09:33:13 +000010803 // Initialize the next enumeration index.
10804 Dictionary<Shape, Key>::cast(obj)->
10805 SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010806 return obj;
10807}
10808
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010809
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010810template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010811MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010812 Heap* heap = Dictionary<Shape, Key>::GetHeap();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010813 int length = HashTable<Shape, Key>::NumberOfElements();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010814
10815 // Allocate and initialize iteration order array.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010816 Object* obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010817 { MaybeObject* maybe_obj = heap->AllocateFixedArray(length);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010818 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10819 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010820 FixedArray* iteration_order = FixedArray::cast(obj);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010821 for (int i = 0; i < length; i++) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010822 iteration_order->set(i, Smi::FromInt(i));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000010823 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010824
10825 // Allocate array with enumeration order.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010826 { MaybeObject* maybe_obj = heap->AllocateFixedArray(length);
lrn@chromium.org303ada72010-10-27 09:33:13 +000010827 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10828 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010829 FixedArray* enumeration_order = FixedArray::cast(obj);
10830
10831 // Fill the enumeration order array with property details.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010832 int capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010833 int pos = 0;
10834 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010835 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010836 enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010837 }
10838 }
10839
10840 // Sort the arrays wrt. enumeration order.
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010841 iteration_order->SortPairs(enumeration_order, enumeration_order->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010842
10843 // Overwrite the enumeration_order with the enumeration indices.
10844 for (int i = 0; i < length; i++) {
10845 int index = Smi::cast(iteration_order->get(i))->value();
10846 int enum_index = PropertyDetails::kInitialIndex + i;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010847 enumeration_order->set(index, Smi::FromInt(enum_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010848 }
10849
10850 // Update the dictionary with new indices.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010851 capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010852 pos = 0;
10853 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010854 if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010855 int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
10856 PropertyDetails details = DetailsAt(i);
10857 PropertyDetails new_details =
10858 PropertyDetails(details.attributes(), details.type(), enum_index);
10859 DetailsAtPut(i, new_details);
10860 }
10861 }
10862
10863 // Set the next enumeration index.
10864 SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
10865 return this;
10866}
10867
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010868template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010869MaybeObject* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000010870 // Check whether there are enough enumeration indices to add n elements.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010871 if (Shape::kIsEnumerable &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010872 !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
10873 // If not, we generate new indices for the properties.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010874 Object* result;
10875 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
10876 if (!maybe_result->ToObject(&result)) return maybe_result;
10877 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010878 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010879 return HashTable<Shape, Key>::EnsureCapacity(n, key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010880}
10881
10882
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010883void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010884 // Do nothing if the interval [from, to) is empty.
10885 if (from >= to) return;
10886
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010887 Heap* heap = GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010888 int removed_entries = 0;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010889 Object* sentinel = heap->null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010890 int capacity = Capacity();
10891 for (int i = 0; i < capacity; i++) {
10892 Object* key = KeyAt(i);
10893 if (key->IsNumber()) {
10894 uint32_t number = static_cast<uint32_t>(key->Number());
10895 if (from <= number && number < to) {
karlklose@chromium.org44bc7082011-04-11 12:33:05 +000010896 SetEntry(i, sentinel, sentinel);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010897 removed_entries++;
10898 }
10899 }
10900 }
10901
10902 // Update the number of elements.
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +000010903 ElementsRemoved(removed_entries);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010904}
10905
10906
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010907template<typename Shape, typename Key>
10908Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +000010909 JSReceiver::DeleteMode mode) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010910 Heap* heap = Dictionary<Shape, Key>::GetHeap();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010911 PropertyDetails details = DetailsAt(entry);
ager@chromium.orge2902be2009-06-08 12:21:35 +000010912 // Ignore attributes if forcing a deletion.
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +000010913 if (details.IsDontDelete() && mode != JSReceiver::FORCE_DELETION) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010914 return heap->false_value();
ager@chromium.orge2902be2009-06-08 12:21:35 +000010915 }
karlklose@chromium.org44bc7082011-04-11 12:33:05 +000010916 SetEntry(entry, heap->null_value(), heap->null_value());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010917 HashTable<Shape, Key>::ElementRemoved();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000010918 return heap->true_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010919}
10920
10921
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010922template<typename Shape, typename Key>
ager@chromium.org04921a82011-06-27 13:21:41 +000010923MaybeObject* Dictionary<Shape, Key>::Shrink(Key key) {
10924 return HashTable<Shape, Key>::Shrink(key);
10925}
10926
10927
10928template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010929MaybeObject* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000010930 int entry = this->FindEntry(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010931
10932 // If the entry is present set the value;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010933 if (entry != Dictionary<Shape, Key>::kNotFound) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010934 ValueAtPut(entry, value);
10935 return this;
10936 }
10937
10938 // Check whether the dictionary should be extended.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010939 Object* obj;
10940 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
10941 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10942 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010943
lrn@chromium.org303ada72010-10-27 09:33:13 +000010944 Object* k;
10945 { MaybeObject* maybe_k = Shape::AsObject(key);
10946 if (!maybe_k->ToObject(&k)) return maybe_k;
10947 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010948 PropertyDetails details = PropertyDetails(NONE, NORMAL);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010949 return Dictionary<Shape, Key>::cast(obj)->
10950 AddEntry(key, value, details, Shape::Hash(key));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010951}
10952
10953
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010954template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010955MaybeObject* Dictionary<Shape, Key>::Add(Key key,
10956 Object* value,
10957 PropertyDetails details) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010958 // Valdate key is absent.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000010959 SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010960 // Check whether the dictionary should be extended.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010961 Object* obj;
10962 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
10963 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
10964 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010965 return Dictionary<Shape, Key>::cast(obj)->
10966 AddEntry(key, value, details, Shape::Hash(key));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010967}
10968
10969
10970// Add a key, value pair to the dictionary.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010971template<typename Shape, typename Key>
lrn@chromium.org303ada72010-10-27 09:33:13 +000010972MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key,
10973 Object* value,
10974 PropertyDetails details,
10975 uint32_t hash) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010976 // Compute the key object.
lrn@chromium.org303ada72010-10-27 09:33:13 +000010977 Object* k;
10978 { MaybeObject* maybe_k = Shape::AsObject(key);
10979 if (!maybe_k->ToObject(&k)) return maybe_k;
10980 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010981
10982 uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010983 // Insert element at empty or deleted entry
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000010984 if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010985 // Assign an enumeration index to the property and update
10986 // SetNextEnumerationIndex.
10987 int index = NextEnumerationIndex();
10988 details = PropertyDetails(details.attributes(), details.type(), index);
10989 SetNextEnumerationIndex(index + 1);
10990 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010991 SetEntry(entry, k, value, details);
10992 ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber()
10993 || Dictionary<Shape, Key>::KeyAt(entry)->IsString()));
10994 HashTable<Shape, Key>::ElementAdded();
10995 return this;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010996}
10997
10998
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010999void NumberDictionary::UpdateMaxNumberKey(uint32_t key) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011000 // If the dictionary requires slow elements an element has already
11001 // been added at a high index.
11002 if (requires_slow_elements()) return;
11003 // Check if this index is high enough that we should require slow
11004 // elements.
11005 if (key > kRequiresSlowElementsLimit) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000011006 set_requires_slow_elements();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011007 return;
11008 }
11009 // Update max key value.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000011010 Object* max_index_object = get(kMaxNumberKeyIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011011 if (!max_index_object->IsSmi() || max_number_key() < key) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011012 FixedArray::set(kMaxNumberKeyIndex,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011013 Smi::FromInt(key << kRequiresSlowElementsTagSize));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011014 }
11015}
11016
11017
lrn@chromium.org303ada72010-10-27 09:33:13 +000011018MaybeObject* NumberDictionary::AddNumberEntry(uint32_t key,
11019 Object* value,
11020 PropertyDetails details) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011021 UpdateMaxNumberKey(key);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000011022 SLOW_ASSERT(this->FindEntry(key) == kNotFound);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011023 return Add(key, value, details);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011024}
11025
11026
lrn@chromium.org303ada72010-10-27 09:33:13 +000011027MaybeObject* NumberDictionary::AtNumberPut(uint32_t key, Object* value) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011028 UpdateMaxNumberKey(key);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011029 return AtPut(key, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011030}
11031
11032
lrn@chromium.org303ada72010-10-27 09:33:13 +000011033MaybeObject* NumberDictionary::Set(uint32_t key,
11034 Object* value,
11035 PropertyDetails details) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011036 int entry = FindEntry(key);
11037 if (entry == kNotFound) return AddNumberEntry(key, value, details);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011038 // Preserve enumeration index.
11039 details = PropertyDetails(details.attributes(),
11040 details.type(),
11041 DetailsAt(entry).index());
lrn@chromium.org303ada72010-10-27 09:33:13 +000011042 MaybeObject* maybe_object_key = NumberDictionaryShape::AsObject(key);
11043 Object* object_key;
11044 if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key;
whesse@chromium.org4a5224e2010-10-20 12:37:07 +000011045 SetEntry(entry, object_key, value, details);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011046 return this;
11047}
11048
11049
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000011050
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011051template<typename Shape, typename Key>
11052int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
11053 PropertyAttributes filter) {
11054 int capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011055 int result = 0;
11056 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011057 Object* k = HashTable<Shape, Key>::KeyAt(i);
11058 if (HashTable<Shape, Key>::IsKey(k)) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011059 PropertyDetails details = DetailsAt(i);
11060 if (details.IsDeleted()) continue;
11061 PropertyAttributes attr = details.attributes();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011062 if ((attr & filter) == 0) result++;
11063 }
11064 }
11065 return result;
11066}
11067
11068
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011069template<typename Shape, typename Key>
11070int Dictionary<Shape, Key>::NumberOfEnumElements() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011071 return NumberOfElementsFilterAttributes(
11072 static_cast<PropertyAttributes>(DONT_ENUM));
11073}
11074
11075
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011076template<typename Shape, typename Key>
sgjesse@chromium.org6db88712011-07-11 11:41:22 +000011077void Dictionary<Shape, Key>::CopyKeysTo(
11078 FixedArray* storage,
11079 PropertyAttributes filter,
11080 typename Dictionary<Shape, Key>::SortMode sort_mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011081 ASSERT(storage->length() >= NumberOfEnumElements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011082 int capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011083 int index = 0;
11084 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011085 Object* k = HashTable<Shape, Key>::KeyAt(i);
11086 if (HashTable<Shape, Key>::IsKey(k)) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011087 PropertyDetails details = DetailsAt(i);
11088 if (details.IsDeleted()) continue;
11089 PropertyAttributes attr = details.attributes();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011090 if ((attr & filter) == 0) storage->set(index++, k);
11091 }
11092 }
sgjesse@chromium.org6db88712011-07-11 11:41:22 +000011093 if (sort_mode == Dictionary<Shape, Key>::SORTED) {
11094 storage->SortPairs(storage, index);
11095 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011096 ASSERT(storage->length() >= index);
11097}
11098
11099
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011100void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
11101 FixedArray* sort_array) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011102 ASSERT(storage->length() >= NumberOfEnumElements());
11103 int capacity = Capacity();
11104 int index = 0;
11105 for (int i = 0; i < capacity; i++) {
11106 Object* k = KeyAt(i);
11107 if (IsKey(k)) {
11108 PropertyDetails details = DetailsAt(i);
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011109 if (details.IsDeleted() || details.IsDontEnum()) continue;
11110 storage->set(index, k);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000011111 sort_array->set(index, Smi::FromInt(details.index()));
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011112 index++;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011113 }
11114 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +000011115 storage->SortPairs(sort_array, sort_array->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011116 ASSERT(storage->length() >= index);
11117}
11118
11119
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011120template<typename Shape, typename Key>
sgjesse@chromium.org6db88712011-07-11 11:41:22 +000011121void Dictionary<Shape, Key>::CopyKeysTo(
11122 FixedArray* storage,
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000011123 int index,
sgjesse@chromium.org6db88712011-07-11 11:41:22 +000011124 typename Dictionary<Shape, Key>::SortMode sort_mode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011125 ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
11126 static_cast<PropertyAttributes>(NONE)));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011127 int capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011128 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011129 Object* k = HashTable<Shape, Key>::KeyAt(i);
11130 if (HashTable<Shape, Key>::IsKey(k)) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011131 PropertyDetails details = DetailsAt(i);
11132 if (details.IsDeleted()) continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011133 storage->set(index++, k);
11134 }
11135 }
sgjesse@chromium.org6db88712011-07-11 11:41:22 +000011136 if (sort_mode == Dictionary<Shape, Key>::SORTED) {
11137 storage->SortPairs(storage, index);
11138 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011139 ASSERT(storage->length() >= index);
11140}
11141
11142
11143// Backwards lookup (slow).
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011144template<typename Shape, typename Key>
11145Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
11146 int capacity = HashTable<Shape, Key>::Capacity();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011147 for (int i = 0; i < capacity; i++) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011148 Object* k = HashTable<Shape, Key>::KeyAt(i);
11149 if (Dictionary<Shape, Key>::IsKey(k)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000011150 Object* e = ValueAt(i);
11151 if (e->IsJSGlobalPropertyCell()) {
11152 e = JSGlobalPropertyCell::cast(e)->value();
11153 }
11154 if (e == value) return k;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011155 }
11156 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011157 Heap* heap = Dictionary<Shape, Key>::GetHeap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011158 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011159}
11160
11161
lrn@chromium.org303ada72010-10-27 09:33:13 +000011162MaybeObject* StringDictionary::TransformPropertiesToFastFor(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011163 JSObject* obj, int unused_property_fields) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011164 // Make sure we preserve dictionary representation if there are too many
11165 // descriptors.
11166 if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
11167
11168 // Figure out if it is necessary to generate new enumeration indices.
11169 int max_enumeration_index =
11170 NextEnumerationIndex() +
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011171 (DescriptorArray::kMaxNumberOfDescriptors -
11172 NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011173 if (!PropertyDetails::IsValidIndex(max_enumeration_index)) {
lrn@chromium.org303ada72010-10-27 09:33:13 +000011174 Object* result;
11175 { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
11176 if (!maybe_result->ToObject(&result)) return maybe_result;
11177 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011178 }
11179
11180 int instance_descriptor_length = 0;
11181 int number_of_fields = 0;
11182
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011183 Heap* heap = GetHeap();
11184
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011185 // Compute the length of the instance descriptor.
11186 int capacity = Capacity();
11187 for (int i = 0; i < capacity; i++) {
11188 Object* k = KeyAt(i);
11189 if (IsKey(k)) {
11190 Object* value = ValueAt(i);
11191 PropertyType type = DetailsAt(i).type();
11192 ASSERT(type != FIELD);
11193 instance_descriptor_length++;
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011194 if (type == NORMAL &&
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011195 (!value->IsJSFunction() || heap->InNewSpace(value))) {
sgjesse@chromium.org846fb742009-12-18 08:56:33 +000011196 number_of_fields += 1;
11197 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011198 }
11199 }
11200
11201 // Allocate the instance descriptor.
lrn@chromium.org303ada72010-10-27 09:33:13 +000011202 Object* descriptors_unchecked;
11203 { MaybeObject* maybe_descriptors_unchecked =
11204 DescriptorArray::Allocate(instance_descriptor_length);
11205 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
11206 return maybe_descriptors_unchecked;
11207 }
11208 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000011209 DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011210
ager@chromium.org32912102009-01-16 10:38:43 +000011211 int inobject_props = obj->map()->inobject_properties();
11212 int number_of_allocated_fields =
11213 number_of_fields + unused_property_fields - inobject_props;
ricow@chromium.orgeb7c1442010-10-04 08:54:21 +000011214 if (number_of_allocated_fields < 0) {
11215 // There is enough inobject space for all fields (including unused).
11216 number_of_allocated_fields = 0;
11217 unused_property_fields = inobject_props - number_of_fields;
11218 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011219
11220 // Allocate the fixed array for the fields.
lrn@chromium.org303ada72010-10-27 09:33:13 +000011221 Object* fields;
11222 { MaybeObject* maybe_fields =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011223 heap->AllocateFixedArray(number_of_allocated_fields);
lrn@chromium.org303ada72010-10-27 09:33:13 +000011224 if (!maybe_fields->ToObject(&fields)) return maybe_fields;
11225 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011226
11227 // Fill in the instance descriptor and the fields.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011228 int next_descriptor = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011229 int current_offset = 0;
11230 for (int i = 0; i < capacity; i++) {
11231 Object* k = KeyAt(i);
11232 if (IsKey(k)) {
11233 Object* value = ValueAt(i);
11234 // Ensure the key is a symbol before writing into the instance descriptor.
lrn@chromium.org303ada72010-10-27 09:33:13 +000011235 Object* key;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011236 { MaybeObject* maybe_key = heap->LookupSymbol(String::cast(k));
lrn@chromium.org303ada72010-10-27 09:33:13 +000011237 if (!maybe_key->ToObject(&key)) return maybe_key;
11238 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011239 PropertyDetails details = DetailsAt(i);
11240 PropertyType type = details.type();
ager@chromium.org32912102009-01-16 10:38:43 +000011241
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011242 if (value->IsJSFunction() && !heap->InNewSpace(value)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011243 ConstantFunctionDescriptor d(String::cast(key),
11244 JSFunction::cast(value),
11245 details.attributes(),
11246 details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011247 descriptors->Set(next_descriptor++, &d);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011248 } else if (type == NORMAL) {
ager@chromium.org32912102009-01-16 10:38:43 +000011249 if (current_offset < inobject_props) {
11250 obj->InObjectPropertyAtPut(current_offset,
11251 value,
11252 UPDATE_WRITE_BARRIER);
11253 } else {
11254 int offset = current_offset - inobject_props;
11255 FixedArray::cast(fields)->set(offset, value);
11256 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011257 FieldDescriptor d(String::cast(key),
11258 current_offset++,
11259 details.attributes(),
11260 details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011261 descriptors->Set(next_descriptor++, &d);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011262 } else if (type == CALLBACKS) {
11263 CallbacksDescriptor d(String::cast(key),
11264 value,
11265 details.attributes(),
11266 details.index());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000011267 descriptors->Set(next_descriptor++, &d);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011268 } else {
11269 UNREACHABLE();
11270 }
11271 }
11272 }
11273 ASSERT(current_offset == number_of_fields);
11274
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000011275 descriptors->Sort();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011276 // Allocate new map.
lrn@chromium.org303ada72010-10-27 09:33:13 +000011277 Object* new_map;
11278 { MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
11279 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
11280 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011281
11282 // Transform the object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011283 obj->set_map(Map::cast(new_map));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000011284 obj->map()->set_instance_descriptors(descriptors);
11285 obj->map()->set_unused_property_fields(unused_property_fields);
11286
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011287 obj->set_properties(FixedArray::cast(fields));
11288 ASSERT(obj->IsJSObject());
11289
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +000011290 descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
ager@chromium.org32912102009-01-16 10:38:43 +000011291 // Check that it really works.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011292 ASSERT(obj->HasFastProperties());
ager@chromium.org32912102009-01-16 10:38:43 +000011293
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011294 return obj;
11295}
11296
11297
vegorov@chromium.org7943d462011-08-01 11:41:52 +000011298Object* ObjectHashTable::Lookup(JSObject* key) {
11299 // If the object does not have an identity hash, it was never used as a key.
11300 MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::OMIT_CREATION);
11301 if (maybe_hash->IsFailure()) return GetHeap()->undefined_value();
11302 int entry = FindEntry(key);
11303 if (entry == kNotFound) return GetHeap()->undefined_value();
11304 return get(EntryToIndex(entry) + 1);
11305}
11306
11307
11308MaybeObject* ObjectHashTable::Put(JSObject* key, Object* value) {
11309 // Make sure the key object has an identity hash code.
11310 int hash;
11311 { MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::ALLOW_CREATION);
11312 if (maybe_hash->IsFailure()) return maybe_hash;
11313 hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value();
11314 }
11315 int entry = FindEntry(key);
11316
11317 // Check whether to perform removal operation.
11318 if (value->IsUndefined()) {
11319 if (entry == kNotFound) return this;
11320 RemoveEntry(entry);
11321 return Shrink(key);
11322 }
11323
11324 // Key is already in table, just overwrite value.
11325 if (entry != kNotFound) {
11326 set(EntryToIndex(entry) + 1, value);
11327 return this;
11328 }
11329
11330 // Check whether the hash table should be extended.
11331 Object* obj;
11332 { MaybeObject* maybe_obj = EnsureCapacity(1, key);
11333 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
11334 }
11335 ObjectHashTable* table = ObjectHashTable::cast(obj);
11336 table->AddEntry(table->FindInsertionEntry(hash), key, value);
11337 return table;
11338}
11339
11340
11341void ObjectHashTable::AddEntry(int entry, JSObject* key, Object* value) {
11342 set(EntryToIndex(entry), key);
11343 set(EntryToIndex(entry) + 1, value);
11344 ElementAdded();
11345}
11346
11347
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000011348void ObjectHashTable::RemoveEntry(int entry, Heap* heap) {
11349 set_null(heap, EntryToIndex(entry));
11350 set_null(heap, EntryToIndex(entry) + 1);
vegorov@chromium.org7943d462011-08-01 11:41:52 +000011351 ElementRemoved();
11352}
11353
11354
ager@chromium.org65dad4b2009-04-23 08:48:43 +000011355#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011356// Check if there is a break point at this code position.
11357bool DebugInfo::HasBreakPoint(int code_position) {
11358 // Get the break point info object for this code position.
11359 Object* break_point_info = GetBreakPointInfo(code_position);
11360
11361 // If there is no break point info object or no break points in the break
11362 // point info object there is no break point at this code position.
11363 if (break_point_info->IsUndefined()) return false;
11364 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
11365}
11366
11367
11368// Get the break point info object for this code position.
11369Object* DebugInfo::GetBreakPointInfo(int code_position) {
11370 // Find the index of the break point info object for this code position.
11371 int index = GetBreakPointInfoIndex(code_position);
11372
11373 // Return the break point info object if any.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011374 if (index == kNoBreakPointInfo) return GetHeap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011375 return BreakPointInfo::cast(break_points()->get(index));
11376}
11377
11378
11379// Clear a break point at the specified code position.
11380void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
11381 int code_position,
11382 Handle<Object> break_point_object) {
11383 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
11384 if (break_point_info->IsUndefined()) return;
11385 BreakPointInfo::ClearBreakPoint(
11386 Handle<BreakPointInfo>::cast(break_point_info),
11387 break_point_object);
11388}
11389
11390
11391void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
11392 int code_position,
11393 int source_position,
11394 int statement_position,
11395 Handle<Object> break_point_object) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011396 Isolate* isolate = Isolate::Current();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011397 Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position));
11398 if (!break_point_info->IsUndefined()) {
11399 BreakPointInfo::SetBreakPoint(
11400 Handle<BreakPointInfo>::cast(break_point_info),
11401 break_point_object);
11402 return;
11403 }
11404
11405 // Adding a new break point for a code position which did not have any
11406 // break points before. Try to find a free slot.
11407 int index = kNoBreakPointInfo;
11408 for (int i = 0; i < debug_info->break_points()->length(); i++) {
11409 if (debug_info->break_points()->get(i)->IsUndefined()) {
11410 index = i;
11411 break;
11412 }
11413 }
11414 if (index == kNoBreakPointInfo) {
11415 // No free slot - extend break point info array.
11416 Handle<FixedArray> old_break_points =
11417 Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011418 Handle<FixedArray> new_break_points =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011419 isolate->factory()->NewFixedArray(
11420 old_break_points->length() +
11421 Debug::kEstimatedNofBreakPointsInFunction);
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +000011422
11423 debug_info->set_break_points(*new_break_points);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011424 for (int i = 0; i < old_break_points->length(); i++) {
11425 new_break_points->set(i, old_break_points->get(i));
11426 }
11427 index = old_break_points->length();
11428 }
11429 ASSERT(index != kNoBreakPointInfo);
11430
11431 // Allocate new BreakPointInfo object and set the break point.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011432 Handle<BreakPointInfo> new_break_point_info = Handle<BreakPointInfo>::cast(
11433 isolate->factory()->NewStruct(BREAK_POINT_INFO_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011434 new_break_point_info->set_code_position(Smi::FromInt(code_position));
11435 new_break_point_info->set_source_position(Smi::FromInt(source_position));
11436 new_break_point_info->
11437 set_statement_position(Smi::FromInt(statement_position));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011438 new_break_point_info->set_break_point_objects(
11439 isolate->heap()->undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011440 BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
11441 debug_info->break_points()->set(index, *new_break_point_info);
11442}
11443
11444
11445// Get the break point objects for a code position.
11446Object* DebugInfo::GetBreakPointObjects(int code_position) {
11447 Object* break_point_info = GetBreakPointInfo(code_position);
11448 if (break_point_info->IsUndefined()) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011449 return GetHeap()->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011450 }
11451 return BreakPointInfo::cast(break_point_info)->break_point_objects();
11452}
11453
11454
11455// Get the total number of break points.
11456int DebugInfo::GetBreakPointCount() {
11457 if (break_points()->IsUndefined()) return 0;
11458 int count = 0;
11459 for (int i = 0; i < break_points()->length(); i++) {
11460 if (!break_points()->get(i)->IsUndefined()) {
11461 BreakPointInfo* break_point_info =
11462 BreakPointInfo::cast(break_points()->get(i));
11463 count += break_point_info->GetBreakPointCount();
11464 }
11465 }
11466 return count;
11467}
11468
11469
11470Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
11471 Handle<Object> break_point_object) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000011472 Heap* heap = debug_info->GetHeap();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011473 if (debug_info->break_points()->IsUndefined()) return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011474 for (int i = 0; i < debug_info->break_points()->length(); i++) {
11475 if (!debug_info->break_points()->get(i)->IsUndefined()) {
11476 Handle<BreakPointInfo> break_point_info =
11477 Handle<BreakPointInfo>(BreakPointInfo::cast(
11478 debug_info->break_points()->get(i)));
11479 if (BreakPointInfo::HasBreakPointObject(break_point_info,
11480 break_point_object)) {
11481 return *break_point_info;
11482 }
11483 }
11484 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011485 return heap->undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011486}
11487
11488
11489// Find the index of the break point info object for the specified code
11490// position.
11491int DebugInfo::GetBreakPointInfoIndex(int code_position) {
11492 if (break_points()->IsUndefined()) return kNoBreakPointInfo;
11493 for (int i = 0; i < break_points()->length(); i++) {
11494 if (!break_points()->get(i)->IsUndefined()) {
11495 BreakPointInfo* break_point_info =
11496 BreakPointInfo::cast(break_points()->get(i));
11497 if (break_point_info->code_position()->value() == code_position) {
11498 return i;
11499 }
11500 }
11501 }
11502 return kNoBreakPointInfo;
11503}
11504
11505
11506// Remove the specified break point object.
11507void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
11508 Handle<Object> break_point_object) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011509 Isolate* isolate = Isolate::Current();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011510 // If there are no break points just ignore.
11511 if (break_point_info->break_point_objects()->IsUndefined()) return;
11512 // If there is a single break point clear it if it is the same.
11513 if (!break_point_info->break_point_objects()->IsFixedArray()) {
11514 if (break_point_info->break_point_objects() == *break_point_object) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011515 break_point_info->set_break_point_objects(
11516 isolate->heap()->undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011517 }
11518 return;
11519 }
11520 // If there are multiple break points shrink the array
11521 ASSERT(break_point_info->break_point_objects()->IsFixedArray());
11522 Handle<FixedArray> old_array =
11523 Handle<FixedArray>(
11524 FixedArray::cast(break_point_info->break_point_objects()));
11525 Handle<FixedArray> new_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011526 isolate->factory()->NewFixedArray(old_array->length() - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011527 int found_count = 0;
11528 for (int i = 0; i < old_array->length(); i++) {
11529 if (old_array->get(i) == *break_point_object) {
11530 ASSERT(found_count == 0);
11531 found_count++;
11532 } else {
11533 new_array->set(i - found_count, old_array->get(i));
11534 }
11535 }
11536 // If the break point was found in the list change it.
11537 if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
11538}
11539
11540
11541// Add the specified break point object.
11542void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
11543 Handle<Object> break_point_object) {
11544 // If there was no break point objects before just set it.
11545 if (break_point_info->break_point_objects()->IsUndefined()) {
11546 break_point_info->set_break_point_objects(*break_point_object);
11547 return;
11548 }
11549 // If the break point object is the same as before just ignore.
11550 if (break_point_info->break_point_objects() == *break_point_object) return;
11551 // If there was one break point object before replace with array.
11552 if (!break_point_info->break_point_objects()->IsFixedArray()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011553 Handle<FixedArray> array = FACTORY->NewFixedArray(2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011554 array->set(0, break_point_info->break_point_objects());
11555 array->set(1, *break_point_object);
11556 break_point_info->set_break_point_objects(*array);
11557 return;
11558 }
11559 // If there was more than one break point before extend array.
11560 Handle<FixedArray> old_array =
11561 Handle<FixedArray>(
11562 FixedArray::cast(break_point_info->break_point_objects()));
11563 Handle<FixedArray> new_array =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000011564 FACTORY->NewFixedArray(old_array->length() + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011565 for (int i = 0; i < old_array->length(); i++) {
11566 // If the break point was there before just ignore.
11567 if (old_array->get(i) == *break_point_object) return;
11568 new_array->set(i, old_array->get(i));
11569 }
11570 // Add the new break point.
11571 new_array->set(old_array->length(), *break_point_object);
11572 break_point_info->set_break_point_objects(*new_array);
11573}
11574
11575
11576bool BreakPointInfo::HasBreakPointObject(
11577 Handle<BreakPointInfo> break_point_info,
11578 Handle<Object> break_point_object) {
11579 // No break point.
11580 if (break_point_info->break_point_objects()->IsUndefined()) return false;
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000011581 // Single break point.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011582 if (!break_point_info->break_point_objects()->IsFixedArray()) {
11583 return break_point_info->break_point_objects() == *break_point_object;
11584 }
11585 // Multiple break points.
11586 FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
11587 for (int i = 0; i < array->length(); i++) {
11588 if (array->get(i) == *break_point_object) {
11589 return true;
11590 }
11591 }
11592 return false;
11593}
11594
11595
11596// Get the number of break points.
11597int BreakPointInfo::GetBreakPointCount() {
11598 // No break point.
11599 if (break_point_objects()->IsUndefined()) return 0;
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000011600 // Single break point.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011601 if (!break_point_objects()->IsFixedArray()) return 1;
11602 // Multiple break points.
11603 return FixedArray::cast(break_point_objects())->length();
11604}
ager@chromium.org65dad4b2009-04-23 08:48:43 +000011605#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011606
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000011607
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000011608} } // namespace v8::internal