jscript: Added support for accessor properties in defineProperty.

Signed-off-by: Jacek Caban <jacek@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Jacek Caban 2018-05-14 13:34:26 +02:00 committed by Alexandre Julliard
parent 5abb2ca6fc
commit f732bfc50e
2 changed files with 178 additions and 26 deletions

View file

@ -32,6 +32,7 @@ typedef enum {
PROP_JSVAL,
PROP_BUILTIN,
PROP_PROTREF,
PROP_ACCESSOR,
PROP_DELETED,
PROP_IDX
} prop_type_t;
@ -47,6 +48,10 @@ struct _dispex_prop_t {
const builtin_prop_t *p;
DWORD ref;
unsigned idx;
struct {
jsdisp_t *getter;
jsdisp_t *setter;
} accessor;
} u;
int bucket_head;
@ -413,6 +418,9 @@ static HRESULT invoke_prop_func(jsdisp_t *This, IDispatch *jsthis, dispex_prop_t
return disp_call_value(This->ctx, get_object(prop->u.val), jsthis, flags, argc, argv, r);
}
case PROP_ACCESSOR:
FIXME("accessor\n");
return E_NOTIMPL;
case PROP_IDX:
FIXME("Invoking PROP_IDX not yet supported\n");
return E_NOTIMPL;
@ -454,6 +462,10 @@ static HRESULT prop_get(jsdisp_t *This, dispex_prop_t *prop, jsval_t *r)
case PROP_JSVAL:
hres = jsval_copy(prop->u.val, r);
break;
case PROP_ACCESSOR:
FIXME("not supported on accessor property\n");
hres = E_NOTIMPL;
break;
case PROP_IDX:
hres = This->builtin_info->idx_get(This, prop->u.idx, r);
break;
@ -760,6 +772,8 @@ static HRESULT delete_prop(dispex_prop_t *prop, BOOL *ret)
jsval_release(prop->u.val);
prop->type = PROP_DELETED;
}
if(prop->type == PROP_ACCESSOR)
FIXME("not supported on accessor property\n");
return S_OK;
}
@ -961,8 +975,19 @@ void jsdisp_free(jsdisp_t *obj)
TRACE("(%p)\n", obj);
for(prop = obj->props; prop < obj->props+obj->prop_cnt; prop++) {
if(prop->type == PROP_JSVAL)
switch(prop->type) {
case PROP_JSVAL:
jsval_release(prop->u.val);
break;
case PROP_ACCESSOR:
if(prop->u.accessor.getter)
jsdisp_release(prop->u.accessor.getter);
if(prop->u.accessor.setter)
jsdisp_release(prop->u.accessor.setter);
break;
default:
break;
};
heap_free(prop->name);
}
heap_free(obj->props);
@ -1622,8 +1647,11 @@ HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t
if(prop->type == PROP_DELETED || prop->type == PROP_PROTREF) {
prop->flags = desc->flags;
if(desc->explicit_getter || desc->explicit_setter) {
FIXME("accessor property\n");
return E_NOTIMPL;
prop->type = PROP_ACCESSOR;
prop->u.accessor.getter = desc->getter ? jsdisp_addref(desc->getter) : NULL;
prop->u.accessor.setter = desc->setter ? jsdisp_addref(desc->setter) : NULL;
TRACE("%s = accessor { get: %p set: %p }\n", debugstr_w(name),
prop->u.accessor.getter, prop->u.accessor.setter);
}else {
prop->type = PROP_JSVAL;
if(desc->explicit_value) {
@ -1649,36 +1677,79 @@ HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t
}
if(desc->explicit_value || (desc->mask & PROPF_WRITABLE)) {
if(!(prop->flags & PROPF_CONFIGURABLE) && !(prop->flags & PROPF_WRITABLE)) {
if((desc->mask & PROPF_WRITABLE) && (desc->flags & PROPF_WRITABLE))
return throw_type_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
if(desc->explicit_value) {
if(prop->type == PROP_JSVAL) {
BOOL eq;
hres = jsval_strict_equal(desc->value, prop->u.val, &eq);
if(FAILED(hres))
return hres;
if(!eq)
return throw_type_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
}else {
FIXME("redefinition of property type %d\n", prop->type);
}
}
}
if(desc->explicit_value) {
if(prop->type == PROP_JSVAL)
jsval_release(prop->u.val);
else
prop->type = PROP_JSVAL;
if(prop->type == PROP_ACCESSOR) {
if(!(prop->flags & PROPF_CONFIGURABLE))
return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
if(prop->u.accessor.getter)
jsdisp_release(prop->u.accessor.getter);
if(prop->u.accessor.setter)
jsdisp_release(prop->u.accessor.setter);
prop->type = PROP_JSVAL;
hres = jsval_copy(desc->value, &prop->u.val);
if(FAILED(hres)) {
prop->u.val = jsval_undefined();
return hres;
}
}else {
if(!(prop->flags & PROPF_CONFIGURABLE) && !(prop->flags & PROPF_WRITABLE)) {
if((desc->mask & PROPF_WRITABLE) && (desc->flags & PROPF_WRITABLE))
return throw_type_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
if(desc->explicit_value) {
if(prop->type == PROP_JSVAL) {
BOOL eq;
hres = jsval_strict_equal(desc->value, prop->u.val, &eq);
if(FAILED(hres))
return hres;
if(!eq)
return throw_type_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
}else {
FIXME("redefinition of property type %d\n", prop->type);
}
}
}
if(desc->explicit_value) {
if(prop->type == PROP_JSVAL)
jsval_release(prop->u.val);
else
prop->type = PROP_JSVAL;
hres = jsval_copy(desc->value, &prop->u.val);
if(FAILED(hres)) {
prop->u.val = jsval_undefined();
return hres;
}
}
}
}else if(desc->explicit_getter || desc->explicit_setter) {
FIXME("accessor property\n");
return E_NOTIMPL;
if(prop->type != PROP_ACCESSOR) {
if(!(prop->flags & PROPF_CONFIGURABLE))
return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
if(prop->type == PROP_JSVAL)
jsval_release(prop->u.val);
prop->type = PROP_ACCESSOR;
prop->u.accessor.getter = prop->u.accessor.setter = NULL;
}else if(!(prop->flags & PROPF_CONFIGURABLE)) {
if((desc->explicit_getter && desc->getter != prop->u.accessor.getter)
|| (desc->explicit_setter && desc->setter != prop->u.accessor.setter))
return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
}
if(desc->explicit_getter) {
if(prop->u.accessor.getter) {
jsdisp_release(prop->u.accessor.getter);
prop->u.accessor.getter = NULL;
}
if(desc->getter)
prop->u.accessor.getter = jsdisp_addref(desc->getter);
}
if(desc->explicit_setter) {
if(prop->u.accessor.setter) {
jsdisp_release(prop->u.accessor.setter);
prop->u.accessor.setter = NULL;
}
if(desc->setter)
prop->u.accessor.setter = jsdisp_addref(desc->setter);
}
}
prop->flags = (prop->flags & ~desc->mask) | (desc->flags & desc->mask);

View file

@ -232,6 +232,17 @@ function test_defineProperty() {
test_own_data_prop_desc(obj, "all", false, true, true);
ok(obj.all === 1, "obj.test = " + obj.test);
var getsetprop_value = 1;
var desc = {
get: function() {
return getsetprop_value;
},
set: function(v) {
getsetprop_value = v;
}
};
Object.defineProperty(obj, "getsetprop", desc);
Object.defineProperty(obj, "notConf", {writable: true, enumerable: true, configurable: false, value: 1});
test_own_data_prop_desc(obj, "notConf", true, true, false);
@ -283,6 +294,59 @@ function test_defineProperty() {
Object.defineProperty(obj, "notConf2", {writable: false, value: 1});
test_own_data_prop_desc(obj, "notConf2", false, false, false);
desc = {
get: function() {
return getsetprop_value;
},
set: function(v) {
getsetprop_value = v;
},
configurable: false
};
Object.defineProperty(obj, "notConfAcc", desc);
expect_exception(function() {
Object.defineProperty(obj, "notConfAcc", {value: 1});
}, JS_E_NONCONFIGURABLE_REDEFINED);
expect_exception(function() {
Object.defineProperty(obj, "notConfAcc", {get: desc.get, set: function () {}});
}, JS_E_NONCONFIGURABLE_REDEFINED);
expect_exception(function() {
Object.defineProperty(obj, "notConfAcc", {get: undefined, set: desc.set});
}, JS_E_NONCONFIGURABLE_REDEFINED);
expect_exception(function() {
Object.defineProperty(obj, "notConfAcc", {writable: true});
}, JS_E_NONCONFIGURABLE_REDEFINED);
Object.defineProperty(obj, "notConfAcc", {get: desc.get});
Object.defineProperty(obj, "notConfAcc", {set: desc.set});
Object.defineProperty(obj, "notConfAcc", {configurable: false});
desc = {
get: function() {
return getsetprop_value;
},
set: function(v) {
getsetprop_value = v;
},
configurable: true
};
Object.defineProperty(obj, "confAcc", desc);
Object.defineProperty(obj, "confAcc", {writable: 1});
test_own_data_prop_desc(obj, "confAcc", true, false, true);
Object.defineProperty(obj, "confAcc", desc);
desc.get = function() {};
desc.set = undefined;
Object.defineProperty(obj, "confAcc", desc);
expect_exception(function() {
Object.defineProperty(obj, "invaliddesc", {get: undefined, value: 1});
}, JS_E_PROP_DESC_MISMATCH);
@ -291,6 +355,23 @@ function test_defineProperty() {
Object.defineProperty(obj, "invaliddesc", {set: undefined, writable: true});
}, JS_E_INVALID_WRITABLE_PROP_DESC);
function child() {}
desc = {
get: function() {
return getsetprop_value;
},
set: function(v) {
getsetprop_value = v;
},
configurable: true
};
Object.defineProperty(child.prototype, "parent_accessor", desc);
obj = new child();
ok(Object.getOwnPropertyDescriptor(obj, "parent_accessor") === undefined,
"getOwnPropertyDescriptor(parent_accessor) did not return undefined");
next_test();
}