Skip to content

Commit

Permalink
Tests for Proxy property descriptor behavior
Browse files Browse the repository at this point in the history
Also add property descriptor print helpers and change a few test cases
to use them.
  • Loading branch information
svaarala committed Nov 29, 2017
1 parent c115f75 commit 6fc448e
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 65 deletions.
75 changes: 27 additions & 48 deletions tests/ecmascript/test-bi-array-proto-reverse.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,6 @@
// XXX: shared test utils

function formatValue(v) {
if (typeof v === 'function') {
return 'function';
}
return typeof(v) + ':' + String(v);
}

function printDescriptor(obj, key) {
var pd = Object.getOwnPropertyDescriptor(obj, key);
if (!pd) {
print('key=' + key, 'nonexistent');
return;
}

print('key=' + key,
'value=' + formatValue(pd.value),
'writable=' + formatValue(pd.writable),
'enumerable=' + formatValue(pd.enumerable),
'configurable=' + formatValue(pd.configurable),
'get=' + formatValue(pd.get),
'set=' + formatValue(pd.set));
}
/*@include util-object.js@*/

function dumpValue(x) {
var i, n, clipped;
Expand Down Expand Up @@ -157,33 +136,33 @@ try {
/*===
attributes
true true false false false true false true
key=0 value=undefined:undefined writable=undefined:undefined enumerable=boolean:false configurable=boolean:true get=function set=function
key=1 value=undefined:undefined writable=undefined:undefined enumerable=boolean:false configurable=boolean:true get=function set=function
key=2 nonexistent
key=3 nonexistent
key=4 nonexistent
key=5 value=undefined:undefined writable=undefined:undefined enumerable=boolean:true configurable=boolean:true get=function set=function
key=6 nonexistent
key=7 value=undefined:undefined writable=undefined:undefined enumerable=boolean:true configurable=boolean:true get=function set=function
key: 0, desc: get=function,set=function,enumerable=false,configurable=true
key: 1, desc: get=function,set=function,enumerable=false,configurable=true
key: 2, desc: none
key: 3, desc: none
key: 4, desc: none
key: 5, desc: get=function,set=function,enumerable=true,configurable=true
key: 6, desc: none
key: 7, desc: get=function,set=function,enumerable=true,configurable=true
get 0
get 7
set 0 val7
set 7 val0
get 1
get 5
true false true false false false true true
key=0 value=undefined:undefined writable=undefined:undefined enumerable=boolean:false configurable=boolean:true get=function set=function
key=1 nonexistent
key=2 value=string:val5 writable=boolean:true enumerable=boolean:true configurable=boolean:true get=undefined:undefined set=undefined:undefined
key=3 nonexistent
key=4 nonexistent
key=5 nonexistent
key=6 value=string:val1 writable=boolean:true enumerable=boolean:true configurable=boolean:true get=undefined:undefined set=undefined:undefined
key=7 value=undefined:undefined writable=undefined:undefined enumerable=boolean:true configurable=boolean:true get=function set=function
key=0 value=string:foo writable=boolean:true enumerable=boolean:false configurable=boolean:true get=undefined:undefined set=undefined:undefined
key=1 nonexistent
key=0 value=string:foo writable=boolean:true enumerable=boolean:false configurable=boolean:true get=undefined:undefined set=undefined:undefined
key=1 nonexistent
key: 0, desc: get=function,set=function,enumerable=false,configurable=true
key: 1, desc: none
key: 2, desc: value="val5",writable=true,enumerable=true,configurable=true
key: 3, desc: none
key: 4, desc: none
key: 5, desc: none
key: 6, desc: value="val1",writable=true,enumerable=true,configurable=true
key: 7, desc: get=function,set=function,enumerable=true,configurable=true
key: 0, desc: value="foo",writable=true,enumerable=false,configurable=true
key: 1, desc: none
key: 0, desc: value="foo",writable=true,enumerable=false,configurable=true
key: 1, desc: none
===*/

print('attributes');
Expand Down Expand Up @@ -245,7 +224,7 @@ function attributesTest() {
t.hasOwnProperty(6), t.hasOwnProperty(7));

for (i = 0; i < 8; i++) {
printDescriptor(t, String(i));
printKeyPropDesc(t, String(i));
}

t.reverse();
Expand All @@ -256,7 +235,7 @@ function attributesTest() {
t.hasOwnProperty(6), t.hasOwnProperty(7));

for (i = 0; i < 8; i++) {
printDescriptor(t, String(i));
printKeyPropDesc(t, String(i));
}

// Here, '0' is not enumerable, but when it gets swapped to position 1,
Expand All @@ -269,11 +248,11 @@ function attributesTest() {
'0': { value: 'foo', writable: true, enumerable: false, configurable: true },
'length': { value: 2 }
});
printDescriptor(t, '0');
printDescriptor(t, '1');
printKeyPropDesc(t, '0');
printKeyPropDesc(t, '1');
[].reverse();
printDescriptor(t, '0');
printDescriptor(t, '1');
printKeyPropDesc(t, '0');
printKeyPropDesc(t, '1');
}

try {
Expand Down
40 changes: 40 additions & 0 deletions tests/ecmascript/test-bi-proxy-defineproperty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Proxy (ES2015) 'defineProperty'.
*/

/*@include util-object.js@*/

/*===
value=123,writable=false,enumerable=false,configurable=true
get=function,set=function,enumerable=false,configurable=true
===*/

function passThroughTest() {
var T = {};
var P = new Proxy(T, {});
var pd;

Object.defineProperty(P, 'foo', {
value: 123,
writable: false,
enumerable: false,
configurable: true
});
Object.defineProperty(P, 'bar', {
get: function getter() {},
set: function setter() {},
enumerable: false,
configurable: true
});

pd = Object.getOwnPropertyDescriptor(T, 'foo');
printPropDesc(pd);
pd = Object.getOwnPropertyDescriptor(T, 'bar');
printPropDesc(pd);
}

try {
passThroughTest();
} catch (e) {
print(e.stack || e);
}
80 changes: 80 additions & 0 deletions tests/ecmascript/test-bi-proxy-getownpropertydescriptor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Proxy (ES2015) 'getOwnPropertyDescriptor'.
*/

/*@include util-object.js@*/

/*===
value=123,writable=true,enumerable=false,configurable=true
get=function,enumerable=true,configurable=false
set=function,enumerable=false,configurable=false
get=function,set=function,enumerable=true,configurable=true
value=123,writable=true,enumerable=false,configurable=true
get=function,enumerable=true,configurable=false
set=function,enumerable=false,configurable=false
get=function,set=function,enumerable=true,configurable=true
===*/

function passThroughTest() {
var T = {};
var P = new Proxy(T, {});
var pd;

Object.defineProperty(T, 'foo', { value: 123, writable: true, enumerable: false, configurable: true });
Object.defineProperty(T, 'bar', { get: function getter() {}, enumerable: true, configurable: false });
Object.defineProperty(T, 'quux', { set: function setter() {}, enumerable: false, configurable: false });
Object.defineProperty(T, 'baz', { get: function getter() {}, set: function setter() {}, enumerable: true, configurable: true });

pd = Object.getOwnPropertyDescriptor(T, 'foo');
printPropDesc(pd);
pd = Object.getOwnPropertyDescriptor(T, 'bar');
printPropDesc(pd);
pd = Object.getOwnPropertyDescriptor(T, 'quux');
printPropDesc(pd);
pd = Object.getOwnPropertyDescriptor(T, 'baz');
printPropDesc(pd);

// As of Duktape 2.2 getOwnPropertyDescriptor() properly passes through
// to the target object.
pd = Object.getOwnPropertyDescriptor(P, 'foo');
printPropDesc(pd);
pd = Object.getOwnPropertyDescriptor(P, 'bar');
printPropDesc(pd);
pd = Object.getOwnPropertyDescriptor(P, 'quux');
printPropDesc(pd);
pd = Object.getOwnPropertyDescriptor(P, 'baz');
printPropDesc(pd);
}

try {
passThroughTest();
} catch (e) {
print(e.stack || e);
}

/*===
===*/

function miscTest() {
var O = { foo: 123 };
var P = new Proxy(O, {});

printPropDesc(Object.getOwnPropertyDescriptor(O, 'foo'));
printPropDesc(Object.getOwnPropertyDescriptor(P, 'foo'));

var O = { foo: 123 };
var P = new Proxy(O, {
getOwnPropertyDescriptor: function () {
return { value: 321, writable: false, enumerable: false, configurable: true };
}
});

printPropDesc(Object.getOwnPropertyDescriptor(O, 'foo'));
printPropDesc(Object.getOwnPropertyDescriptor(P, 'foo'));
}

try {
miscTest();
} catch (e) {
print(e.stack || e);
}
62 changes: 62 additions & 0 deletions tests/ecmascript/test-bi-proxy-virtualized-enum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Example of virtualizing enumeration using a Proxy.
*/

/*===
ownKeys trap: true
getOwnPropertyDescriptor trap: true true string 0
getOwnPropertyDescriptor trap: true true string 1
getOwnPropertyDescriptor trap: true true string 2
getOwnPropertyDescriptor trap: true true string 3
getOwnPropertyDescriptor trap: true true string 4
getOwnPropertyDescriptor trap: true true string noSuch
getOwnPropertyDescriptor trap: true true string length
0
1
2
3
4
noSuch
===*/

function test() {
var target = [ 'foo', 'bar', 'quux' ];
var handler = {
ownKeys: function () {
// The ownKeys result set is validated against the Proxy to ensure
// enumerated keys are enumerable; unless a getOwnPropertyDescriptor
// trap exists these checks will be made against the target which
// means non-existent properties won't be enumerated.§
print('ownKeys trap:', this === handler);
return [ '0', '1', '2', '3', '4', 'noSuch', 'length' ]
},
getOwnPropertyDescriptor: function (targ, key) {
// Cannot report 'length' as enumerable (that would violate
// getOwnPropertyDescriptor trap constraints). However,
// Duktape 2.2 (at least) won't enforce that constraint yet.
print('getOwnPropertyDescriptor trap:', this === handler, targ === target, typeof key, key);
if (key === 'length') {
return Object.getOwnPropertyDescriptor(target, key);
}
return {
value: target[key],
enumerable: true,
writable: true,
configurable: true
};
}
};
var proxy = new Proxy(target, handler);

// Without 'getOwnPropertyDescriptor' trap the only keys visible would be
// '0', '1', and '2' which backing in the target object.
for (var k in proxy) {
print(k);
}
}

try {
test();
} catch (e) {
print(e.stack || e);
}
32 changes: 15 additions & 17 deletions tests/ecmascript/test-dev-array-property-attrs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,29 @@
* and without an internal "array part".
*/

function printDesc(desc) {
print(desc.writable, desc.enumerable, desc.configurable);
}
/*@include util-object.js@*/

/*===
true true true
true true true
true true true
true true true
true true true
true true true
true true true
value=1,writable=true,enumerable=true,configurable=true
value=2,writable=true,enumerable=true,configurable=true
value=3,writable=true,enumerable=true,configurable=true
value=1,writable=true,enumerable=true,configurable=true
value=2,writable=true,enumerable=true,configurable=true
value=3,writable=true,enumerable=true,configurable=true
value=4,writable=true,enumerable=true,configurable=true
===*/

/* array is initially dense (array part exists) */
a = [1,2,3];

printDesc(Object.getOwnPropertyDescriptor(a, '0'));
printDesc(Object.getOwnPropertyDescriptor(a, '1'));
printDesc(Object.getOwnPropertyDescriptor(a, '2'));
printPropDesc(Object.getOwnPropertyDescriptor(a, '0'));
printPropDesc(Object.getOwnPropertyDescriptor(a, '1'));
printPropDesc(Object.getOwnPropertyDescriptor(a, '2'));

/* force array to be sparse (array part is abandoned) */
a[10000] = 4;

printDesc(Object.getOwnPropertyDescriptor(a, '0'));
printDesc(Object.getOwnPropertyDescriptor(a, '1'));
printDesc(Object.getOwnPropertyDescriptor(a, '2'));
printDesc(Object.getOwnPropertyDescriptor(a, '10000'));
printPropDesc(Object.getOwnPropertyDescriptor(a, '0'));
printPropDesc(Object.getOwnPropertyDescriptor(a, '1'));
printPropDesc(Object.getOwnPropertyDescriptor(a, '2'));
printPropDesc(Object.getOwnPropertyDescriptor(a, '10000'));
47 changes: 47 additions & 0 deletions tests/ecmascript/util-object.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,50 @@ function getValuePublicType(val) {
function getValueInternalTag(val) {
return getDuktapeInfoField(val, 1, 'itag');
}

// String convert a descriptor object.
function propDescToString(pd) {
function fmt(v) {
if (typeof v === 'function') {
return 'function';
}
return Duktape.enc('jx', v);
}

if (!pd) {
return 'none';
}

var res = [];
if (pd.value !== void 0) {
res.push('value=' + fmt(pd.value));
}
if (pd.get !== void 0) {
res.push('get=' + fmt(pd.get));
}
if (pd.set !== void 0) {
res.push('set=' + fmt(pd.set));
}
if (pd.writable !== void 0) {
res.push('writable=' + fmt(pd.writable));
}
if (pd.enumerable !== void 0) {
res.push('enumerable=' + fmt(pd.enumerable));
}
if (pd.configurable !== void 0) {
res.push('configurable=' + fmt(pd.configurable));
}

return res.join(',');
}

// Print a descriptor object.
function printPropDesc(pd) {
print(propDescToString(pd));
}

// Print a key and a related property descriptor.
function printKeyPropDesc(obj, key) {
var pd = Object.getOwnPropertyDescriptor(obj, key);
print('key: ' + key + ', desc: ' + propDescToString(pd));
}

0 comments on commit 6fc448e

Please sign in to comment.