Commit 040225a3 authored by dcarney's avatar dcarney Committed by Commit bot

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}
parent fd012f10
......@@ -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
......@@ -236,6 +236,8 @@ inline size_t NumberToSize(Isolate* isolate,
return result;
}
bool IsNonArrayIndexInteger(String* string);
} } // namespace v8::internal
#endif // V8_CONVERSIONS_H_
......@@ -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;
......
......@@ -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) {
......
......@@ -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: {
......
......@@ -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:
......
......@@ -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();
......
......@@ -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;
}
......
......@@ -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_;
......
......@@ -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: {
......
......@@ -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();
......
......@@ -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");
}
// 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());
......@@ -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]);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment