Introduce v8::Object::CreateDataProperty

Also deprecate ForceSet

BUG=chromium:475206
R=adamk@chromium.org,verwaest@chromium.org
LOG=y

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

Cr-Commit-Position: refs/heads/master@{#28660}
This commit is contained in:
jochen 2015-05-27 08:03:28 -07:00 committed by Commit bot
parent 5450fc07ba
commit 2a058de88f
7 changed files with 250 additions and 12 deletions

View File

@ -2586,6 +2586,20 @@ class V8_EXPORT Object : public Value {
V8_WARN_UNUSED_RESULT Maybe<bool> Set(Local<Context> context, uint32_t index,
Local<Value> value);
// Implements CreateDataProperty (ECMA-262, 7.3.4).
//
// Defines a configurable, writable, enumerable property with the given value
// on the object unless the property already exists and is not configurable
// or the object is not extensible.
//
// Returns true on success.
V8_WARN_UNUSED_RESULT Maybe<bool> CreateDataProperty(Local<Context> context,
Local<Name> key,
Local<Value> value);
V8_WARN_UNUSED_RESULT Maybe<bool> CreateDataProperty(Local<Context> context,
uint32_t index,
Local<Value> value);
// Sets an own property on this object bypassing interceptors and
// overriding accessors or read-only properties.
//
@ -2594,12 +2608,13 @@ class V8_EXPORT Object : public Value {
// will only be returned if the interceptor doesn't return a value.
//
// Note also that this only works for named properties.
V8_DEPRECATE_SOON("Use maybe version",
V8_DEPRECATE_SOON("Use CreateDataProperty",
bool ForceSet(Handle<Value> key, Handle<Value> value,
PropertyAttribute attribs = None));
// TODO(dcarney): mark V8_WARN_UNUSED_RESULT
Maybe<bool> ForceSet(Local<Context> context, Local<Value> key,
Local<Value> value, PropertyAttribute attribs = None);
V8_DEPRECATE_SOON("Use CreateDataProperty",
Maybe<bool> ForceSet(Local<Context> context,
Local<Value> key, Local<Value> value,
PropertyAttribute attribs = None));
V8_DEPRECATE_SOON("Use maybe version", Local<Value> Get(Handle<Value> key));
V8_WARN_UNUSED_RESULT MaybeLocal<Value> Get(Local<Context> context,

View File

@ -3476,6 +3476,106 @@ bool v8::Object::Set(uint32_t index, v8::Handle<Value> value) {
}
Maybe<bool> v8::Object::CreateDataProperty(v8::Local<v8::Context> context,
v8::Local<Name> key,
v8::Local<Value> value) {
PREPARE_FOR_EXECUTION_PRIMITIVE(context, "v8::Object::CreateDataProperty()",
bool);
auto self = Utils::OpenHandle(this);
auto key_obj = Utils::OpenHandle(*key);
auto value_obj = Utils::OpenHandle(*value);
if (self->IsAccessCheckNeeded() && !isolate->MayAccess(self)) {
isolate->ReportFailedAccessCheck(self);
return Nothing<bool>();
}
if (!self->IsExtensible()) return Just(false);
uint32_t index = 0;
if (key_obj->AsArrayIndex(&index)) {
return CreateDataProperty(context, index, value);
}
// Special case for Array.length.
if (self->IsJSArray() &&
key->StrictEquals(Utils::ToLocal(isolate->factory()->length_string()))) {
// Length is not configurable, however, CreateDataProperty always attempts
// to create a configurable property, so we just fail here.
return Just(false);
}
i::LookupIterator it(self, key_obj, i::LookupIterator::OWN_SKIP_INTERCEPTOR);
if (it.IsFound() && it.state() == i::LookupIterator::ACCESS_CHECK) {
DCHECK(isolate->MayAccess(self));
it.Next();
}
if (it.state() == i::LookupIterator::DATA ||
it.state() == i::LookupIterator::ACCESSOR) {
if (!it.IsConfigurable()) return Just(false);
if (it.state() == i::LookupIterator::ACCESSOR) {
has_pending_exception = i::JSObject::SetOwnPropertyIgnoreAttributes(
self, key_obj, value_obj, NONE,
i::JSObject::DONT_FORCE_FIELD).is_null();
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(true);
}
}
has_pending_exception = i::Runtime::DefineObjectProperty(
self, key_obj, value_obj, NONE).is_null();
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(true);
}
Maybe<bool> v8::Object::CreateDataProperty(v8::Local<v8::Context> context,
uint32_t index,
v8::Local<Value> value) {
PREPARE_FOR_EXECUTION_PRIMITIVE(context, "v8::Object::CreateDataProperty()",
bool);
auto self = Utils::OpenHandle(this);
auto value_obj = Utils::OpenHandle(*value);
if (self->IsAccessCheckNeeded() && !isolate->MayAccess(self)) {
isolate->ReportFailedAccessCheck(self);
return Nothing<bool>();
}
if (!self->IsExtensible()) return Just(false);
if (self->IsJSArray()) {
size_t length =
i::NumberToSize(isolate, i::Handle<i::JSArray>::cast(self)->length());
if (index >= length) {
i::Handle<i::Object> args[] = {
self, isolate->factory()->Uint32ToString(index), value_obj};
i::Handle<i::Object> result;
has_pending_exception =
!CallV8HeapFunction(isolate, "$objectDefineArrayProperty",
isolate->factory()->undefined_value(),
arraysize(args), args).ToHandle(&result);
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(result->BooleanValue());
}
}
Maybe<PropertyAttributes> attributes =
i::JSReceiver::GetOwnElementAttribute(self, index);
if (attributes.IsJust() && attributes.FromJust() & DONT_DELETE) {
return Just(false);
}
has_pending_exception = i::Runtime::DefineObjectProperty(
self, isolate->factory()->Uint32ToString(index),
value_obj, NONE).is_null();
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(true);
}
Maybe<bool> v8::Object::ForceSet(v8::Local<v8::Context> context,
v8::Local<Value> key, v8::Local<Value> value,
v8::PropertyAttribute attribs) {

View File

@ -5726,6 +5726,17 @@ MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
}
bool JSObject::IsExtensible() {
if (IsJSGlobalProxy()) {
PrototypeIterator iter(GetIsolate(), this);
if (iter.IsAtEnd()) return false;
DCHECK(iter.GetCurrent()->IsJSGlobalObject());
return JSObject::cast(iter.GetCurrent())->map()->is_extensible();
}
return map()->is_extensible();
}
Handle<SeededNumberDictionary> JSObject::GetNormalizedElementDictionary(
Handle<JSObject> object) {
DCHECK(!object->elements()->IsDictionary());

View File

@ -2155,10 +2155,12 @@ class JSObject: public JSReceiver {
// Check whether this object references another object
bool ReferencesObject(Object* obj);
// Disalow further properties to be added to the object.
// Disalow further properties to be added to the oject.
MUST_USE_RESULT static MaybeHandle<Object> PreventExtensions(
Handle<JSObject> object);
bool IsExtensible();
// ES5 Object.seal
MUST_USE_RESULT static MaybeHandle<Object> Seal(Handle<JSObject> object);

View File

@ -457,13 +457,7 @@ RUNTIME_FUNCTION(Runtime_IsExtensible) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, obj, 0);
if (obj->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate, obj);
if (iter.IsAtEnd()) return isolate->heap()->false_value();
DCHECK(iter.GetCurrent()->IsJSGlobalObject());
obj = JSObject::cast(iter.GetCurrent());
}
return isolate->heap()->ToBoolean(obj->map()->is_extensible());
return isolate->heap()->ToBoolean(obj->IsExtensible());
}

View File

@ -4,6 +4,7 @@
var $functionSourceString;
var $globalEval;
var $objectDefineArrayProperty;
var $objectGetOwnPropertyDescriptor;
var $toCompletePropertyDescriptor;
@ -895,6 +896,17 @@ function DefineArrayProperty(obj, p, desc, should_throw) {
}
function DefineArrayPropertyFromAPI(obj, p, value) {
return DefineArrayProperty(obj, p, ToPropertyDescriptor({
value: value,
configurable: true,
enumerable: true,
writable: true
}),
false);
}
// ES5 section 8.12.9, ES5 section 15.4.5.1 and Harmony proxies.
function DefineOwnProperty(obj, p, desc, should_throw) {
if (%_IsJSProxy(obj)) {
@ -1825,6 +1837,7 @@ function GetIterator(obj, method) {
$functionSourceString = FunctionSourceString;
$globalEval = GlobalEval;
$objectDefineArrayProperty = DefineArrayPropertyFromAPI;
$objectGetOwnPropertyDescriptor = ObjectGetOwnPropertyDescriptor;
$toCompletePropertyDescriptor = ToCompletePropertyDescriptor;

View File

@ -13305,6 +13305,109 @@ TEST(ForceSetWithInterceptor) {
}
TEST(CreateDataProperty) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope handle_scope(isolate);
CompileRun(
"var a = {};"
"var b = [];"
"Object.defineProperty(a, 'foo', {value: 23});"
"Object.defineProperty(a, 'bar', {value: 23, configurable: true});");
v8::Local<v8::Object> obj =
v8::Local<v8::Object>::Cast(env->Global()->Get(v8_str("a")));
v8::Local<v8::Array> arr =
v8::Local<v8::Array>::Cast(env->Global()->Get(v8_str("b")));
{
// Can't change a non-configurable properties.
v8::TryCatch try_catch(isolate);
CHECK(!obj->CreateDataProperty(env.local(), v8_str("foo"),
v8::Integer::New(isolate, 42)).FromJust());
CHECK(!try_catch.HasCaught());
CHECK(obj->CreateDataProperty(env.local(), v8_str("bar"),
v8::Integer::New(isolate, 42)).FromJust());
CHECK(!try_catch.HasCaught());
v8::Local<v8::Value> val =
obj->Get(env.local(), v8_str("bar")).ToLocalChecked();
CHECK(val->IsNumber());
CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust());
}
{
// Set a regular property.
v8::TryCatch try_catch(isolate);
CHECK(obj->CreateDataProperty(env.local(), v8_str("blub"),
v8::Integer::New(isolate, 42)).FromJust());
CHECK(!try_catch.HasCaught());
v8::Local<v8::Value> val =
obj->Get(env.local(), v8_str("blub")).ToLocalChecked();
CHECK(val->IsNumber());
CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust());
}
{
// Set an indexed property.
v8::TryCatch try_catch(isolate);
CHECK(obj->CreateDataProperty(env.local(), v8_str("1"),
v8::Integer::New(isolate, 42)).FromJust());
CHECK(!try_catch.HasCaught());
v8::Local<v8::Value> val = obj->Get(env.local(), 1).ToLocalChecked();
CHECK(val->IsNumber());
CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust());
}
{
// Special cases for arrays.
v8::TryCatch try_catch(isolate);
CHECK(!arr->CreateDataProperty(env.local(), v8_str("length"),
v8::Integer::New(isolate, 1)).FromJust());
CHECK(!try_catch.HasCaught());
}
{
// Special cases for arrays: index exceeds the array's length
v8::TryCatch try_catch(isolate);
CHECK(arr->CreateDataProperty(env.local(), 1, v8::Integer::New(isolate, 23))
.FromJust());
CHECK(!try_catch.HasCaught());
CHECK_EQ(2, arr->Length());
v8::Local<v8::Value> val = arr->Get(env.local(), 1).ToLocalChecked();
CHECK(val->IsNumber());
CHECK_EQ(23.0, val->NumberValue(env.local()).FromJust());
// Set an existing entry.
CHECK(arr->CreateDataProperty(env.local(), 0, v8::Integer::New(isolate, 42))
.FromJust());
CHECK(!try_catch.HasCaught());
val = arr->Get(env.local(), 0).ToLocalChecked();
CHECK(val->IsNumber());
CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust());
}
CompileRun("Object.freeze(a);");
{
// Can't change non-extensible objects.
v8::TryCatch try_catch(isolate);
CHECK(!obj->CreateDataProperty(env.local(), v8_str("baz"),
v8::Integer::New(isolate, 42)).FromJust());
CHECK(!try_catch.HasCaught());
}
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
templ->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL);
v8::Local<v8::Object> access_checked =
templ->NewInstance(env.local()).ToLocalChecked();
{
v8::TryCatch try_catch(isolate);
CHECK(access_checked->CreateDataProperty(env.local(), v8_str("foo"),
v8::Integer::New(isolate, 42))
.IsNothing());
CHECK(try_catch.HasCaught());
}
}
static v8::Local<Context> calling_context0;
static v8::Local<Context> calling_context1;
static v8::Local<Context> calling_context2;