Browse Source

Merge branch 'make-func-name-writable'

Make Function.prototype.name writable so that user code can set a "name"
property for Duktape/C functions.  Duktape/C functions don't have a "name"
property by default and a non-writable ancestor prevents creating one.

Fixes GH-79.
pull/85/head
Sami Vaarala 10 years ago
parent
commit
99807ae89b
  1. 3
      RELEASES.rst
  2. 55
      api-testcases/test-bug-set-cfunc-name.c
  3. 73
      api-testcases/test-dev-cfunc-name.c
  4. 12
      src/genbuiltins.py

3
RELEASES.rst

@ -658,6 +658,9 @@ Planned
* Change JSON.parse() to include a byte offset with a syntax error to help * Change JSON.parse() to include a byte offset with a syntax error to help
pinpoint JSON errors pinpoint JSON errors
* Make Function.prototype.name writable so that application code can set
a 'name' property on Duktape/C functions (GH-79)
* Use deep C stack for dukweb.js to remove some compiler recursion limit * Use deep C stack for dukweb.js to remove some compiler recursion limit
limitations (see GH-67) limitations (see GH-67)

55
api-testcases/test-bug-set-cfunc-name.c

@ -0,0 +1,55 @@
/*
* Duktape 1.0 issue: cannot set Duktape/C function 'name' property after
* creation.
*
* The cause: Function.prototype.name, containing an empty string, is
* non-writable. This is an unintended side effect of all built-in
* functions (such as Array, Math.cos, etc) having a non-writable name.
* Function.prototype, although never really called, is technically a
* function so its empty name is unintentionally non-writable.
*
* Fixed in Duktape 1.1 so that Function.prototype.name is writable.
* The best fix would actually be to have the property non-writable
* while simultaneously allowing a Function instance's 'name' property
* to be set - but Ecmascript cannot express such an access control
* policy.
*/
/*===
*** test_1 (duk_safe_call)
writable: true
enumerable: false
configurable: false
MyFunc.name: my_func_name
final top: 0
==> rc=0, result='undefined'
===*/
static duk_ret_t my_func(duk_context *ctx) {
(void) ctx;
return 0;
}
static duk_ret_t test_1(duk_context *ctx) {
/* Check that Function.prototype.name is writable. */
duk_eval_string_noresult(ctx,
"var pd = Object.getOwnPropertyDescriptor(Function.prototype, 'name');\n"
"print('writable:', pd.writable);\n"
"print('enumerable:', pd.enumerable);\n"
"print('configurable:', pd.configurable);\n");
duk_push_c_function(ctx, my_func, 0);
duk_push_string(ctx, "my_func_name");
duk_put_prop_string(ctx, -2, "name");
duk_put_global_string(ctx, "MyFunc");
duk_eval_string_noresult(ctx,
"print('MyFunc.name:', MyFunc.name);\n");
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
void test(duk_context *ctx) {
TEST_SAFE_CALL(test_1);
}

73
api-testcases/test-dev-cfunc-name.c

@ -0,0 +1,73 @@
/*
* A Duktape/C function does not have an automatic name in Duktape 1.x.
* You can set it yourself in Duktape 1.1 to get nicer tracebacks.
* In Duktape 1.0 Function.prototype.name is not writable so you can't
* do this.
*/
/*===
*** test_without_name (duk_safe_call)
my name is: ''
URIError: uri error (rc -106)
anon native strict preventsyield
forEach native strict preventsyield
eval XXX preventsyield
==> rc=0, result='undefined'
*** test_with_name (duk_safe_call)
my name is: 'my_func'
URIError: uri error (rc -106)
my_func native strict preventsyield
forEach native strict preventsyield
eval XXX preventsyield
==> rc=0, result='undefined'
===*/
static duk_ret_t my_func(duk_context *ctx) {
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, "name");
printf("my name is: '%s'\n", duk_safe_to_string(ctx, -1));
duk_pop_2(ctx);
return DUK_RET_URI_ERROR;
}
static duk_ret_t test_without_name(duk_context *ctx) {
duk_push_c_function(ctx, my_func, 0);
duk_put_global_string(ctx, "MyFunc");
duk_eval_string_noresult(ctx,
"try {\n"
" [1].forEach(MyFunc);\n"
"} catch (e) {\n"
" print(sanitize(e.stack || e));\n"
"}\n");
return 0;
}
static duk_ret_t test_with_name(duk_context *ctx) {
duk_get_global_string(ctx, "MyFunc");
duk_push_string(ctx, "my_func");
duk_put_prop_string(ctx, -2, "name");
duk_pop(ctx);
duk_eval_string_noresult(ctx,
"try {\n"
" [1].forEach(MyFunc);\n"
"} catch (e) {\n"
" print(sanitize(e.stack || e));\n"
"}\n");
return 0;
}
void test(duk_context *ctx) {
duk_eval_string_noresult(ctx,
"var sanitize = function(v) {\n"
" v = v.replace(/eval \\S+/, 'eval XXX');\n"
" return v;\n"
"}\n");
TEST_SAFE_CALL(test_without_name);
TEST_SAFE_CALL(test_with_name);
}

12
src/genbuiltins.py

@ -385,14 +385,22 @@ bi_function_prototype = {
'internal_prototype': 'bi_object_prototype', 'internal_prototype': 'bi_object_prototype',
'external_constructor': 'bi_function_constructor', 'external_constructor': 'bi_function_constructor',
'class': 'Function', 'class': 'Function',
'name': '', 'name': '', # dummy
'length': 0, 'length': 0,
'native': 'duk_bi_function_prototype', 'native': 'duk_bi_function_prototype',
'callable': True, 'callable': True,
'constructable': False, # Note: differs from other global Function classed objects (matches e.g. V8 behavior). 'constructable': False, # Note: differs from other global Function classed objects (matches e.g. V8 behavior).
'values': [], 'values': [
# Each built-in of class Function has a 'name' which is
# non-writable (the empty string above). Function.prototype
# is a special case: it is a function but we want it's name
# to be writable so that user code can set a 'name' property
# for Duktape/C functions. If the Function.prototype.name
# property were non-writable, that would be prevented.
{ 'name': 'name', 'value': '', 'attributes': 'w' }
],
'functions': [ 'functions': [
# test262 ch15/15.3/15.3.4/15.3.4.2/S15.3.4.2_A11 checks that Function.prototype.toString.length # test262 ch15/15.3/15.3.4/15.3.4.2/S15.3.4.2_A11 checks that Function.prototype.toString.length
# is zero, cannot find specification support for that but 0 is a good value. # is zero, cannot find specification support for that but 0 is a good value.

Loading…
Cancel
Save