mirror of https://github.com/svaarala/duktape.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
479 lines
15 KiB
479 lines
15 KiB
// 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));
|
|
}
|
|
|
|
function dumpValue(x) {
|
|
var i, n, clipped;
|
|
var tmp=[];
|
|
|
|
n = x.length;
|
|
if (n > 1000) {
|
|
n = 1000;
|
|
clipped=true;
|
|
}
|
|
for (i = 0; i < n; i++) {
|
|
if (x.hasOwnProperty(i)) {
|
|
tmp.push(String(x[i]));
|
|
} else {
|
|
tmp.push('nonexistent');
|
|
}
|
|
}
|
|
if (clipped) {
|
|
tmp.push('...');
|
|
}
|
|
return tmp.join(',');
|
|
}
|
|
|
|
function test(this_value, args, suppress_print) {
|
|
var t;
|
|
if (args === undefined) {
|
|
args = [];
|
|
}
|
|
|
|
try {
|
|
t = Array.prototype.reverse.apply(this_value, args);
|
|
if (suppress_print) {
|
|
print('success');
|
|
} else {
|
|
if (t.length === undefined) {
|
|
print(typeof t, 'nolength');
|
|
} else {
|
|
print(typeof t, t.length, dumpValue(t));
|
|
}
|
|
}
|
|
} catch (e) {
|
|
print(e.name);
|
|
}
|
|
}
|
|
|
|
/*===
|
|
basic
|
|
object 0
|
|
object 1 1
|
|
object 2 2,1
|
|
object 3 3,2,1
|
|
object 4 4,3,2,1
|
|
object 5 5,4,3,2,1
|
|
object 10 10,9,8,7,6,5,4,3,2,1
|
|
object 11 11,10,9,8,7,6,5,4,3,2,1
|
|
object 101 3,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,2,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,1
|
|
object 100 3,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,2,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,nonexistent,1
|
|
10000001 1 2
|
|
success
|
|
10000001 2 1
|
|
object 4 4,3,2,1
|
|
object 4 4,3,2,1
|
|
5 6
|
|
object 8 nonexistent,4,nonexistent,3,nonexistent,nonexistent,2,1
|
|
===*/
|
|
|
|
print('basic');
|
|
|
|
function basicTest() {
|
|
var obj;
|
|
|
|
// basic dense array tests (odd and even lengths)
|
|
|
|
test([]);
|
|
test([1]);
|
|
test([1,2]);
|
|
test([1,2,3]);
|
|
test([1,2,3,4]);
|
|
test([1,2,3,4,5]);
|
|
test([1,2,3,4,5,6,7,8,9,10]);
|
|
test([1,2,3,4,5,6,7,8,9,10,11]);
|
|
|
|
// sparse
|
|
|
|
obj = [1];
|
|
obj[100] = 3;
|
|
obj[50] = 2;
|
|
test(obj);
|
|
|
|
obj = [1];
|
|
obj[99] = 3;
|
|
obj[50] = 2;
|
|
test(obj);
|
|
|
|
// Extremely sparse array. The standard algorithm spends a lot of
|
|
// time on this.
|
|
|
|
obj = [];
|
|
obj[0] = 1;
|
|
obj[10000000] = 2;
|
|
print(obj.length, obj[0], obj[10000000]);
|
|
test(obj, undefined, true); // suppress printing
|
|
print(obj.length, obj[0], obj[10000000]);
|
|
|
|
// non-array
|
|
|
|
obj = { '0': 1, '1': 2, '2': 3, '3': 4, length: 4 };
|
|
test(obj);
|
|
|
|
obj = { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6, length: 4 }; // '4' and '5' are ignored
|
|
test(obj);
|
|
print(obj['4'], obj['5']);
|
|
|
|
// combinations of upper/lower exists
|
|
// exists: true true false false true false true false
|
|
// reverse pairs: (true,false) (true,true) (false,false) (false,true)
|
|
|
|
obj=[];
|
|
obj[0] = 1;
|
|
obj[1] = 2;
|
|
obj[4] = 3;
|
|
obj[6] = 4;
|
|
obj.length = 8;
|
|
test(obj);
|
|
}
|
|
|
|
try {
|
|
basicTest();
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
|
|
/*===
|
|
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
|
|
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
|
|
===*/
|
|
|
|
print('attributes');
|
|
|
|
function attributesTest() {
|
|
var t;
|
|
|
|
// Since [[Get]] and [[Put]] are used, getter/setter calls are made normally.
|
|
// When swapping two elements:
|
|
// - If both exists, call order is: lower get, upper get, lower set, upper set
|
|
// - If only lower exists: lower get; no other side effects (lower is deleted, upper is [[Put]]'d as a plain property)
|
|
// - If only upper exists: upper get; no other side effects (upper is deleted, lower is [[Put]]'d as a plain property)
|
|
// - If neither exists, no side effects
|
|
//
|
|
// Here:
|
|
// - 0 and 7: both exist
|
|
// - 1 and 6: 1 exists, 6 does not
|
|
// - 2 and 5: 5 exists, 2 does not
|
|
// - 3 and 4: neither exists
|
|
|
|
t = [];
|
|
t.my0 = 'val0';
|
|
t.my1 = 'val1';
|
|
t.my5 = 'val5';
|
|
t.my7 = 'val7';
|
|
Object.defineProperties(t, {
|
|
'0': {
|
|
enumerable: false,
|
|
configurable: true,
|
|
get: function() { print('get 0'); return this.my0; },
|
|
set: function(v) { print('set 0', v); this.my0 = v; }
|
|
},
|
|
'1': {
|
|
enumerable: false,
|
|
configurable: true,
|
|
get: function() { print('get 1'); return this.my1; },
|
|
set: function(v) { print('set 1', v); this.my1 = v; }
|
|
},
|
|
// '2' skipped
|
|
// '3' skipped
|
|
// '4' skipped
|
|
'5': {
|
|
enumerable: true,
|
|
configurable: true,
|
|
get: function() { print('get 5'); return this.my5; },
|
|
set: function(v) { print('set 5', v); this.my5 = v; }
|
|
},
|
|
// '6' skipped
|
|
'7': {
|
|
enumerable: true,
|
|
configurable: true,
|
|
get: function() { print('get 7'); return this.my7; },
|
|
set: function(v) { print('set 7', v); this.my7 = v; }
|
|
},
|
|
});
|
|
print(t.hasOwnProperty(0), t.hasOwnProperty(1),
|
|
t.hasOwnProperty(2), t.hasOwnProperty(3),
|
|
t.hasOwnProperty(4), t.hasOwnProperty(5),
|
|
t.hasOwnProperty(6), t.hasOwnProperty(7));
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
printDescriptor(t, String(i));
|
|
}
|
|
|
|
t.reverse();
|
|
|
|
print(t.hasOwnProperty(0), t.hasOwnProperty(1),
|
|
t.hasOwnProperty(2), t.hasOwnProperty(3),
|
|
t.hasOwnProperty(4), t.hasOwnProperty(5),
|
|
t.hasOwnProperty(6), t.hasOwnProperty(7));
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
printDescriptor(t, String(i));
|
|
}
|
|
|
|
// Here, '0' is not enumerable, but when it gets swapped to position 1,
|
|
// the specification algorithm calls [[Put]] with key "1" (and [[Delete]]
|
|
// with key "0"). The property "1" will have default properties, and will
|
|
// be enumerable.
|
|
|
|
t = [];
|
|
Object.defineProperties(t, {
|
|
'0': { value: 'foo', writable: true, enumerable: false, configurable: true },
|
|
'length': { value: 2 }
|
|
});
|
|
printDescriptor(t, '0');
|
|
printDescriptor(t, '1');
|
|
[].reverse();
|
|
printDescriptor(t, '0');
|
|
printDescriptor(t, '1');
|
|
}
|
|
|
|
try {
|
|
attributesTest();
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
|
|
/*===
|
|
protected
|
|
1 2
|
|
TypeError
|
|
2 2
|
|
1 2
|
|
TypeError
|
|
1 2
|
|
1 1
|
|
TypeError
|
|
1 1
|
|
1 undefined
|
|
TypeError
|
|
1 undefined
|
|
undefined 1
|
|
TypeError
|
|
1 1
|
|
1 undefined
|
|
TypeError
|
|
undefined undefined
|
|
undefined 1
|
|
TypeError
|
|
undefined 1
|
|
===*/
|
|
|
|
/* All [[Get]], [[Put]], [[Delete]] operations have Throw=true */
|
|
|
|
print('protected');
|
|
|
|
function protectedTest() {
|
|
var obj;
|
|
|
|
// non-writable property; '1' is not writable but '0' must still get updated
|
|
// ([[Put]] "0" happens before [[Put]] "1")
|
|
|
|
obj = {};
|
|
Object.defineProperties(obj, {
|
|
'0': { value: 1, writable: true, enumerable: true, configurable: true },
|
|
'1': { value: 2, writable: false, enumerable: true, configurable: true }
|
|
});
|
|
obj.length = 2;
|
|
print(obj['0'], obj['1']);
|
|
test(obj);
|
|
print(obj['0'], obj['1']);
|
|
|
|
// non-writable property; '0' is not writable, '1' must not get updated
|
|
// ([[Put]] "0" happens before [[Put]] "1")
|
|
|
|
obj = {};
|
|
Object.defineProperties(obj, {
|
|
'0': { value: 1, writable: false, enumerable: true, configurable: true },
|
|
'1': { value: 2, writable: true, enumerable: true, configurable: true }
|
|
});
|
|
obj.length = 2;
|
|
print(obj['0'], obj['1']);
|
|
test(obj);
|
|
print(obj['0'], obj['1']);
|
|
|
|
// non-writable property; even though values match with SameValue, [[CanPut]]
|
|
// should TypeError
|
|
|
|
obj = {};
|
|
Object.defineProperties(obj, {
|
|
'0': { value: 1, writable: false, enumerable: true, configurable: true },
|
|
'1': { value: 1, writable: false, enumerable: true, configurable: true }
|
|
});
|
|
obj.length = 2;
|
|
print(obj['0'], obj['1']);
|
|
test(obj);
|
|
print(obj['0'], obj['1']);
|
|
|
|
// non-configurable property, cannot be deleted; [[Delete]] for '0' happens
|
|
// before [[Put]] for '1', so '1' should not be set
|
|
|
|
obj = {};
|
|
Object.defineProperties(obj, {
|
|
'0': { value: 1, writable: true, enumerable: true, configurable: false }
|
|
});
|
|
obj.length = 2;
|
|
print(obj['0'], obj['1']);
|
|
test(obj);
|
|
print(obj['0'], obj['1']);
|
|
|
|
// non-configurable property, cannot be deleted; [[Put]] for '0' happens
|
|
// before [[Delete]] for '1', so '1' *SHOULD* be set
|
|
|
|
obj = {};
|
|
Object.defineProperties(obj, {
|
|
'1': { value: 1, writable: true, enumerable: true, configurable: false }
|
|
});
|
|
obj.length = 2;
|
|
print(obj['0'], obj['1']);
|
|
test(obj);
|
|
print(obj['0'], obj['1']);
|
|
|
|
// non-extensible object, new property cannot be added; [[Delete]] '0' happens
|
|
// before [[Put]] '1', so [[Delete]] should succeed
|
|
|
|
obj = {};
|
|
Object.defineProperties(obj, {
|
|
'0': { value: 1, writable: true, enumerable: true, configurable: true }
|
|
});
|
|
obj.length = 2;
|
|
Object.preventExtensions(obj);
|
|
print(obj['0'], obj['1']);
|
|
test(obj);
|
|
print(obj['0'], obj['1']);
|
|
|
|
// non-extensible object, new property cannot be added; [[Put]] '0' happens
|
|
// before [[Delete]] '1'; since [[Put]] fails, also won't [[Delete]]
|
|
|
|
obj = {};
|
|
Object.defineProperties(obj, {
|
|
'1': { value: 1, writable: true, enumerable: true, configurable: true }
|
|
});
|
|
obj.length = 2;
|
|
Object.preventExtensions(obj);
|
|
print(obj['0'], obj['1']);
|
|
test(obj);
|
|
print(obj['0'], obj['1']);
|
|
}
|
|
|
|
try {
|
|
protectedTest();
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
|
|
/*===
|
|
coercion
|
|
TypeError
|
|
TypeError
|
|
object nolength
|
|
object nolength
|
|
object nolength
|
|
object 0
|
|
TypeError
|
|
object 3 3,2,1
|
|
object nolength
|
|
object 3 quux,bar,foo
|
|
success
|
|
3.9 quux bar foo baz
|
|
success
|
|
4294967299.9 quux bar foo baz
|
|
success
|
|
-4294967292.1 baz quux bar foo
|
|
success
|
|
4 quux bar foo baz
|
|
===*/
|
|
|
|
print('coercion');
|
|
|
|
function coercionTest() {
|
|
var obj;
|
|
|
|
// this coercion
|
|
|
|
test(undefined);
|
|
test(null);
|
|
test(true);
|
|
test(false);
|
|
test(123);
|
|
test(''); // length = 0 -> no action
|
|
test('foo'); // has numeric properties but they're not writable/configurable -> TypeError
|
|
test([1,2,3]);
|
|
test({ foo: 1, bar: 2 });
|
|
test({ '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: 3 });
|
|
|
|
// length coercion
|
|
|
|
obj = { '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: '3.9' };
|
|
test(obj, undefined, true);
|
|
print(obj.length, obj['0'], obj['1'], obj['2'], obj['3']);
|
|
|
|
obj = { '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: 256*256*256*256 + 3.9 };
|
|
test(obj, undefined, true);
|
|
print(obj.length, obj['0'], obj['1'], obj['2'], obj['3']);
|
|
|
|
obj = { '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: -256*256*256*256 + 3.9 }; // coerces to 4
|
|
test(obj, undefined, true);
|
|
print(obj.length, obj['0'], obj['1'], obj['2'], obj['3']);
|
|
|
|
obj = { '0': 'foo', '1': 'bar', '2': 'quux', '3': 'baz', length: {
|
|
toString: function() { return 4; },
|
|
valueOf: function() { return 3; }
|
|
} };
|
|
test(obj, undefined, true);
|
|
print(obj.length, obj['0'], obj['1'], obj['2'], obj['3']);
|
|
}
|
|
|
|
try {
|
|
coercionTest();
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
|