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.

359 lines
8.4 KiB

/*===
*** test_ex_writable (duk_safe_call)
strict: 0
put rc=1
result: {"foo":"bar"}
final top: 1
==> rc=0, result='undefined'
*** test_ex_writable (duk_pcall)
strict: 1
put rc=1
result: {"foo":"bar"}
final top: 1
==> rc=0, result='undefined'
*** test_ex_nonwritable (duk_safe_call)
strict: 0
get Math -> rc=1
Math.PI=3.141592653589793
put rc=0
Math.PI=3.141592653589793
final top: 2
==> rc=0, result='undefined'
*** test_ex_nonwritable (duk_pcall)
strict: 1
get Math -> rc=1
Math.PI=3.141592653589793
==> rc=1, result='TypeError: property not writable'
*** test_ex_accessor_wo_setter (duk_safe_call)
strict: 0
eval:
(function () {
var o = {};
Object.defineProperty(o, 'foo', {
configurable: true,
extensible: true,
get: function() { print('getter'); }
// no setter
});
return o;
})()
top after eval: 1
put rc=0
result: {}
final top: 1
==> rc=0, result='undefined'
*** test_ex_accessor_wo_setter (duk_pcall)
strict: 1
eval:
(function () {
var o = {};
Object.defineProperty(o, 'foo', {
configurable: true,
extensible: true,
get: function() { print('getter'); }
// no setter
});
return o;
})()
top after eval: 1
==> rc=1, result='TypeError: undefined setter for accessor'
*** test_ex_setter_throws (duk_safe_call)
strict: 0
eval:
(function () {
var o = {};
Object.defineProperty(o, 'foo', {
configurable: true,
extensible: true,
get: function() { print('getter'); },
set: function() { print('setter, throw error'); throw 'setter error' }
});
return o;
})()
top after eval: 1
setter, throw error
==> rc=1, result='setter error'
*** test_ex_setter_throws (duk_pcall)
strict: 1
eval:
(function () {
var o = {};
Object.defineProperty(o, 'foo', {
configurable: true,
extensible: true,
get: function() { print('getter'); },
set: function() { print('setter, throw error'); throw 'setter error' }
});
return o;
})()
top after eval: 1
setter, throw error
==> rc=1, result='setter error'
*** test_new_extensible (duk_safe_call)
strict: 0
put rc=1
result: {"foo":1,"bar":"quux"}
final top: 1
==> rc=0, result='undefined'
*** test_new_extensible (duk_pcall)
strict: 1
put rc=1
result: {"foo":1,"bar":"quux"}
final top: 1
==> rc=0, result='undefined'
*** test_new_not_extensible (duk_safe_call)
strict: 0
eval:
(function () { var o = { foo: 1 }; Object.preventExtensions(o); return o; })()
top after eval: 1
put rc=0
result: {"foo":1}
final top: 1
==> rc=0, result='undefined'
*** test_new_not_extensible (duk_pcall)
strict: 1
eval:
(function () { var o = { foo: 1 }; Object.preventExtensions(o); return o; })()
top after eval: 1
==> rc=1, result='TypeError: object not extensible'
===*/
/* Test property writing API call.
*
* Property write behavior is quite complex in Ecmascript and there
* are many Ecmascript testcases to cover the behavior. The purpose
* of this testcase is to ensure the exposed API behavior is as
* expected without covering every specification case. In particular,
* different throwing / return code combinations need to be covered,
* in both strict and non-strict mode.
*
* The test case functions are called both with duk_safe_call()
* and duk_pcall(). These two establish different execution
* contexts: one is called outside a Duktape/C activation and
* is non-strict, while the other is called inside a Duktape/C
* activation and is strict. A single test function is called
* from both contexts.
*/
/*
* Not covered cases include:
*
* - new property, ancestor has writable data property
* - new property, ancestor has write-protected data property
* - new property, ancestor has accessor with setter
* + setter succeeds
* + setter fails
* - new property, ancestor has accessor without setter
*/
/* success */
int test_ex_writable(duk_context *ctx) {
int rc;
printf("strict: %d\n", duk_is_strict_call(ctx));
duk_set_top(ctx, 0);
duk_push_string(ctx, "{ \"foo\": 1 }");
duk_json_decode(ctx, 0);
duk_push_string(ctx, "foo");
duk_push_string(ctx, "bar");
rc = duk_put_prop(ctx, -3);
printf("put rc=%d\n", rc);
duk_json_encode(ctx, 0);
printf("result: %s\n", duk_to_string(ctx, 0));
printf("final top: %d\n", duk_get_top(ctx));
return 0;
}
/* strict: error
* non-strict: return 0
*/
int test_ex_nonwritable(duk_context *ctx) {
int rc;
printf("strict: %d\n", duk_is_strict_call(ctx));
/* Math.PI is not writable */
duk_set_top(ctx, 0);
duk_push_global_object(ctx);
rc = duk_get_prop_string(ctx, -1, "Math"); /* -> [ global Math ] */
printf("get Math -> rc=%d\n", rc);
rc = duk_get_prop_string(ctx, -1, "PI");
printf("Math.PI=%s\n", duk_to_string(ctx, -1));
duk_pop(ctx);
duk_push_string(ctx, "PI");
duk_push_string(ctx, "bar");
rc = duk_put_prop(ctx, -3);
printf("put rc=%d\n", rc);
rc = duk_get_prop_string(ctx, -1, "PI");
printf("Math.PI=%s\n", duk_to_string(ctx, -1));
duk_pop(ctx);
printf("final top: %d\n", duk_get_top(ctx));
return 0;
}
/* strict: error
* non-strict: return 0
*/
int test_ex_accessor_wo_setter(duk_context *ctx) {
const char *src;
int rc;
printf("strict: %d\n", duk_is_strict_call(ctx));
src = "(function () {\n"
" var o = {};\n"
" Object.defineProperty(o, 'foo', {\n"
" configurable: true,\n"
" extensible: true,\n"
" get: function() { print('getter'); }\n"
" // no setter\n"
" });\n"
" return o;\n"
"})()";
duk_set_top(ctx, 0);
duk_push_string(ctx, src);
printf("eval:\n%s\n", duk_get_string(ctx, -1));
duk_eval(ctx);
printf("top after eval: %d\n", duk_get_top(ctx));
duk_push_string(ctx, "foo");
duk_push_string(ctx, "bar");
rc = duk_put_prop(ctx, -3);
printf("put rc=%d\n", rc);
duk_json_encode(ctx, 0);
printf("result: %s\n", duk_to_string(ctx, 0));
printf("final top: %d\n", duk_get_top(ctx));
return 0;
}
/* strict: setter error propagates
* non-strict: same
*/
int test_ex_setter_throws(duk_context *ctx) {
const char *src;
int rc;
printf("strict: %d\n", duk_is_strict_call(ctx));
src = "(function () {\n"
" var o = {};\n"
" Object.defineProperty(o, 'foo', {\n"
" configurable: true,\n"
" extensible: true,\n"
" get: function() { print('getter'); },\n"
" set: function() { print('setter, throw error'); throw 'setter error' }\n"
" });\n"
" return o;\n"
"})()";
duk_set_top(ctx, 0);
duk_push_string(ctx, src);
printf("eval:\n%s\n", duk_get_string(ctx, -1));
duk_eval(ctx);
printf("top after eval: %d\n", duk_get_top(ctx));
duk_push_string(ctx, "foo");
duk_push_string(ctx, "bar");
rc = duk_put_prop(ctx, -3);
printf("put rc=%d\n", rc);
duk_json_encode(ctx, 0);
printf("result: %s\n", duk_to_string(ctx, 0));
printf("final top: %d\n", duk_get_top(ctx));
return 0;
printf("strict: %d\n", duk_is_strict_call(ctx));
printf("final top: %d\n", duk_get_top(ctx));
return 0;
}
/* success */
int test_new_extensible(duk_context *ctx) {
int rc;
printf("strict: %d\n", duk_is_strict_call(ctx));
duk_set_top(ctx, 0);
duk_push_string(ctx, "{ \"foo\": 1 }");
duk_json_decode(ctx, 0);
duk_push_string(ctx, "bar");
duk_push_string(ctx, "quux");
rc = duk_put_prop(ctx, -3);
printf("put rc=%d\n", rc);
duk_json_encode(ctx, 0);
printf("result: %s\n", duk_to_string(ctx, 0));
printf("final top: %d\n", duk_get_top(ctx));
return 0;
}
/* strict: error
* non-strict: return 0
*/
int test_new_not_extensible(duk_context *ctx) {
const char *src;
int rc;
printf("strict: %d\n", duk_is_strict_call(ctx));
src = "(function () { var o = { foo: 1 }; Object.preventExtensions(o); return o; })()";
duk_set_top(ctx, 0);
duk_push_string(ctx, src);
printf("eval:\n%s\n", duk_get_string(ctx, -1));
duk_eval(ctx);
printf("top after eval: %d\n", duk_get_top(ctx));
duk_push_string(ctx, "bar");
duk_push_string(ctx, "quux");
rc = duk_put_prop(ctx, -3);
printf("put rc=%d\n", rc);
duk_json_encode(ctx, 0);
printf("result: %s\n", duk_to_string(ctx, 0));
printf("final top: %d\n", duk_get_top(ctx));
return 0;
}
#define TEST(func) do { \
TEST_SAFE_CALL(func); \
TEST_PCALL(func); \
} while (0)
void test(duk_context *ctx) {
/*
* Cases where own property already exists
*/
TEST(test_ex_writable);
TEST(test_ex_nonwritable);
TEST(test_ex_accessor_wo_setter);
TEST(test_ex_setter_throws);
/*
* Cases where no own property, possibly ancestor
* property of same name
*/
TEST(test_new_extensible);
TEST(test_new_not_extensible);
}