Make the runtime entry for setting/changing accessors "atomic".

Previously, there were 1 or 2 calls to the runtime when accessors were changed
or set. This doesn't really work well with property attributes, leading to some
hacks and complicates things even further when trying to share maps in presence
of accessors. Therefore, the runtime entry now takes the full triple (getter,
setter, attributes), where the getter and/or the setter can be null in case they
shouldn't be changed.

For now, we do basically the same on the native side as we did before on the
JavaScript side, but this will change in future CLs, the current CL is already
large enough.

Note that object literals with a getter and a setter for the same property still
do 2 calls, but this is a little bit more tricky to fix and will be handled in a
separate CL.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10956 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
svenpanne@chromium.org 2012-03-07 13:24:44 +00:00
parent b732b2e32a
commit 1729e3c0dd
10 changed files with 124 additions and 110 deletions

View File

@ -1498,11 +1498,15 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
__ ldr(r0, MemOperand(sp)); __ ldr(r0, MemOperand(sp));
__ push(r0); __ push(r0);
VisitForStackValue(key); VisitForStackValue(key);
__ mov(r1, Operand(property->kind() == ObjectLiteral::Property::SETTER ? if (property->kind() == ObjectLiteral::Property::GETTER) {
Smi::FromInt(1) : VisitForStackValue(value);
Smi::FromInt(0))); __ LoadRoot(r1, Heap::kNullValueRootIndex);
__ push(r1); __ push(r1);
VisitForStackValue(value); } else {
__ LoadRoot(r1, Heap::kNullValueRootIndex);
__ push(r1);
VisitForStackValue(value);
}
__ mov(r0, Operand(Smi::FromInt(NONE))); __ mov(r0, Operand(Smi::FromInt(NONE)));
__ push(r0); __ push(r0);
__ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5); __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);

View File

@ -1519,10 +1519,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
case ObjectLiteral::Property::GETTER: case ObjectLiteral::Property::GETTER:
__ push(Operand(esp, 0)); // Duplicate receiver. __ push(Operand(esp, 0)); // Duplicate receiver.
VisitForStackValue(key); VisitForStackValue(key);
__ push(Immediate(property->kind() == ObjectLiteral::Property::SETTER ? if (property->kind() == ObjectLiteral::Property::GETTER) {
Smi::FromInt(1) : VisitForStackValue(value);
Smi::FromInt(0))); __ push(Immediate(isolate()->factory()->null_value()));
VisitForStackValue(value); } else {
__ push(Immediate(isolate()->factory()->null_value()));
VisitForStackValue(value);
}
__ push(Immediate(Smi::FromInt(NONE))); __ push(Immediate(Smi::FromInt(NONE)));
__ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5); __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
break; break;

View File

@ -1,4 +1,4 @@
// Copyright 2011 the V8 project authors. All rights reserved. // Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
@ -769,8 +769,7 @@ function DefineOneShotAccessor(obj, name, fun) {
hasBeenSet = true; hasBeenSet = true;
value = v; value = v;
}; };
%DefineOrRedefineAccessorProperty(obj, name, GETTER, getter, DONT_ENUM); %DefineOrRedefineAccessorProperty(obj, name, getter, setter, DONT_ENUM);
%DefineOrRedefineAccessorProperty(obj, name, SETTER, setter, DONT_ENUM);
} }
function CallSite(receiver, fun, pos) { function CallSite(receiver, fun, pos) {

View File

@ -1500,11 +1500,15 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
__ lw(a0, MemOperand(sp)); __ lw(a0, MemOperand(sp));
__ push(a0); __ push(a0);
VisitForStackValue(key); VisitForStackValue(key);
__ li(a1, Operand(property->kind() == ObjectLiteral::Property::SETTER ? if (property->kind() == ObjectLiteral::Property::GETTER) {
Smi::FromInt(1) : VisitForStackValue(value);
Smi::FromInt(0))); __ LoadRoot(a1, Heap::kNullValueRootIndex);
__ push(a1); __ push(a1);
VisitForStackValue(value); } else {
__ LoadRoot(a1, Heap::kNullValueRootIndex);
__ push(a1);
VisitForStackValue(value);
}
__ li(a0, Operand(Smi::FromInt(NONE))); __ li(a0, Operand(Smi::FromInt(NONE)));
__ push(a0); __ push(a0);
__ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5); __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);

View File

@ -854,6 +854,8 @@ class JSReceiver;
class Object : public MaybeObject { class Object : public MaybeObject {
public: public:
// Type testing. // Type testing.
bool IsObject() { return true; }
#define IS_TYPE_FUNCTION_DECL(type_) inline bool Is##type_(); #define IS_TYPE_FUNCTION_DECL(type_) inline bool Is##type_();
OBJECT_TYPE_LIST(IS_TYPE_FUNCTION_DECL) OBJECT_TYPE_LIST(IS_TYPE_FUNCTION_DECL)
HEAP_OBJECT_TYPE_LIST(IS_TYPE_FUNCTION_DECL) HEAP_OBJECT_TYPE_LIST(IS_TYPE_FUNCTION_DECL)

View File

@ -1,4 +1,4 @@
// Copyright 2006-2009 the V8 project authors. All rights reserved. // Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
@ -421,18 +421,12 @@ function SetUpRegExp() {
LAST_INPUT(lastMatchInfo) = ToString(string); LAST_INPUT(lastMatchInfo) = ToString(string);
}; };
%DefineOrRedefineAccessorProperty($RegExp, 'input', GETTER, RegExpGetInput, %DefineOrRedefineAccessorProperty($RegExp, 'input', RegExpGetInput,
DONT_DELETE); RegExpSetInput, DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, 'input', SETTER, RegExpSetInput, %DefineOrRedefineAccessorProperty($RegExp, '$_', RegExpGetInput,
DONT_DELETE); RegExpSetInput, DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$_', GETTER, RegExpGetInput, %DefineOrRedefineAccessorProperty($RegExp, '$input', RegExpGetInput,
DONT_ENUM | DONT_DELETE); RegExpSetInput, DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$_', SETTER, RegExpSetInput,
DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$input', GETTER, RegExpGetInput,
DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$input', SETTER, RegExpSetInput,
DONT_ENUM | DONT_DELETE);
// The properties multiline and $* are aliases for each other. When this // The properties multiline and $* are aliases for each other. When this
// value is set in SpiderMonkey, the value it is set to is coerced to a // value is set in SpiderMonkey, the value it is set to is coerced to a
@ -446,13 +440,10 @@ function SetUpRegExp() {
var RegExpGetMultiline = function() { return multiline; }; var RegExpGetMultiline = function() { return multiline; };
var RegExpSetMultiline = function(flag) { multiline = flag ? true : false; }; var RegExpSetMultiline = function(flag) { multiline = flag ? true : false; };
%DefineOrRedefineAccessorProperty($RegExp, 'multiline', GETTER, %DefineOrRedefineAccessorProperty($RegExp, 'multiline', RegExpGetMultiline,
RegExpGetMultiline, DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, 'multiline', SETTER,
RegExpSetMultiline, DONT_DELETE); RegExpSetMultiline, DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$*', GETTER, RegExpGetMultiline, %DefineOrRedefineAccessorProperty($RegExp, '$*', RegExpGetMultiline,
DONT_ENUM | DONT_DELETE); RegExpSetMultiline,
%DefineOrRedefineAccessorProperty($RegExp, '$*', SETTER, RegExpSetMultiline,
DONT_ENUM | DONT_DELETE); DONT_ENUM | DONT_DELETE);
@ -460,44 +451,28 @@ function SetUpRegExp() {
// Static properties set by a successful match. // Static properties set by a successful match.
%DefineOrRedefineAccessorProperty($RegExp, 'lastMatch', GETTER, %DefineOrRedefineAccessorProperty($RegExp, 'lastMatch', RegExpGetLastMatch,
RegExpGetLastMatch, DONT_DELETE); NoOpSetter, DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, 'lastMatch', SETTER, NoOpSetter, %DefineOrRedefineAccessorProperty($RegExp, '$&', RegExpGetLastMatch,
NoOpSetter, DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, 'lastParen', RegExpGetLastParen,
NoOpSetter, DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$+', RegExpGetLastParen,
NoOpSetter, DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, 'leftContext',
RegExpGetLeftContext, NoOpSetter,
DONT_DELETE); DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$&', GETTER, RegExpGetLastMatch, %DefineOrRedefineAccessorProperty($RegExp, '$`', RegExpGetLeftContext,
DONT_ENUM | DONT_DELETE); NoOpSetter, DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$&', SETTER, NoOpSetter, %DefineOrRedefineAccessorProperty($RegExp, 'rightContext',
DONT_ENUM | DONT_DELETE); RegExpGetRightContext, NoOpSetter,
%DefineOrRedefineAccessorProperty($RegExp, 'lastParen', GETTER,
RegExpGetLastParen, DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, 'lastParen', SETTER, NoOpSetter,
DONT_DELETE); DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$+', GETTER, RegExpGetLastParen, %DefineOrRedefineAccessorProperty($RegExp, "$'", RegExpGetRightContext,
DONT_ENUM | DONT_DELETE); NoOpSetter, DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$+', SETTER, NoOpSetter,
DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, 'leftContext', GETTER,
RegExpGetLeftContext, DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, 'leftContext', SETTER, NoOpSetter,
DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$`', GETTER, RegExpGetLeftContext,
DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, '$`', SETTER, NoOpSetter,
DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, 'rightContext', GETTER,
RegExpGetRightContext, DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, 'rightContext', SETTER, NoOpSetter,
DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, "$'", GETTER,
RegExpGetRightContext,
DONT_ENUM | DONT_DELETE);
%DefineOrRedefineAccessorProperty($RegExp, "$'", SETTER, NoOpSetter,
DONT_ENUM | DONT_DELETE);
for (var i = 1; i < 10; ++i) { for (var i = 1; i < 10; ++i) {
%DefineOrRedefineAccessorProperty($RegExp, '$' + i, GETTER, %DefineOrRedefineAccessorProperty($RegExp, '$' + i,
RegExpMakeCaptureGetter(i), DONT_DELETE); RegExpMakeCaptureGetter(i), NoOpSetter,
%DefineOrRedefineAccessorProperty($RegExp, '$' + i, SETTER, NoOpSetter,
DONT_DELETE); DONT_DELETE);
} }
} }

View File

@ -995,23 +995,14 @@ enum PropertyDescriptorIndices {
DESCRIPTOR_SIZE DESCRIPTOR_SIZE
}; };
// Returns an array with the property description:
// if args[1] is not a property on args[0] static MaybeObject* GetOwnProperty(Isolate* isolate,
// returns undefined Handle<JSObject> obj,
// if args[1] is a data property on args[0] Handle<String> name) {
// [false, value, Writeable, Enumerable, Configurable]
// if args[1] is an accessor on args[0]
// [true, GetFunction, SetFunction, Enumerable, Configurable]
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) {
ASSERT(args.length() == 2);
Heap* heap = isolate->heap(); Heap* heap = isolate->heap();
HandleScope scope(isolate);
Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE); Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE);
Handle<JSArray> desc = isolate->factory()->NewJSArrayWithElements(elms); Handle<JSArray> desc = isolate->factory()->NewJSArrayWithElements(elms);
LookupResult result(isolate); LookupResult result(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
// This could be an element. // This could be an element.
uint32_t index; uint32_t index;
if (name->AsArrayIndex(&index)) { if (name->AsArrayIndex(&index)) {
@ -1145,6 +1136,22 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) {
} }
// Returns an array with the property description:
// if args[1] is not a property on args[0]
// returns undefined
// if args[1] is a data property on args[0]
// [false, value, Writeable, Enumerable, Configurable]
// if args[1] is an accessor on args[0]
// [true, GetFunction, SetFunction, Enumerable, Configurable]
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) {
ASSERT(args.length() == 2);
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
return GetOwnProperty(isolate, obj, name);
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_PreventExtensions) { RUNTIME_FUNCTION(MaybeObject*, Runtime_PreventExtensions) {
ASSERT(args.length() == 1); ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, obj, 0); CONVERT_ARG_CHECKED(JSObject, obj, 0);
@ -4307,6 +4314,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) {
args.at<Object>(1)); args.at<Object>(1));
} }
static bool IsValidAccessor(Handle<Object> obj) {
return obj->IsUndefined() || obj->IsSpecFunction() || obj->IsNull();
}
// Implements part of 8.12.9 DefineOwnProperty. // Implements part of 8.12.9 DefineOwnProperty.
// There are 3 cases that lead here: // There are 3 cases that lead here:
// Step 4b - define a new accessor property. // Step 4b - define a new accessor property.
@ -4317,18 +4330,37 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineAccessorProperty) {
ASSERT(args.length() == 5); ASSERT(args.length() == 5);
HandleScope scope(isolate); HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
CONVERT_ARG_CHECKED(String, name, 1); RUNTIME_ASSERT(!obj->IsNull());
CONVERT_SMI_ARG_CHECKED(flag, 2); CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
Object* fun = args[3]; CONVERT_ARG_HANDLE_CHECKED(Object, getter, 2);
RUNTIME_ASSERT(IsValidAccessor(getter));
CONVERT_ARG_HANDLE_CHECKED(Object, setter, 3);
RUNTIME_ASSERT(IsValidAccessor(setter));
CONVERT_SMI_ARG_CHECKED(unchecked, 4); CONVERT_SMI_ARG_CHECKED(unchecked, 4);
RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0); RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked); PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
RUNTIME_ASSERT(!obj->IsNull()); // TODO(svenpanne) Define getter/setter/attributes in a single step.
RUNTIME_ASSERT(fun->IsSpecFunction() || fun->IsUndefined()); if (getter->IsNull() && setter->IsNull()) {
AccessorComponent component = flag == 0 ? ACCESSOR_GETTER : ACCESSOR_SETTER; JSArray* array;
return obj->DefineAccessor(name, component, fun, attr); { MaybeObject* maybe_array = GetOwnProperty(isolate, obj, name);
if (!maybe_array->To(&array)) return maybe_array;
}
Object* current = FixedArray::cast(array->elements())->get(GETTER_INDEX);
getter = Handle<Object>(current, isolate);
}
if (!getter->IsNull()) {
MaybeObject* ok =
obj->DefineAccessor(*name, ACCESSOR_GETTER, *getter, attr);
if (ok->IsFailure()) return ok;
}
if (!setter->IsNull()) {
MaybeObject* ok =
obj->DefineAccessor(*name, ACCESSOR_SETTER, *setter, attr);
if (ok->IsFailure()) return ok;
}
return isolate->heap()->undefined_value();
} }
// Implements part of 8.12.9 DefineOwnProperty. // Implements part of 8.12.9 DefineOwnProperty.
@ -4342,9 +4374,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) {
HandleScope scope(isolate); HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, js_object, 0); CONVERT_ARG_HANDLE_CHECKED(JSObject, js_object, 0);
CONVERT_ARG_HANDLE_CHECKED(String, name, 1); CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
Handle<Object> obj_value = args.at<Object>(2); CONVERT_ARG_HANDLE_CHECKED(Object, obj_value, 2);
CONVERT_SMI_ARG_CHECKED(unchecked, 3); CONVERT_SMI_ARG_CHECKED(unchecked, 3);
RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0); RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked); PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);

View File

@ -1,4 +1,4 @@
// Copyright 2011 the V8 project authors. All rights reserved. // Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
@ -834,10 +834,6 @@ function DefineObjectProperty(obj, p, desc, should_throw) {
} }
%DefineOrRedefineDataProperty(obj, p, value, flag); %DefineOrRedefineDataProperty(obj, p, value, flag);
} else if (IsGenericDescriptor(desc)) {
// Step 12 - updating an existing accessor property with generic
// descriptor. Changing flags only.
%DefineOrRedefineAccessorProperty(obj, p, GETTER, current.getGet(), flag);
} else { } else {
// There are 3 cases that lead here: // There are 3 cases that lead here:
// Step 4b - defining a new accessor property. // Step 4b - defining a new accessor property.
@ -845,12 +841,9 @@ function DefineObjectProperty(obj, p, desc, should_throw) {
// property. // property.
// Step 12 - updating an existing accessor property with an accessor // Step 12 - updating an existing accessor property with an accessor
// descriptor. // descriptor.
if (desc.hasGetter()) { var getter = desc.hasGetter() ? desc.getGet() : null;
%DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag); var setter = desc.hasSetter() ? desc.getSet() : null;
} %DefineOrRedefineAccessorProperty(obj, p, getter, setter, flag);
if (desc.hasSetter()) {
%DefineOrRedefineAccessorProperty(obj, p, SETTER, desc.getSet(), flag);
}
} }
return true; return true;
} }

View File

@ -1459,10 +1459,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
case ObjectLiteral::Property::GETTER: case ObjectLiteral::Property::GETTER:
__ push(Operand(rsp, 0)); // Duplicate receiver. __ push(Operand(rsp, 0)); // Duplicate receiver.
VisitForStackValue(key); VisitForStackValue(key);
__ Push(property->kind() == ObjectLiteral::Property::SETTER ? if (property->kind() == ObjectLiteral::Property::GETTER) {
Smi::FromInt(1) : VisitForStackValue(value);
Smi::FromInt(0)); __ PushRoot(Heap::kNullValueRootIndex);
VisitForStackValue(value); } else {
__ PushRoot(Heap::kNullValueRootIndex);
VisitForStackValue(value);
}
__ Push(Smi::FromInt(NONE)); __ Push(Smi::FromInt(NONE));
__ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5); __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
break; break;

View File

@ -1,4 +1,4 @@
// Copyright 2010 the V8 project authors. All rights reserved. // Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
@ -503,7 +503,7 @@ try {
// Defining properties null should fail even when we have // Defining properties null should fail even when we have
// other allowed values // other allowed values
try { try {
%DefineOrRedefineAccessorProperty(null, 'foo', 0, func, 0); %DefineOrRedefineAccessorProperty(null, 'foo', func, null, 0);
} catch (e) { } catch (e) {
assertTrue(/illegal access/.test(e)); assertTrue(/illegal access/.test(e));
} }