Revision: 12990
Author: [email protected]
Date: Fri Nov 16 05:28:34 2012
Log: Clean-up refactoring to eliminate GetLocalElementKind.
Eliminates substantial amounts of fragile code duplication and special
casing.
Also fixes "a".propertyIsEnumerable(0) to correctly return true.
[email protected]
BUG=
Review URL: https://codereview.chromium.org/11420011
http://code.google.com/p/v8/source/detail?r=12990
Modified:
/branches/bleeding_edge/src/objects-inl.h
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/test/mjsunit/regress/regress-1692.js
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Thu Nov 15 05:31:27 2012
+++ /branches/bleeding_edge/src/objects-inl.h Fri Nov 16 05:28:34 2012
@@ -5033,6 +5033,10 @@
PropertyAttributes JSReceiver::GetPropertyAttribute(String* key) {
+ uint32_t index;
+ if (IsJSObject() && key->AsArrayIndex(&index)) {
+ return GetElementAttribute(index);
+ }
return GetPropertyAttributeWithReceiver(this, key);
}
=======================================
--- /branches/bleeding_edge/src/objects.cc Fri Nov 16 00:38:11 2012
+++ /branches/bleeding_edge/src/objects.cc Fri Nov 16 05:28:34 2012
@@ -245,6 +245,18 @@
return *result;
}
+
+
+Handle<Object> Object::GetProperty(Handle<Object> object, Handle<String>
name) {
+ // TODO(rossberg): The index test should not be here but in the
GetProperty
+ // method (or somewhere else entirely). Needs more global clean-up.
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) return GetElement(object, index);
+ Isolate* isolate = object->IsHeapObject()
+ ? Handle<HeapObject>::cast(object)->GetIsolate()
+ : Isolate::Current();
+ CALL_HEAP_FUNCTION(isolate, object->GetProperty(*name), Object);
+}
Handle<Object> Object::GetElement(Handle<Object> object, uint32_t index) {
@@ -3333,6 +3345,11 @@
if (HasIndexedInterceptor() && !isolate->bootstrapper()->IsActive()) {
return GetElementAttributeWithInterceptor(receiver, index,
continue_search);
}
+
+ // Handle [] on String objects.
+ if (this->IsStringObjectWithCharacterAt(index)) {
+ return static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
+ }
return GetElementAttributeWithoutInterceptor(
receiver, index, continue_search);
@@ -9628,8 +9645,10 @@
if (name->AsArrayIndex(&index)) {
return GetLocalElementAccessorPair(index);
}
+
LookupResult lookup(GetIsolate());
- LocalLookup(name, &lookup);
+ LocalLookupRealNamedProperty(name, &lookup);
+
if (lookup.IsPropertyCallbacks() &&
lookup.GetCallbackObject()->IsAccessorPair()) {
return AccessorPair::cast(lookup.GetCallbackObject());
@@ -9639,116 +9658,17 @@
AccessorPair* JSObject::GetLocalElementAccessorPair(uint32_t index) {
- return GetElementsAccessor()->GetAccessorPair(this, this, index);
-}
-
-
-JSObject::LocalElementKind JSObject::GetLocalElementKind(uint32_t index) {
- // Check access rights if needed.
- if (IsAccessCheckNeeded()) {
- Heap* heap = GetHeap();
- if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
- return UNDEFINED_ELEMENT;
- }
- }
-
if (IsJSGlobalProxy()) {
Object* proto = GetPrototype();
- if (proto->IsNull()) return UNDEFINED_ELEMENT;
+ if (proto->IsNull()) return NULL;
ASSERT(proto->IsJSGlobalObject());
- return JSObject::cast(proto)->GetLocalElementKind(index);
+ return JSObject::cast(proto)->GetLocalElementAccessorPair(index);
}
- // Check for lookup interceptor
- if (HasIndexedInterceptor()) {
- return GetElementAttributeWithInterceptor(this, index, false) != ABSENT
- ? INTERCEPTED_ELEMENT : UNDEFINED_ELEMENT;
- }
-
- // Handle [] on String objects.
- if (this->IsStringObjectWithCharacterAt(index)) {
- return STRING_CHARACTER_ELEMENT;
- }
-
- switch (GetElementsKind()) {
- case FAST_SMI_ELEMENTS:
- case FAST_ELEMENTS:
- case FAST_HOLEY_SMI_ELEMENTS:
- case FAST_HOLEY_ELEMENTS: {
- uint32_t length = IsJSArray() ?
- static_cast<uint32_t>
- (Smi::cast(JSArray::cast(this)->length())->value()) :
- static_cast<uint32_t>(FixedArray::cast(elements())->length());
- if ((index < length) &&
- !FixedArray::cast(elements())->get(index)->IsTheHole()) {
- return FAST_ELEMENT;
- }
- break;
- }
- case FAST_DOUBLE_ELEMENTS:
- case FAST_HOLEY_DOUBLE_ELEMENTS: {
- uint32_t length = IsJSArray() ?
- static_cast<uint32_t>
- (Smi::cast(JSArray::cast(this)->length())->value()) :
-
static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
- if ((index < length) &&
- !FixedDoubleArray::cast(elements())->is_the_hole(index)) {
- return FAST_ELEMENT;
- }
- break;
- }
- case EXTERNAL_PIXEL_ELEMENTS: {
- ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
- if (index < static_cast<uint32_t>(pixels->length())) return
FAST_ELEMENT;
- break;
- }
- case EXTERNAL_BYTE_ELEMENTS:
- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
- case EXTERNAL_SHORT_ELEMENTS:
- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
- case EXTERNAL_INT_ELEMENTS:
- case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS:
- case EXTERNAL_DOUBLE_ELEMENTS: {
- ExternalArray* array = ExternalArray::cast(elements());
- if (index < static_cast<uint32_t>(array->length())) return
FAST_ELEMENT;
- break;
- }
- case DICTIONARY_ELEMENTS: {
- if (element_dictionary()->FindEntry(index) !=
- SeededNumberDictionary::kNotFound) {
- return DICTIONARY_ELEMENT;
- }
- break;
- }
- case NON_STRICT_ARGUMENTS_ELEMENTS: {
- // Aliased parameters and non-aliased elements in a fast backing
store
- // behave as FAST_ELEMENT. Non-aliased elements in a dictionary
- // backing store behave as DICTIONARY_ELEMENT.
- FixedArray* parameter_map = FixedArray::cast(elements());
- uint32_t length = parameter_map->length();
- Object* probe =
- index < (length - 2) ? parameter_map->get(index + 2) : NULL;
- if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT;
- // If not aliased, check the arguments.
- FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
- if (arguments->IsDictionary()) {
- SeededNumberDictionary* dictionary =
- SeededNumberDictionary::cast(arguments);
- if (dictionary->FindEntry(index) !=
SeededNumberDictionary::kNotFound) {
- return DICTIONARY_ELEMENT;
- }
- } else {
- length = arguments->length();
- probe = (index < length) ? arguments->get(index) : NULL;
- if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT;
- }
- break;
- }
- }
+ // Check for lookup interceptor.
+ if (HasIndexedInterceptor()) return NULL;
- return UNDEFINED_ELEMENT;
+ return GetElementsAccessor()->GetAccessorPair(this, this, index);
}
=======================================
--- /branches/bleeding_edge/src/objects.h Fri Nov 16 00:38:11 2012
+++ /branches/bleeding_edge/src/objects.h Fri Nov 16 05:28:34 2012
@@ -973,6 +973,7 @@
String* key,
PropertyAttributes* attributes);
+ static Handle<Object> GetProperty(Handle<Object> object, Handle<String>
key);
static Handle<Object> GetProperty(Handle<Object> object,
Handle<Object> receiver,
LookupResult* result,
@@ -1846,29 +1847,11 @@
PropertyType GetLocalPropertyType(String* name);
PropertyType GetLocalElementType(uint32_t index);
+
+ // These methods do not perform access checks!
AccessorPair* GetLocalPropertyAccessorPair(String* name);
AccessorPair* GetLocalElementAccessorPair(uint32_t index);
- // Tells whether the index'th element is present and how it is stored.
- enum LocalElementKind {
- // There is no element with given index.
- UNDEFINED_ELEMENT,
-
- // Element with given index is handled by interceptor.
- INTERCEPTED_ELEMENT,
-
- // Element with given index is character in string.
- STRING_CHARACTER_ELEMENT,
-
- // Element with given index is stored in fast backing store.
- FAST_ELEMENT,
-
- // Element with given index is stored in slow backing store.
- DICTIONARY_ELEMENT
- };
-
- LocalElementKind GetLocalElementKind(uint32_t index);
-
MUST_USE_RESULT MaybeObject* SetFastElement(uint32_t index,
Object* value,
StrictModeFlag strict_mode,
=======================================
--- /branches/bleeding_edge/src/runtime.cc Fri Nov 16 03:58:21 2012
+++ /branches/bleeding_edge/src/runtime.cc Fri Nov 16 05:28:34 2012
@@ -977,95 +977,107 @@
Object* proto = obj->GetPrototype();
if (proto->IsJSObject() &&
- JSObject::cast(proto)->map()->is_hidden_prototype())
+ JSObject::cast(proto)->map()->is_hidden_prototype()) {
GetOwnPropertyImplementation(JSObject::cast(proto),
name, result);
+ }
}
-static bool CheckAccessException(LookupResult* result,
+static bool CheckAccessException(Object* callback,
v8::AccessType access_type) {
- if (result->type() == CALLBACKS) {
- Object* callback = result->GetCallbackObject();
- if (callback->IsAccessorInfo()) {
- AccessorInfo* info = AccessorInfo::cast(callback);
- bool can_access =
- (access_type == v8::ACCESS_HAS &&
- (info->all_can_read() || info->all_can_write())) ||
- (access_type == v8::ACCESS_GET && info->all_can_read()) ||
- (access_type == v8::ACCESS_SET && info->all_can_write());
- return can_access;
- }
+ if (callback->IsAccessorInfo()) {
+ AccessorInfo* info = AccessorInfo::cast(callback);
+ return
+ (access_type == v8::ACCESS_HAS &&
+ (info->all_can_read() || info->all_can_write())) ||
+ (access_type == v8::ACCESS_GET && info->all_can_read()) ||
+ (access_type == v8::ACCESS_SET && info->all_can_write());
}
-
return false;
}
-static bool CheckAccess(JSObject* obj,
- String* name,
- LookupResult* result,
- v8::AccessType access_type) {
- ASSERT(result->IsProperty());
-
- JSObject* holder = result->holder();
- JSObject* current = obj;
- Isolate* isolate = obj->GetIsolate();
- while (true) {
+template<class Key>
+static bool CheckGenericAccess(
+ JSObject* receiver,
+ JSObject* holder,
+ Key key,
+ v8::AccessType access_type,
+ bool (Isolate::*mayAccess)(JSObject*, Key, v8::AccessType)) {
+ Isolate* isolate = receiver->GetIsolate();
+ for (JSObject* current = receiver;
+ true;
+ current = JSObject::cast(current->GetPrototype())) {
if (current->IsAccessCheckNeeded() &&
- !isolate->MayNamedAccess(current, name, access_type)) {
- // Access check callback denied the access, but some properties
- // can have a special permissions which override callbacks descision
- // (currently see v8::AccessControl).
- break;
+ !(isolate->*mayAccess)(current, key, access_type)) {
+ return false;
}
+ if (current == holder) break;
+ }
+ return true;
+}
- if (current == holder) {
- return true;
- }
- current = JSObject::cast(current->GetPrototype());
+static bool CheckElementAccess(
+ JSObject* obj,
+ uint32_t index,
+ v8::AccessType access_type) {
+ // TODO(1095): we should traverse hidden prototype hierachy as well.
+ if (CheckGenericAccess(
+ obj, obj, index, access_type, &Isolate::MayIndexedAccess)) {
+ return true;
}
+ obj->GetIsolate()->ReportFailedAccessCheck(obj, access_type);
+ return false;
+}
+
+
+static bool CheckPropertyAccess(
+ JSObject* obj,
+ String* name,
+ v8::AccessType access_type) {
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) {
+ return CheckElementAccess(obj, index, access_type);
+ }
+
+ LookupResult lookup(obj->GetIsolate());
+ obj->LocalLookup(name, &lookup);
+
+ if (CheckGenericAccess<Object*>(
+ obj, lookup.holder(), name, access_type,
&Isolate::MayNamedAccess)) {
+ return true;
+ }
+
+ // Access check callback denied the access, but some properties
+ // can have a special permissions which override callbacks descision
+ // (currently see v8::AccessControl).
// API callbacks can have per callback access exceptions.
- switch (result->type()) {
- case CALLBACKS: {
- if (CheckAccessException(result, access_type)) {
+ switch (lookup.type()) {
+ case CALLBACKS:
+ if (CheckAccessException(lookup.GetCallbackObject(), access_type)) {
return true;
}
break;
- }
- case INTERCEPTOR: {
+ case INTERCEPTOR:
// If the object has an interceptor, try real named properties.
// Overwrite the result to fetch the correct property later.
- holder->LookupRealNamedProperty(name, result);
- if (result->IsProperty()) {
- if (CheckAccessException(result, access_type)) {
+ lookup.holder()->LookupRealNamedProperty(name, &lookup);
+ if (lookup.IsProperty() && lookup.IsPropertyCallbacks()) {
+ if (CheckAccessException(lookup.GetCallbackObject(), access_type))
{
return true;
}
}
break;
- }
default:
break;
}
- isolate->ReportFailedAccessCheck(current, access_type);
+ obj->GetIsolate()->ReportFailedAccessCheck(obj, access_type);
return false;
}
-
-
-// TODO(1095): we should traverse hidden prototype hierachy as well.
-static bool CheckElementAccess(JSObject* obj,
- uint32_t index,
- v8::AccessType access_type) {
- if (obj->IsAccessCheckNeeded() &&
- !obj->GetIsolate()->MayIndexedAccess(obj, index, access_type)) {
- return false;
- }
-
- return true;
-}
// Enumerator used as indices into the array returned from GetOwnProperty
@@ -1085,141 +1097,33 @@
Handle<JSObject> obj,
Handle<String> name) {
Heap* heap = isolate->heap();
+ PropertyAttributes attrs = obj->GetLocalPropertyAttribute(*name);
+ if (attrs == ABSENT) return heap->undefined_value();
+ AccessorPair* accessors = obj->GetLocalPropertyAccessorPair(*name);
+
Handle<FixedArray> elms =
isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE);
- Handle<JSArray> desc = isolate->factory()->NewJSArrayWithElements(elms);
- LookupResult result(isolate);
- // This could be an element.
- uint32_t index;
- if (name->AsArrayIndex(&index)) {
- switch (obj->GetLocalElementKind(index)) {
- case JSObject::UNDEFINED_ELEMENT:
- return heap->undefined_value();
+ elms->set(ENUMERABLE_INDEX, heap->ToBoolean((attrs & DONT_ENUM) == 0));
+ elms->set(CONFIGURABLE_INDEX, heap->ToBoolean((attrs & DONT_DELETE) ==
0));
+ elms->set(IS_ACCESSOR_INDEX, heap->ToBoolean(accessors != NULL));
- case JSObject::STRING_CHARACTER_ELEMENT: {
- // Special handling of string objects according to ECMAScript 5
- // 15.5.5.2. Note that this might be a string object with elements
- // other than the actual string value. This is covered by the
- // subsequent cases.
- Handle<JSValue> js_value = Handle<JSValue>::cast(obj);
- Handle<String> str(String::cast(js_value->value()));
- Handle<String> substr = SubString(str, index, index + 1,
NOT_TENURED);
-
- elms->set(IS_ACCESSOR_INDEX, heap->false_value());
- elms->set(VALUE_INDEX, *substr);
- elms->set(WRITABLE_INDEX, heap->false_value());
- elms->set(ENUMERABLE_INDEX, heap->true_value());
- elms->set(CONFIGURABLE_INDEX, heap->false_value());
- return *desc;
- }
-
- case JSObject::INTERCEPTED_ELEMENT:
- case JSObject::FAST_ELEMENT: {
- elms->set(IS_ACCESSOR_INDEX, heap->false_value());
- Handle<Object> value = Object::GetElement(obj, index);
- RETURN_IF_EMPTY_HANDLE(isolate, value);
- elms->set(VALUE_INDEX, *value);
- elms->set(WRITABLE_INDEX, heap->true_value());
- elms->set(ENUMERABLE_INDEX, heap->true_value());
- elms->set(CONFIGURABLE_INDEX, heap->true_value());
- return *desc;
- }
-
- case JSObject::DICTIONARY_ELEMENT: {
- Handle<JSObject> holder = obj;
- if (obj->IsJSGlobalProxy()) {
- Object* proto = obj->GetPrototype();
- if (proto->IsNull()) return heap->undefined_value();
- ASSERT(proto->IsJSGlobalObject());
- holder = Handle<JSObject>(JSObject::cast(proto));
- }
- FixedArray* elements = FixedArray::cast(holder->elements());
- SeededNumberDictionary* dictionary = NULL;
- if (elements->map() == heap->non_strict_arguments_elements_map()) {
- dictionary = SeededNumberDictionary::cast(elements->get(1));
- } else {
- dictionary = SeededNumberDictionary::cast(elements);
- }
- int entry = dictionary->FindEntry(index);
- ASSERT(entry != SeededNumberDictionary::kNotFound);
- PropertyDetails details = dictionary->DetailsAt(entry);
- switch (details.type()) {
- case CALLBACKS: {
- // This is an accessor property with getter and/or setter.
- AccessorPair* accessors =
- AccessorPair::cast(dictionary->ValueAt(entry));
- elms->set(IS_ACCESSOR_INDEX, heap->true_value());
- if (CheckElementAccess(*obj, index, v8::ACCESS_GET)) {
- elms->set(GETTER_INDEX,
accessors->GetComponent(ACCESSOR_GETTER));
- }
- if (CheckElementAccess(*obj, index, v8::ACCESS_SET)) {
- elms->set(SETTER_INDEX,
accessors->GetComponent(ACCESSOR_SETTER));
- }
- break;
- }
- case NORMAL: {
- // This is a data property.
- elms->set(IS_ACCESSOR_INDEX, heap->false_value());
- Handle<Object> value = Object::GetElement(obj, index);
- ASSERT(!value.is_null());
- elms->set(VALUE_INDEX, *value);
- elms->set(WRITABLE_INDEX,
heap->ToBoolean(!details.IsReadOnly()));
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
- elms->set(ENUMERABLE_INDEX,
heap->ToBoolean(!details.IsDontEnum()));
- elms->set(CONFIGURABLE_INDEX,
heap->ToBoolean(!details.IsDontDelete()));
- return *desc;
- }
- }
- }
-
- // Use recursive implementation to also traverse hidden prototypes
- GetOwnPropertyImplementation(*obj, *name, &result);
-
- if (!result.IsProperty()) {
- return heap->undefined_value();
- }
-
- if (!CheckAccess(*obj, *name, &result, v8::ACCESS_HAS)) {
- return heap->false_value();
- }
-
- elms->set(ENUMERABLE_INDEX, heap->ToBoolean(!result.IsDontEnum()));
- elms->set(CONFIGURABLE_INDEX, heap->ToBoolean(!result.IsDontDelete()));
-
- bool is_js_accessor = result.IsPropertyCallbacks() &&
- (result.GetCallbackObject()->IsAccessorPair());
-
- if (is_js_accessor) {
- // __defineGetter__/__defineSetter__ callback.
- elms->set(IS_ACCESSOR_INDEX, heap->true_value());
-
- AccessorPair* accessors =
AccessorPair::cast(result.GetCallbackObject());
+ if (accessors == NULL) {
+ elms->set(WRITABLE_INDEX, heap->ToBoolean((attrs & READ_ONLY) == 0));
+ // GetProperty does access check.
+ elms->set(VALUE_INDEX, *GetProperty(obj, name));
+ } else {
+ // Access checks are performed for both accessors separately.
+ // When they fail, the respective field is not set in the descriptor.
Object* getter = accessors->GetComponent(ACCESSOR_GETTER);
- if (!getter->IsMap() && CheckAccess(*obj, *name, &result,
v8::ACCESS_GET)) {
+ Object* setter = accessors->GetComponent(ACCESSOR_SETTER);
+ if (!getter->IsMap() && CheckPropertyAccess(*obj, *name,
v8::ACCESS_GET)) {
elms->set(GETTER_INDEX, getter);
}
- Object* setter = accessors->GetComponent(ACCESSOR_SETTER);
- if (!setter->IsMap() && CheckAccess(*obj, *name, &result,
v8::ACCESS_SET)) {
+ if (!setter->IsMap() && CheckPropertyAccess(*obj, *name,
v8::ACCESS_SET)) {
elms->set(SETTER_INDEX, setter);
}
- } else {
- elms->set(IS_ACCESSOR_INDEX, heap->false_value());
- elms->set(WRITABLE_INDEX, heap->ToBoolean(!result.IsReadOnly()));
-
- PropertyAttributes attrs;
- Object* value;
- // GetProperty will check access and report any violations.
- { MaybeObject* maybe_value = obj->GetProperty(*obj, &result, *name,
&attrs);
- if (!maybe_value->ToObject(&value)) return maybe_value;
- }
- elms->set(VALUE_INDEX, value);
}
- return *desc;
+ return *isolate->factory()->NewJSArrayWithElements(elms);
}
@@ -4726,41 +4630,6 @@
CONVERT_ARG_CHECKED(JSObject, object, 0);
CONVERT_ARG_CHECKED(String, key, 1);
-
- uint32_t index;
- if (key->AsArrayIndex(&index)) {
- JSObject::LocalElementKind type = object->GetLocalElementKind(index);
- switch (type) {
- case JSObject::UNDEFINED_ELEMENT:
- case JSObject::STRING_CHARACTER_ELEMENT:
- return isolate->heap()->false_value();
- case JSObject::INTERCEPTED_ELEMENT:
- case JSObject::FAST_ELEMENT:
- return isolate->heap()->true_value();
- case JSObject::DICTIONARY_ELEMENT: {
- if (object->IsJSGlobalProxy()) {
- Object* proto = object->GetPrototype();
- if (proto->IsNull()) {
- return isolate->heap()->false_value();
- }
- ASSERT(proto->IsJSGlobalObject());
- object = JSObject::cast(proto);
- }
- FixedArray* elements = FixedArray::cast(object->elements());
- SeededNumberDictionary* dictionary = NULL;
- if (elements->map() ==
- isolate->heap()->non_strict_arguments_elements_map()) {
- dictionary = SeededNumberDictionary::cast(elements->get(1));
- } else {
- dictionary = SeededNumberDictionary::cast(elements);
- }
- int entry = dictionary->FindEntry(index);
- ASSERT(entry != SeededNumberDictionary::kNotFound);
- PropertyDetails details = dictionary->DetailsAt(entry);
- return isolate->heap()->ToBoolean(!details.IsDontEnum());
- }
- }
- }
PropertyAttributes att = object->GetLocalPropertyAttribute(key);
return isolate->heap()->ToBoolean(att != ABSENT && (att & DONT_ENUM) ==
0);
=======================================
--- /branches/bleeding_edge/test/mjsunit/regress/regress-1692.js Mon Oct 3
02:15:58 2011
+++ /branches/bleeding_edge/test/mjsunit/regress/regress-1692.js Fri Nov 16
05:28:34 2012
@@ -82,7 +82,7 @@
// Non-string property on String object.
o[10] = 42;
assertTrue(o.propertyIsEnumerable(10));
-assertFalse(o.propertyIsEnumerable(0));
+assertTrue(o.propertyIsEnumerable(0));
// Fast elements.
var o = [1,2,3,4,5];
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev