Fix redefining of attributes on aliased arguments.

This allows elements of the non-strict arguments object to be redefined
with custom attributes and still maintain an alias into the context.
Such a slow alias is maintained by placing a special marker into the
dictionary backing store of the arguments object.

R=rossberg@chromium.org
BUG=v8:1772
TEST=test262,mjsunit/object-define-property

Review URL: https://chromiumcodereview.appspot.com/9460004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10827 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 4a288089
...@@ -3850,7 +3850,7 @@ class Internals { ...@@ -3850,7 +3850,7 @@ class Internals {
static const int kFullStringRepresentationMask = 0x07; static const int kFullStringRepresentationMask = 0x07;
static const int kExternalTwoByteRepresentationTag = 0x02; static const int kExternalTwoByteRepresentationTag = 0x02;
static const int kJSObjectType = 0xa8; static const int kJSObjectType = 0xa9;
static const int kFirstNonstringType = 0x80; static const int kFirstNonstringType = 0x80;
static const int kForeignType = 0x85; static const int kForeignType = 0x85;
......
...@@ -705,10 +705,20 @@ class NonStrictArgumentsElementsAccessor ...@@ -705,10 +705,20 @@ class NonStrictArgumentsElementsAccessor
} else { } else {
// Object is not mapped, defer to the arguments. // Object is not mapped, defer to the arguments.
FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
return ElementsAccessor::ForArray(arguments)->Get(arguments, MaybeObject* maybe_result = ElementsAccessor::ForArray(arguments)->Get(
key, arguments, key, obj, receiver);
obj, Object* result;
receiver); if (!maybe_result->ToObject(&result)) return maybe_result;
// Elements of the arguments object in slow mode might be slow aliases.
if (result->IsAliasedArgumentsEntry()) {
AliasedArgumentsEntry* entry = AliasedArgumentsEntry::cast(result);
Context* context = Context::cast(parameter_map->get(0));
int context_index = entry->aliased_context_slot();
ASSERT(!context->get(context_index)->IsTheHole());
return context->get(context_index);
} else {
return result;
}
} }
} }
......
...@@ -1951,6 +1951,16 @@ MaybeObject* Heap::AllocateTypeFeedbackInfo() { ...@@ -1951,6 +1951,16 @@ MaybeObject* Heap::AllocateTypeFeedbackInfo() {
} }
MaybeObject* Heap::AllocateAliasedArgumentsEntry(int aliased_context_slot) {
AliasedArgumentsEntry* entry;
{ MaybeObject* maybe_result = AllocateStruct(ALIASED_ARGUMENTS_ENTRY_TYPE);
if (!maybe_result->To(&entry)) return maybe_result;
}
entry->set_aliased_context_slot(aliased_context_slot);
return entry;
}
const Heap::StringTypeTable Heap::string_type_table[] = { const Heap::StringTypeTable Heap::string_type_table[] = {
#define STRING_TYPE_ELEMENT(type, size, name, camel_name) \ #define STRING_TYPE_ELEMENT(type, size, name, camel_name) \
{type, size, k##camel_name##MapRootIndex}, {type, size, k##camel_name##MapRootIndex},
......
...@@ -641,6 +641,9 @@ class Heap { ...@@ -641,6 +641,9 @@ class Heap {
// Allocates an empty TypeFeedbackInfo. // Allocates an empty TypeFeedbackInfo.
MUST_USE_RESULT MaybeObject* AllocateTypeFeedbackInfo(); MUST_USE_RESULT MaybeObject* AllocateTypeFeedbackInfo();
// Allocates an AliasedArgumentsEntry.
MUST_USE_RESULT MaybeObject* AllocateAliasedArgumentsEntry(int slot);
// Clear the Instanceof cache (used when a prototype changes). // Clear the Instanceof cache (used when a prototype changes).
inline void ClearInstanceofCache(); inline void ClearInstanceofCache();
......
...@@ -333,6 +333,11 @@ void TypeFeedbackInfo::TypeFeedbackInfoVerify() { ...@@ -333,6 +333,11 @@ void TypeFeedbackInfo::TypeFeedbackInfoVerify() {
} }
void AliasedArgumentsEntry::AliasedArgumentsEntryVerify() {
VerifySmiField(kAliasedContextSlot);
}
void FixedArray::FixedArrayVerify() { void FixedArray::FixedArrayVerify() {
for (int i = 0; i < length(); i++) { for (int i = 0; i < length(); i++) {
Object* e = get(i); Object* e = get(i);
......
...@@ -4806,6 +4806,9 @@ ACCESSORS(TypeFeedbackInfo, type_feedback_cells, TypeFeedbackCells, ...@@ -4806,6 +4806,9 @@ ACCESSORS(TypeFeedbackInfo, type_feedback_cells, TypeFeedbackCells,
kTypeFeedbackCellsOffset) kTypeFeedbackCellsOffset)
SMI_ACCESSORS(AliasedArgumentsEntry, aliased_context_slot, kAliasedContextSlot)
Relocatable::Relocatable(Isolate* isolate) { Relocatable::Relocatable(Isolate* isolate) {
ASSERT(isolate == Isolate::Current()); ASSERT(isolate == Isolate::Current());
isolate_ = isolate; isolate_ = isolate;
......
...@@ -563,6 +563,12 @@ void TypeFeedbackInfo::TypeFeedbackInfoPrint(FILE* out) { ...@@ -563,6 +563,12 @@ void TypeFeedbackInfo::TypeFeedbackInfoPrint(FILE* out) {
} }
void AliasedArgumentsEntry::AliasedArgumentsEntryPrint(FILE* out) {
HeapObject::PrintHeader(out, "AliasedArgumentsEntry");
PrintF(out, "\n - aliased_context_slot: %d", aliased_context_slot());
}
void FixedArray::FixedArrayPrint(FILE* out) { void FixedArray::FixedArrayPrint(FILE* out) {
HeapObject::PrintHeader(out, "FixedArray"); HeapObject::PrintHeader(out, "FixedArray");
PrintF(out, " - length: %d", length()); PrintF(out, " - length: %d", length());
......
...@@ -9559,19 +9559,31 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, ...@@ -9559,19 +9559,31 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
// value is being defined we skip attribute checks completely. // value is being defined we skip attribute checks completely.
if (set_mode == DEFINE_PROPERTY) { if (set_mode == DEFINE_PROPERTY) {
details = PropertyDetails(attributes, NORMAL, details.index()); details = PropertyDetails(attributes, NORMAL, details.index());
dictionary->ValueAtPut(entry, value);
dictionary->DetailsAtPut(entry, details); dictionary->DetailsAtPut(entry, details);
} else if (!details.IsReadOnly() || element->IsTheHole()) { } else if (details.IsReadOnly() && !element->IsTheHole()) {
dictionary->ValueAtPut(entry, value); if (strict_mode == kNonStrictMode) {
} else if (strict_mode == kStrictMode) { return isolate->heap()->undefined_value();
Handle<Object> holder(this); } else {
Handle<Object> number = isolate->factory()->NewNumberFromUint(index); Handle<Object> holder(this);
Handle<Object> args[2] = { number, holder }; Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
Handle<Object> error = Handle<Object> args[2] = { number, holder };
isolate->factory()->NewTypeError("strict_read_only_property", Handle<Object> error =
HandleVector(args, 2)); isolate->factory()->NewTypeError("strict_read_only_property",
return isolate->Throw(*error); HandleVector(args, 2));
return isolate->Throw(*error);
}
} }
// Elements of the arguments object in slow mode might be slow aliases.
if (is_arguments && element->IsAliasedArgumentsEntry()) {
AliasedArgumentsEntry* entry = AliasedArgumentsEntry::cast(element);
Context* context = Context::cast(elements->get(0));
int context_index = entry->aliased_context_slot();
ASSERT(!context->get(context_index)->IsTheHole());
context->set(context_index, value);
// For elements that are still writable we keep slow aliasing.
if (!details.IsReadOnly()) value = element;
}
dictionary->ValueAtPut(entry, value);
} }
} else { } else {
// Index not already used. Look for an accessor in the prototype chain. // Index not already used. Look for an accessor in the prototype chain.
...@@ -9927,17 +9939,23 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, ...@@ -9927,17 +9939,23 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
int context_index = Smi::cast(probe)->value(); int context_index = Smi::cast(probe)->value();
ASSERT(!context->get(context_index)->IsTheHole()); ASSERT(!context->get(context_index)->IsTheHole());
context->set(context_index, value); context->set(context_index, value);
return value; // Redefining attributes of an aliased element destroys fast aliasing.
} else { if (set_mode == SET_PROPERTY || attr == NONE) return value;
// Object is not mapped, defer to the arguments. parameter_map->set_the_hole(index + 2);
FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); // For elements that are still writable we re-establish slow aliasing.
if (arguments->IsDictionary()) { if ((attr & READ_ONLY) == 0) {
return SetDictionaryElement(index, value, attr, strict_mode, MaybeObject* maybe_entry =
check_prototype, set_mode); isolate->heap()->AllocateAliasedArgumentsEntry(context_index);
} else { if (!maybe_entry->ToObject(&value)) return maybe_entry;
return SetFastElement(index, value, strict_mode, check_prototype);
} }
} }
FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
if (arguments->IsDictionary()) {
return SetDictionaryElement(index, value, attr, strict_mode,
check_prototype, set_mode);
} else {
return SetFastElement(index, value, strict_mode, check_prototype);
}
} }
} }
// All possible cases have been handled above. Add a return to avoid the // All possible cases have been handled above. Add a return to avoid the
......
...@@ -440,7 +440,8 @@ const int kVariableSizeSentinel = 0; ...@@ -440,7 +440,8 @@ const int kVariableSizeSentinel = 0;
V(SCRIPT, Script, script) \ V(SCRIPT, Script, script) \
V(CODE_CACHE, CodeCache, code_cache) \ V(CODE_CACHE, CodeCache, code_cache) \
V(POLYMORPHIC_CODE_CACHE, PolymorphicCodeCache, polymorphic_code_cache) \ V(POLYMORPHIC_CODE_CACHE, PolymorphicCodeCache, polymorphic_code_cache) \
V(TYPE_FEEDBACK_INFO, TypeFeedbackInfo, type_feedback_info) V(TYPE_FEEDBACK_INFO, TypeFeedbackInfo, type_feedback_info) \
V(ALIASED_ARGUMENTS_ENTRY, AliasedArgumentsEntry, aliased_arguments_entry)
#ifdef ENABLE_DEBUGGER_SUPPORT #ifdef ENABLE_DEBUGGER_SUPPORT
#define STRUCT_LIST_DEBUGGER(V) \ #define STRUCT_LIST_DEBUGGER(V) \
...@@ -596,6 +597,7 @@ enum InstanceType { ...@@ -596,6 +597,7 @@ enum InstanceType {
CODE_CACHE_TYPE, CODE_CACHE_TYPE,
POLYMORPHIC_CODE_CACHE_TYPE, POLYMORPHIC_CODE_CACHE_TYPE,
TYPE_FEEDBACK_INFO_TYPE, TYPE_FEEDBACK_INFO_TYPE,
ALIASED_ARGUMENTS_ENTRY_TYPE,
// The following two instance types are only used when ENABLE_DEBUGGER_SUPPORT // The following two instance types are only used when ENABLE_DEBUGGER_SUPPORT
// is defined. However as include/v8.h contain some of the instance type // is defined. However as include/v8.h contain some of the instance type
// constants always having them avoids them getting different numbers // constants always having them avoids them getting different numbers
...@@ -6435,6 +6437,39 @@ class TypeFeedbackInfo: public Struct { ...@@ -6435,6 +6437,39 @@ class TypeFeedbackInfo: public Struct {
}; };
// Representation of a slow alias as part of a non-strict arguments objects.
// For fast aliases (if HasNonStrictArgumentsElements()):
// - the parameter map contains an index into the context
// - all attributes of the element have default values
// For slow aliases (if HasDictionaryArgumentsElements()):
// - the parameter map contains no fast alias mapping (i.e. the hole)
// - this struct (in the slow backing store) contains an index into the context
// - all attributes are available as part if the property details
class AliasedArgumentsEntry: public Struct {
public:
inline int aliased_context_slot();
inline void set_aliased_context_slot(int count);
static inline AliasedArgumentsEntry* cast(Object* obj);
#ifdef OBJECT_PRINT
inline void AliasedArgumentsEntryPrint() {
AliasedArgumentsEntryPrint(stdout);
}
void AliasedArgumentsEntryPrint(FILE* out);
#endif
#ifdef DEBUG
void AliasedArgumentsEntryVerify();
#endif
static const int kAliasedContextSlot = HeapObject::kHeaderSize;
static const int kSize = kAliasedContextSlot + kPointerSize;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(AliasedArgumentsEntry);
};
enum AllowNullsFlag {ALLOW_NULLS, DISALLOW_NULLS}; enum AllowNullsFlag {ALLOW_NULLS, DISALLOW_NULLS};
enum RobustnessFlag {ROBUST_STRING_TRAVERSAL, FAST_STRING_TRAVERSAL}; enum RobustnessFlag {ROBUST_STRING_TRAVERSAL, FAST_STRING_TRAVERSAL};
......
...@@ -1053,4 +1053,25 @@ for (var i = 0; i < 1000; i++) { ...@@ -1053,4 +1053,25 @@ for (var i = 0; i < 1000; i++) {
// Non-enumerable property forces dictionary mode. // Non-enumerable property forces dictionary mode.
Object.defineProperty(o, i, {value: i, enumerable: false}); Object.defineProperty(o, i, {value: i, enumerable: false});
} }
assertEquals(999, o[999]); assertEquals(999, o[999]);
\ No newline at end of file
// Regression test: Bizzare behavior on non-strict arguments object.
(function test(arg0) {
// Here arguments[0] is a fast alias on arg0.
Object.defineProperty(arguments, "0", {
value:1,
enumerable:false
});
// Here arguments[0] is a slow alias on arg0.
Object.defineProperty(arguments, "0", {
value:2,
writable:false
});
// Here arguments[0] is no alias at all.
Object.defineProperty(arguments, "0", {
value:3
});
assertEquals(2, arg0);
assertEquals(3, arguments[0]);
})(0);
...@@ -42,19 +42,6 @@ S10.4.2.1_A1: FAIL ...@@ -42,19 +42,6 @@ S10.4.2.1_A1: FAIL
15.2.3.6-4-415: FAIL 15.2.3.6-4-415: FAIL
15.2.3.6-4-420: FAIL 15.2.3.6-4-420: FAIL
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1772
15.2.3.6-4-292-1: FAIL
15.2.3.6-4-293-2: FAIL
15.2.3.6-4-293-3: FAIL
15.2.3.6-4-294-1: FAIL
15.2.3.6-4-295-1: FAIL
15.2.3.6-4-296-1: FAIL
15.2.3.7-6-a-281: FAIL
15.2.3.7-6-a-282: FAIL
15.2.3.7-6-a-283: FAIL
15.2.3.7-6-a-284: FAIL
15.2.3.7-6-a-285: FAIL
##################### DELIBERATE INCOMPATIBILITIES ##################### ##################### DELIBERATE INCOMPATIBILITIES #####################
# We deliberately treat arguments to parseInt() with a leading zero as # We deliberately treat arguments to parseInt() with a leading zero as
......
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