diff --git a/tests/api/test-def-prop-virtual.c b/tests/api/test-def-prop-virtual.c new file mode 100644 index 00000000..c9c40bfd --- /dev/null +++ b/tests/api/test-def-prop-virtual.c @@ -0,0 +1,156 @@ +/* + * Test DUK_DEFPROP_FORCE for virtual properties. + */ + +/*=== +*** test_array_length_enumerable_noforce (duk_safe_call) +set array .length enumerable +==> rc=1, result='TypeError: not configurable' +*** test_array_length_enumerable_force (duk_safe_call) +set array .length enumerable +==> rc=1, result='TypeError: property is virtual' +*** test_array_length_configurable_noforce (duk_safe_call) +set array .length configurable +==> rc=1, result='TypeError: not configurable' +*** test_array_length_configurable_force (duk_safe_call) +set array .length configurable +==> rc=1, result='TypeError: property is virtual' +*** test_array_length_overwrite_same_noforce (duk_safe_call) +["foo","bar","quux"] +final top: 0 +==> rc=0, result='undefined' +*** test_array_length_overwrite_same_force (duk_safe_call) +["foo","bar","quux"] +final top: 0 +==> rc=0, result='undefined' +*** test_array_length_overwrite_bigger_noforce (duk_safe_call) +==> rc=1, result='TypeError: not configurable' +*** test_array_length_overwrite_bigger_force (duk_safe_call) +["foo","bar","quux",null,null] +final top: 0 +==> rc=0, result='undefined' +*** test_array_length_overwrite_smaller_noforce (duk_safe_call) +==> rc=1, result='TypeError: array length non-writable' +*** test_array_length_overwrite_smaller_force (duk_safe_call) +["foo"] +final top: 0 +==> rc=0, result='undefined' +===*/ + +static duk_ret_t test__array_length_enumerable(duk_context *ctx, int force) { + duk_push_array(ctx); + + duk_push_string(ctx, "length"); + printf("set array .length enumerable\n"); + fflush(stdout); + duk_def_prop(ctx, -2, DUK_DEFPROP_SET_ENUMERABLE | (force ? DUK_DEFPROP_FORCE : 0)); + + duk_eval_string(ctx, + "(function (v) { print(Duktape.enc('jx', Object.getOwnPropertyDescriptor(v, 'length' ))); })"); + duk_dup(ctx, -2); + duk_call(ctx, 1); + + duk_pop_2(ctx); + printf("final top: %ld\n", (long) duk_get_top(ctx)); + return 0; +} + +static duk_ret_t test_array_length_enumerable_noforce(duk_context *ctx, void *udata) { + (void) udata; + return test__array_length_enumerable(ctx, 0); +} +static duk_ret_t test_array_length_enumerable_force(duk_context *ctx, void *udata) { + (void) udata; + return test__array_length_enumerable(ctx, 1); +} + +static duk_ret_t test__array_length_configurable(duk_context *ctx, int force) { + duk_push_array(ctx); + + duk_push_string(ctx, "length"); + printf("set array .length configurable\n"); + fflush(stdout); + duk_def_prop(ctx, -2, DUK_DEFPROP_SET_CONFIGURABLE | (force ? DUK_DEFPROP_FORCE : 0)); + + duk_eval_string(ctx, + "(function (v) { print(Duktape.enc('jx', Object.getOwnPropertyDescriptor(v, 'length' ))); })"); + duk_dup(ctx, -2); + duk_call(ctx, 1); + + duk_pop_2(ctx); + printf("final top: %ld\n", (long) duk_get_top(ctx)); + return 0; +} + +static duk_ret_t test_array_length_configurable_noforce(duk_context *ctx, void *udata) { + (void) udata; + return test__array_length_configurable(ctx, 0); +} +static duk_ret_t test_array_length_configurable_force(duk_context *ctx, void *udata) { + (void) udata; + return test__array_length_configurable(ctx, 1); +} + +static duk_ret_t test__length_overwrite(duk_context *ctx, int force, int new_len) { + duk_eval_string(ctx, + "(function () {\n" + " var arr = [ 'foo', 'bar', 'quux' ];\n" + " Object.defineProperty(arr, 'length', { writable: false });\n" + " return arr;\n" + "})()\n"); + + /* Array .length is not writable; with DUK_DEFPROP_FORCE we can still + * write it. + */ + duk_push_string(ctx, "length"); + duk_push_int(ctx, new_len); + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | (force ? DUK_DEFPROP_FORCE : 0)); + + duk_eval_string(ctx, + "(function (v) { print(Duktape.enc('jx', v)); })"); + duk_dup(ctx, -2); + duk_call(ctx, 1); + + duk_pop_2(ctx); + + printf("final top: %ld\n", (long) duk_get_top(ctx)); + return 0; +} + +static duk_ret_t test_array_length_overwrite_same_noforce(duk_context *ctx, void *udata) { + (void) udata; + return test__length_overwrite(ctx, 0, 3); +} +static duk_ret_t test_array_length_overwrite_same_force(duk_context *ctx, void *udata) { + (void) udata; + return test__length_overwrite(ctx, 1, 3); +} +static duk_ret_t test_array_length_overwrite_bigger_noforce(duk_context *ctx, void *udata) { + (void) udata; + return test__length_overwrite(ctx, 0, 5); +} +static duk_ret_t test_array_length_overwrite_bigger_force(duk_context *ctx, void *udata) { + (void) udata; + return test__length_overwrite(ctx, 1, 5); +} +static duk_ret_t test_array_length_overwrite_smaller_noforce(duk_context *ctx, void *udata) { + (void) udata; + return test__length_overwrite(ctx, 0, 1); +} +static duk_ret_t test_array_length_overwrite_smaller_force(duk_context *ctx, void *udata) { + (void) udata; + return test__length_overwrite(ctx, 1, 1); +} + +void test(duk_context *ctx) { + TEST_SAFE_CALL(test_array_length_enumerable_noforce); + TEST_SAFE_CALL(test_array_length_enumerable_force); + TEST_SAFE_CALL(test_array_length_configurable_noforce); + TEST_SAFE_CALL(test_array_length_configurable_force); + TEST_SAFE_CALL(test_array_length_overwrite_same_noforce); + TEST_SAFE_CALL(test_array_length_overwrite_same_force); + TEST_SAFE_CALL(test_array_length_overwrite_bigger_noforce); + TEST_SAFE_CALL(test_array_length_overwrite_bigger_force); + TEST_SAFE_CALL(test_array_length_overwrite_smaller_noforce); + TEST_SAFE_CALL(test_array_length_overwrite_smaller_force); +} diff --git a/tests/api/test-def-prop.c b/tests/api/test-def-prop.c index 79f153e3..a3ead7e6 100644 --- a/tests/api/test-def-prop.c +++ b/tests/api/test-def-prop.c @@ -162,38 +162,38 @@ final top: 1 final top: 1 ==> rc=0, result='undefined' *** test_fail_array_smaller (duk_safe_call) +"length" {value:4,writable:true,enumerable:false,configurable:false} no-getter no-setter "0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter "1" {value:"bar",writable:true,enumerable:true,configurable:true} no-getter no-setter "2" {value:"quux",writable:true,enumerable:true,configurable:false} no-getter no-setter "3" {value:"baz",writable:true,enumerable:true,configurable:true} no-getter no-setter -"length" {value:4,writable:true,enumerable:false,configurable:false} no-getter no-setter ==> rc=1, result='TypeError: array length write failed' *** test_force_array_smaller (duk_safe_call) +"length" {value:4,writable:true,enumerable:false,configurable:false} no-getter no-setter "0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter "1" {value:"bar",writable:true,enumerable:true,configurable:true} no-getter no-setter "2" {value:"quux",writable:true,enumerable:true,configurable:false} no-getter no-setter "3" {value:"baz",writable:true,enumerable:true,configurable:true} no-getter no-setter -"length" {value:4,writable:true,enumerable:false,configurable:false} no-getter no-setter -"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter "length" {value:1,writable:true,enumerable:false,configurable:false} no-getter no-setter +"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter json: ["foo"] final top: 1 ==> rc=0, result='undefined' *** test_fail_array_smaller_nonwritablelength (duk_safe_call) +"length" {value:4,writable:false,enumerable:false,configurable:false} no-getter no-setter "0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter "1" {value:"bar",writable:true,enumerable:true,configurable:true} no-getter no-setter "2" {value:"quux",writable:true,enumerable:true,configurable:false} no-getter no-setter "3" {value:"baz",writable:true,enumerable:true,configurable:true} no-getter no-setter -"length" {value:4,writable:false,enumerable:false,configurable:false} no-getter no-setter ==> rc=1, result='TypeError: array length non-writable' *** test_force_array_smaller_nonwritablelength (duk_safe_call) +"length" {value:4,writable:false,enumerable:false,configurable:false} no-getter no-setter "0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter "1" {value:"bar",writable:true,enumerable:true,configurable:true} no-getter no-setter "2" {value:"quux",writable:true,enumerable:true,configurable:false} no-getter no-setter "3" {value:"baz",writable:true,enumerable:true,configurable:true} no-getter no-setter -"length" {value:4,writable:false,enumerable:false,configurable:false} no-getter no-setter -"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter "length" {value:1,writable:false,enumerable:false,configurable:false} no-getter no-setter +"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter json: ["foo"] final top: 1 ==> rc=0, result='undefined' @@ -927,6 +927,11 @@ static duk_ret_t test_force_accessor2data(duk_context *ctx, void *udata) { /* Make array smaller, ignoring non-configurable elements. */ static duk_ret_t test_force_array_smaller_raw(duk_context *ctx, duk_bool_t length_writable, duk_bool_t forced) { + /* The array will have a non-configurable element (not very common) + * which causes the array part to be abandoned and affects enumeration + * order; in Duktape 2.0 'length' will enumerate before the properties + * because .length is virtual. + */ if (length_writable) { duk_eval_string(ctx, "(function () {\n" diff --git a/tests/ecmascript/test-bi-json-enc-fastpath.js b/tests/ecmascript/test-bi-json-enc-fastpath.js index 29d11756..f2df48b0 100644 --- a/tests/ecmascript/test-bi-json-enc-fastpath.js +++ b/tests/ecmascript/test-bi-json-enc-fastpath.js @@ -59,12 +59,13 @@ top level value test 6 "foo" 7 {"foo":123} 8 ["foo"] -9 undefined -10 "1970-01-01T00:00:00.123Z" -11 undefined +9 [null,null,null,null,null,null,null,null,null,null] +10 undefined +11 "1970-01-01T00:00:00.123Z" 12 undefined 13 undefined -14 {"type":"Buffer","data":[65,66,67,68,69,70,71,72]} +14 undefined +15 {"type":"Buffer","data":[65,66,67,68,69,70,71,72]} ===*/ /* Top level value */ @@ -73,6 +74,7 @@ function jsonStringifyFastPathTopLevelValueTest() { var values = [ undefined, null, true, false, 123, 123.456, 'foo', { foo: 123 }, [ 'foo' ], + new Array(10), // .length is larger than underlying array part length function myfunc() {}, new Date(123), Duktape.dec('hex', 'deadbeef'), diff --git a/tests/ecmascript/test-dev-duk-harray.js b/tests/ecmascript/test-dev-duk-harray.js new file mode 100644 index 00000000..3ace1091 --- /dev/null +++ b/tests/ecmascript/test-dev-duk-harray.js @@ -0,0 +1,423 @@ +/* + * Dev testcases when adding duk_harray + * + * https://github.com/svaarala/duktape/pull/703 + */ + +/*=== +array literal test +3 +1,2,3 +===*/ + +function arrayLiteralTest() { + var arr; + + arr = [ 1, 2, 3 ]; + print(arr.length); + print(arr); +} + +try { + print('array literal test'); + arrayLiteralTest(); +} catch (e) { + print(e.stack || e); +} + +/*=== +array constructor test +10 [null,null,null,null,null,null,null,null,null,null] +10 [null,null,null,null,null,"foo",null,null,null,null] +6 [1,2,3,"foo","bar","quux"] +6 [1,2,3,"foo","bar","baz"] +256 +===*/ + +function arrayConstructorTest() { + var arr; + var i; + + // Create Array using numeric argument + arr = new Array(10); + print(arr.length, JSON.stringify(arr)); + arr[5] = 'foo'; + print(arr.length, JSON.stringify(arr)); + for (i = 0; i < 256; i++) { + arr = new Array(i); + if (arr.length !== i) { + throw new Error('failed for index ' + i); + } + } + + // Create Array from initializer arguments + arr = new Array(1, 2, 3, 'foo', 'bar', 'quux'); + print(arr.length, JSON.stringify(arr)); + arr[5] = 'baz'; + print(arr.length, JSON.stringify(arr)); + + // Create Array with a lot of initializer arguments (more than value stack reserve). + arr = new Array( + // 256 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + ); + print(arr.length); +} + +try { + print('array constructor test'); + arrayConstructorTest(); +} catch (e) { + print(e.stack || e); +} + +/*=== +array enumeration test +0,1,2,3,length +0,1,2,3 +0,1,2,3,4,length +0,1,2,3,4 +0,1,2,3,4,length,foo +0,1,2,3,4,foo +===*/ + +function arrayEnumerationTest() { + var arr; + + arr = [ 1,2,3,4 ]; + print(Object.getOwnPropertyNames(arr)); + print(Object.keys(arr)); + arr[4] = 5; + print(Object.getOwnPropertyNames(arr)); + print(Object.keys(arr)); + arr.foo = 'bar'; + print(Object.getOwnPropertyNames(arr)); + print(Object.keys(arr)); +} + +try { + print('array enumeration test'); + arrayEnumerationTest(); +} catch (e) { + print(e.stack || e); +} + +/*=== +array .length property descriptor test +6 true false false +6 [1,2,3,4,5,6] +5 [1,2,3,4,5] +5 [1,2,3,4,5] +5 [1,2,3,4,5] +5 false false false +defineProperty +5 [1,2,3,4,5] +5 [1,2,3,4,5] +TypeError: array length non-writable +TypeError: not configurable +TypeError: not configurable +TypeError: not configurable +TypeError: not configurable +2 [null,null] +===*/ + +function arrayLengthPropertyDescriptorTest() { + var arr, pd; + + // Make .length non-writable. + arr = [ 1, 2, 3, 4, 5, 6 ]; + pd = Object.getOwnPropertyDescriptor(arr, 'length'); + print(pd.value, pd.writable, pd.enumerable, pd.configurable); + print(arr.length, JSON.stringify(arr)); + arr.length = 5; + print(arr.length, JSON.stringify(arr)); + Object.defineProperty(arr, 'length', { writable: false }); + print(arr.length, JSON.stringify(arr)); + arr.length = 4; + print(arr.length, JSON.stringify(arr)); + pd = Object.getOwnPropertyDescriptor(arr, 'length'); + print(pd.value, pd.writable, pd.enumerable, pd.configurable); + print('defineProperty'); + try { + Object.defineProperty(arr, 'length', { value: 5 }); // no change, ok + print(arr.length, JSON.stringify(arr)); + } catch (e) { + print(e.stack || e); + } + try { + Object.defineProperty(arr, 'length', { value: 5, writable: false, enumerable: false, configurable: false }); // no change, ok + print(arr.length, JSON.stringify(arr)); + } catch (e) { + print(e.stack || e); + } + try { + Object.defineProperty(arr, 'length', { value: 4 }); // Not OK + print('never here'); + print(arr.length, JSON.stringify(arr)); + } catch (e) { + //print(e.stack); + print(e); + } + try { + Object.defineProperty(arr, 'length', { writable: true }); // Not OK + print('never here'); + print(arr.length, JSON.stringify(arr)); + } catch (e) { + //print(e.stack); + print(e); + } + try { + Object.defineProperty(arr, 'length', { enumerable: true }); // Not OK + print('never here'); + print(arr.length, JSON.stringify(arr)); + } catch (e) { + //print(e.stack); + print(e); + } + try { + Object.defineProperty(arr, 'length', { configurable: true }); // Not OK + print('never here'); + print(arr.length, JSON.stringify(arr)); + } catch (e) { + //print(e.stack); + print(e); + } + try { + Object.defineProperty(arr, 'length', { set: function () {}, get: function () {} }); // Not OK + print('never here'); + print(arr.length, JSON.stringify(arr)); + } catch (e) { + //print(e.stack); + print(e); + } + + // Set .length using Object.defineProperty(). + arr = []; + Object.defineProperty(arr, 'length', { value: 2 }); + print(arr.length, JSON.stringify(arr)); +} + +try { + print('array .length property descriptor test'); + arrayLengthPropertyDescriptorTest(); +} catch (e) { + print(e.stack || e); +} + +/*=== +array length test +false +TypeError: not configurable +4 +4 +true +0,1,2,length +true + +1,2,3,4,5,6,7,8,9,10 +1,2,3,4,5,foo,7,8,9,10 +TypeError: not writable +1,2,3,4,5,foo,7,8,9,10 +===*/ + +function arrayLengthTest() { + var arr; + var obj; + + // Attempt to delete Array .length fails; result false in non-strict + // mode, error in strict mode. + arr = [ 1, 2, 3, 4, 5 ]; + print(delete arr.length); + try { + (function deleteTest() { 'use strict'; delete arr.length; })(); + } catch (e) { + //print(e.stack); + print(e); + } + + // Attempt to write a non-writable Array .length. + arr = [ 1, 2, 3, 4 ]; + Object.defineProperty(arr, 'length', { writable: false }); + print(arr.length); + arr.length = 2; + print(arr.length); + + // Property existence for Array .length. + arr = [ 1, 2, 3 ]; + print('length' in arr); + print(Object.getOwnPropertyNames(arr)); + obj = Object.create(arr); // inherit from 'arr' + print('length' in obj); + print(Object.getOwnPropertyNames(obj)); + + // Writing to an Array when .length is write protected. + arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + Object.defineProperty(arr, 'length', { writable: false }); + print(arr); + arr[5] = 'foo'; // OK, length not changed + print(arr); + try { + arr[10] = 'foo'; + } catch (e) { + //print(e.stack); + print(e); + } + print(arr); +} + +try { + print('array length test'); + arrayLengthTest(); +} catch (e) { + print(e.stack || e); +} + +/*=== +enumeration order for sparse arrays +0,1,2,length +0,1,2 +0,1,2,length,foo +0,1,2,foo +length,0,1,2,foo +0,1,2,foo +length,0,1,2,foo,bar +0,1,2,foo,bar +===*/ + +/* The enumeration order for sparse arrays (= arrays whose array part has been + * abandoned) changes with the introduction of duk_harray. The enumeration + * order is: (1) array part, (2) virtual .length property, (3) entry part. + * The virtual .length only comes into play when also non-enumerable own + * properties are listed, e.g. Object.getOwnPropertyNames() is used. + * + * For a three-member dense array this would result in the enumeration order + * 0,1,2,length. When that array becomes sparse, Duktape 1.x would still + * enumerate it as 0,1,2,length because .length was stored explicitly and + * could thus be moved to the entry part. Duktape 2.x has a virtual Array + * .length and the sparse array will thus enumerate as length,0,1,2. + * + * ((o) Duktape 1.5.0 (v1.4.0-421-g8e90d3d-dirty) + * duk> a = [1,2,3]; a[100] = 1; a.length = 3; + * = 3 + * duk> Object.getOwnPropertyNames(a) + * = 0,1,2,length + * + * ((o) Duktape [linenoise] 1.99.99 (v1.5.0-168-g86373ab-dirty) + * duk> a = [1,2,3]; a[100] = 1; a.length = 3; + * = 3 + * duk> Object.getOwnPropertyNames(a) + * = length,0,1,2 + */ + +function sparseArrayEnumTest() { + var arr; + + arr = [ 1, 2, 3 ]; + print(Object.getOwnPropertyNames(arr)); + print(Object.keys(arr)); + + arr.foo = 'bar'; + print(Object.getOwnPropertyNames(arr)); + print(Object.keys(arr)); + + arr[100] = 1; arr.length = 3; // make sparse + print(Object.getOwnPropertyNames(arr)); + print(Object.keys(arr)); + + arr.bar = 'quux'; + print(Object.getOwnPropertyNames(arr)); + print(Object.keys(arr)); +} + +try { + print('enumeration order for sparse arrays'); + sparseArrayEnumTest(); +} catch (e) { + print(e.stack || e); +} + +/*=== +Array.prototype test +[object Array] +[object Array] +0 foo bar quux undefined +0 foo undefined undefined undefined +0 undefined undefined undefined undefined +dummy +undefined +===*/ + +function arrayPrototypeTest() { + var arr; + + // Array.prototype is an Array instance. + // This is useful to check for ROM built-ins. + print(Object.prototype.toString.call([])); + print(Object.prototype.toString.call(Array.prototype)); + + // It can also be written to (unusual but required). + Array.prototype.push('foo', 'bar', 'quux'); + arr = []; + print(arr.length, arr[0], arr[1], arr[2], arr[3]); + Array.prototype.length = 1; + print(arr.length, arr[0], arr[1], arr[2], arr[3]); + Array.prototype.length = 0; + print(arr.length, arr[0], arr[1], arr[2], arr[3]); + + // Array.prototype is dense by default; make it sparse. + //print(Duktape.enc('jx', Duktape.info(Array.prototype))); + Array.prototype[1000] = 'dummy'; + //print(Duktape.enc('jx', Duktape.info(Array.prototype))); + arr = []; + print(arr[1000]); + Array.prototype.length = 0; + //print(Duktape.enc('jx', Duktape.info(Array.prototype))); + print(arr[1000]); +} + +try { + print('Array.prototype test'); + arrayPrototypeTest(); +} catch (e) { + print(e.stack || e); +} + +/*=== +100 +1000000000 +===*/ + +function arrayMiscTest() { + var arr; + + // Creating an Array with a small count creates a dense array + // with a preallocated array part. + arr = new Array(100); + print(arr.length); + + // Creating an Array with a huge count creates a dense array + // but with no preallocated array part. + arr = new Array(1e9); + print(arr.length); +} + +try { + arrayMiscTest(); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-dev-enum-abandoned-array.js b/tests/ecmascript/test-dev-enum-abandoned-array.js new file mode 100644 index 00000000..6548d673 --- /dev/null +++ b/tests/ecmascript/test-dev-enum-abandoned-array.js @@ -0,0 +1,65 @@ +/* + * Enumeration order for abandoned array part changed in Duktape 2.x. + * This testcase illustrates the change. The change is related to array + * instance .length which is non-enumerable, so that the change only affects + * Object.getOwnPropertyNames() and duk_enum() calls which request enumeration + * of non-enumerable properties. + */ + +/*=== +with array part +- 0 +- 1 +- 2 +- length +- myProperty +without array part +- length +- 0 +- 1 +- 2 +- myProperty +array index added after abandoning array part +- length +- 0 +- 1 +- 2 +- myProperty +- 3 +===*/ + +function test() { + var arr = [ 'foo', 'bar', 'quux' ]; + arr.myProperty = true; + + // When array part is present the array index properties enumerate first, + // then the virtual .length property, and finally any other properties. + + print('with array part'); + Object.getOwnPropertyNames(arr).forEach(function (k) { print('-', k); }); + + arr[100] = 'dummy'; // abandon array part + arr.length = 3; + + // When array part is not present, the virtual .length property enumerates + // first, followed by index properties moved into the entry part, followed + // by other properties (and any array index writes which happen after the + // array part is abandoned. + // + // In Duktape 1.x the .length property is concrete and would be enumerated + // after the index properties moved into the entry part. Array indexes + // added after array part abandonment would still appear last. + + print('without array part'); + Object.getOwnPropertyNames(arr).forEach(function (k) { print('-', k); }); + + arr.push('baz'); + print('array index added after abandoning array part'); + Object.getOwnPropertyNames(arr).forEach(function (k) { print('-', k); }); +} + +try { + test(); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-dev-rom-builtins-1.js b/tests/ecmascript/test-dev-rom-builtins-1.js index 56085cb3..c0ad0ad4 100644 --- a/tests/ecmascript/test-dev-rom-builtins-1.js +++ b/tests/ecmascript/test-dev-rom-builtins-1.js @@ -383,19 +383,29 @@ function romObjectRegExpTest() { /*=== --- Error.prototype setter/getter +fileName {get:{_func:true},set:{_func:true},enumerable:false,configurable:false} get length: 0, set length: 0 +lineNumber {get:{_func:true},set:{_func:true},enumerable:false,configurable:false} get length: 0, set length: 0 +stack {get:{_func:true},set:{_func:true},enumerable:false,configurable:false} get length: 0, set length: 0 ===*/ function romObjectErrorPrototypeAccessorTest() { function test(key) { - var pd = Object.getOwnPropertyDescriptor(Error.prototype, key); - print(Duktape.enc('jx', pd)); - print('get length: ' + pd.get.length + ', set length: ' + pd.set.length); + print(key); + try { + var pd = Object.getOwnPropertyDescriptor(Error.prototype, key); + print(Duktape.enc('jx', pd)); + print('get length: ' + pd.get.length + ', set length: ' + pd.set.length); + } catch (e) { + print(typeof e, typeof e.message, typeof e.name, e.message, e.name); + print(e instanceof Error); + print(e.stack || e); + } } test('fileName'); @@ -415,6 +425,21 @@ function romObjectAccessorTest() { print(global.__proto__ == Object.getPrototypeOf(global)); } +/*=== +--- Array.prototype test +[object Array] +[object Array] +0 +===*/ + +function romObjectArrayPrototypeTest() { + // Array.prototype is also an Array instance; this affects the + // ROM init data and is thus useful to check. + print(Object.prototype.toString.call([])); + print(Object.prototype.toString.call(Array.prototype)); + print(Array.prototype.length); +} + // Read-only code paths related to object properties which aren't covered: // // duk_hobject_props.c:duk_realloc_props(): assert, can't be exercised directly. @@ -463,6 +488,9 @@ try { print('--- Accessor test') romObjectAccessorTest(); + + print('--- Array.prototype test'); + romObjectArrayPrototypeTest(); } catch (e) { print(e.stack || e); }