handle the special snowflakes that are Integer Indexed Exotic objects

the implementation doesn't yet throw on strict mode assignment

BUG=

Review URL: https://codereview.chromium.org/992913002

Cr-Commit-Position: refs/heads/master@{#27121}
This commit is contained in:
dcarney 2015-03-10 12:11:11 -07:00 committed by Commit bot
parent fd012f1080
commit 040225a315
14 changed files with 231 additions and 30 deletions

View File

@ -9,6 +9,7 @@
#include "src/v8.h"
#include "src/assert-scope.h"
#include "src/char-predicates-inl.h"
#include "src/conversions-inl.h"
#include "src/conversions.h"
#include "src/dtoa.h"
@ -502,4 +503,54 @@ double StringToDouble(UnicodeCache* unicode_cache, Handle<String> string,
}
bool IsNonArrayIndexInteger(String* string) {
const int kBufferSize = 64;
const int kUint32MaxChars = 11;
uint16_t buffer[kBufferSize];
int offset = 0;
const int length = string->length();
DCHECK_NE(0, length);
// First iteration, check for minus, 0 followed by anything else, etc.
int to = std::min(offset + kUint32MaxChars, length);
{
String::WriteToFlat(string, buffer, offset, to);
bool negative = false;
if (buffer[offset] == '-') {
negative = true;
++offset;
if (offset == to) return false; // Just '-' is bad.
}
if (buffer[offset] == '0') {
return to == 2 && negative; // Match just '-0'.
}
// Process positive integers.
if (!negative) {
uint64_t acc = 0;
for (; offset < to; ++offset) {
uint64_t digit = buffer[offset] - '0';
if (digit > 9) return false;
acc = 10 * acc + digit;
}
// String is consumed. Evaluate what we have.
if (offset == length) {
return acc >
static_cast<uint64_t>(std::numeric_limits<uint32_t>::max());
}
}
}
// Consume rest of string. If we get here, we're way out of uint32_t bounds
// or negative.
int i = offset;
while (true) {
for (; offset < to; ++offset, ++i) {
if (!IsDecimalDigit(buffer[i])) return false;
}
if (offset == length) break;
// Read next chunk.
to = std::min(offset + kBufferSize, length);
String::WriteToFlat(string, buffer, offset, to);
i = 0;
}
return true;
}
} } // namespace v8::internal

View File

@ -236,6 +236,8 @@ inline size_t NumberToSize(Isolate* isolate,
return result;
}
bool IsNonArrayIndexInteger(String* string);
} } // namespace v8::internal
#endif // V8_CONVERSIONS_H_

View File

@ -5266,6 +5266,7 @@ HOptimizedGraphBuilder::LookupGlobalProperty(Variable* var, LookupIterator* it,
case LookupIterator::ACCESSOR:
case LookupIterator::ACCESS_CHECK:
case LookupIterator::INTERCEPTOR:
case LookupIterator::INTEGER_INDEXED_EXOTIC:
case LookupIterator::NOT_FOUND:
return kUseGeneric;
case LookupIterator::DATA:
@ -6071,6 +6072,12 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() {
}
bool HOptimizedGraphBuilder::PropertyAccessInfo::IsIntegerIndexedExotic() {
InstanceType instance_type = map_->instance_type();
return instance_type == JS_TYPED_ARRAY_TYPE && IsNonArrayIndexInteger(*name_);
}
bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessMonomorphic() {
if (!CanInlinePropertyAccess(map_)) return false;
if (IsJSObjectFieldAccessor()) return IsLoad();
@ -6080,6 +6087,7 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessMonomorphic() {
}
if (!LookupDescriptor()) return false;
if (IsFound()) return IsLoad() || !IsReadOnly();
if (IsIntegerIndexedExotic()) return false;
if (!LookupInPrototypes()) return false;
if (IsLoad()) return true;

View File

@ -2641,6 +2641,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
void LoadFieldMaps(Handle<Map> map);
bool LookupDescriptor();
bool LookupInPrototypes();
bool IsIntegerIndexedExotic();
bool IsCompatible(PropertyAccessInfo* other);
void GeneralizeRepresentation(Representation r) {

View File

@ -280,6 +280,7 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
case LookupIterator::INTEGER_INDEXED_EXOTIC:
break;
case LookupIterator::DATA:
inline_followup =
@ -346,6 +347,7 @@ void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
case LookupIterator::INTEGER_INDEXED_EXOTIC:
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::DATA: {

View File

@ -262,6 +262,7 @@ static void LookupForRead(LookupIterator* it) {
}
return;
case LookupIterator::ACCESSOR:
case LookupIterator::INTEGER_INDEXED_EXOTIC:
case LookupIterator::DATA:
return;
}
@ -1316,6 +1317,8 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
lookup->GetConstantIndex());
}
case LookupIterator::INTEGER_INDEXED_EXOTIC:
return slow_stub();
case LookupIterator::ACCESS_CHECK:
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
@ -1506,6 +1509,8 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
break;
case LookupIterator::ACCESSOR:
return !it->IsReadOnly();
case LookupIterator::INTEGER_INDEXED_EXOTIC:
return false;
case LookupIterator::DATA: {
if (it->IsReadOnly()) return false;
Handle<JSObject> holder = it->GetHolder<JSObject>();
@ -1530,7 +1535,6 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
}
}
if (it->IsSpecialNumericIndex()) return false;
it->PrepareTransitionToDataProperty(value, NONE, store_mode);
return it->IsCacheableTransition();
}
@ -1835,6 +1839,7 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
break;
}
case LookupIterator::INTEGER_INDEXED_EXOTIC:
case LookupIterator::ACCESS_CHECK:
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:

View File

@ -44,6 +44,10 @@ LookupIterator::State LookupIterator::LookupInHolder(Map* map,
}
// Fall through.
case ACCESS_CHECK:
if (exotic_index_state_ != ExoticIndexState::kNoIndex &&
IsIntegerIndexedExotic(holder)) {
return INTEGER_INDEXED_EXOTIC;
}
if (check_interceptor() && map->has_named_interceptor()) {
return INTERCEPTOR;
}
@ -75,6 +79,7 @@ LookupIterator::State LookupIterator::LookupInHolder(Map* map,
case ACCESSOR:
case DATA:
return NOT_FOUND;
case INTEGER_INDEXED_EXOTIC:
case JSPROXY:
case TRANSITION:
UNREACHABLE();

View File

@ -120,9 +120,9 @@ void LookupIterator::PrepareTransitionToDataProperty(
Handle<Object> value, PropertyAttributes attributes,
Object::StoreFromKeyed store_mode) {
if (state_ == TRANSITION) return;
DCHECK(state_ != LookupIterator::ACCESSOR);
DCHECK_NE(LookupIterator::ACCESSOR, state_);
DCHECK_NE(LookupIterator::INTEGER_INDEXED_EXOTIC, state_);
DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
DCHECK(!IsSpecialNumericIndex());
// Can only be called when the receiver is a JSObject. JSProxy has to be
// handled via a trap. Adding properties to primitive values is not
// observable.
@ -333,25 +333,23 @@ Handle<Object> LookupIterator::WriteDataValue(Handle<Object> value) {
}
bool LookupIterator::IsSpecialNumericIndex() const {
if (GetStoreTarget()->IsJSTypedArray() && name()->IsString()) {
bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
DCHECK(exotic_index_state_ != ExoticIndexState::kNoIndex);
// Currently typed arrays are the only such objects.
if (!holder->IsJSTypedArray()) return false;
if (exotic_index_state_ == ExoticIndexState::kIndex) return true;
DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
bool result = false;
// Compute and cache result.
if (name()->IsString()) {
Handle<String> name_string = Handle<String>::cast(name());
if (name_string->length() > 0) {
double d =
StringToDouble(isolate()->unicode_cache(), name_string, NO_FLAGS);
if (!std::isnan(d)) {
if (String::Equals(isolate()->factory()->minus_zero_string(),
name_string))
return true;
Factory* factory = isolate()->factory();
Handle<Object> num = factory->NewNumber(d);
Handle<String> roundtrip_string = factory->NumberToString(num);
if (String::Equals(name_string, roundtrip_string)) return true;
}
if (name_string->length() != 0) {
result = IsNonArrayIndexInteger(*name_string);
}
}
return false;
exotic_index_state_ =
result ? ExoticIndexState::kIndex : ExoticIndexState::kNoIndex;
return result;
}

View File

@ -31,6 +31,7 @@ class LookupIterator FINAL BASE_EMBEDDED {
enum State {
ACCESS_CHECK,
INTEGER_INDEXED_EXOTIC,
INTERCEPTOR,
JSPROXY,
NOT_FOUND,
@ -46,6 +47,7 @@ class LookupIterator FINAL BASE_EMBEDDED {
Configuration configuration = PROTOTYPE_CHAIN)
: configuration_(ComputeConfiguration(configuration, name)),
state_(NOT_FOUND),
exotic_index_state_(ExoticIndexState::kUninitialized),
property_details_(NONE, v8::internal::DATA, 0),
isolate_(name->GetIsolate()),
name_(name),
@ -61,6 +63,7 @@ class LookupIterator FINAL BASE_EMBEDDED {
Configuration configuration = PROTOTYPE_CHAIN)
: configuration_(ComputeConfiguration(configuration, name)),
state_(NOT_FOUND),
exotic_index_state_(ExoticIndexState::kUninitialized),
property_details_(NONE, v8::internal::DATA, 0),
isolate_(name->GetIsolate()),
name_(name),
@ -140,11 +143,6 @@ class LookupIterator FINAL BASE_EMBEDDED {
// Usually returns the value that was passed in, but may perform
// non-observable modifications on it, such as internalize strings.
Handle<Object> WriteDataValue(Handle<Object> value);
// Checks whether the receiver is an indexed exotic object
// and name is a special numeric index.
bool IsSpecialNumericIndex() const;
void InternalizeName();
private:
@ -185,11 +183,15 @@ class LookupIterator FINAL BASE_EMBEDDED {
}
}
enum class ExoticIndexState { kUninitialized, kNoIndex, kIndex };
bool IsIntegerIndexedExotic(JSReceiver* holder);
// If configuration_ becomes mutable, update
// HolderIsReceiverOrHiddenPrototype.
Configuration configuration_;
State state_;
bool has_property_;
ExoticIndexState exotic_index_state_;
PropertyDetails property_details_;
Isolate* isolate_;
Handle<Name> name_;

View File

@ -133,6 +133,8 @@ MaybeHandle<Object> Object::GetProperty(LookupIterator* it) {
return GetPropertyWithAccessor(it->GetReceiver(), it->name(),
it->GetHolder<JSObject>(),
it->GetAccessors());
case LookupIterator::INTEGER_INDEXED_EXOTIC:
return it->factory()->undefined_value();
case LookupIterator::DATA:
return it->GetDataValue();
}
@ -168,6 +170,8 @@ Handle<Object> JSObject::GetDataProperty(LookupIterator* it) {
// relevant.
it->NotFound();
return it->isolate()->factory()->undefined_value();
case LookupIterator::INTEGER_INDEXED_EXOTIC:
return it->isolate()->factory()->undefined_value();
case LookupIterator::DATA:
return it->GetDataValue();
}
@ -3130,6 +3134,10 @@ MaybeHandle<Object> Object::SetPropertyInternal(LookupIterator* it,
it->GetHolder<JSObject>(),
it->GetAccessors(), language_mode);
case LookupIterator::INTEGER_INDEXED_EXOTIC:
done = true;
break;
case LookupIterator::DATA:
if (it->property_details().IsReadOnly()) {
return WriteToReadOnlyProperty(it, value, language_mode);
@ -3192,6 +3200,9 @@ MaybeHandle<Object> Object::SetSuperProperty(LookupIterator* it,
return JSObject::AddDataProperty(&own_lookup, value, NONE, language_mode,
store_mode);
case LookupIterator::INTEGER_INDEXED_EXOTIC:
return result;
case LookupIterator::DATA: {
PropertyDetails details = own_lookup.property_details();
if (details.IsConfigurable() || !details.IsReadOnly()) {
@ -3321,16 +3332,14 @@ MaybeHandle<Object> Object::AddDataProperty(LookupIterator* it,
return WriteToReadOnlyProperty(it, value, language_mode);
}
if (it->state() == LookupIterator::INTEGER_INDEXED_EXOTIC) return value;
Handle<JSObject> receiver = it->GetStoreTarget();
// If the receiver is a JSGlobalProxy, store on the prototype (JSGlobalObject)
// instead. If the prototype is Null, the proxy is detached.
if (receiver->IsJSGlobalProxy()) return value;
// If the receiver is Indexed Exotic object (currently only typed arrays),
// disallow adding properties with numeric names.
if (it->IsSpecialNumericIndex()) return value;
// Possibly migrate to the most up-to-date map that will be able to store
// |value| under it->name() with |attributes|.
it->PrepareTransitionToDataProperty(value, attributes, store_mode);
@ -4158,6 +4167,7 @@ MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
!it.isolate()->IsInternallyUsedPropertyName(name);
for (; it.IsFound(); it.Next()) {
switch (it.state()) {
case LookupIterator::INTEGER_INDEXED_EXOTIC:
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
@ -4336,6 +4346,8 @@ Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes(
case LookupIterator::ACCESS_CHECK:
if (it->HasAccess()) break;
return JSObject::GetPropertyAttributesWithFailedAccessCheck(it);
case LookupIterator::INTEGER_INDEXED_EXOTIC:
return Just(ABSENT);
case LookupIterator::ACCESSOR:
case LookupIterator::DATA:
return Just(it->property_details().attributes());
@ -5337,6 +5349,8 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
if (it.isolate()->has_pending_exception()) return maybe_result;
break;
}
case LookupIterator::INTEGER_INDEXED_EXOTIC:
return it.isolate()->factory()->true_value();
case LookupIterator::DATA:
if (is_observed) {
old_value = it.GetDataValue();
@ -6764,6 +6778,8 @@ MaybeHandle<Object> JSObject::GetAccessor(Handle<JSObject> object,
case LookupIterator::JSPROXY:
return isolate->factory()->undefined_value();
case LookupIterator::INTEGER_INDEXED_EXOTIC:
return isolate->factory()->undefined_value();
case LookupIterator::DATA:
continue;
case LookupIterator::ACCESSOR: {

View File

@ -72,6 +72,7 @@ static Handle<Object> DebugGetProperty(LookupIterator* it,
case LookupIterator::ACCESS_CHECK:
// Ignore access checks.
break;
case LookupIterator::INTEGER_INDEXED_EXOTIC:
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY:
return it->isolate()->factory()->undefined_value();

View File

@ -362,3 +362,57 @@ TEST(BitField64) {
CHECK(x == MiddleBits::encode(3));
CHECK_EQ(3, MiddleBits::decode(x));
}
static void CheckNonArrayIndex(bool expected, const char* chars) {
auto isolate = CcTest::i_isolate();
auto string = isolate->factory()->NewStringFromAsciiChecked(chars);
CHECK_EQ(expected, IsNonArrayIndexInteger(*string));
}
TEST(NonArrayIndexParsing) {
auto isolate = CcTest::i_isolate();
HandleScope scope(isolate);
CheckNonArrayIndex(false, "-");
CheckNonArrayIndex(false, "0");
CheckNonArrayIndex(false, "01");
CheckNonArrayIndex(false, "-01");
CheckNonArrayIndex(false, "4294967295");
CheckNonArrayIndex(false, "429496.7295");
CheckNonArrayIndex(false, "43s3");
CheckNonArrayIndex(true, "-0");
CheckNonArrayIndex(true, "-1");
CheckNonArrayIndex(true, "4294967296");
CheckNonArrayIndex(true, "-4294967296");
CheckNonArrayIndex(
true,
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296");
CheckNonArrayIndex(
true,
"-429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296"
"429496729642949672964294967296429496729642949672964294967296");
}

View File

@ -0,0 +1,56 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
Object.prototype["10"] = "unreachable";
Object.prototype["7"] = "unreachable";
Object.prototype["-1"] = "unreachable";
Object.prototype["-0"] = "unreachable";
Object.prototype["4294967296"] = "unreachable";
var array = new Int32Array(10);
function check() {
for (var i = 0; i < 4; i++) {
assertEquals(undefined, array["-1"]);
assertEquals(undefined, array["-0"]);
assertEquals(undefined, array["10"]);
assertEquals(undefined, array["4294967296"]);
}
assertEquals("unreachable", array.__proto__["-1"]);
assertEquals("unreachable", array.__proto__["-0"]);
assertEquals("unreachable", array.__proto__["10"]);
assertEquals("unreachable", array.__proto__["4294967296"]);
}
check();
array["-1"] = "unreachable";
array["-0"] = "unreachable";
array["10"] = "unreachable";
array["4294967296"] = "unreachable";
check();
delete array["-0"];
delete array["-1"];
delete array["10"];
delete array["4294967296"];
assertEquals(undefined, Object.getOwnPropertyDescriptor(array, "-1"));
assertEquals(undefined, Object.getOwnPropertyDescriptor(array, "-0"));
assertEquals(undefined, Object.getOwnPropertyDescriptor(array, "10"));
assertEquals(undefined, Object.getOwnPropertyDescriptor(array, "4294967296"));
assertEquals(10, Object.keys(array).length);
check();
function f() { return array["-1"]; }
for (var i = 0; i < 3; i++) {
assertEquals(undefined, f());
}
%OptimizeFunctionOnNextCall(f);
assertEquals(undefined, f());

View File

@ -530,7 +530,7 @@ function TestTypedArraysWithIllegalIndices() {
* assertEquals(undefined, a[-Infinity]);
*/
a[1.5] = 10;
assertEquals(undefined, a[1.5]);
assertEquals(10, a[1.5]);
var nan = Math.sqrt(-1);
a[nan] = 5;
assertEquals(5, a[nan]);
@ -579,7 +579,7 @@ function TestTypedArraysWithIllegalIndicesStrict() {
* assertEquals(undefined, a[-Infinity]);
*/
a[1.5] = 10;
assertEquals(undefined, a[1.5]);
assertEquals(10, a[1.5]);
var nan = Math.sqrt(-1);
a[nan] = 5;
assertEquals(5, a[nan]);