Browse Source

First round of lightfunc changes

A lot of changes to add preliminary lightfunc support:

* Add LIGHTFUNC tagged type to duk_tval.h and API.

* Internal changes for preliminary to support lightfuncs in call handling
  and other operations (FIXMEs left in obvious places where support is
  still missing after this commit)

* Preliminary Ecmascript and API testcases for lightfuncs

Detailed notes:

* Because magic is signed, reading it back involves sign extension which is
  quite verbose to do in C.  Use macros for reading the magic value and other
  bit fields encoded in the flags.

* Function.prototype.bind(): the 'length' property of a bound function now
  comes out wrong.  We could simply look up the virtual 'length' property
  even if h_target is NULL: no extra code and binding is relatively rare in
  hot paths.  Rewrite more cleanly in any case.

* The use flag DUK_USE_LIGHTFUNC_BUILTINS controls the forced lightfunc
  conversion of built-ins.  This results in non-compliant built-ins but
  significant memory savings in very memory poor environments.

* Reject eval(), Thread.yield/resume as lightfuncs.  These functions have
  current assertions that they must be called as fully fledged functions.

* Lightfuncs are serialized like ordinary functions for JSON, JX, and JC
  by this diff.

* Add 'magic' to activation for lightfuncs.  It will be needed for lightweight
  functions: we don't have the duk_tval related to the lightfunc, so we must
  copy the magic value to the activation when a call is made.

* When lightfuncs are used as property lookup base values, continue property
  lookup from the Function.prototype object.  This is necessary to allow e.g.
  ``func.call()`` and ``func.apply()`` to be used.

* Call handling had to be reworked for lightfuncs, especially how bound
  function chains are handled.  This is a relatively large change but is
  necessary to support lightweight functions properly in bound function
  resolution.

  The current solution is not ideal.  The bytecode executor will first try an
  ecma-to-ecma call setup which resolves the bound function chain first.  If
  the final, unbound function is not viable (a native function) the call setup
  returns with an error code.  The caller will then perform a normal call.
  Although bound function resolution has already been done, the normal call
  handling code will re-do it (and detect there is nothing to do).

  This situation could be avoided by decoupling bound function handling and
  effective this binding computation from the actual call setup.  The caller
  could then to do this prestep first, and only then decide whether to use an
  ecma-to-ecma call or an ordinary heavyweight call.

  Remove duk__find_nonbound_function as unused.

* Use indirect magic to allow LIGHTFUNCs for Date.  Most of the built-in
  functions not directly eligible as lightfuncs are the Date built-in methods,
  whose magic values contain too much information to fit into the 8-bit magic
  of a LIGHTFUNC value.

  To work around this, add an array (duk__date_magics[]) containing the
  actual control flags needed by the built-ins, and make the Date built-in
  magic value an index into this table.  With this change Date built-ins are
  successfully converted to lightfuncs.

Testcase fixes:

- Whitespace fixes
- Print error for indirect eval error to make diagnosis easier
- Fix error string to match errmsg updated in this branch
pull/85/head
Sami Vaarala 11 years ago
parent
commit
fef0870107
  1. 76
      api-testcases/test-dev-lightfunc.c
  2. 2
      api-testcases/test-get-set-magic.c
  3. 4
      api-testcases/test-indirect-eval.c
  4. 10
      api-testcases/test-is-calls.c
  5. 44
      api-testcases/test-magic-modify-during-call.c
  6. 60
      ecmascript-testcases/test-bi-duktape-json-lightfunc.js
  7. 8
      ecmascript-testcases/test-dev-date-gmtutc-func.js
  8. 1079
      ecmascript-testcases/test-dev-lightfunc.js
  9. 6
      ecmascript-testcases/test-dev-notail-directive.js
  10. 36
      src/duk_api_call.c
  11. 7
      src/duk_api_internal.h
  12. 2
      src/duk_api_object.c
  13. 5
      src/duk_api_public.h.in
  14. 263
      src/duk_api_stack.c
  15. 145
      src/duk_bi_date.c
  16. 13
      src/duk_bi_duktape.c
  17. 18
      src/duk_bi_error.c
  18. 17
      src/duk_bi_function.c
  19. 16
      src/duk_bi_json.c
  20. 7
      src/duk_bi_logger.c
  21. 39
      src/duk_bi_object.c
  22. 20
      src/duk_bi_thread.c
  23. 3
      src/duk_debug_hobject.c
  24. 5
      src/duk_debug_vsnprintf.c
  25. 20
      src/duk_error_augment.c
  26. 1
      src/duk_features.h.in
  27. 2
      src/duk_heap_markandsweep.c
  28. 2
      src/duk_heap_refcount.c
  29. 108
      src/duk_hobject_props.c
  30. 16
      src/duk_hthread.h
  31. 73
      src/duk_hthread_builtins.c
  32. 103
      src/duk_hthread_stacks.c
  33. 2
      src/duk_js.h
  34. 264
      src/duk_js_call.c
  35. 4
      src/duk_js_compiler.c
  36. 269
      src/duk_js_executor.c
  37. 31
      src/duk_js_ops.c
  38. 12
      src/duk_js_var.c
  39. 86
      src/duk_tval.h
  40. 110
      src/genbuiltins.py

76
api-testcases/test-dev-lightfunc.c

@ -0,0 +1,76 @@
/*
* Behavior of lightweight functions in various situations.
*
* Also documents the detailed behavior and limitations of lightfuncs.
*/
/*===
FIXME
still here
===*/
/* FIXME: test all arg counts and lengths, including varargs */
/* FIXME: duk_to_object() coercion, stack policy */
/* FIXME: duk_push_current_function() for a lightfunc, check magic, perhaps length */
static duk_ret_t test_magic(duk_context *ctx) {
/* FIXME */
/* magic limits, C api test, check what happens if you exceed */
return 0;
}
static duk_ret_t test_enum(duk_context *ctx) {
/* FIXME: push lightfunc here instead of relying on a built-in */
duk_eval_string(ctx, "Math.max");
printf("enum defaults\n");
duk_enum(ctx, -1, 0);
while (duk_next(ctx, -1, 0 /*get_value*/)) {
printf("key: %s\n", duk_to_string(ctx, -1));
duk_pop(ctx);
}
duk_pop(ctx);
printf("top: %ld\n", (long) duk_get_top(ctx));
printf("enum nonenumerable\n");
duk_enum(ctx, -1, DUK_ENUM_INCLUDE_NONENUMERABLE);
while (duk_next(ctx, -1, 0 /*get_value*/)) {
printf("key: %s\n", duk_to_string(ctx, -1));
duk_pop(ctx);
}
duk_pop(ctx);
printf("top: %ld\n", (long) duk_get_top(ctx));
printf("enum own\n");
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY);
while (duk_next(ctx, -1, 0 /*get_value*/)) {
printf("key: %s\n", duk_to_string(ctx, -1));
duk_pop(ctx);
}
duk_pop(ctx);
printf("top: %ld\n", (long) duk_get_top(ctx));
printf("enum own non-enumerable\n");
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_INCLUDE_NONENUMERABLE);
while (duk_next(ctx, -1, 0 /*get_value*/)) {
printf("key: %s\n", duk_to_string(ctx, -1));
duk_pop(ctx);
}
duk_pop(ctx);
printf("top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
void test(duk_context *ctx) {
/* nargs / length limits, C api test, check what happens if you exceed */
/* Example of using lightfunc as a constructor, separate testcase, doc ref */
TEST_SAFE_CALL(test_magic);
TEST_SAFE_CALL(test_enum);
printf("still here\n");
fflush(stdout);
}

2
api-testcases/test-get-set-magic.c

@ -21,7 +21,7 @@ magic: -16657
final top: 2
==> rc=0, result='undefined'
*** test_2 (duk_safe_call)
==> rc=1, result='TypeError: not nativefunction'
==> rc=1, result='TypeError: unexpected type'
*** test_3 (duk_safe_call)
==> rc=1, result='TypeError: not nativefunction'
*** test_4 (duk_safe_call)

4
api-testcases/test-indirect-eval.c

@ -22,6 +22,10 @@ void test(duk_context *ctx) {
duk_get_prop_string(ctx, -1, "testFunc");
rc = duk_pcall(ctx, 0);
printf("rc=%d\n", (int) rc);
if (rc != 0) {
/* unexpected error */
printf("error=%s\n", duk_safe_to_string(ctx, -1));
}
duk_pop_2(ctx);
printf("final top: %ld\n", (long) duk_get_top(ctx));

10
api-testcases/test-is-calls.c

@ -1,4 +1,5 @@
/*===
*** test_1 (duk_safe_call)
00: und=1 null=0 noru=1 bool=0 num=0 nan=0 str=0 obj=0 arr=0 fun=0 cfun=0 efun=0 bfun=0 call=0 thr=0 buf=0 dyn=0 fix=0 ptr=0 prim=1 objcoerc=0
01: und=0 null=1 noru=1 bool=0 num=0 nan=0 str=0 obj=0 arr=0 fun=0 cfun=0 efun=0 bfun=0 call=0 thr=0 buf=0 dyn=0 fix=0 ptr=0 prim=1 objcoerc=0
02: und=0 null=0 noru=0 bool=1 num=0 nan=0 str=0 obj=0 arr=0 fun=0 cfun=0 efun=0 bfun=0 call=0 thr=0 buf=0 dyn=0 fix=0 ptr=0 prim=1 objcoerc=1
@ -19,6 +20,7 @@
17: und=0 null=0 noru=0 bool=0 num=0 nan=0 str=0 obj=0 arr=0 fun=0 cfun=0 efun=0 bfun=0 call=0 thr=0 buf=1 dyn=0 fix=1 ptr=0 prim=1 objcoerc=1
18: und=0 null=0 noru=0 bool=0 num=0 nan=0 str=0 obj=0 arr=0 fun=0 cfun=0 efun=0 bfun=0 call=0 thr=0 buf=1 dyn=1 fix=0 ptr=0 prim=1 objcoerc=1
19: und=0 null=0 noru=0 bool=0 num=0 nan=0 str=0 obj=0 arr=0 fun=0 cfun=0 efun=0 bfun=0 call=0 thr=0 buf=0 dyn=0 fix=0 ptr=1 prim=1 objcoerc=1
==> rc=0, result='undefined'
===*/
#include <math.h>
@ -27,7 +29,7 @@ static duk_ret_t my_c_func(duk_context *ctx) {
return 0;
}
void test(duk_context *ctx) {
static duk_ret_t test_1(duk_context *ctx) {
duk_idx_t i, n;
/*
@ -126,4 +128,10 @@ void test(duk_context *ctx) {
printf(" objcoerc=%d", (int) duk_is_object_coercible(ctx, i));
printf("\n");
}
return 0;
}
void test(duk_context *ctx) {
TEST_SAFE_CALL(test_1);
}

44
api-testcases/test-magic-modify-during-call.c

@ -0,0 +1,44 @@
/*
* If a function modifies its own 'magic' value while it is executing,
* the change is visible immediately.
*
* This is not necessarily stable behavior so user code should not rely
* on this. The current behavior is a side effect of the way the current
* call stack state keeping is implemented. This test case just documents
* the current behavior.
*/
/*===
*** test_1 (duk_safe_call)
current magic (on entry): 345
current magic (after set): 456
final top: 1
==> rc=0, result='undefined'
===*/
static duk_ret_t my_func(duk_context *ctx) {
printf("current magic (on entry): %ld\n", (long) duk_get_current_magic(ctx));
duk_push_current_function(ctx);
duk_set_magic(ctx, -1, 456);
printf("current magic (after set): %ld\n", (long) duk_get_current_magic(ctx));
return 0;
}
static duk_ret_t test_1(duk_context *ctx) {
duk_push_c_function(ctx, my_func, 2 /*nargs*/);
duk_set_magic(ctx, -1, 345);
duk_push_int(ctx, 123);
duk_push_int(ctx, 234);
duk_call(ctx, 2);
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
void test(duk_context *ctx) {
TEST_SAFE_CALL(test_1);
}

60
ecmascript-testcases/test-bi-duktape-json-lightfunc.js

@ -0,0 +1,60 @@
/*
* JSON, JX, and JC serialization of lightfuncs.
*/
/*---
{
"custom": true,
"specialoptions": "DUK_OPT_LIGHTFUNC_BUILTINS"
}
---*/
/*===
undefined undefined
string [1,2,3,null,4,5,6]
string {"foo":123}
string {_func:true}
string [1,2,3,{_func:true},4,5,6]
string {foo:123,bar:{_func:true}}
string {"_func":true}
string [1,2,3,{"_func":true},4,5,6]
string {"foo":123,"bar":{"_func":true}}
===*/
function test() {
// FIXME: rely on Math.cos being a lightfunc
var lf = Math.cos;
function json(x) {
var res = JSON.stringify(x);
print(typeof res, res);
}
function jx(x) {
var res = Duktape.enc('jx', x);
print(typeof res, res);
}
function jc(x) {
var res = Duktape.enc('jc', x);
print(typeof res, res);
}
json(lf);
json([ 1, 2, 3, lf, 4, 5, 6 ]);
json({ foo: 123, bar: lf });
jx(lf);
jx([ 1, 2, 3, lf, 4, 5, 6 ]);
jx({ foo: 123, bar: lf });
jc(lf);
jc([ 1, 2, 3, lf, 4, 5, 6 ]);
jc({ foo: 123, bar: lf });
}
try {
test();
} catch (e) {
print(e);
}

8
ecmascript-testcases/test-dev-date-gmtutc-func.js

@ -1,7 +1,3 @@
/*===
true
===*/
/*
* Date.prototype.toGMTString is required to have the same Function
* object as Date.prototype.toUTCString in E5 Section B.2.6.
@ -10,4 +6,8 @@ true
* objects are distinct).
*/
/*===
true
===*/
print(Date.prototype.toGMTString === Date.prototype.toUTCString);

1079
ecmascript-testcases/test-dev-lightfunc.js

File diff suppressed because it is too large

6
ecmascript-testcases/test-dev-notail-directive.js

@ -11,8 +11,9 @@ test2: act test2func test2 useNotailDirectiveTest
function test1func() {
// tailcall, call stack indices:
// -1 = Duktape.act, -2 = test1func, -3 = useNotailDirectiveTest
// Duktape.act.name is not printed directly because it may be a lightfunc
print('test1:',
Duktape.act(-1).function.name,
Duktape.act(-1).function === Duktape.act ? 'act' : '???',
Duktape.act(-2).function.name,
Duktape.act(-3).function.name);
}
@ -26,8 +27,9 @@ function test2func() {
// no tailcall, call stack indices:
// -1 = Duktape.act, -2 = test1func, -3 = test1, -4 = useNotailDirectiveTest
// Duktape.act.name is not printed directly because it may be a lightfunc
print('test2:',
Duktape.act(-1).function.name,
Duktape.act(-1).function === Duktape.act ? 'act' : '???',
Duktape.act(-2).function.name,
Duktape.act(-3).function.name,
Duktape.act(-4).function.name);

36
src/duk_api_call.c

@ -449,7 +449,13 @@ DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_context *ctx) {
act = duk_hthread_get_current_activation(thr);
if (act) {
func = act->func;
func = DUK_ACT_GET_FUNC(act);
if (!func) {
duk_tval *tv = &act->tv_func;
duk_small_uint_t lf_flags;
lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv);
return (duk_int_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags);
}
DUK_ASSERT(func != NULL);
if (DUK_HOBJECT_IS_NATIVEFUNCTION(func)) {
@ -461,13 +467,33 @@ DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_context *ctx) {
}
DUK_EXTERNAL duk_int_t duk_get_magic(duk_context *ctx, duk_idx_t index) {
duk_hnativefunction *nf;
duk_hthread *thr = (duk_hthread *) ctx;
duk_tval *tv;
duk_hobject *h;
DUK_ASSERT(ctx != NULL);
nf = duk_require_hnativefunction(ctx, index);
DUK_ASSERT(nf != NULL);
return (duk_int_t) nf->magic;
/* FIXME: hardcoded values for flag partitioning */
tv = duk_require_tval(ctx, index);
if (DUK_TVAL_IS_OBJECT(tv)) {
h = DUK_TVAL_GET_OBJECT(tv);
DUK_ASSERT(h != NULL);
if (!DUK_HOBJECT_HAS_NATIVEFUNCTION(h)) {
goto type_error;
}
return (duk_int_t) ((duk_hnativefunction *) h)->magic;
} else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
/* FIXME: use shared macro */
duk_int32_t tmp = (duk_int32_t) (DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv) >> 8);
tmp = (tmp << 16) >> 24; /* 0x0000##00 -> 0x##000000 -> sign extend to 0xssssss## */
return (duk_int_t) tmp;
}
/* fall through */
type_error:
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_UNEXPECTED_TYPE);
return 0;
}
DUK_EXTERNAL void duk_set_magic(duk_context *ctx, duk_idx_t index, duk_int_t magic) {

7
src/duk_api_internal.h

@ -99,6 +99,9 @@ DUK_INTERNAL_DECL duk_hnativefunction *duk_require_hnativefunction(duk_context *
DUK_TAG_OBJECT | \
DUK_GETTAGGED_FLAG_CHECK_CLASS | ((classnum) << DUK_GETTAGGED_CLASS_SHIFT)))
DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_or_lfunc(duk_context *ctx, duk_idx_t index);
DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_or_lfunc_coerce(duk_context *ctx, duk_idx_t index);
#if 0 /*unused*/
DUK_INTERNAL_DECL void duk_push_unused(duk_context *ctx);
#endif
@ -120,6 +123,10 @@ DUK_INTERNAL_DECL duk_idx_t duk_push_compiledfunction(duk_context *ctx);
DUK_INTERNAL_DECL void duk_push_c_function_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs);
DUK_INTERNAL_DECL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs);
DUK_INTERNAL_DECL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, duk_size_t sz);
DUK_INTERNAL_DECL void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv);
DUK_INTERNAL_DECL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv);
DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx); /* [] -> [val] */
DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx); /* [val] -> [] */
DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx); /* [] -> [] */

2
src/duk_api_object.c

@ -357,8 +357,8 @@ DUK_EXTERNAL void duk_compact(duk_context *ctx, duk_idx_t obj_index) {
DUK_EXTERNAL void duk_enum(duk_context *ctx, duk_idx_t obj_index, duk_uint_t enum_flags) {
DUK_ASSERT(ctx != NULL);
duk_require_hobject(ctx, obj_index);
duk_dup(ctx, obj_index);
duk_require_hobject_or_lfunc_coerce(ctx, -1);
duk_hobject_enumerator_create(ctx, enum_flags); /* [target] -> [enum] */
}

5
src/duk_api_public.h.in

@ -115,6 +115,7 @@ struct duk_number_list_entry {
#define DUK_TYPE_OBJECT 6 /* Ecmascript object: includes objects, arrays, functions, threads */
#define DUK_TYPE_BUFFER 7 /* fixed or dynamic, garbage collected byte buffer */
#define DUK_TYPE_POINTER 8 /* raw void pointer */
#define DUK_TYPE_LIGHTFUNC 9 /* lightweight function pointer */
/* Value mask types, used by e.g. duk_get_type_mask() */
#define DUK_TYPE_MASK_NONE (1 << DUK_TYPE_NONE)
@ -126,6 +127,7 @@ struct duk_number_list_entry {
#define DUK_TYPE_MASK_OBJECT (1 << DUK_TYPE_OBJECT)
#define DUK_TYPE_MASK_BUFFER (1 << DUK_TYPE_BUFFER)
#define DUK_TYPE_MASK_POINTER (1 << DUK_TYPE_POINTER)
#define DUK_TYPE_MASK_LIGHTFUNC (1 << DUK_TYPE_LIGHTFUNC)
#define DUK_TYPE_MASK_THROW (1 << 10) /* internal flag value: throw if mask doesn't match */
/* Coercion hints */
@ -436,7 +438,8 @@ DUK_EXTERNAL_DECL duk_bool_t duk_is_primitive(duk_context *ctx, duk_idx_t index)
DUK_TYPE_MASK_STRING | \
DUK_TYPE_MASK_OBJECT | \
DUK_TYPE_MASK_BUFFER | \
DUK_TYPE_MASK_POINTER)
DUK_TYPE_MASK_POINTER | \
DUK_TYPE_MASK_LIGHTFUNC)
DUK_EXTERNAL_DECL duk_errcode_t duk_get_error_code(duk_context *ctx, duk_idx_t index);
#define duk_is_error(ctx,index) \

263
src/duk_api_stack.c

@ -12,6 +12,12 @@
#include "duk_internal.h"
/*
* Forward declarations
*/
static duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_uint_t flags);
/*
* Global state for working around missing variadic macros
*/
@ -1420,7 +1426,8 @@ DUK_EXTERNAL void *duk_require_heapptr(duk_context *ctx, duk_idx_t index) {
DUK_ASSERT(ctx != NULL);
tv = duk_require_tval(ctx, index);
if (tv && DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
DUK_ASSERT(tv != NULL);
if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
ret = (void *) DUK_TVAL_GET_HEAPHDR(tv);
DUK_ASSERT(ret != NULL);
return ret;
@ -1430,6 +1437,49 @@ DUK_EXTERNAL void *duk_require_heapptr(duk_context *ctx, duk_idx_t index) {
return (void *) NULL; /* not reachable */
}
/* Useful for internal call sites where we either expect an object (function)
* or a lightfunc. Returns NULL for a lightfunc.
*/
DUK_INTERNAL duk_hobject *duk_require_hobject_or_lfunc(duk_context *ctx, duk_idx_t index) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_tval *tv;
DUK_ASSERT(ctx != NULL);
tv = duk_require_tval(ctx, index);
DUK_ASSERT(tv != NULL);
if (DUK_TVAL_IS_OBJECT(tv)) {
return DUK_TVAL_GET_OBJECT(tv);
} else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
return NULL;
}
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_UNEXPECTED_TYPE);
return NULL; /* not reachable */
}
/* Useful for internal call sites where we either expect an object (function)
* or a lightfunc. Accepts an object (returned as is) or a lightfunc (coerced
* to an object). Return value is never NULL.
*/
DUK_INTERNAL duk_hobject *duk_require_hobject_or_lfunc_coerce(duk_context *ctx, duk_idx_t index) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_tval *tv;
DUK_ASSERT(ctx != NULL);
tv = duk_require_tval(ctx, index);
if (DUK_TVAL_IS_OBJECT(tv)) {
return DUK_TVAL_GET_OBJECT(tv);
} else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
duk_to_object(ctx, index);
return duk_require_hobject(ctx, index);
}
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_UNEXPECTED_TYPE);
return NULL; /* not reachable */
}
DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t index) {
duk_tval *tv;
@ -1461,6 +1511,10 @@ DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t index) {
DUK_ASSERT(h != NULL);
return (duk_size_t) DUK_HBUFFER_GET_SIZE(h);
}
case DUK_TAG_LIGHTFUNC: {
/* FIXME: return value for virtual length property */
return 0;
}
default:
/* number */
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
@ -1902,6 +1956,11 @@ DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t index) {
}
break;
}
case DUK_TAG_LIGHTFUNC: {
/* Should match Function.prototype.toString() */
duk_push_lightfunc_tostring(ctx, tv);
break;
}
default: {
/* number */
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
@ -2021,6 +2080,12 @@ DUK_EXTERNAL void *duk_to_pointer(duk_context *ctx, duk_idx_t index) {
*/
res = (void *) DUK_TVAL_GET_HEAPHDR(tv);
break;
case DUK_TAG_LIGHTFUNC:
/* FIXME: function pointers may not cast correctly to void *,
* but try anyway?
*/
res = NULL;
break;
default:
/* number */
res = NULL;
@ -2035,8 +2100,8 @@ DUK_EXTERNAL void *duk_to_pointer(duk_context *ctx, duk_idx_t index) {
DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_tval *tv;
duk_uint_t shared_flags = 0; /* shared flags for a subset of types */
duk_small_int_t shared_proto = 0;
duk_uint_t flags = 0; /* shared flags for a subset of types */
duk_small_int_t proto = 0;
DUK_ASSERT(ctx != NULL);
@ -2052,16 +2117,16 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) {
break;
}
case DUK_TAG_BOOLEAN: {
shared_flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BOOLEAN);
shared_proto = DUK_BIDX_BOOLEAN_PROTOTYPE;
flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BOOLEAN);
proto = DUK_BIDX_BOOLEAN_PROTOTYPE;
goto create_object;
}
case DUK_TAG_STRING: {
shared_flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING);
shared_proto = DUK_BIDX_STRING_PROTOTYPE;
flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING);
proto = DUK_BIDX_STRING_PROTOTYPE;
goto create_object;
}
case DUK_TAG_OBJECT: {
@ -2069,29 +2134,78 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) {
break;
}
case DUK_TAG_BUFFER: {
shared_flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_EXOTIC_BUFFEROBJ |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER);
shared_proto = DUK_BIDX_BUFFER_PROTOTYPE;
flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_EXOTIC_BUFFEROBJ |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER);
proto = DUK_BIDX_BUFFER_PROTOTYPE;
goto create_object;
}
case DUK_TAG_POINTER: {
shared_flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER);
shared_proto = DUK_BIDX_POINTER_PROTOTYPE;
flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER);
proto = DUK_BIDX_POINTER_PROTOTYPE;
goto create_object;
}
case DUK_TAG_LIGHTFUNC: {
/* Lightfunc coerces to a Function instance with concrete
* properties. Since 'length' is virtual for Duktape/C
* functions, don't need to define that.
*
* The result is made extensible to mimic what happens to
* strings:
* > Object.isExtensible(Object('foo'))
* true
*/
duk_small_uint_t lf_flags;
duk_small_uint_t nargs;
duk_small_uint_t lf_len;
duk_c_function func;
duk_hnativefunction *nf;
DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);
nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags);
if (nargs == 0x0f) {
nargs = DUK_VARARGS;
}
flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_CONSTRUCTABLE |
DUK_HOBJECT_FLAG_NATIVEFUNCTION |
DUK_HOBJECT_FLAG_NEWENV |
DUK_HOBJECT_FLAG_STRICT |
DUK_HOBJECT_FLAG_NOTAIL |
/* DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC: omitted here intentionally */
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
(void) duk__push_c_function_raw(ctx, func, (duk_idx_t) nargs, flags);
lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
if (lf_len != nargs) {
/* Explicit length is only needed if it differs from 'nargs'. */
duk_push_int(ctx, (duk_int_t) lf_len);
duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);
}
duk_push_lightfunc_name(ctx, tv);
duk_def_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);
nf = duk_get_hnativefunction(ctx, -1);
DUK_ASSERT(nf != NULL);
nf->magic = (duk_int16_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags);
/* Enable DUKFUNC exotic behavior once properties are set up. */
DUK_HOBJECT_SET_EXOTIC_DUKFUNC((duk_hobject *) nf);
goto replace_value;
}
default: {
shared_flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER);
shared_proto = DUK_BIDX_NUMBER_PROTOTYPE;
proto = DUK_BIDX_NUMBER_PROTOTYPE;
goto create_object;
}
}
return;
create_object:
(void) duk_push_object_helper(ctx, shared_flags, shared_proto);
(void) duk_push_object_helper(ctx, flags, proto);
/* Note: Boolean prototype's internal value property is not writable,
* but duk_def_prop_stridx() disregards the write protection. Boolean
@ -2103,6 +2217,7 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) {
duk_dup(ctx, index);
duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
replace_value:
duk_replace(ctx, index);
}
@ -2154,6 +2269,8 @@ DUK_EXTERNAL duk_int_t duk_get_type(duk_context *ctx, duk_idx_t index) {
return DUK_TYPE_BUFFER;
case DUK_TAG_POINTER:
return DUK_TYPE_POINTER;
case DUK_TAG_LIGHTFUNC:
return DUK_TYPE_LIGHTFUNC;
default:
/* Note: number has no explicit tag (in 8-byte representation) */
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
@ -2188,6 +2305,8 @@ DUK_EXTERNAL duk_uint_t duk_get_type_mask(duk_context *ctx, duk_idx_t index) {
return DUK_TYPE_MASK_BUFFER;
case DUK_TAG_POINTER:
return DUK_TYPE_MASK_POINTER;
case DUK_TAG_LIGHTFUNC:
return DUK_TYPE_MASK_LIGHTFUNC;
default:
/* Note: number has no explicit tag (in 8-byte representation) */
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
@ -2304,6 +2423,10 @@ DUK_EXTERNAL duk_bool_t duk_is_array(duk_context *ctx, duk_idx_t index) {
}
DUK_EXTERNAL duk_bool_t duk_is_function(duk_context *ctx, duk_idx_t index) {
duk_tval *tv = duk_get_tval(ctx, index);
if (tv && DUK_TVAL_IS_LIGHTFUNC(tv)) {
return 1;
}
return duk__obj_flag_any_default_false(ctx,
index,
DUK_HOBJECT_FLAG_COMPILEDFUNCTION |
@ -2337,11 +2460,7 @@ DUK_EXTERNAL duk_bool_t duk_is_thread(duk_context *ctx, duk_idx_t index) {
DUK_EXTERNAL duk_bool_t duk_is_callable(duk_context *ctx, duk_idx_t index) {
/* XXX: currently same as duk_is_function() */
return duk__obj_flag_any_default_false(ctx,
index,
DUK_HOBJECT_FLAG_COMPILEDFUNCTION |
DUK_HOBJECT_FLAG_NATIVEFUNCTION |
DUK_HOBJECT_FLAG_BOUND);
return duk_is_function(ctx, index);
}
DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t index) {
@ -2704,16 +2823,27 @@ DUK_INTERNAL duk_hstring *duk_push_this_coercible_to_string(duk_context *ctx) {
DUK_EXTERNAL void duk_push_current_function(duk_context *ctx) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_activation *act;
duk_hobject *func;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(ctx != NULL);
DUK_ASSERT_DISABLE(thr->callstack_top >= 0);
DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
/* FIXME: lightfunc handling: perhaps we'll need the lightfunc
* ptr after all... with all the duk_tval fields.
*/
act = duk_hthread_get_current_activation(thr);
if (act) {
DUK_ASSERT(act->func != NULL);
duk_push_hobject(ctx, act->func);
/* FIXME: change if tv_func gets always initialized */
func = DUK_ACT_GET_FUNC(act);
if (func) {
duk_push_hobject(ctx, func);
} else {
DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(&act->tv_func));
duk_push_tval(ctx, &act->tv_func);
}
} else {
duk_push_undefined(ctx);
}
@ -3494,6 +3624,10 @@ DUK_EXTERNAL void duk_error_stash(duk_context *ctx, duk_errcode_t err_code, cons
}
#endif
/*
* Comparison
*/
DUK_EXTERNAL duk_bool_t duk_equals(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_tval *tv1, *tv2;
@ -3528,3 +3662,78 @@ DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_context *ctx, duk_idx_t index1, du
/* No coercions or other side effects, so safe */
return duk_js_strict_equals(tv1, tv2);
}
/*
* Lightfunc
*/
void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv) {
/* FIXME: add pointer, and perhaps flags here? */
duk_c_function func;
DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv));
/* Lightfunc name, includes Duktape/C native function pointer, which
* can often be used to locate the function from a symbol table.
* The name also includes the 16-bit duk_tval flags field because it
* includes the magic value. Because a single native function often
* provides different functionality depending on the magic value, it
* seems reasonably to include it in the name.
*
* On the other hand, a complicated name increases string table
* pressure in low memory environments (but only when function name
* is accessed).
*/
#if 1
func = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv);
duk_push_sprintf(ctx, "light_");
duk_push_string_funcptr(ctx, (duk_uint8_t *) &func, sizeof(func));
duk_push_sprintf(ctx, "_%04x", (unsigned int) DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv));
duk_concat(ctx, 3);
#else
duk_push_string(ctx, "light");
#endif
}
void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv) {
DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv));
duk_push_string(ctx, "function ");
duk_push_lightfunc_name(ctx, tv);
duk_push_string(ctx, "() {/* light */}");
duk_concat(ctx, 3);
}
/*
* Function pointers
*
* Printing function pointers is non-portable, so we do that by hex printing
* bytes from memory.
*/
void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, duk_size_t sz) {
duk_uint8_t buf[32 * 2];
duk_uint8_t *p, *q;
duk_small_uint_t i;
duk_small_uint_t t;
DUK_ASSERT(sz <= 32); /* sanity limit for function pointer size */
p = buf;
#if defined(DUK_USE_INTEGER_LE)
q = ptr + sz;
#else
q = ptr;
#endif
for (i = 0; i < sz; i++) {
#if defined(DUK_USE_INTEGER_LE)
t = *(--q);
#else
t = *(q++);
#endif
*p++ = duk_lc_digits[t >> 4];
*p++ = duk_lc_digits[t & 0x0f];
}
duk_push_lstring(ctx, (const char *) buf, sz * 2);
}

145
src/duk_bi_date.c

@ -1747,6 +1747,145 @@ DUK_INTERNAL void duk_bi_date_format_timeval(duk_double_t timeval, duk_uint8_t *
out_buf);
}
/*
* Indirect magic value lookup for Date methods.
*
* Date methods don't put their control flags into the function magic value
* because they wouldn't fit into a LIGHTFUNC's magic field. Instead, the
* magic value is set to an index pointing to the array of control flags
* below.
*
* This must be kept in strict sync with genbuiltins.py!
*/
static duk_uint16_t duk__date_magics[] = {
/* 0: toString */
DUK__FLAG_TOSTRING_DATE + DUK__FLAG_TOSTRING_TIME + DUK__FLAG_LOCALTIME,
/* 1: toDateString */
DUK__FLAG_TOSTRING_DATE + DUK__FLAG_LOCALTIME,
/* 2: toTimeString */
DUK__FLAG_TOSTRING_TIME + DUK__FLAG_LOCALTIME,
/* 3: toLocaleString */
DUK__FLAG_TOSTRING_DATE + DUK__FLAG_TOSTRING_TIME + DUK__FLAG_TOSTRING_LOCALE + DUK__FLAG_LOCALTIME,
/* 4: toLocaleDateString */
DUK__FLAG_TOSTRING_DATE + DUK__FLAG_TOSTRING_LOCALE + DUK__FLAG_LOCALTIME,
/* 5: toLocaleTimeString */
DUK__FLAG_TOSTRING_TIME + DUK__FLAG_TOSTRING_LOCALE + DUK__FLAG_LOCALTIME,
/* 6: toUTCString */
DUK__FLAG_TOSTRING_DATE + DUK__FLAG_TOSTRING_TIME,
/* 7: toISOString */
DUK__FLAG_TOSTRING_DATE + DUK__FLAG_TOSTRING_TIME + DUK__FLAG_NAN_TO_RANGE_ERROR + DUK__FLAG_SEP_T,
/* 8: getFullYear */
DUK__FLAG_LOCALTIME + (DUK__IDX_YEAR << DUK__FLAG_VALUE_SHIFT),
/* 9: getUTCFullYear */
0 + (DUK__IDX_YEAR << DUK__FLAG_VALUE_SHIFT),
/* 10: getMonth */
DUK__FLAG_LOCALTIME + (DUK__IDX_MONTH << DUK__FLAG_VALUE_SHIFT),
/* 11: getUTCMonth */
0 + (DUK__IDX_MONTH << DUK__FLAG_VALUE_SHIFT),
/* 12: getDate */
DUK__FLAG_ONEBASED + DUK__FLAG_LOCALTIME + (DUK__IDX_DAY << DUK__FLAG_VALUE_SHIFT),
/* 13: getUTCDate */
DUK__FLAG_ONEBASED + (DUK__IDX_DAY << DUK__FLAG_VALUE_SHIFT),
/* 14: getDay */
DUK__FLAG_LOCALTIME + (DUK__IDX_WEEKDAY << DUK__FLAG_VALUE_SHIFT),
/* 15: getUTCDay */
0 + (DUK__IDX_WEEKDAY << DUK__FLAG_VALUE_SHIFT),
/* 16: getHours */
DUK__FLAG_LOCALTIME + (DUK__IDX_HOUR << DUK__FLAG_VALUE_SHIFT),
/* 17: getUTCHours */
0 + (DUK__IDX_HOUR << DUK__FLAG_VALUE_SHIFT),
/* 18: getMinutes */
DUK__FLAG_LOCALTIME + (DUK__IDX_MINUTE << DUK__FLAG_VALUE_SHIFT),
/* 19: getUTCMinutes */
0 + (DUK__IDX_MINUTE << DUK__FLAG_VALUE_SHIFT),
/* 20: getSeconds */
DUK__FLAG_LOCALTIME + (DUK__IDX_SECOND << DUK__FLAG_VALUE_SHIFT),
/* 21: getUTCSeconds */
0 + (DUK__IDX_SECOND << DUK__FLAG_VALUE_SHIFT),
/* 22: getMilliseconds */
DUK__FLAG_LOCALTIME + (DUK__IDX_MILLISECOND << DUK__FLAG_VALUE_SHIFT),
/* 23: getUTCMilliseconds */
0 + (DUK__IDX_MILLISECOND << DUK__FLAG_VALUE_SHIFT),
/* 24: setMilliseconds */
DUK__FLAG_TIMESETTER + DUK__FLAG_LOCALTIME + (1 << DUK__FLAG_VALUE_SHIFT),
/* 25: setUTCMilliseconds */
DUK__FLAG_TIMESETTER + (1 << DUK__FLAG_VALUE_SHIFT),
/* 26: setSeconds */
DUK__FLAG_TIMESETTER + DUK__FLAG_LOCALTIME + (2 << DUK__FLAG_VALUE_SHIFT),
/* 27: setUTCSeconds */
DUK__FLAG_TIMESETTER + (2 << DUK__FLAG_VALUE_SHIFT),
/* 28: setMinutes */
DUK__FLAG_TIMESETTER + DUK__FLAG_LOCALTIME + (3 << DUK__FLAG_VALUE_SHIFT),
/* 29: setUTCMinutes */
DUK__FLAG_TIMESETTER + (3 << DUK__FLAG_VALUE_SHIFT),
/* 30: setHours */
DUK__FLAG_TIMESETTER + DUK__FLAG_LOCALTIME + (4 << DUK__FLAG_VALUE_SHIFT),
/* 31: setUTCHours */
DUK__FLAG_TIMESETTER + (4 << DUK__FLAG_VALUE_SHIFT),
/* 32: setDate */
DUK__FLAG_LOCALTIME + (1 << DUK__FLAG_VALUE_SHIFT),
/* 33: setUTCDate */
0 + (1 << DUK__FLAG_VALUE_SHIFT),
/* 34: setMonth */
DUK__FLAG_LOCALTIME + (2 << DUK__FLAG_VALUE_SHIFT),
/* 35: setUTCMonth */
0 + (2 << DUK__FLAG_VALUE_SHIFT),
/* 36: setFullYear */
DUK__FLAG_NAN_TO_ZERO + DUK__FLAG_LOCALTIME + (3 << DUK__FLAG_VALUE_SHIFT),
/* 37: setUTCFullYear */
DUK__FLAG_NAN_TO_ZERO + (3 << DUK__FLAG_VALUE_SHIFT),
/* 38: getYear */
DUK__FLAG_LOCALTIME + DUK__FLAG_SUB1900 + (DUK__IDX_YEAR << DUK__FLAG_VALUE_SHIFT),
/* 39: setYear */
DUK__FLAG_NAN_TO_ZERO + DUK__FLAG_YEAR_FIXUP + (3 << DUK__FLAG_VALUE_SHIFT),
};
duk_small_uint_t duk__date_get_indirect_magic(duk_context *ctx) {
duk_small_int_t magicidx = (duk_small_uint_t) duk_get_current_magic(ctx);
DUK_ASSERT(magicidx >= 0 && magicidx < (duk_small_int_t) (sizeof(duk__date_magics) / sizeof(duk_uint16_t)));
return (duk_small_uint_t) duk__date_magics[magicidx];
}
/*
* Constructor calls
*/
@ -1865,7 +2004,7 @@ DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_context *ctx) {
*/
DUK_INTERNAL duk_ret_t duk_bi_date_prototype_tostring_shared(duk_context *ctx) {
duk_small_uint_t flags = (duk_small_uint_t) duk_get_current_magic(ctx);
duk_small_uint_t flags = duk__date_get_indirect_magic(ctx);
return duk__to_string_helper(ctx, flags);
}
@ -1947,7 +2086,7 @@ DUK_INTERNAL duk_ret_t duk_bi_date_prototype_to_json(duk_context *ctx) {
*/
DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_shared(duk_context *ctx) {
duk_small_uint_t flags_and_idx = (duk_small_uint_t) duk_get_current_magic(ctx);
duk_small_uint_t flags_and_idx = duk__date_get_indirect_magic(ctx);
return duk__get_part_helper(ctx, flags_and_idx);
}
@ -2032,7 +2171,7 @@ DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ct
*/
DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_shared(duk_context *ctx) {
duk_small_uint_t flags_and_maxnargs = (duk_small_uint_t) duk_get_current_magic(ctx);
duk_small_uint_t flags_and_maxnargs = duk__date_get_indirect_magic(ctx);
return duk__set_part_helper(ctx, flags_and_maxnargs);
}

13
src/duk_bi_duktape.c

@ -139,9 +139,16 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_act(duk_context *ctx) {
duk_push_object(ctx);
h_func = act->func;
DUK_ASSERT(h_func != NULL);
duk_push_hobject(ctx, h_func);
/* FIXME: push act->tv_func for lightfuncs, or perhaps tv_func just
* directly if it is changed to be always initialized.
*/
h_func = DUK_ACT_GET_FUNC(act);
if (!h_func) {
DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(&act->tv_func));
duk_push_tval(ctx, &act->tv_func);
} else {
duk_push_hobject(ctx, h_func);
}
pc = (duk_uint_fast32_t) act->pc;
duk_push_uint(ctx, (duk_uint_t) pc);

18
src/duk_bi_error.c

@ -165,15 +165,14 @@ DUK_LOCAL duk_ret_t duk__traceback_getter_helper(duk_context *ctx, duk_small_int
flags = (duk_int_t) DUK_FLOOR(d / DUK_DOUBLE_2TO32);
t = (duk_small_int_t) duk_get_type(ctx, -2);
if (t == DUK_TYPE_OBJECT) {
if (t == DUK_TYPE_OBJECT || t == DUK_TYPE_LIGHTFUNC) {
/*
* Ecmascript/native function call
* Ecmascript/native function call or lightfunc call
*/
/* [ ... v1(func) v2(pc+flags) ] */
h_func = duk_get_hobject(ctx, -2);
DUK_ASSERT(h_func != NULL);
h_func = duk_get_hobject(ctx, -2); /* NULL for lightfunc */
duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME);
duk_get_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME);
@ -201,7 +200,15 @@ DUK_LOCAL duk_ret_t duk__traceback_getter_helper(duk_context *ctx, duk_small_int
DUK_ASSERT(funcname != NULL);
DUK_ASSERT(filename != NULL);
if (DUK_HOBJECT_HAS_NATIVEFUNCTION(h_func)) {
if (h_func == NULL) {
duk_push_sprintf(ctx, "%s light%s%s%s%s%s",
(const char *) funcname,
(const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty),
(const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcalled : str_empty),
(const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty),
(const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
(const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
} else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(h_func)) {
duk_push_sprintf(ctx, "%s %s native%s%s%s%s%s",
(const char *) funcname,
(const char *) filename,
@ -210,7 +217,6 @@ DUK_LOCAL duk_ret_t duk__traceback_getter_helper(duk_context *ctx, duk_small_int
(const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty),
(const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
(const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
} else {
duk_push_sprintf(ctx, "%s %s:%ld%s%s%s%s%s",
(const char *) funcname,

17
src/duk_bi_function.c

@ -132,14 +132,16 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) {
if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj)) {
/* XXX: actual source, if available */
duk_push_sprintf(ctx, "function %s() {/* source code */}", (const char *) func_name);
duk_push_sprintf(ctx, "function %s() {/* ecmascript */}", (const char *) func_name);
} else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(obj)) {
duk_push_sprintf(ctx, "function %s() {/* native code */}", (const char *) func_name);
duk_push_sprintf(ctx, "function %s() {/* native */}", (const char *) func_name);
} else if (DUK_HOBJECT_HAS_BOUND(obj)) {
duk_push_sprintf(ctx, "function %s() {/* bound */}", (const char *) func_name);
} else {
goto type_error;
}
} else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
duk_push_lightfunc_tostring(ctx, tv);
} else {
goto type_error;
}
@ -298,8 +300,9 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx) {
/* bound function 'length' property is interesting */
h_target = duk_get_hobject(ctx, -2);
DUK_ASSERT(h_target != NULL);
if (DUK_HOBJECT_GET_CLASS_NUMBER(h_target) == DUK_HOBJECT_CLASS_FUNCTION) {
if (h_target == NULL || /* lightfunc */
DUK_HOBJECT_GET_CLASS_NUMBER(h_target) == DUK_HOBJECT_CLASS_FUNCTION) {
/* For lightfuncs, simply read the virtual property. */
duk_int_t tmp;
duk_get_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH);
tmp = duk_to_int(ctx, -1) - (nargs - 1); /* step 15.a */
@ -326,7 +329,11 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx) {
* function. Not sure if this is correct, because the specification
* is a bit ambiguous on this point but it would make sense.
*/
if (DUK_HOBJECT_HAS_STRICT(h_target)) {
if (h_target == NULL) {
/* lightfunc */
/* FIXME: assume strict? */
DUK_HOBJECT_SET_STRICT(h_bound);
} else if (DUK_HOBJECT_HAS_STRICT(h_target)) {
DUK_HOBJECT_SET_STRICT(h_bound);
}
DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval *) duk_get_tval(ctx, -1)));

16
src/duk_bi_json.c

@ -1341,6 +1341,8 @@ DUK_LOCAL duk_bool_t duk__enc_value1(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hol
if (h != NULL) {
duk_get_prop_stridx(ctx, -1, DUK_STRIDX_TO_JSON);
h = duk_get_hobject(ctx, -1);
/* FIXME: lightfunc support */
if (h != NULL && DUK_HOBJECT_IS_CALLABLE(h)) {
DUK_DDD(DUK_DDDPRINT("value is object, has callable toJSON() -> call it"));
duk_dup(ctx, -2); /* -> [ ... key val toJSON val ] */
@ -1579,6 +1581,17 @@ DUK_LOCAL void duk__enc_value2(duk_json_enc_ctx *js_ctx) {
break;
}
#endif /* DUK_USE_JX || DUK_USE_JC */
case DUK_TAG_LIGHTFUNC: {
#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
/* We only get here when doing non-standard JSON encoding */
DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible);
DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_function);
#else
/* Standard JSON omits functions */
DUK_NEVER_HERE();
#endif
break;
}
default: {
/* number */
duk_double_t d;
@ -1855,7 +1868,8 @@ void duk_bi_json_stringify_helper(duk_context *ctx,
{
js_ctx->mask_for_undefined = DUK_TYPE_MASK_UNDEFINED |
DUK_TYPE_MASK_POINTER |
DUK_TYPE_MASK_BUFFER;
DUK_TYPE_MASK_BUFFER |
DUK_TYPE_MASK_LIGHTFUNC;
}
(void) duk_push_dynamic_buffer(ctx, 0);

7
src/duk_bi_logger.c

@ -39,12 +39,15 @@ DUK_INTERNAL duk_ret_t duk_bi_logger_constructor(duk_context *ctx) {
if (thr->callstack_top >= 2) {
duk_activation *act_caller = thr->callstack + thr->callstack_top - 2;
if (act_caller->func) {
duk_hobject *func_caller;
func_caller = DUK_ACT_GET_FUNC(act_caller);
if (func_caller) {
/* Stripping the filename might be a good idea
* ("/foo/bar/quux.js" -> logger name "quux"),
* but now used verbatim.
*/
duk_push_hobject(ctx, act_caller->func);
duk_push_hobject(ctx, func_caller);
duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME);
duk_replace(ctx, 0);
}

39
src/duk_bi_object.c

@ -49,6 +49,15 @@ DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx) {
duk_insert(ctx, 0);
}
/* FIXME: lightfunc hack, rework */
{
duk_tval *tv = duk_get_tval(ctx, 0);
if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
duk_push_hobject_bidx(ctx, DUK_BIDX_FUNCTION_PROTOTYPE);
return 1;
}
}
h = duk_require_hobject(ctx, 0);
DUK_ASSERT(h != NULL);
@ -194,6 +203,8 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_context
duk_hobject *h;
duk_bool_t is_freeze;
/* FIXME: lightfunc */
h = duk_require_hobject(ctx, 0);
DUK_ASSERT(h != NULL);
@ -212,6 +223,8 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_context
duk_hthread *thr = (duk_hthread *) ctx;
duk_hobject *h;
/* FIXME: lightfunc */
h = duk_require_hobject(ctx, 0);
DUK_ASSERT(h != NULL);
@ -230,22 +243,30 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_con
duk_bool_t is_frozen;
duk_bool_t rc;
h = duk_require_hobject(ctx, 0);
DUK_ASSERT(h != NULL);
/* FIXME: lightfunc */
is_frozen = duk_get_current_magic(ctx);
rc = duk_hobject_object_is_sealed_frozen_helper(h, is_frozen /*is_frozen*/);
duk_push_boolean(ctx, rc);
h = duk_require_hobject_or_lfunc(ctx, 0);
if (!h) {
duk_push_true(ctx); /* frozen and sealed */
} else {
is_frozen = duk_get_current_magic(ctx);
rc = duk_hobject_object_is_sealed_frozen_helper(h, is_frozen /*is_frozen*/);
duk_push_boolean(ctx, rc);
}
return 1;
}
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_extensible(duk_context *ctx) {
duk_hobject *h;
h = duk_require_hobject(ctx, 0);
DUK_ASSERT(h != NULL);
/* FIXME: lightfunc */
duk_push_boolean(ctx, DUK_HOBJECT_HAS_EXTENSIBLE(h));
h = duk_require_hobject_or_lfunc(ctx, 0);
if (!h) {
duk_push_false(ctx);
} else {
duk_push_boolean(ctx, DUK_HOBJECT_HAS_EXTENSIBLE(h));
}
return 1;
}
@ -265,7 +286,7 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx) {
DUK_ASSERT_TOP(ctx, 1);
obj = duk_require_hobject(ctx, 0);
obj = duk_require_hobject_or_lfunc_coerce(ctx, 0);
DUK_ASSERT(obj != NULL);
DUK_UNREF(obj);

20
src/duk_bi_thread.c

@ -52,6 +52,7 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) {
duk_tval tv_tmp;
duk_tval *tv;
duk_hobject *func;
duk_hobject *caller_func;
duk_small_int_t is_error;
DUK_DDD(DUK_DDDPRINT("Duktape.Thread.resume(): thread=%!T, value=%!T, is_error=%!T",
@ -76,11 +77,12 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) {
DUK_DD(DUK_DDPRINT("resume state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.resume)"));
goto state_error;
}
DUK_ASSERT((thr->callstack + thr->callstack_top - 1)->func != NULL); /* us */
DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION((thr->callstack + thr->callstack_top - 1)->func));
DUK_ASSERT((thr->callstack + thr->callstack_top - 2)->func != NULL); /* caller */
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL); /* us */
DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)));
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != NULL); /* caller */
if (!DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)) {
caller_func = DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2);
if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(caller_func)) {
DUK_DD(DUK_DDPRINT("resume state invalid: caller must be Ecmascript code"));
goto state_error;
}
@ -207,6 +209,7 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) {
DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_tval tv_tmp;
duk_hobject *caller_func;
duk_small_int_t is_error;
DUK_DDD(DUK_DDDPRINT("Duktape.Thread.yield(): value=%!T, is_error=%!T",
@ -235,11 +238,12 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) {
DUK_DD(DUK_DDPRINT("yield state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.yield)"));
goto state_error;
}
DUK_ASSERT((thr->callstack + thr->callstack_top - 1)->func != NULL); /* us */
DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION((thr->callstack + thr->callstack_top - 1)->func));
DUK_ASSERT((thr->callstack + thr->callstack_top - 2)->func != NULL); /* caller */
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL); /* us */
DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)));
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != NULL); /* caller */
if (!DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)) {
caller_func = DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2);
if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(caller_func)) {
DUK_DD(DUK_DDPRINT("yield state invalid: caller must be Ecmascript code"));
goto state_error;
}

3
src/duk_debug_hobject.c

@ -93,6 +93,9 @@ DUK_LOCAL char duk__get_tval_summary_char(duk_tval *tv) {
case DUK_TAG_POINTER: {
return 'P';
}
case DUK_TAG_LIGHTFUNC: {
return 'L';
}
default:
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
return 'd';

5
src/duk_debug_vsnprintf.c

@ -724,6 +724,11 @@ DUK_LOCAL void duk__print_tval(duk__dprint_state *st, duk_tval *tv) {
duk_fb_sprintf(fb, "pointer:%p", (void *) DUK_TVAL_GET_POINTER(tv));
break;
}
case DUK_TAG_LIGHTFUNC: {
/* FIXME: func ptr and flags */
duk_fb_sprintf(fb, "lightfunc");
break;
}
default: {
/* IEEE double is approximately 16 decimal digits; print a couple extra */
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));

20
src/duk_error_augment.c

@ -224,6 +224,7 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack,
DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX); /* callstack limits */
for (i = (duk_int_t) (thr_callstack->callstack_top - 1); i >= i_min; i--) {
duk_uint32_t pc;
duk_hobject *func;
/*
* Note: each API operation potentially resizes the callstack,
@ -235,17 +236,24 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack,
/* [... arr] */
/* FIXME: proper lightfunc support */
#if 0
DUK_ASSERT(thr_callstack->callstack[i].func != NULL);
DUK_ASSERT_DISABLE(thr_callstack->callstack[i].pc >= 0); /* unsigned */
#endif
/* add function */
duk_push_hobject(ctx, thr_callstack->callstack[i].func); /* -> [... arr func] */
/* Add function object. */
func = DUK_ACT_GET_FUNC(thr_callstack->callstack + i);
if (func) {
duk_push_hobject(ctx, func); /* -> [... arr func] */
} else {
duk_tval *tv = &(thr_callstack->callstack + i)->tv_func;
duk_push_tval(ctx, tv);
}
duk_def_prop_index_wec(ctx, -2, arr_idx);
arr_idx++;
/* add a number containing: pc, activation flags */
/* Add a number containing: pc, activation flag
/* Add a number containing: pc, activation flags.
*
* PC points to next instruction, find offending PC. Note that
* PC == 0 for native code.
@ -322,7 +330,7 @@ DUK_LOCAL void duk__err_augment_builtin_throw(duk_hthread *thr, duk_hthread *thr
act = thr_callstack->callstack + thr_callstack->callstack_top - 1;
DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size);
func = act->func;
func = DUK_ACT_GET_FUNC(act);
if (func) {
duk_uint32_t pc;
duk_uint32_t line;

1
src/duk_features.h.in

@ -2521,7 +2521,6 @@ typedef FILE duk_file;
* values and such, but is very useful in low memory environments (can save
* around 14kB of initial RAM footprint).
*/
#undef DUK_USE_LIGHTFUNC_BUILTINS
#if defined(DUK_OPT_LIGHTFUNC_BUILTINS)
#define DUK_USE_LIGHTFUNC_BUILTINS

2
src/duk_heap_markandsweep.c

@ -109,7 +109,7 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) {
for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) {
duk_activation *act = t->callstack + i;
duk__mark_heaphdr(heap, (duk_heaphdr *) act->func);
duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_ACT_GET_FUNC(act));
duk__mark_heaphdr(heap, (duk_heaphdr *) act->var_env);
duk__mark_heaphdr(heap, (duk_heaphdr *) act->lex_env);
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY

2
src/duk_heap_refcount.c

@ -119,7 +119,7 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h)
for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) {
duk_activation *act = t->callstack + i;
duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->func);
duk_heap_heaphdr_decref(thr, (duk_heaphdr *) DUK_ACT_GET_FUNC(act));
duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->var_env);
duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->lex_env);
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY

108
src/duk_hobject_props.c

@ -134,6 +134,12 @@ DUK_LOCAL duk_uint32_t duk__push_tval_to_hstring_arr_idx(duk_context *ctx, duk_t
return arr_idx;
}
/* String is an own (virtual) property of a lightfunc. */
static duk_bool_t duk__key_is_lightfunc_ownprop(duk_hthread *thr, duk_hstring *key) {
return (key == DUK_HTHREAD_STRING_LENGTH(thr) ||
key == DUK_HTHREAD_STRING_NAME(thr));
}
/*
* Helpers for managing property storage size
*/
@ -2221,6 +2227,30 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj,
break;
}
case DUK_TAG_LIGHTFUNC: {
duk_int_t lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv_obj);
/* FIXME: remaining virtual properties */
/* Must coerce key: if key is an object, it may coerce to e.g. 'length'. */
arr_idx = duk__push_tval_to_hstring_arr_idx(ctx, tv_key, &key);
if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
duk_int_t lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
duk_pop(ctx);
duk_push_int(ctx, lf_len);
return 1;
} else if (key == DUK_HTHREAD_STRING_NAME(thr)) {
duk_pop(ctx);
duk_push_lightfunc_name(ctx, tv_obj);
return 1;
}
DUK_DDD(DUK_DDDPRINT("base object is a lightfunc, start lookup from function prototype"));
curr = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
goto lookup; /* avoid double coercion */
}
default: {
/* number */
DUK_DDD(DUK_DDDPRINT("base object is a number, start lookup from number prototype"));
@ -2399,18 +2429,47 @@ DUK_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj,
DUK_TVAL_SET_TVAL(&tv_key_copy, tv_key);
tv_key = &tv_key_copy;
if (!DUK_TVAL_IS_OBJECT(tv_obj)) {
/*
* The 'in' operator requires an object as its right hand side,
* throwing a TypeError unconditionally if this is not the case.
*
* However, lightfuncs need to behave like fully fledged objects
* here to be maximally transparent, so we need to handle them
* here.
*/
/* FIXME: refactor to avoid double call for key coercion */
/* FIXME: remaining virtual properties */
if (DUK_TVAL_IS_OBJECT(tv_obj)) {
obj = DUK_TVAL_GET_OBJECT(tv_obj);
DUK_ASSERT(obj != NULL);
arr_idx = duk__push_tval_to_hstring_arr_idx(ctx, tv_key, &key);
} else if (DUK_TVAL_IS_LIGHTFUNC(tv_obj)) {
arr_idx = duk__push_tval_to_hstring_arr_idx(ctx, tv_key, &key);
if (duk__key_is_lightfunc_ownprop(thr, key)) {
/* FOUND */
rc = 1;
goto pop_and_return;
}
/* If not found, resume existence check from Function.prototype.
* We can just substitute the value in this case; nothing will
* need the original base value (as would be the case with e.g.
* setters/getters.
*/
obj = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
} else {
/* Note: unconditional throw */
DUK_DDD(DUK_DDDPRINT("base object is not an object -> reject"));
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_BASE);
}
obj = DUK_TVAL_GET_OBJECT(tv_obj);
DUK_ASSERT(obj != NULL);
/* XXX: fast path for arrays? */
arr_idx = duk__push_tval_to_hstring_arr_idx(ctx, tv_key, &key);
DUK_ASSERT(key != NULL);
DUK_ASSERT(obj != NULL);
DUK_UNREF(arr_idx);
#if defined(DUK_USE_ES6_PROXY)
@ -2464,6 +2523,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj,
rc = duk__get_property_desc(thr, obj, key, &desc, 0 /*flags*/); /* don't push value */
pop_and_return:
duk_pop(ctx); /* [ key ] -> [] */
return rc;
}
@ -3067,6 +3127,25 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj,
break;
}
case DUK_TAG_LIGHTFUNC: {
/* All lightfunc own properties are non-writable and the lightfunc
* is considered non-extensible. However, the write may be captured
* by an inherited setter which means we can't stop the lookup here.
*/
/* FIXME: remaining virtual properties */
arr_idx = duk__push_tval_to_hstring_arr_idx(ctx, tv_key, &key);
if (duk__key_is_lightfunc_ownprop(thr, key)) {
goto fail_not_writable;
}
DUK_DDD(DUK_DDDPRINT("base object is a lightfunc, start lookup from function prototype"));
curr = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
goto lookup; /* avoid double coercion */
}
default: {
/* number */
DUK_DDD(DUK_DDDPRINT("base object is a number, start lookup from number prototype"));
@ -3894,6 +3973,18 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj,
arr_idx < DUK_HBUFFER_GET_SIZE(h)) {
goto fail_not_configurable;
}
} else if (DUK_TVAL_IS_LIGHTFUNC(tv_obj)) {
/* Lightfunc virtual properties are non-configurable, so
* reject if match any of them.
*/
duk_to_string(ctx, -1);
key = duk_get_hstring(ctx, -1);
DUK_ASSERT(key != NULL);
if (duk__key_is_lightfunc_ownprop(thr, key)) {
goto fail_not_configurable;
}
}
/* non-object base, no offending virtual property */
@ -4372,11 +4463,18 @@ DUK_LOCAL void duk__normalize_property_descriptor(duk_context *ctx) {
* property descriptor with 'missing values' so it's easier to avoid it
* entirely.
*
* Note: this is only called for actual objects, not primitive values.
* Thist must support virtual properties for full objects (e.g. Strings)
* but not for plain values (e.g. lightfuncs).
*
* This is a Duktape/C function.
*/
/* XXX: this is a major target for size optimization */
/* FIXME: lightfunc support: because this operation can be done on objects,
* lightfuncs must also be supported here...
*/
/* XXX: this is a major target for size optimization */
DUK_INTERNAL duk_ret_t duk_hobject_object_define_property(duk_context *ctx) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_hobject *obj;

16
src/duk_hthread.h

@ -54,6 +54,8 @@
#define DUK_ACT_FLAG_PREVENT_YIELD (1 << 3) /* activation prevents yield (native call or "new") */
#define DUK_ACT_FLAG_DIRECT_EVAL (1 << 4) /* activation is a direct eval call */
#define DUK_ACT_GET_FUNC(act) ((act)->func)
/*
* Flags for __FILE__ / __LINE__ registered into tracedata
*/
@ -133,9 +135,14 @@
* Struct defines
*/
/* Note: it's nice if size is 2^N (now 32 bytes on 32 bit without 'caller' property) */
/* FIXME: for a memory-code tradeoff, remove 'func' and make it's access either a function
* or a macro. This would make the activation 32 bytes long on 32-bit platforms again.
*/
/* Note: it's nice if size is 2^N (at least for 32-bit platforms). */
struct duk_activation {
duk_hobject *func; /* function being executed; for bound function calls, this is the final, real function */
duk_tval tv_func; /* borrowed: full duk_tval for function being executed; for lightfuncs */
duk_hobject *func; /* borrowed: function being executed; for bound function calls, this is the final, real function, NULL for lightfuncs */
duk_hobject *var_env; /* current variable environment (may be NULL if delayed) */
duk_hobject *lex_env; /* current lexical environment (may be NULL if delayed) */
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
@ -179,11 +186,6 @@ struct duk_activation {
* (calling) valstack. This works for everything except tail
* calls, which must not "cumulate" valstack temps.
*/
#if defined(DUK_USE_32BIT_PTRS) && !defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
/* Minor optimization: pad structure to 2^N size on 32-bit platforms. */
duk_int_t unused1; /* pad to 2^N */
#endif
};
/* Note: it's nice if size is 2^N (not 4x4 = 16 bytes on 32 bit) */

73
src/duk_hthread_builtins.c

@ -6,6 +6,10 @@
#include "duk_internal.h"
/* FIXME: lightfunc check for other function values - constructors like
* Number etc?
*/
/*
* Encoding constants, must match genbuiltins.py
*/
@ -43,6 +47,17 @@
* by genbuiltins.py.
*/
#ifdef DUK_USE_LIGHTFUNC_BUILTINS
/* FIXME: test function */
static int duk__lightfunc_test(duk_context *ctx) {
int v1 = duk_get_int(ctx, 0);
int v2 = duk_get_int(ctx, 1);
fprintf(stderr, "Lightfunc called, top=%d, args: %d %d\n", duk_get_top(ctx), v1, v2);
duk_push_int(ctx, v1 + v2);
return 1;
}
#endif
DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
duk_context *ctx = (duk_context *) thr;
duk_bitdecoder_ctx bd_ctx;
@ -96,6 +111,7 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
}
/* XXX: set magic directly here? (it could share the c_nargs arg) */
DUK_D(DUK_DPRINT("FIXME: lightweight potential?"));
duk_push_c_function_noexotic(ctx, c_func, c_nargs);
h = duk_require_hobject(ctx, -1);
@ -338,6 +354,7 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
c_func_getter = duk_bi_native_functions[natidx_getter];
c_func_setter = duk_bi_native_functions[natidx_setter];
DUK_D(DUK_DPRINT("FIXME: lightweight potential?"));
duk_push_c_function_noconstruct_noexotic(ctx, c_func_getter, 0); /* always 0 args */
duk_push_c_function_noconstruct_noexotic(ctx, c_func_setter, 1); /* always 1 arg */
@ -377,6 +394,9 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
duk_int16_t magic;
duk_c_function c_func;
duk_hnativefunction *h_func;
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
duk_small_int_t lightfunc_eligible;
#endif
stridx = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS);
natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS);
@ -393,6 +413,43 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
(long) i, (long) j, (long) stridx, (long) natidx, (long) c_length,
(c_nargs == DUK_VARARGS ? (long) -1 : (long) c_nargs)));
/* Cast converts magic to 16-bit signed value */
magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0);
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
/* FIXME: hardcoded values, use constants */
/* FIXME: fix awkward control flow */
lightfunc_eligible =
((c_nargs >= 0 && c_nargs <= 0x0e) || (c_nargs == DUK_VARARGS)) &&
(c_length <= 0x0f) &&
(magic >= -0x80 && magic <= 0x7f);
if (stridx == DUK_STRIDX_EVAL ||
stridx == DUK_STRIDX_YIELD ||
stridx == DUK_STRIDX_RESUME ||
stridx == DUK_STRIDX_REQUIRE) {
/* These functions have trouble working as lightfuncs.
* Some of them have specific asserts and some may have
* additional properties (e.g. 'require.id' may be written).
*/
DUK_D(DUK_DPRINT("reject as lightfunc: stridx=%d, i=%d, j=%d", (int) stridx, (int) i, (int) j));
lightfunc_eligible = 0;
}
if (lightfunc_eligible) {
duk_tval tv_lfunc;
duk_small_uint_t lf_flags =
((magic << 8) & 0xff00UL) |
(c_length << 4) |
(c_nargs == DUK_VARARGS ? 0x0f : c_nargs);
DUK_TVAL_SET_LIGHTFUNC(&tv_lfunc, c_func, lf_flags);
duk_push_tval(ctx, &tv_lfunc);
DUK_D(DUK_DPRINT("built-in function eligible as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic));
goto lightfunc_skip;
}
#endif /* DUK_USE_LIGHTFUNC_BUILTINS */
DUK_D(DUK_DPRINT("built-in function NOT ELIGIBLE as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic));
/* [ (builtin objects) ] */
duk_push_c_function_noconstruct_noexotic(ctx, c_func, c_nargs);
@ -415,8 +472,6 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
/* XXX: any way to avoid decoding magic bit; there are quite
* many function properties and relatively few with magic values.
*/
/* Cast converts magic to 16-bit signed value */
magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0);
h_func->magic = magic;
/* [ (builtin objects) func ] */
@ -439,6 +494,10 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
* function valued properties of built-in objects now.
*/
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
lightfunc_skip:
#endif
duk_def_prop_stridx(ctx, i, stridx, DUK_PROPDESC_FLAGS_WC);
/* [ (builtin objects) ] */
@ -577,6 +636,16 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
}
#endif
#ifdef DUK_USE_LIGHTFUNC_BUILTINS /* FIXME: lightfunc testing hack */
{
duk_tval tv_lfunc;
DUK_TVAL_SET_LIGHTFUNC(&tv_lfunc, duk__lightfunc_test, DUK_LFUNC_FLAGS_PACK(0, 2, 2));
duk_push_string(ctx, "fixme_lightfunc_test");
duk_push_tval(ctx, &tv_lfunc);
duk_def_prop(ctx, DUK_BIDX_DUKTAPE, DUK_PROPDESC_FLAGS_WC);
}
#endif
/*
* Pop built-ins from stack: they are now INCREF'd and
* reachable from the builtins[] array.

103
src/duk_hthread_stacks.c

@ -128,7 +128,8 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_
idx = thr->callstack_top;
while (idx > new_top) {
duk_activation *p;
duk_activation *act;
duk_hobject *func;
#ifdef DUK_USE_REFERENCE_COUNTING
duk_hobject *tmp;
#endif
@ -137,48 +138,49 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_
DUK_ASSERT_DISABLE(idx >= 0); /* unsigned */
DUK_ASSERT((duk_size_t) idx < thr->callstack_size); /* true, despite side effect resizes */
p = thr->callstack + idx;
DUK_ASSERT(p->func != NULL);
act = thr->callstack + idx;
/* With lightfuncs, act 'func' may be NULL */
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
/*
* Restore 'caller' property for non-strict callee functions.
*/
if (!DUK_HOBJECT_HAS_STRICT(p->func)) {
func = DUK_ACT_GET_FUNC(act);
if (func != NULL && !DUK_HOBJECT_HAS_STRICT(func)) {
duk_tval *tv_caller;
duk_tval tv_tmp;
duk_hobject *h_tmp;
tv_caller = duk_hobject_find_existing_entry_tval_ptr(p->func, DUK_HTHREAD_STRING_CALLER(thr));
tv_caller = duk_hobject_find_existing_entry_tval_ptr(func, DUK_HTHREAD_STRING_CALLER(thr));
/* The p->prev_caller should only be set if the entry for 'caller'
/* The act->prev_caller should only be set if the entry for 'caller'
* exists (as it is only set in that case, and the property is not
* configurable), but handle all the cases anyway.
*/
if (tv_caller) {
DUK_TVAL_SET_TVAL(&tv_tmp, tv_caller);
if (p->prev_caller) {
/* Just transfer the refcount from p->prev_caller to tv_caller,
if (act->prev_caller) {
/* Just transfer the refcount from act->prev_caller to tv_caller,
* so no need for a refcount update. This is the expected case.
*/
DUK_TVAL_SET_OBJECT(tv_caller, p->prev_caller);
p->prev_caller = NULL;
DUK_TVAL_SET_OBJECT(tv_caller, act->prev_caller);
act->prev_caller = NULL;
} else {
DUK_TVAL_SET_NULL(tv_caller); /* no incref needed */
DUK_ASSERT(p->prev_caller == NULL);
DUK_ASSERT(act->prev_caller == NULL);
}
DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
} else {
h_tmp = p->prev_caller;
h_tmp = act->prev_caller;
if (h_tmp) {
p->prev_caller = NULL;
act->prev_caller = NULL;
DUK_HOBJECT_DECREF(thr, h_tmp); /* side effects */
}
}
p = thr->callstack + idx; /* avoid side effects */
DUK_ASSERT(p->prev_caller == NULL);
act = thr->callstack + idx; /* avoid side effects */
DUK_ASSERT(act->prev_caller == NULL);
}
#endif
@ -192,46 +194,49 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_
* environment is created for e.g. an eval call, it must not be closed.
*/
if (!DUK_HOBJECT_HAS_NEWENV(p->func)) {
func = DUK_ACT_GET_FUNC(act);
if (func != NULL && !DUK_HOBJECT_HAS_NEWENV(func)) {
DUK_DDD(DUK_DDDPRINT("skip closing environments, envs not owned by this activation"));
goto skip_env_close;
}
DUK_ASSERT(p->lex_env == p->var_env);
if (p->var_env != NULL) {
/* FIXME: lightfunc handling, explicit check or comment */
DUK_ASSERT(act->lex_env == act->var_env);
if (act->var_env != NULL) {
DUK_DDD(DUK_DDDPRINT("closing var_env record %p -> %!O",
(void *) p->var_env, (duk_heaphdr *) p->var_env));
duk_js_close_environment_record(thr, p->var_env, p->func, p->idx_bottom);
p = thr->callstack + idx; /* avoid side effect issues */
(void *) act->var_env, (duk_heaphdr *) act->var_env));
duk_js_close_environment_record(thr, act->var_env, DUK_ACT_GET_FUNC(act), act->idx_bottom);
act = thr->callstack + idx; /* avoid side effect issues */
}
#if 0
if (p->lex_env != NULL) {
if (p->lex_env == p->var_env) {
if (act->lex_env != NULL) {
if (act->lex_env == act->var_env) {
/* common case, already closed, so skip */
DUK_DD(DUK_DDPRINT("lex_env and var_env are the same and lex_env "
"already closed -> skip closing lex_env"));
;
} else {
DUK_DD(DUK_DDPRINT("closing lex_env record %p -> %!O",
(void *) p->lex_env, (duk_heaphdr *) p->lex_env));
duk_js_close_environment_record(thr, p->lex_env, p->func, p->idx_bottom);
p = thr->callstack + idx; /* avoid side effect issues */
(void *) act->lex_env, (duk_heaphdr *) act->lex_env));
duk_js_close_environment_record(thr, act->lex_env, DUK_ACT_GET_FUNC(act), act->idx_bottom);
act = thr->callstack + idx; /* avoid side effect issues */
}
}
#endif
DUK_ASSERT((p->lex_env == NULL) ||
((duk_hobject_find_existing_entry_tval_ptr(p->lex_env, DUK_HTHREAD_STRING_INT_CALLEE(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(p->lex_env, DUK_HTHREAD_STRING_INT_VARMAP(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(p->lex_env, DUK_HTHREAD_STRING_INT_THREAD(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(p->lex_env, DUK_HTHREAD_STRING_INT_REGBASE(thr)) == NULL)));
DUK_ASSERT((act->lex_env == NULL) ||
((duk_hobject_find_existing_entry_tval_ptr(act->lex_env, DUK_HTHREAD_STRING_INT_CALLEE(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(act->lex_env, DUK_HTHREAD_STRING_INT_VARMAP(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(act->lex_env, DUK_HTHREAD_STRING_INT_THREAD(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(act->lex_env, DUK_HTHREAD_STRING_INT_REGBASE(thr)) == NULL)));
DUK_ASSERT((p->var_env == NULL) ||
((duk_hobject_find_existing_entry_tval_ptr(p->var_env, DUK_HTHREAD_STRING_INT_CALLEE(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(p->var_env, DUK_HTHREAD_STRING_INT_VARMAP(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(p->var_env, DUK_HTHREAD_STRING_INT_THREAD(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(p->var_env, DUK_HTHREAD_STRING_INT_REGBASE(thr)) == NULL)));
DUK_ASSERT((act->var_env == NULL) ||
((duk_hobject_find_existing_entry_tval_ptr(act->var_env, DUK_HTHREAD_STRING_INT_CALLEE(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(act->var_env, DUK_HTHREAD_STRING_INT_VARMAP(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(act->var_env, DUK_HTHREAD_STRING_INT_THREAD(thr)) == NULL) &&
(duk_hobject_find_existing_entry_tval_ptr(act->var_env, DUK_HTHREAD_STRING_INT_REGBASE(thr)) == NULL)));
skip_env_close:
@ -239,7 +244,7 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_
* Update preventcount
*/
if (p->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
DUK_ASSERT(thr->callstack_preventcount >= 1);
thr->callstack_preventcount--;
}
@ -254,34 +259,34 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_
*/
#ifdef DUK_USE_REFERENCE_COUNTING
tmp = p->var_env;
tmp = act->var_env;
#endif
p->var_env = NULL;
act->var_env = NULL;
#ifdef DUK_USE_REFERENCE_COUNTING
DUK_HOBJECT_DECREF(thr, tmp);
p = thr->callstack + idx; /* avoid side effect issues */
act = thr->callstack + idx; /* avoid side effect issues */
#endif
#ifdef DUK_USE_REFERENCE_COUNTING
tmp = p->lex_env;
tmp = act->lex_env;
#endif
p->lex_env = NULL;
act->lex_env = NULL;
#ifdef DUK_USE_REFERENCE_COUNTING
DUK_HOBJECT_DECREF(thr, tmp);
p = thr->callstack + idx; /* avoid side effect issues */
act = thr->callstack + idx; /* avoid side effect issues */
#endif
/* Note: this may cause a corner case situation where a finalizer
* may see a currently reachable activation whose 'func' is NULL.
*/
#ifdef DUK_USE_REFERENCE_COUNTING
tmp = p->func;
tmp = DUK_ACT_GET_FUNC(act);
#endif
p->func = NULL;
act->func = NULL;
#ifdef DUK_USE_REFERENCE_COUNTING
DUK_HOBJECT_DECREF(thr, tmp);
p = thr->callstack + idx; /* avoid side effect issues */
DUK_UNREF(p);
act = thr->callstack + idx; /* avoid side effect issues */
DUK_UNREF(act);
#endif
}
@ -293,8 +298,8 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_
*/
#if 0
if (thr->callstack_top > 0) {
duk_activation *p = thr->callstack + thr->callstack_top - 1;
p->idx_retval = 0;
duk_activation *act = thr->callstack + thr->callstack_top - 1;
act->idx_retval = 0;
}
#endif

2
src/duk_js.h

@ -86,7 +86,7 @@ void duk_js_push_closure(duk_hthread *thr,
/* call handling */
DUK_INTERNAL_DECL duk_int_t duk_handle_call(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags);
DUK_INTERNAL_DECL duk_int_t duk_handle_safe_call(duk_hthread *thr, duk_safe_call_function func, duk_idx_t num_stack_args, duk_idx_t num_stack_res);
DUK_INTERNAL_DECL void duk_handle_ecma_call_setup(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags);
DUK_INTERNAL_DECL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags);
/* bytecode execution */
DUK_INTERNAL_DECL void duk_js_execute_bytecode(duk_hthread *entry_thread);

264
src/duk_js_call.c

@ -318,43 +318,58 @@ void duk__handle_createargs_for_call(duk_hthread *thr,
* function. This would make call time handling much easier.
*/
/* FIXME: lightfunc handling */
DUK_LOCAL
void duk__handle_bound_chain_for_call(duk_hthread *thr,
duk_idx_t idx_func,
duk_idx_t *p_num_stack_args, /* may be changed by call */
duk_hobject **p_func, /* changed by call */
duk_bool_t is_constructor_call) {
duk_context *ctx = (duk_context *) thr;
duk_idx_t num_stack_args;
duk_tval *tv_func;
duk_hobject *func;
duk_uint_t sanity;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(p_num_stack_args != NULL);
DUK_ASSERT(p_func != NULL);
DUK_ASSERT(*p_func != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_BOUND(*p_func));
/* On entry, item at idx_func is a bound, non-lightweight function,
* but we don't rely on that below.
*/
num_stack_args = *p_num_stack_args;
func = *p_func;
sanity = DUK_HOBJECT_BOUND_CHAIN_SANITY;
do {
duk_idx_t i, len;
if (!DUK_HOBJECT_HAS_BOUND(func)) {
tv_func = duk_require_tval(ctx, idx_func);
DUK_ASSERT(tv_func != NULL);
if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
/* Lightweight function: never bound, so terminate. */
break;
} else if (DUK_TVAL_IS_OBJECT(tv_func)) {
func = DUK_TVAL_GET_OBJECT(tv_func);
if (!DUK_HOBJECT_HAS_BOUND(func)) {
/* Normal non-bound function. */
break;
}
} else {
/* Function.prototype.bind() should never let this happen,
* ugly error message is enough.
*/
DUK_ERROR(thr, DUK_ERR_INTERNAL_ERROR, DUK_STR_INTERNAL_ERROR);
}
DUK_DDD(DUK_DDDPRINT("bound function encountered, ptr=%p", (void *) func));
DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv_func) != NULL);
/* XXX: this could be more compact by accessing the internal properties
* directly as own properties (they cannot be inherited, and are not
* externally visible).
*/
DUK_DDD(DUK_DDDPRINT("bound function encountered, ptr=%p, num_stack_args=%ld",
(void *) func, (long) num_stack_args));
DUK_DDD(DUK_DDDPRINT("bound function encountered, ptr=%p, num_stack_args=%ld: %!T",
(void *) DUK_TVAL_GET_OBJECT(tv), (long) num_stack_args, tv_func));
/* [ ... func this arg1 ... argN ] */
@ -389,25 +404,32 @@ void duk__handle_bound_chain_for_call(duk_hthread *thr,
/* [ ... func this <bound args> arg1 ... argN ] */
duk_get_prop_stridx(ctx, idx_func, DUK_STRIDX_INT_TARGET);
duk_replace(ctx, idx_func); /* replace also in stack; not strictly necessary */
func = duk_require_hobject(ctx, idx_func);
duk_replace(ctx, idx_func); /* replace in stack */
DUK_DDD(DUK_DDDPRINT("bound function handled, num_stack_args=%ld, idx_func=%ld",
(long) num_stack_args, (long) idx_func));
DUK_DDD(DUK_DDDPRINT("bound function handled, num_stack_args=%ld, idx_func=%ld, curr func=%!T",
(long) num_stack_args, (long) idx_func, duk_get_tval(ctx, idx_func)));
} while (--sanity > 0);
if (sanity == 0) {
DUK_ERROR(thr, DUK_ERR_INTERNAL_ERROR, DUK_STR_BOUND_CHAIN_LIMIT);
}
DUK_DDD(DUK_DDDPRINT("final non-bound function is: %p", (void *) func));
DUK_DDD(DUK_DDDPRINT("final non-bound function is: %!T", duk_get_tval(ctx, idx_func)));
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func) || DUK_HOBJECT_HAS_NATIVEFUNCTION(func));
#ifdef DUK_USE_ASSERTIONS
tv_func = duk_require_tval(ctx, idx_func);
DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func) || DUK_TVAL_IS_OBJECT(tv_func));
if (DUK_TVAL_IS_OBJECT(tv_func)) {
func = DUK_TVAL_GET_OBJECT(tv_func);
DUK_ASSERT(func != NULL);
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func) ||
DUK_HOBJECT_HAS_NATIVEFUNCTION(func));
}
#endif
/* write back */
*p_num_stack_args = num_stack_args;
*p_func = func;
}
/*
@ -561,8 +583,15 @@ void duk__coerce_effective_this_binding(duk_hthread *thr,
duk_hobject *func,
duk_idx_t idx_this) {
duk_context *ctx = (duk_context *) thr;
duk_small_int_t strict;
if (DUK_HOBJECT_HAS_STRICT(func)) {
if (func) {
strict = DUK_HOBJECT_HAS_STRICT(func);
} else {
strict = 1; /* FIXME: lightweight, bit? */
}
if (strict) {
DUK_DDD(DUK_DDDPRINT("this binding: strict -> use directly"));
} else {
duk_tval *tv_this = duk_require_tval(ctx, idx_this);
@ -652,7 +681,8 @@ duk_int_t duk_handle_call(duk_hthread *thr,
duk_idx_t nargs; /* # argument registers target function wants (< 0 => "as is") */
duk_idx_t nregs; /* # total registers target function wants on entry (< 0 => "as is") */
duk_size_t vs_min_size;
duk_hobject *func; /* 'func' on stack (borrowed reference) */
duk_hobject *func; /* 'func' on stack (borrowed reference) */
duk_tval *tv_func; /* duk_tval ptr for 'func' on stack (borrowed reference) */
duk_activation *act;
duk_hobject *env;
duk_jmpbuf our_jmpbuf;
@ -908,35 +938,54 @@ duk_int_t duk_handle_call(duk_hthread *thr,
}
/*
* Check the function type, handle bound function chains,
* and prepare parameters for the rest of the call handling.
* Also figure out the effective 'this' binding, which
* replaces the current value at idx_func + 1.
* Check the function type, handle bound function chains, and prepare
* parameters for the rest of the call handling. Also figure out the
* effective 'this' binding, which replaces the current value at
* idx_func + 1.
*
* If the target function is a 'bound' one, follow the chain of 'bound'
* functions until a non-bound function is found. During this process,
* bound arguments are 'prepended' to existing ones, and the "this"
* binding is overridden. See E5 Section 15.3.4.5.1.
*
* If the target function is a 'bound' one, follow the chain
* of 'bound' functions until a non-bound function is found.
* During this process, bound arguments are 'prepended' to
* existing ones, and the "this" binding is overridden.
* See E5 Section 15.3.4.5.1.
* Lightfunc detection happens here too. Note that lightweight functions
* can be wrapped by (non-lightweight) bound functions so we must resolve
* the bound function chain first.
*/
if (!duk_is_callable(thr, idx_func)) {
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_CALLABLE);
}
func = duk_get_hobject(thr, idx_func);
DUK_ASSERT(func != NULL);
tv_func = duk_get_tval(ctx, idx_func);
DUK_ASSERT(tv_func != NULL);
/* FIXME: avoid calling duk_is_callable() in the first place? */
if (DUK_TVAL_IS_OBJECT(tv_func)) {
func = DUK_TVAL_GET_OBJECT(tv_func);
if (DUK_HOBJECT_HAS_BOUND(func)) {
duk__handle_bound_chain_for_call(thr, idx_func, &num_stack_args, call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL);
}
}
tv_func = NULL; /* invalidated */
tv_func = duk_get_tval(ctx, idx_func); /* relookup, valstack resize possible */
DUK_ASSERT((DUK_TVAL_IS_OBJECT(tv_func) && DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(tv_func))) ||
DUK_TVAL_IS_LIGHTFUNC(tv_func));
if (DUK_HOBJECT_HAS_BOUND(func)) {
/* slow path for bound functions */
duk__handle_bound_chain_for_call(thr, idx_func, &num_stack_args, &func, call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL);
if (DUK_TVAL_IS_OBJECT(tv_func)) {
func = DUK_TVAL_GET_OBJECT(tv_func);
} else {
func = NULL;
}
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(func) ||
DUK_HOBJECT_IS_NATIVEFUNCTION(func));
DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func));
DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPILEDFUNCTION(func) ||
DUK_HOBJECT_IS_NATIVEFUNCTION(func)));
duk__coerce_effective_this_binding(thr, func, idx_func + 1);
DUK_DDD(DUK_DDDPRINT("effective 'this' binding is: %!T",
(duk_tval *) duk_get_tval(ctx, idx_func + 1)));
tv_func = NULL; /* invalidated */
/* These base values are never used, but if the compiler doesn't know
* that DUK_ERROR() won't return, these are needed to silence warnings.
@ -946,7 +995,21 @@ duk_int_t duk_handle_call(duk_hthread *thr,
nargs = 0; DUK_UNREF(nargs);
nregs = 0; DUK_UNREF(nregs);
if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
if (func == NULL) {
duk_small_uint_t lf_flags;
DUK_DDD(DUK_DDDPRINT("lightfunc call handling"));
tv_func = duk_get_tval(ctx, idx_func); /* relookup, valstack resize possible */
DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func));
lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv_func);
func = NULL;
nargs = lf_flags & 0x0f; /* FIXME: constants */
if (nargs == 0x0f) {
nargs = -1; /* vararg */
}
nregs = nargs;
/* FIXME: extract magic here from lf_flags directly */
} else if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
nargs = ((duk_hcompiledfunction *) func)->nargs;
nregs = ((duk_hcompiledfunction *) func)->nregs;
DUK_ASSERT(nregs >= nargs);
@ -981,7 +1044,8 @@ duk_int_t duk_handle_call(duk_hthread *thr,
vs_min_size = (thr->valstack_bottom - thr->valstack) + /* bottom of current func */
idx_args; /* bottom of new func */
vs_min_size += (nregs >= 0 ? nregs : num_stack_args); /* num entries of new func at entry */
if (DUK_HOBJECT_IS_NATIVEFUNCTION(func)) {
if (func == NULL || DUK_HOBJECT_IS_NATIVEFUNCTION(func)) {
/* FIXME: lightweight funcs must be handled above */
vs_min_size += DUK_VALSTACK_API_ENTRY_MINIMUM; /* Duktape/C API guaranteed entries (on top of args) */
}
vs_min_size += DUK_VALSTACK_INTERNAL_EXTRA, /* + spare */
@ -1023,17 +1087,17 @@ duk_int_t duk_handle_call(duk_hthread *thr,
thr->callstack_top++;
DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
DUK_ASSERT(thr->valstack_top > thr->valstack_bottom); /* at least effective 'this' */
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func));
act->flags = 0;
if (DUK_HOBJECT_HAS_STRICT(func)) {
if (func == NULL || DUK_HOBJECT_HAS_STRICT(func)) {
act->flags |= DUK_ACT_FLAG_STRICT;
}
if (call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) {
act->flags |= DUK_ACT_FLAG_CONSTRUCT;
/*act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;*/
}
if (DUK_HOBJECT_IS_NATIVEFUNCTION(func)) {
if (func == NULL || DUK_HOBJECT_IS_NATIVEFUNCTION(func)) {
/*act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;*/
}
if (call_flags & DUK_CALL_FLAG_DIRECT_EVAL) {
@ -1045,7 +1109,7 @@ duk_int_t duk_handle_call(duk_hthread *thr,
*/
act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;
act->func = func;
act->func = func; /* FIXME: this is an issue */
act->var_env = NULL;
act->lex_env = NULL;
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
@ -1056,16 +1120,25 @@ duk_int_t duk_handle_call(duk_hthread *thr,
#if 0 /* topmost activation idx_retval is considered garbage, no need to init */
act->idx_retval = 0;
#endif
if (func == NULL) {
/* FIXME: use macro, avoid block */
/* FIXME: setting tv_func in other cases */
tv_func = duk_get_tval(ctx, idx_func); /* relookup, valstack resize possible */
DUK_TVAL_SET_TVAL(&act->tv_func, tv_func); /* borrowed, no refcount */
}
if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
/* duk_hthread_callstack_unwind() will decrease this on unwind */
thr->callstack_preventcount++;
}
/* FIXME: lightfunc? */
DUK_HOBJECT_INCREF(thr, func); /* act->func */
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
duk__update_func_caller_prop(thr, func);
if (func) {
duk__update_func_caller_prop(thr, func);
}
act = thr->callstack + thr->callstack_top - 1;
#endif
@ -1089,9 +1162,9 @@ duk_int_t duk_handle_call(duk_hthread *thr,
* Delayed creation (on demand) is handled in duk_js_var.c.
*/
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func)); /* bound function chain has already been resolved */
DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func)); /* bound function chain has already been resolved */
if (!DUK_HOBJECT_HAS_NEWENV(func)) {
if (func != NULL && !DUK_HOBJECT_HAS_NEWENV(func)) {
/* use existing env (e.g. for non-strict eval); cannot have
* an own 'arguments' object (but can refer to the existing one)
*/
@ -1105,9 +1178,9 @@ duk_int_t duk_handle_call(duk_hthread *thr,
goto env_done;
}
DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
DUK_ASSERT(func == NULL || DUK_HOBJECT_HAS_NEWENV(func));
if (!DUK_HOBJECT_HAS_CREATEARGS(func)) {
if (func == NULL || !DUK_HOBJECT_HAS_CREATEARGS(func)) {
/* no need to create environment record now; leave as NULL */
DUK_ASSERT(act->lex_env == NULL);
DUK_ASSERT(act->var_env == NULL);
@ -1156,7 +1229,7 @@ duk_int_t duk_handle_call(duk_hthread *thr,
* Determine call type; then setup activation and call
*/
if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
if (func != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
goto ecmascript_call;
} else {
goto native_call;
@ -1177,7 +1250,7 @@ duk_int_t duk_handle_call(duk_hthread *thr,
DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
DUK_ASSERT(((duk_hnativefunction *) func)->func != NULL);
DUK_ASSERT(func == NULL || ((duk_hnativefunction *) func)->func != NULL);
/* [... func this | arg1 ... argN] ('this' must precede new bottom) */
@ -1191,7 +1264,13 @@ duk_int_t duk_handle_call(duk_hthread *thr,
* other invalid
*/
rc = ((duk_hnativefunction *) func)->func((duk_context *) thr);
if (func) {
rc = ((duk_hnativefunction *) func)->func((duk_context *) thr);
} else {
/* FIXME: lightfunc */
duk_c_function funcptr = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv_func);
rc = funcptr((duk_context *) thr);
}
if (rc < 0) {
duk_error_throw_from_negative_rc(thr, rc);
@ -1796,18 +1875,24 @@ duk_int_t duk_handle_safe_call(duk_hthread *thr,
* The callstack of the target contains an earlier Ecmascript call in case
* of an Ecmascript-to-Ecmascript call (whose idx_retval is updated), or
* is empty in case of an initial Duktape.Thread.resume().
*
* The first thing to do here is to figure out whether an ecma-to-ecma
* call is actually possible. It's not always the case if the target is
* a bound function; the final function may be native. In that case,
* return an error so caller can fall back to a normal call path.
*/
DUK_INTERNAL
void duk_handle_ecma_call_setup(duk_hthread *thr,
duk_idx_t num_stack_args,
duk_small_uint_t call_flags) {
duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
duk_idx_t num_stack_args,
duk_small_uint_t call_flags) {
duk_context *ctx = (duk_context *) thr;
duk_size_t entry_valstack_bottom_index;
duk_idx_t idx_func; /* valstack index of 'func' and retval (relative to entry valstack_bottom) */
duk_idx_t idx_args; /* valstack index of start of args (arg1) (relative to entry valstack_bottom) */
duk_idx_t nargs; /* # argument registers target function wants (< 0 => never for ecma calls) */
duk_idx_t nregs; /* # total registers target function wants on entry (< 0 => never for ecma calls) */
duk_tval *tv_func; /* duk_tval ptr for 'func' on stack (borrowed reference) */
duk_hobject *func; /* 'func' on stack (borrowed reference) */
duk_activation *act;
duk_hobject *env;
@ -1840,8 +1925,8 @@ void duk_handle_ecma_call_setup(duk_hthread *thr,
our_callstack_index = thr->callstack_top - 1;
DUK_ASSERT_DISABLE(our_callstack_index >= 0);
DUK_ASSERT(our_callstack_index < thr->callstack_size);
DUK_ASSERT(thr->callstack[our_callstack_index].func != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(thr->callstack[our_callstack_index].func));
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + our_callstack_index) != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + our_callstack_index)));
/* No entry in the catchstack which would actually catch a
* throw can refer to the callstack entry being reused.
@ -1883,34 +1968,61 @@ void duk_handle_ecma_call_setup(duk_hthread *thr,
}
/*
* Check the function type, handle bound function chains,
* and prepare parameters for the rest of the call handling.
* Also figure out the effective 'this' binding, which replaces
* the current value at idx_func + 1.
* Check the function type, handle bound function chains, and prepare
* parameters for the rest of the call handling. Also figure out the
* effective 'this' binding, which replaces the current value at
* idx_func + 1.
*
* If the target function is a 'bound' one, follow the chain of 'bound'
* functions until a non-bound function is found. During this process,
* bound arguments are 'prepended' to existing ones, and the "this"
* binding is overridden. See E5 Section 15.3.4.5.1.
*
* If the target function is a 'bound' one, follow the chain
* of 'bound' functions until a non-bound function is found.
* During this process, bound arguments are 'prepended' to
* existing ones, and the "this" binding is overridden.
* See E5 Section 15.3.4.5.1.
* If the final target function cannot be handled by an ecma-to-ecma
* call, return to the caller with a return value indicating this case.
* The bound chain is resolved and the caller can resume with a plain
* function call.
*/
if (!duk_is_callable(thr, idx_func)) {
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_CALLABLE);
}
func = duk_get_hobject(thr, idx_func);
DUK_ASSERT(func != NULL);
if (DUK_HOBJECT_HAS_BOUND(func)) {
/* slow path for bound functions */
duk__handle_bound_chain_for_call(thr, idx_func, &num_stack_args, &func, call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL);
tv_func = duk_get_tval(ctx, idx_func);
DUK_ASSERT(tv_func != NULL);
/* FIXME: avoid calling duk_is_callable() in the first place? */
if (DUK_TVAL_IS_OBJECT(tv_func)) {
func = DUK_TVAL_GET_OBJECT(tv_func);
if (DUK_HOBJECT_HAS_BOUND(func)) {
duk__handle_bound_chain_for_call(thr, idx_func, &num_stack_args, call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL);
}
}
tv_func = NULL; /* invalidated */
tv_func = duk_get_tval(ctx, idx_func); /* relookup, valstack resize possible */
DUK_ASSERT((DUK_TVAL_IS_OBJECT(tv_func) && DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(tv_func))) ||
DUK_TVAL_IS_LIGHTFUNC(tv_func));
if (DUK_TVAL_IS_OBJECT(tv_func)) {
func = DUK_TVAL_GET_OBJECT(tv_func);
if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION(func));
DUK_DDD(DUK_DDDPRINT("final target is a native function, cannot do ecma-to-ecma call"));
return 0;
}
} else {
DUK_DDD(DUK_DDDPRINT("final target is a lightfunc, cannot do ecma-to-ecma call"));
return 0;
}
DUK_ASSERT(func != NULL);
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(func)); /* caller must ensure this */
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(func));
duk__coerce_effective_this_binding(thr, func, idx_func + 1);
DUK_DDD(DUK_DDDPRINT("effective 'this' binding is: %!T",
duk_get_tval(ctx, idx_func + 1)));
tv_func = NULL; /* invalidated */
nargs = ((duk_hcompiledfunction *) func)->nargs;
nregs = ((duk_hcompiledfunction *) func)->nregs;
@ -2035,7 +2147,7 @@ void duk_handle_ecma_call_setup(duk_hthread *thr,
DUK_ACT_FLAG_STRICT | DUK_ACT_FLAG_TAILCALLED :
DUK_ACT_FLAG_TAILCALLED);
DUK_ASSERT(act->func == func); /* already updated */
DUK_ASSERT(DUK_ACT_GET_FUNC(act) == func); /* already updated */
DUK_ASSERT(act->var_env == NULL); /* already NULLed (by unwind) */
DUK_ASSERT(act->lex_env == NULL); /* already NULLed (by unwind) */
DUK_ASSERT(act->pc == 0); /* already zeroed */
@ -2112,8 +2224,8 @@ void duk_handle_ecma_call_setup(duk_hthread *thr,
DUK_ASSERT(thr->callstack_top < thr->callstack_size);
DUK_ASSERT(thr->callstack_top >= 1);
act = thr->callstack + thr->callstack_top - 1;
DUK_ASSERT(act->func != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(act->func));
DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(act)));
act->idx_retval = entry_valstack_bottom_index + idx_func;
}
@ -2242,4 +2354,6 @@ void duk_handle_ecma_call_setup(duk_hthread *thr,
* Return to bytecode executor, which will resume execution from
* the topmost activation.
*/
return 1;
}

4
src/duk_js_compiler.c

@ -1834,6 +1834,10 @@ duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx,
DUK_UNREACHABLE();
break;
}
case DUK_TAG_LIGHTFUNC: {
DUK_UNREACHABLE();
break;
}
default: {
/* number */
duk_reg_t dest;

269
src/duk_js_executor.c

@ -10,41 +10,6 @@
DUK_LOCAL_DECL void duk__reconfig_valstack(duk_hthread *thr, duk_size_t act_idx, duk_small_uint_t retval_count);
/*
* Helper for finding the final non-bound function in a "bound function" chain.
*/
/* XXX: overlap with other helpers, rework */
DUK_LOCAL duk_hobject *duk__find_nonbound_function(duk_hthread *thr, duk_hobject *func) {
duk_context *ctx = (duk_context *) thr;
duk_uint_t sanity;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(func != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_BOUND(func));
sanity = DUK_HOBJECT_BOUND_CHAIN_SANITY;
do {
if (!DUK_HOBJECT_HAS_BOUND(func)) {
break;
}
duk_push_hobject(ctx, func);
duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TARGET);
func = duk_require_hobject(ctx, -1);
duk_pop_2(ctx);
} while (--sanity > 0);
if (sanity == 0) {
DUK_ERROR(thr, DUK_ERR_INTERNAL_ERROR, DUK_STR_BOUND_CHAIN_LIMIT);
}
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func) || DUK_HOBJECT_HAS_NATIVEFUNCTION(func));
return func;
}
/*
* Arithmetic, binary, and logical helpers.
*
@ -477,8 +442,8 @@ DUK_LOCAL void duk__reconfig_valstack(duk_hthread *thr, duk_size_t act_idx, duk_
DUK_ASSERT(thr != NULL);
DUK_ASSERT_DISABLE(act_idx >= 0); /* unsigned */
DUK_ASSERT(thr->callstack[act_idx].func != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(thr->callstack[act_idx].func));
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + act_idx) != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + act_idx)));
DUK_ASSERT_DISABLE(thr->callstack[act_idx].idx_retval >= 0); /* unsigned */
thr->valstack_bottom = thr->valstack + thr->callstack[act_idx].idx_bottom;
@ -496,7 +461,7 @@ DUK_LOCAL void duk__reconfig_valstack(duk_hthread *thr, duk_size_t act_idx, duk_
* top to 'nregs' always.
*/
h_func = (duk_hcompiledfunction *) thr->callstack[act_idx].func;
h_func = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(thr->callstack + act_idx);
(void) duk_valstack_resize_raw((duk_context *) thr,
(thr->valstack_bottom - thr->valstack) + /* bottom of current func */
@ -554,11 +519,11 @@ DUK_LOCAL void duk__handle_catch_or_finally(duk_hthread *thr, duk_size_t cat_idx
*/
DUK_ASSERT(thr->callstack_top >= 1);
DUK_ASSERT(thr->callstack[thr->callstack_top - 1].func != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(thr->callstack[thr->callstack_top - 1].func));
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)));
thr->valstack_bottom = thr->valstack + (thr->callstack + thr->callstack_top - 1)->idx_bottom;
duk_set_top((duk_context *) thr, ((duk_hcompiledfunction *) (thr->callstack + thr->callstack_top - 1)->func)->nregs);
duk_set_top((duk_context *) thr, ((duk_hcompiledfunction *) DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1))->nregs);
/*
* Reset PC: resume execution from catch or finally jump slot.
@ -600,7 +565,7 @@ DUK_LOCAL void duk__handle_catch_or_finally(duk_hthread *thr, duk_size_t cat_idx
}
DUK_ASSERT(act->lex_env != NULL);
DUK_ASSERT(act->var_env != NULL);
DUK_ASSERT(act->func != NULL);
DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
DUK_UNREF(act); /* unreferenced without assertions */
act = thr->callstack + thr->callstack_top - 1;
@ -654,8 +619,8 @@ DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx) {
act = thr->callstack + thr->callstack_top - 1;
DUK_ASSERT(act->func != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(act->func));
DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(act)));
/* +0 = break, +1 = continue */
act->pc = thr->catchstack[cat_idx].pc_base + (thr->heap->lj.type == DUK_LJ_TYPE_CONTINUE ? 1 : 0);
@ -668,7 +633,7 @@ DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx) {
#if defined(DUK_USE_ASSERTIONS)
act = thr->callstack + thr->callstack_top - 1;
DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) ==
(duk_size_t) ((duk_hcompiledfunction *) act->func)->nregs);
(duk_size_t) ((duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act))->nregs);
#endif
}
@ -683,8 +648,8 @@ DUK_LOCAL void duk__handle_yield(duk_hthread *thr, duk_hthread *resumer, duk_siz
* lj.value1 is correct.
*/
DUK_ASSERT(resumer->callstack[act_idx].func != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(resumer->callstack[act_idx].func)); /* resume caller must be an ecmascript func */
DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack + act_idx) != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(resumer->callstack + act_idx))); /* resume caller must be an ecmascript func */
DUK_DDD(DUK_DDDPRINT("resume idx_retval is %ld", (long) resumer->callstack[act_idx].idx_retval));
@ -755,11 +720,11 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); /* unchanged by Duktape.Thread.resume() */
DUK_ASSERT(thr->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.resume() activation */
DUK_ASSERT((thr->callstack + thr->callstack_top - 1)->func != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION((thr->callstack + thr->callstack_top - 1)->func) &&
((duk_hnativefunction *) (thr->callstack + thr->callstack_top - 1)->func)->func == duk_bi_thread_resume);
DUK_ASSERT((thr->callstack + thr->callstack_top - 2)->func != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)); /* an Ecmascript function */
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)) &&
((duk_hnativefunction *) DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1))->func == duk_bi_thread_resume);
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2))); /* an Ecmascript function */
DUK_ASSERT_DISABLE((thr->callstack + thr->callstack_top - 2)->idx_retval >= 0); /* unsigned */
tv = &thr->heap->lj.value2; /* resumee */
@ -775,12 +740,12 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED ||
resumee->callstack_top >= 2); /* YIELDED: Ecmascript activation + Duktape.Thread.yield() activation */
DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED ||
((resumee->callstack + resumee->callstack_top - 1)->func != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION((resumee->callstack + resumee->callstack_top - 1)->func) &&
((duk_hnativefunction *) (resumee->callstack + resumee->callstack_top - 1)->func)->func == duk_bi_thread_yield));
(DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 1) != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 1)) &&
((duk_hnativefunction *) DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 1))->func == duk_bi_thread_yield));
DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED ||
((resumee->callstack + resumee->callstack_top - 2)->func != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION((resumee->callstack + resumee->callstack_top - 2)->func))); /* an Ecmascript function */
(DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 2) != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 2)))); /* an Ecmascript function */
DUK_ASSERT_DISABLE(resumee->state != DUK_HTHREAD_STATE_YIELDED ||
(resumee->callstack + resumee->callstack_top - 2)->idx_retval >= 0); /* idx_retval unsigned */
DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_INACTIVE ||
@ -845,7 +810,8 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
retval = DUK__LONGJMP_RESTART;
goto wipe_and_return;
} else {
int call_flags;
duk_small_uint_t call_flags;
duk_bool_t setup_rc;
/* resumee: [... initial_func] (currently actually: [initial_func]) */
@ -857,9 +823,13 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
call_flags = DUK_CALL_FLAG_IS_RESUME; /* is resume, not a tailcall */
duk_handle_ecma_call_setup(resumee,
1, /* num_stack_args */
call_flags); /* call_flags */
setup_rc = duk_handle_ecma_call_setup(resumee,
1, /* num_stack_args */
call_flags); /* call_flags */
if (setup_rc == 0) {
/* FIXME: cannot happen? if not, document */
DUK_ERROR(thr, DUK_ERR_INTERNAL_ERROR, DUK_STR_INTERNAL_ERROR);
}
resumee->resumer = thr;
resumee->state = DUK_HTHREAD_STATE_RUNNING;
@ -894,11 +864,11 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_ASSERT(thr != entry_thread); /* Duktape.Thread.yield() should prevent */
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); /* unchanged from Duktape.Thread.yield() */
DUK_ASSERT(thr->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.yield() activation */
DUK_ASSERT((thr->callstack + thr->callstack_top - 1)->func != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION((thr->callstack + thr->callstack_top - 1)->func) &&
((duk_hnativefunction *) (thr->callstack + thr->callstack_top - 1)->func)->func == duk_bi_thread_yield);
DUK_ASSERT((thr->callstack + thr->callstack_top - 2)->func != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)); /* an Ecmascript function */
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)) &&
((duk_hnativefunction *) DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1))->func == duk_bi_thread_yield);
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2))); /* an Ecmascript function */
DUK_ASSERT_DISABLE((thr->callstack + thr->callstack_top - 2)->idx_retval >= 0); /* unsigned */
resumer = thr->resumer;
@ -906,11 +876,11 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_ASSERT(resumer != NULL);
DUK_ASSERT(resumer->state == DUK_HTHREAD_STATE_RESUMED); /* written by a previous RESUME handling */
DUK_ASSERT(resumer->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.resume() activation */
DUK_ASSERT((resumer->callstack + resumer->callstack_top - 1)->func != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION((resumer->callstack + resumer->callstack_top - 1)->func) &&
((duk_hnativefunction *) (resumer->callstack + resumer->callstack_top - 1)->func)->func == duk_bi_thread_resume);
DUK_ASSERT((resumer->callstack + resumer->callstack_top - 2)->func != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION((resumer->callstack + resumer->callstack_top - 2)->func)); /* an Ecmascript function */
DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 1) != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 1)) &&
((duk_hnativefunction *) DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 1))->func == duk_bi_thread_resume);
DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 2) != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 2))); /* an Ecmascript function */
DUK_ASSERT_DISABLE((resumer->callstack + resumer->callstack_top - 2)->idx_retval >= 0); /* unsigned */
if (thr->heap->lj.iserror) {
@ -1024,7 +994,7 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
(long) (thr->callstack + thr->callstack_top - 2)->idx_retval,
(duk_tval *) &thr->heap->lj.value1));
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)); /* must be ecmascript */
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2))); /* must be ecmascript */
tv1 = thr->valstack + (thr->callstack + thr->callstack_top - 2)->idx_retval;
DUK_TVAL_SET_TVAL(&tv_tmp, tv1);
@ -1049,11 +1019,11 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_ASSERT(thr->resumer != NULL);
DUK_ASSERT(thr->resumer->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.resume() activation */
DUK_ASSERT((thr->resumer->callstack + thr->resumer->callstack_top - 1)->func != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION((thr->resumer->callstack + thr->resumer->callstack_top - 1)->func) &&
((duk_hnativefunction *) (thr->resumer->callstack + thr->resumer->callstack_top - 1)->func)->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */
DUK_ASSERT((thr->resumer->callstack + thr->resumer->callstack_top - 2)->func != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->resumer->callstack + thr->resumer->callstack_top - 2)->func)); /* an Ecmascript function */
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1) != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1)) &&
((duk_hnativefunction *) DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1))->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2) != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2))); /* an Ecmascript function */
DUK_ASSERT_DISABLE((thr->resumer->callstack + thr->resumer->callstack_top - 2)->idx_retval >= 0); /* unsigned */
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED);
@ -1223,11 +1193,11 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_ASSERT(thr->resumer != NULL);
DUK_ASSERT(thr->resumer->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.resume() activation */
DUK_ASSERT((thr->resumer->callstack + thr->resumer->callstack_top - 1)->func != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION((thr->resumer->callstack + thr->resumer->callstack_top - 1)->func) &&
((duk_hnativefunction *) (thr->resumer->callstack + thr->resumer->callstack_top - 1)->func)->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */
DUK_ASSERT((thr->resumer->callstack + thr->resumer->callstack_top - 2)->func != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->resumer->callstack + thr->resumer->callstack_top - 2)->func)); /* an Ecmascript function */
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1) != NULL &&
DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1)) &&
((duk_hnativefunction *) DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1))->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2) != NULL &&
DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2))); /* an Ecmascript function */
resumer = thr->resumer;
@ -1319,7 +1289,7 @@ duk_bool_t duk__handle_fast_return(duk_hthread *thr,
* it would have matched the entry level check).
*/
DUK_ASSERT(thr->callstack_top >= 2);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)); /* must be ecmascript */
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2))); /* must be ecmascript */
tv1 = thr->valstack + (thr->callstack + thr->callstack_top - 2)->idx_retval;
DUK_TVAL_SET_TVAL(&tv_tmp, tv1);
@ -1380,7 +1350,7 @@ DUK_LOCAL void duk__executor_interrupt(duk_hthread *thr) {
DUK_ASSERT(thr->callstack_top > 0);
act = thr->callstack + thr->callstack_top - 1;
fun = (duk_hcompiledfunction *) act->func;
fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act);
DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION((duk_hobject *) fun));
DUK_UNREF(fun);
@ -1510,8 +1480,8 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *entry_thread) {
DUK_ASSERT(entry_thread != NULL);
DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR((duk_heaphdr *) entry_thread);
DUK_ASSERT(entry_thread->callstack_top >= 1); /* at least one activation, ours */
DUK_ASSERT((entry_thread->callstack + entry_thread->callstack_top - 1)->func != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((entry_thread->callstack + entry_thread->callstack_top - 1)->func));
DUK_ASSERT(DUK_ACT_GET_FUNC(entry_thread->callstack + entry_thread->callstack_top - 1) != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(entry_thread->callstack + entry_thread->callstack_top - 1)));
thr = entry_thread;
@ -1628,14 +1598,14 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *entry_thread) {
DUK_ASSERT(thr != NULL);
DUK_ASSERT(thr->callstack_top >= 1);
DUK_ASSERT((thr->callstack + thr->callstack_top - 1)->func != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 1)->func));
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)));
/* XXX: shrink check flag? */
/* assume that thr->valstack_bottom has been set-up before getting here */
act = thr->callstack + thr->callstack_top - 1;
fun = (duk_hcompiledfunction *) act->func;
fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act);
bcode = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(fun);
DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= fun->nregs);
@ -2727,8 +2697,9 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *entry_thread) {
duk_small_uint_t flag_tailcall;
duk_small_uint_t flag_evalcall;
duk_tval *tv_func;
duk_hobject *obj_func; /* target function, possibly a bound function */
duk_hobject *obj_final_func; /* final target function, non-bound function */
duk_hobject *obj_func;
duk_bool_t setup_rc;
duk_idx_t num_stack_args;
/* A -> flags
* B -> base register for call (base -> func, base+1 -> this, base+2 -> arg1 ... base+2+N-1 -> argN)
@ -2756,66 +2727,90 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *entry_thread) {
}
#endif
tv_func = DUK__REGP(idx);
if (!DUK_TVAL_IS_OBJECT(tv_func)) {
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "call target not an object");
}
obj_func = DUK_TVAL_GET_OBJECT(tv_func);
/*
* To determine whether to use an optimized Ecmascript-to-Ecmascript
* call, we need to know whether the final, non-bound function is an
* Ecmascript function. We need to follow the "bound" chain to do that;
* the "bound" chain will be followed for the second time when calling.
* This overhead only affects bound functions (in particular, helper
* functions should not be called if the immediate target function is
* not bound).
* Ecmascript function.
*
* Even so, this awkward solution could be avoided by e.g. replicating
* final, non-bound target function flags to the bound function objects
* (so that a bound function would e.g. have both a "BOUND" flag and
* a "COMPILEDFUNCTION" flag). Also, bound functions could also keep
* a direct reference to the final non-bound function ("shortcut").
* This is now implemented so that we start to do an ecma-to-ecma call
* setup which will resolve the bound chain as the first thing. If the
* final function is not eligible, the return value indicates that the
* ecma-to-ecma call is not possible. The setup will overwrite the call
* target at DUK__REGP(idx) with the final, non-bound function (which
* may be a lightfunc), and fudge arguments if necessary.
*
* FIXME: the call setup won't do "effective this binding" resolution
* if an ecma-to-ecma call is not possible. This is quite confusing,
* so perhaps add a helper for doing bound function and effective this
* binding resolution - and call that explicitly? Ecma-to-ecma call
* setup and normal function handling can then assume this prestep has
* been done by the caller.
*/
if (DUK_HOBJECT_HAS_BOUND(obj_func)) {
obj_final_func = duk__find_nonbound_function(thr, obj_func);
} else {
obj_final_func = obj_func;
}
duk_set_top(ctx, (duk_idx_t) (idx + c + 2)); /* [ ... func this arg1 ... argN ] */
if (DUK_HOBJECT_IS_COMPILEDFUNCTION(obj_final_func)) {
/*
* Ecmascript-to-Ecmascript call: avoid C recursion
* by being clever.
call_flags = 0;
if (flag_tailcall) {
/* We request a tailcall, but in some corner cases
* call handling can decide that a tailcall is
* actually not possible.
* See: test-bug-tailcall-preventyield-assert.c.
*/
call_flags |= DUK_CALL_FLAG_IS_TAILCALL;
}
/* Compared to duk_handle_call():
* - protected call: never
* - ignore recursion limit: never
/* Compared to duk_handle_call():
* - protected call: never
* - ignore recursion limit: never
*/
num_stack_args = c;
setup_rc = duk_handle_ecma_call_setup(thr,
num_stack_args,
call_flags);
if (setup_rc) {
/* Ecma-to-ecma call possible, may or may not be a tailcall.
* Avoid C recursion by being clever.
*/
DUK_DDD(DUK_DDDPRINT("ecma-to-ecma call setup possible, restart execution"));
goto restart_execution;
}
/* XXX: optimize flag handling, by coordinating with bytecode */
DUK_DDD(DUK_DDDPRINT("ecma-to-ecma call not possible, target is native (may be lightfunc)"));
call_flags = 0;
if (flag_tailcall) {
/* We request a tailcall, but in some corner cases
* call handling can decide that a tailcall is
* actually not possible.
* See: test-bug-tailcall-preventyield-assert.c.
*/
call_flags |= DUK_CALL_FLAG_IS_TAILCALL;
}
/* Recompute argument count: bound function handling may have shifted. */
num_stack_args = duk_get_top(ctx) - (idx + 2);
DUK_DDD(DUK_DDDPRINT("recomputed arg count: %ld\n", (long) num_stack_args));
duk_handle_ecma_call_setup(thr,
c, /* num_stack_args */
call_flags); /* call_flags */
tv_func = DUK__REGP(idx); /* Relookup if relocated */
if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
call_flags = 0; /* not protected, respect reclimit, not constructor */
/* restart execution -> starts executing new function */
goto restart_execution;
/* FIXME: eval needs special handling if it can also be a lightfunc
* (already excepted from lightfunc status, explain here).
*/
/* FIXME: the call target bound chain is already resolved by the ecma
* call setup attempt, document here... Perhaps the bound chain resolution
* could be a shared helper instead so that we could skip it here.
*/
duk_handle_call(thr,
num_stack_args,
call_flags);
/* FIXME: who should restore? */
duk_require_stack_top(ctx, fun->nregs); /* may have shrunk by inner calls, must recheck */
duk_set_top(ctx, fun->nregs);
/* No need to reinit setjmp() catchpoint, as call handling
* will store and restore our state.
*/
} else {
/* Call setup checks callability. */
DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_func));
obj_func = DUK_TVAL_GET_OBJECT(tv_func);
DUK_ASSERT(obj_func != NULL);
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(obj_func));
/*
* Other cases, use C recursion.
*
@ -2841,8 +2836,8 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *entry_thread) {
}
duk_handle_call(thr,
c, /* num_stack_args */
call_flags); /* call_flags */
num_stack_args,
call_flags);
/* XXX: who should restore? */
duk_require_stack_top(ctx, (duk_idx_t) fun->nregs); /* may have shrunk by inner calls, must recheck */

31
src/duk_js_ops.c

@ -87,6 +87,9 @@ DUK_INTERNAL duk_bool_t duk_js_toboolean(duk_tval *tv) {
void *p = DUK_TVAL_GET_POINTER(tv);
return (p != NULL ? 1 : 0);
}
case DUK_TAG_LIGHTFUNC: {
return 1;
}
default: {
/* number */
int c;
@ -220,6 +223,10 @@ DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) {
void *p = DUK_TVAL_GET_POINTER(tv);
return (p != NULL ? 1.0 : 0.0);
}
case DUK_TAG_LIGHTFUNC: {
/* +(function(){}) -> NaN */
return DUK_DOUBLE_NAN;
}
default: {
/* number */
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
@ -574,6 +581,19 @@ DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, d
return (DUK_MEMCMP(buf_x, buf_y, len_x) == 0) ? 1 : 0;
}
}
case DUK_TAG_LIGHTFUNC: {
/* At least 'magic' has a significant impact on function
* identity.
*/
duk_small_uint_t lf_flags_x;
duk_small_uint_t lf_flags_y;
duk_c_function func_x;
duk_c_function func_y;
DUK_TVAL_GET_LIGHTFUNC(tv_x, func_x, lf_flags_x);
DUK_TVAL_GET_LIGHTFUNC(tv_y, func_y, lf_flags_y);
return ((func_x == func_y) && (lf_flags_x == lf_flags_y)) ? 1 : 0;
}
default: {
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x));
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_y));
@ -943,7 +963,7 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_
duk_push_tval(ctx, tv_x);
duk_push_tval(ctx, tv_y);
func = duk_require_hobject(ctx, -1);
func = duk_require_hobject(ctx, -1); /* FIXME: lightfunc */
/*
* For bound objects, [[HasInstance]] just calls the target function
@ -1082,9 +1102,12 @@ DUK_INTERNAL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv
* form (which is a shame).
*/
/* TypeError if rval is not an object (or lightfunc which should behave
* like a Function instance).
*/
duk_push_tval(ctx, tv_x);
duk_push_tval(ctx, tv_y);
(void) duk_require_hobject(ctx, -1); /* TypeError if rval not object */
duk_require_type_mask(ctx, -1, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC);
duk_to_string(ctx, -2); /* coerce lval with ToString() */
retval = duk_hobject_hasprop(thr, duk_get_tval(ctx, -1), duk_get_tval(ctx, -2));
@ -1148,6 +1171,10 @@ DUK_INTERNAL duk_hstring *duk_js_typeof(duk_hthread *thr, duk_tval *tv_x) {
stridx = DUK_STRIDX_LC_BUFFER;
break;
}
case DUK_TAG_LIGHTFUNC: {
stridx = DUK_STRIDX_LC_FUNCTION;
break;
}
default: {
/* number */
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x));

12
src/duk_js_var.c

@ -513,9 +513,9 @@ void duk_js_init_activation_environment_records_delayed(duk_hthread *thr,
duk_hobject *func;
duk_hobject *env;
func = act->func;
func = DUK_ACT_GET_FUNC(act);
DUK_ASSERT(func != NULL);
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func)); /* bound functions are never in act->func */
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func)); /* bound functions are never in act 'func' */
/*
* Delayed initialization only occurs for 'NEWENV' functions.
@ -568,7 +568,7 @@ DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject
DUK_ASSERT(thr != NULL);
DUK_ASSERT(env != NULL);
DUK_ASSERT(func != NULL);
/* FIXME: DUK_ASSERT(func != NULL); */
if (!DUK_HOBJECT_IS_DECENV(env) || DUK_HOBJECT_HAS_ENVRECCLOSED(env)) {
DUK_DDD(DUK_DDDPRINT("environment record not a declarative record, "
@ -609,7 +609,7 @@ DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject
}
#endif
if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
if (func != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
duk_hobject *varmap;
duk_hstring *key;
duk_tval *tv;
@ -828,7 +828,7 @@ duk_bool_t duk__getid_activation_regs(duk_hthread *thr,
DUK_ASSERT(act != NULL);
DUK_ASSERT(out != NULL);
func = act->func;
func = DUK_ACT_GET_FUNC(act);
DUK_ASSERT(func != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
@ -945,7 +945,7 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
goto fail_not_found;
}
func = act->func;
func = DUK_ACT_GET_FUNC(act);
DUK_ASSERT(func != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));

86
src/duk_tval.h

@ -49,9 +49,10 @@ typedef union duk_double_union duk_tval;
#define DUK_TAG_BOOLEAN 0xfff3UL /* embed: 0 or 1 (false or true) */
/* DUK_TAG_NUMBER would logically go here, but it has multiple 'tags' */
#define DUK_TAG_POINTER 0xfff4UL /* embed: void ptr */
#define DUK_TAG_STRING 0xfff5UL /* embed: duk_hstring ptr */
#define DUK_TAG_OBJECT 0xfff6UL /* embed: duk_hobject ptr */
#define DUK_TAG_BUFFER 0xfff7UL /* embed: duk_hbuffer ptr */
#define DUK_TAG_LIGHTFUNC 0xfff5UL /* embed: func ptr */
#define DUK_TAG_STRING 0xfff6UL /* embed: duk_hstring ptr */
#define DUK_TAG_OBJECT 0xfff7UL /* embed: duk_hobject ptr */
#define DUK_TAG_BUFFER 0xfff8UL /* embed: duk_hbuffer ptr */
/* for convenience */
#define DUK_XTAG_UNDEFINED_ACTUAL 0xfff10000UL
@ -98,6 +99,28 @@ typedef union duk_double_union duk_tval;
} while (0)
#endif /* DUK_USE_64BIT_OPS */
#ifdef DUK_USE_64BIT_OPS
/* Double casting for pointer to avoid gcc warning (cast from pointer to integer of different size) */
#ifdef DUK_USE_DOUBLE_ME
#define DUK__TVAL_SET_LIGHTFUNC(v,fp,flags) do { \
(v)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_LIGHTFUNC) << 16) | \
((duk_uint64_t) (flags)) | \
(((duk_uint64_t) (duk_uint32_t) (fp)) << 32); \
} while (0)
#else
#define DUK__TVAL_SET_LIGHTFUNC(v,fp,flags) do { \
(v)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_LIGHTFUNC) << 48) | \
(((duk_uint64_t) (flags)) << 32) | \
((duk_uint64_t) (duk_uint32_t) (fp)); \
} while (0)
#endif
#else /* DUK_USE_64BIT_OPS */
#define DUK__TVAL_SET_LIGHTFUNC(v,fp,flags) do { \
(v)->ui[DUK_DBL_IDX_UI0] = (((duk_uint32_t) DUK_TAG_LIGHTFUNC) << 16) | ((duk_uint32_t) (flags)); \
(v)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (fp); \
} while (0)
#endif /* DUK_USE_64BIT_OPS */
/* select actual setters */
#ifdef DUK_USE_FULL_TVAL
#define DUK_TVAL_SET_UNDEFINED_ACTUAL(v) DUK__TVAL_SET_UNDEFINED_ACTUAL_FULL((v))
@ -115,6 +138,7 @@ typedef union duk_double_union duk_tval;
#define DUK_TVAL_SET_NAN(v) DUK__TVAL_SET_NAN_NOTFULL((v))
#endif
#define DUK_TVAL_SET_LIGHTFUNC(v,fp,flags) DUK__TVAL_SET_LIGHTFUNC((v),(fp),(flags))
#define DUK_TVAL_SET_STRING(v,h) DUK__TVAL_SET_TAGGEDPOINTER((v),(h),DUK_TAG_STRING)
#define DUK_TVAL_SET_OBJECT(v,h) DUK__TVAL_SET_TAGGEDPOINTER((v),(h),DUK_TAG_OBJECT)
#define DUK_TVAL_SET_BUFFER(v,h) DUK__TVAL_SET_TAGGEDPOINTER((v),(h),DUK_TAG_BUFFER)
@ -125,6 +149,12 @@ typedef union duk_double_union duk_tval;
/* getters */
#define DUK_TVAL_GET_BOOLEAN(v) ((int) (v)->us[DUK_DBL_IDX_US1])
#define DUK_TVAL_GET_NUMBER(v) ((v)->d)
#define DUK_TVAL_GET_LIGHTFUNC(v,out_fp,out_flags) do { \
(out_flags) = (v)->ui[DUK_DBL_IDX_UI0] & 0xffffUL; \
(out_fp) = (duk_c_function) (v)->ui[DUK_DBL_IDX_UI1]; \
} while (0)
#define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(v) ((duk_c_function) ((v)->ui[DUK_DBL_IDX_UI1]))
#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(v) (((int) (v)->ui[DUK_DBL_IDX_UI0]) & 0xffffUL)
#define DUK_TVAL_GET_STRING(v) ((duk_hstring *) (v)->vp[DUK_DBL_IDX_VP1])
#define DUK_TVAL_GET_OBJECT(v) ((duk_hobject *) (v)->vp[DUK_DBL_IDX_VP1])
#define DUK_TVAL_GET_BUFFER(v) ((duk_hbuffer *) (v)->vp[DUK_DBL_IDX_VP1])
@ -141,6 +171,7 @@ typedef union duk_double_union duk_tval;
#define DUK_TVAL_IS_BOOLEAN(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_BOOLEAN)
#define DUK_TVAL_IS_BOOLEAN_TRUE(v) ((v)->ui[DUK_DBL_IDX_UI0] == DUK_XTAG_BOOLEAN_TRUE)
#define DUK_TVAL_IS_BOOLEAN_FALSE(v) ((v)->ui[DUK_DBL_IDX_UI0] == DUK_XTAG_BOOLEAN_FALSE)
#define DUK_TVAL_IS_LIGHTFUNC(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_LIGHTFUNC)
#define DUK_TVAL_IS_STRING(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_STRING)
#define DUK_TVAL_IS_OBJECT(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_OBJECT)
#define DUK_TVAL_IS_BUFFER(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_BUFFER)
@ -165,6 +196,7 @@ typedef struct duk_tval_struct duk_tval;
struct duk_tval_struct {
duk_small_uint_t t;
duk_small_uint_t v_flags;
union {
duk_double_t d;
duk_small_int_t i;
@ -176,6 +208,7 @@ struct duk_tval_struct {
duk_hthread *hthread;
duk_hbuffer *hbuffer;
duk_heaphdr *heaphdr;
duk_c_function lightfunc;
} v;
};
@ -184,9 +217,10 @@ struct duk_tval_struct {
#define DUK_TAG_NULL 2
#define DUK_TAG_BOOLEAN 3
#define DUK_TAG_POINTER 4
#define DUK_TAG_STRING 5
#define DUK_TAG_OBJECT 6
#define DUK_TAG_BUFFER 7
#define DUK_TAG_LIGHTFUNC 5
#define DUK_TAG_STRING 6
#define DUK_TAG_OBJECT 7
#define DUK_TAG_BUFFER 8
/* DUK__TAG_NUMBER is intentionally first, as it is the default clause in code
* to support the 8-byte representation. Further, it is a non-heap-allocated
@ -219,6 +253,17 @@ struct duk_tval_struct {
(tv)->v.d = (val); \
} while (0)
#define DUK_TVAL_SET_POINTER(tv,hptr) do { \
(tv)->t = DUK_TAG_POINTER; \
(tv)->v.voidptr = (hptr); \
} while (0)
#define DUK_TVAL_SET_LIGHTFUNC(tv,fp,flags) do { \
(tv)->t = DUK_TAG_LIGHTFUNC; \
(tv)->v_flags = (flags); \
(tv)->v.lightfunc = (duk_c_function) (fp); \
} while (0)
#define DUK_TVAL_SET_STRING(tv,hptr) do { \
(tv)->t = DUK_TAG_STRING; \
(tv)->v.hstring = (hptr); \
@ -234,11 +279,6 @@ struct duk_tval_struct {
(tv)->v.hbuffer = (hptr); \
} while (0)
#define DUK_TVAL_SET_POINTER(tv,hptr) do { \
(tv)->t = DUK_TAG_POINTER; \
(tv)->v.voidptr = (hptr); \
} while (0)
#define DUK_TVAL_SET_NAN(tv) do { \
/* in non-packed representation we don't care about which NaN is used */ \
(tv)->t = DUK__TAG_NUMBER; \
@ -250,15 +290,20 @@ struct duk_tval_struct {
/* getters */
#define DUK_TVAL_GET_BOOLEAN(tv) ((tv)->v.i)
#define DUK_TVAL_GET_NUMBER(tv) ((tv)->v.d)
#define DUK_TVAL_GET_POINTER(tv) ((tv)->v.voidptr)
#define DUK_TVAL_GET_LIGHTFUNC(tv,out_fp,out_flags) do { \
(out_flags) = (duk_uint32_t) (tv)->v_flags; \
(out_fp) = (tv)->v.lightfunc; \
} while (0)
#define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv) ((tv)->v.lightfunc)
#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv) ((duk_uint32_t) ((tv)->v_flags))
#define DUK_TVAL_GET_STRING(tv) ((tv)->v.hstring)
#define DUK_TVAL_GET_OBJECT(tv) ((tv)->v.hobject)
#define DUK_TVAL_GET_BUFFER(tv) ((tv)->v.hbuffer)
#define DUK_TVAL_GET_POINTER(tv) ((tv)->v.voidptr)
#define DUK_TVAL_GET_HEAPHDR(tv) ((tv)->v.heaphdr)
/* decoding */
#define DUK_TVAL_GET_TAG(tv) ((tv)->t)
#define DUK_TVAL_IS_NUMBER(tv) ((tv)->t == DUK__TAG_NUMBER)
#define DUK_TVAL_IS_UNDEFINED(tv) ((tv)->t == DUK_TAG_UNDEFINED)
#define DUK_TVAL_IS_UNDEFINED_ACTUAL(tv) (((tv)->t == DUK_TAG_UNDEFINED) && ((tv)->v.i == 0))
#define DUK_TVAL_IS_UNDEFINED_UNUSED(tv) (((tv)->t == DUK_TAG_UNDEFINED) && ((tv)->v.i != 0))
@ -266,10 +311,12 @@ struct duk_tval_struct {
#define DUK_TVAL_IS_BOOLEAN(tv) ((tv)->t == DUK_TAG_BOOLEAN)
#define DUK_TVAL_IS_BOOLEAN_TRUE(tv) (((tv)->t == DUK_TAG_BOOLEAN) && ((tv)->v.i != 0))
#define DUK_TVAL_IS_BOOLEAN_FALSE(tv) (((tv)->t == DUK_TAG_BOOLEAN) && ((tv)->v.i == 0))
#define DUK_TVAL_IS_NUMBER(tv) ((tv)->t == DUK__TAG_NUMBER)
#define DUK_TVAL_IS_POINTER(tv) ((tv)->t == DUK_TAG_POINTER)
#define DUK_TVAL_IS_LIGHTFUNC(tv) ((tv)->t == DUK_TAG_LIGHTFUNC)
#define DUK_TVAL_IS_STRING(tv) ((tv)->t == DUK_TAG_STRING)
#define DUK_TVAL_IS_OBJECT(tv) ((tv)->t == DUK_TAG_OBJECT)
#define DUK_TVAL_IS_BUFFER(tv) ((tv)->t == DUK_TAG_BUFFER)
#define DUK_TVAL_IS_POINTER(tv) ((tv)->t == DUK_TAG_POINTER)
#define DUK_TVAL_IS_HEAP_ALLOCATED(tv) ((tv)->t >= DUK_TAG_STRING)
@ -282,4 +329,15 @@ struct duk_tval_struct {
#define DUK_TVAL_SET_BOOLEAN_TRUE(v) DUK_TVAL_SET_BOOLEAN(v, 1)
#define DUK_TVAL_SET_BOOLEAN_FALSE(v) DUK_TVAL_SET_BOOLEAN(v, 0)
/* Lightfunc flags packing and unpacking. */
/* Sign extend: 0x0000##00 -> 0x##000000 -> sign extend to 0xssssss## */
#define DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags) \
((((duk_int32_t) (lf_flags)) << 16) >> 24)
#define DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags) \
(((lf_flags) >> 4) & 0x0f)
#define DUK_LFUNC_FLAGS_GET_NARGS(lf_flags) \
((lf_flags) & 0x0f)
#define DUK_LFUNC_FLAGS_PACK(magic,length,nargs) \
((magic) << 8) | ((length) << 4) | (nargs)
#endif /* DUK_TVAL_H_INCLUDED */

110
src/genbuiltins.py

@ -130,29 +130,6 @@ PROPDESC_FLAG_ENUMERABLE = (1 << 1)
PROPDESC_FLAG_CONFIGURABLE = (1 << 2)
PROPDESC_FLAG_ACCESSOR = (1 << 3) # unused now
# magic values for Date built-in, must match duk_bi_date.c
BI_DATE_FLAG_NAN_TO_ZERO = (1 << 0)
BI_DATE_FLAG_NAN_TO_RANGE_ERROR = (1 << 1)
BI_DATE_FLAG_ONEBASED = (1 << 2)
BI_DATE_FLAG_EQUIVYEAR = (1 << 3)
BI_DATE_FLAG_LOCALTIME = (1 << 4)
BI_DATE_FLAG_SUB1900 = (1 << 5)
BI_DATE_FLAG_TOSTRING_DATE = (1 << 6)
BI_DATE_FLAG_TOSTRING_TIME = (1 << 7)
BI_DATE_FLAG_TOSTRING_LOCALE = (1 << 8)
BI_DATE_FLAG_TIMESETTER = (1 << 9)
BI_DATE_FLAG_YEAR_FIXUP = (1 << 10)
BI_DATE_FLAG_SEP_T = (1 << 11)
BI_DATE_FLAG_VALUE_SHIFT = 12
BI_DATE_IDX_YEAR = 0
BI_DATE_IDX_MONTH = 1
BI_DATE_IDX_DAY = 2
BI_DATE_IDX_HOUR = 3
BI_DATE_IDX_MINUTE = 4
BI_DATE_IDX_SECOND = 5
BI_DATE_IDX_MILLISECOND = 6
BI_DATE_IDX_WEEKDAY = 7
# magic values for Array built-in
BI_ARRAY_ITER_EVERY = 0
BI_ARRAY_ITER_SOME = 1
@ -665,58 +642,65 @@ bi_date_prototype = {
{ 'name': internal('Value'), 'value': DBL_NAN, 'attributes': 'w' }
],
# NOTE: The magic values for Date prototype are special. The actual control
# flags needed for the built-ins don't fit into LIGHTFUNC magic field, so
# the values here are indices to duk__date_magics[] in duk_bi_date.c which
# contains the actual control flags. Magic values here must be kept in strict
# sync with duk_bi_date.c!
'functions': [
{ 'name': 'toString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TOSTRING_DATE + BI_DATE_FLAG_TOSTRING_TIME + BI_DATE_FLAG_LOCALTIME } },
{ 'name': 'toDateString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TOSTRING_DATE + BI_DATE_FLAG_LOCALTIME } },
{ 'name': 'toTimeString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TOSTRING_TIME + BI_DATE_FLAG_LOCALTIME } },
{ 'name': 'toLocaleString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TOSTRING_DATE + BI_DATE_FLAG_TOSTRING_TIME + BI_DATE_FLAG_TOSTRING_LOCALE + BI_DATE_FLAG_LOCALTIME } },
{ 'name': 'toLocaleDateString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TOSTRING_DATE + BI_DATE_FLAG_TOSTRING_LOCALE + BI_DATE_FLAG_LOCALTIME } },
{ 'name': 'toLocaleTimeString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TOSTRING_TIME + BI_DATE_FLAG_TOSTRING_LOCALE + BI_DATE_FLAG_LOCALTIME } },
{ 'name': 'toUTCString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TOSTRING_DATE + BI_DATE_FLAG_TOSTRING_TIME } },
{ 'name': 'toISOString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TOSTRING_DATE + BI_DATE_FLAG_TOSTRING_TIME + BI_DATE_FLAG_NAN_TO_RANGE_ERROR + BI_DATE_FLAG_SEP_T } },
{ 'name': 'toString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 0 } },
{ 'name': 'toDateString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 1 } },
{ 'name': 'toTimeString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 2 } },
{ 'name': 'toLocaleString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 3 } },
{ 'name': 'toLocaleDateString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 4 } },
{ 'name': 'toLocaleTimeString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 5 } },
{ 'name': 'toUTCString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 6 } },
{ 'name': 'toISOString', 'native': 'duk_bi_date_prototype_tostring_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 7 } },
{ 'name': 'toJSON', 'native': 'duk_bi_date_prototype_to_json', 'length': 1 },
{ 'name': 'valueOf', 'native': 'duk_bi_date_prototype_value_of', 'length': 0 },
{ 'name': 'getTime', 'native': 'duk_bi_date_prototype_value_of', 'length': 0 }, # Native function shared on purpose
{ 'name': 'getFullYear', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_LOCALTIME + (BI_DATE_IDX_YEAR << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getUTCFullYear', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 0 + (BI_DATE_IDX_YEAR << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getMonth', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_LOCALTIME + (BI_DATE_IDX_MONTH << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getUTCMonth', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 0 + (BI_DATE_IDX_MONTH << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getDate', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_ONEBASED + BI_DATE_FLAG_LOCALTIME + (BI_DATE_IDX_DAY << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getUTCDate', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_ONEBASED + (BI_DATE_IDX_DAY << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getDay', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_LOCALTIME + (BI_DATE_IDX_WEEKDAY << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getUTCDay', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 0 + (BI_DATE_IDX_WEEKDAY << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getHours', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_LOCALTIME + (BI_DATE_IDX_HOUR << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getUTCHours', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 0 + (BI_DATE_IDX_HOUR << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getMinutes', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_LOCALTIME + (BI_DATE_IDX_MINUTE << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getUTCMinutes', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 0 + (BI_DATE_IDX_MINUTE << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getSeconds', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_LOCALTIME + (BI_DATE_IDX_SECOND << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getUTCSeconds', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 0 + (BI_DATE_IDX_SECOND << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getMilliseconds', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_LOCALTIME + (BI_DATE_IDX_MILLISECOND << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getUTCMilliseconds', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 0 + (BI_DATE_IDX_MILLISECOND << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getFullYear', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 8 } },
{ 'name': 'getUTCFullYear', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 9 } },
{ 'name': 'getMonth', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 10 } },
{ 'name': 'getUTCMonth', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 11 } },
{ 'name': 'getDate', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 12 } },
{ 'name': 'getUTCDate', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 13 } },
{ 'name': 'getDay', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 14 } },
{ 'name': 'getUTCDay', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 15 } },
{ 'name': 'getHours', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 16 } },
{ 'name': 'getUTCHours', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 17 } },
{ 'name': 'getMinutes', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 18 } },
{ 'name': 'getUTCMinutes', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 19 } },
{ 'name': 'getSeconds', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 20 } },
{ 'name': 'getUTCSeconds', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 21 } },
{ 'name': 'getMilliseconds', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 22 } },
{ 'name': 'getUTCMilliseconds', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'magic': { 'type': 'plain', 'value': 23 } },
{ 'name': 'getTimezoneOffset', 'native': 'duk_bi_date_prototype_get_timezone_offset', 'length': 0 },
{ 'name': 'setTime', 'native': 'duk_bi_date_prototype_set_time', 'length': 1 },
{ 'name': 'setMilliseconds', 'native': 'duk_bi_date_prototype_set_shared', 'length': 1, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TIMESETTER + BI_DATE_FLAG_LOCALTIME + (1 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setUTCMilliseconds', 'native': 'duk_bi_date_prototype_set_shared', 'length': 1, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TIMESETTER + (1 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setSeconds', 'native': 'duk_bi_date_prototype_set_shared', 'length': 2, 'varargs': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TIMESETTER + BI_DATE_FLAG_LOCALTIME + (2 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setUTCSeconds', 'native': 'duk_bi_date_prototype_set_shared', 'length': 2, 'varargs': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TIMESETTER + (2 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setMinutes', 'native': 'duk_bi_date_prototype_set_shared', 'length': 3, 'varargs': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TIMESETTER + BI_DATE_FLAG_LOCALTIME + (3 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setUTCMinutes', 'native': 'duk_bi_date_prototype_set_shared', 'length': 3, 'varargs': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TIMESETTER + (3 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setHours', 'native': 'duk_bi_date_prototype_set_shared', 'length': 4, 'varargs': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TIMESETTER + BI_DATE_FLAG_LOCALTIME + (4 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setUTCHours', 'native': 'duk_bi_date_prototype_set_shared', 'length': 4, 'varargs': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_TIMESETTER + (4 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setDate', 'native': 'duk_bi_date_prototype_set_shared', 'length': 1, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_LOCALTIME + (1 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setUTCDate', 'native': 'duk_bi_date_prototype_set_shared', 'length': 1, 'magic': { 'type': 'plain', 'value': 0 + (1 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setMonth', 'native': 'duk_bi_date_prototype_set_shared', 'length': 2, 'varargs': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_LOCALTIME + (2 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setUTCMonth', 'native': 'duk_bi_date_prototype_set_shared', 'length': 2, 'varargs': True, 'magic': { 'type': 'plain', 'value': 0 + (2 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setFullYear', 'native': 'duk_bi_date_prototype_set_shared', 'length': 3, 'varargs': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_NAN_TO_ZERO + BI_DATE_FLAG_LOCALTIME + (3 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setUTCFullYear', 'native': 'duk_bi_date_prototype_set_shared', 'length': 3, 'varargs': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_NAN_TO_ZERO + (3 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setMilliseconds', 'native': 'duk_bi_date_prototype_set_shared', 'length': 1, 'magic': { 'type': 'plain', 'value': 24 } },
{ 'name': 'setUTCMilliseconds', 'native': 'duk_bi_date_prototype_set_shared', 'length': 1, 'magic': { 'type': 'plain', 'value': 25 } },
{ 'name': 'setSeconds', 'native': 'duk_bi_date_prototype_set_shared', 'length': 2, 'varargs': True, 'magic': { 'type': 'plain', 'value': 26 } },
{ 'name': 'setUTCSeconds', 'native': 'duk_bi_date_prototype_set_shared', 'length': 2, 'varargs': True, 'magic': { 'type': 'plain', 'value': 27 } },
{ 'name': 'setMinutes', 'native': 'duk_bi_date_prototype_set_shared', 'length': 3, 'varargs': True, 'magic': { 'type': 'plain', 'value': 28 } },
{ 'name': 'setUTCMinutes', 'native': 'duk_bi_date_prototype_set_shared', 'length': 3, 'varargs': True, 'magic': { 'type': 'plain', 'value': 29 } },
{ 'name': 'setHours', 'native': 'duk_bi_date_prototype_set_shared', 'length': 4, 'varargs': True, 'magic': { 'type': 'plain', 'value': 30 } },
{ 'name': 'setUTCHours', 'native': 'duk_bi_date_prototype_set_shared', 'length': 4, 'varargs': True, 'magic': { 'type': 'plain', 'value': 31 } },
{ 'name': 'setDate', 'native': 'duk_bi_date_prototype_set_shared', 'length': 1, 'magic': { 'type': 'plain', 'value': 32 } },
{ 'name': 'setUTCDate', 'native': 'duk_bi_date_prototype_set_shared', 'length': 1, 'magic': { 'type': 'plain', 'value': 33 } },
{ 'name': 'setMonth', 'native': 'duk_bi_date_prototype_set_shared', 'length': 2, 'varargs': True, 'magic': { 'type': 'plain', 'value': 34 } },
{ 'name': 'setUTCMonth', 'native': 'duk_bi_date_prototype_set_shared', 'length': 2, 'varargs': True, 'magic': { 'type': 'plain', 'value': 35 } },
{ 'name': 'setFullYear', 'native': 'duk_bi_date_prototype_set_shared', 'length': 3, 'varargs': True, 'magic': { 'type': 'plain', 'value': 36 } },
{ 'name': 'setUTCFullYear', 'native': 'duk_bi_date_prototype_set_shared', 'length': 3, 'varargs': True, 'magic': { 'type': 'plain', 'value': 37 } },
# Non-standard extensions: E5 Section B.2.4, B.2.5, B.2.6
#
# 'length' values are not given explicitly but follows the general rule.
# The lengths below agree with V8.
{ 'name': 'getYear', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'section_b': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_LOCALTIME + BI_DATE_FLAG_SUB1900 + (BI_DATE_IDX_YEAR << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'setYear', 'native': 'duk_bi_date_prototype_set_shared', 'length': 1, 'section_b': True, 'magic': { 'type': 'plain', 'value': BI_DATE_FLAG_NAN_TO_ZERO + BI_DATE_FLAG_YEAR_FIXUP + (3 << BI_DATE_FLAG_VALUE_SHIFT) } },
{ 'name': 'getYear', 'native': 'duk_bi_date_prototype_get_shared', 'length': 0, 'section_b': True, 'magic': { 'type': 'plain', 'value': 38 } },
{ 'name': 'setYear', 'native': 'duk_bi_date_prototype_set_shared', 'length': 1, 'section_b': True, 'magic': { 'type': 'plain', 'value': 39 } },
# Note: toGMTString() is required to initially be the same Function object as the initial
# Date.prototype.toUTCString. In other words: Date.prototype.toGMTString === Date.prototype.toUTCString --> true.

Loading…
Cancel
Save