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:
parent
5450fc07ba
commit
2a058de88f
23
include/v8.h
23
include/v8.h
@ -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,
|
||||
|
100
src/api.cc
100
src/api.cc
@ -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) {
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user