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.
 
 
 
 
 
 

480 lines
15 KiB

// FIXME: 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);
}