You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

830 lines
28 KiB

/*
* Initialize built-in objects. Current thread must have a valstack
* and initialization errors may longjmp, so a setjmp() catch point
* must exist.
*/
#include "duk_internal.h"
/*
* Encoding constants, must match genbuiltins.py
*/
#define DUK__CLASS_BITS 5
#define DUK__BIDX_BITS 7
#define DUK__STRIDX_BITS 9 /* XXX: try to optimize to 8 (would now be possible, <200 used) */
#define DUK__NATIDX_BITS 8
#define DUK__NUM_NORMAL_PROPS_BITS 6
#define DUK__NUM_FUNC_PROPS_BITS 6
#define DUK__PROP_FLAGS_BITS 3
#define DUK__STRING_LENGTH_BITS 8
#define DUK__STRING_CHAR_BITS 7
#define DUK__LENGTH_PROP_BITS 3
#define DUK__NARGS_BITS 3
#define DUK__PROP_TYPE_BITS 3
#define DUK__MAGIC_BITS 16
#define DUK__ACCESSOR_MAGIC_BITS 2 /* just a few shared accessors now */
#define DUK__NARGS_VARARGS_MARKER 0x07
#define DUK__NO_CLASS_MARKER 0x00 /* 0 = DUK_HOBJECT_CLASS_NONE */
#define DUK__NO_BIDX_MARKER 0x7f
#define DUK__NO_STRIDX_MARKER 0xff
#define DUK__PROP_TYPE_DOUBLE 0
#define DUK__PROP_TYPE_STRING 1
#define DUK__PROP_TYPE_STRIDX 2
#define DUK__PROP_TYPE_BUILTIN 3
#define DUK__PROP_TYPE_UNDEFINED 4
#define DUK__PROP_TYPE_BOOLEAN_TRUE 5
#define DUK__PROP_TYPE_BOOLEAN_FALSE 6
#define DUK__PROP_TYPE_ACCESSOR 7
/*
* Create built-in objects by parsing an init bitstream generated
* by genbuiltins.py.
*/
#if defined(DUK_USE_ROM_OBJECTS)
#if defined(DUK_USE_ROM_GLOBAL_CLONE) || defined(DUK_USE_ROM_GLOBAL_INHERIT)
DUK_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) {
duk_context *ctx;
duk_hobject *h1;
#if defined(DUK_USE_ROM_GLOBAL_CLONE)
duk_hobject *h2;
duk_uint8_t *props;
duk_size_t alloc_size;
#endif
ctx = (duk_context *) thr;
/* XXX: refactor into internal helper, duk_clone_hobject() */
#if defined(DUK_USE_ROM_GLOBAL_INHERIT)
/* Inherit from ROM-based global object: less RAM usage, less transparent. */
duk_push_object_helper(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL),
DUK_BIDX_GLOBAL);
h1 = duk_get_hobject(ctx, -1);
DUK_ASSERT(h1 != NULL);
#elif defined(DUK_USE_ROM_GLOBAL_CLONE)
/* Clone the properties of the ROM-based global object to create a
* fully RAM-based global object. Uses more memory than the inherit
* model but more compliant.
*/
duk_push_object_helper(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL),
DUK_BIDX_OBJECT_PROTOTYPE);
h1 = duk_get_hobject(ctx, -1);
DUK_ASSERT(h1 != NULL);
h2 = thr->builtins[DUK_BIDX_GLOBAL];
DUK_ASSERT(h2 != NULL);
/* Copy the property table verbatim; this handles attributes etc.
* For ROM objects it's not necessary (or possible) to update
* refcounts so leave them as is.
*/
alloc_size = DUK_HOBJECT_P_ALLOC_SIZE(h2);
DUK_ASSERT(alloc_size > 0);
props = DUK_ALLOC(thr->heap, alloc_size);
if (!props) {
DUK_ERROR_ALLOC_FAILED(thr);
return;
}
DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h2) != NULL);
DUK_MEMCPY((void *) props, (const void *) DUK_HOBJECT_GET_PROPS(thr->heap, h2), alloc_size);
/* XXX: keep property attributes or tweak them here?
* Properties will now be non-configurable even when they're
* normally configurable for the global object.
*/
DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h1) == NULL);
DUK_HOBJECT_SET_PROPS(thr->heap, h1, props);
DUK_HOBJECT_SET_ESIZE(h1, DUK_HOBJECT_GET_ESIZE(h2));
DUK_HOBJECT_SET_ENEXT(h1, DUK_HOBJECT_GET_ENEXT(h2));
DUK_HOBJECT_SET_ASIZE(h1, DUK_HOBJECT_GET_ASIZE(h2));
DUK_HOBJECT_SET_HSIZE(h1, DUK_HOBJECT_GET_HSIZE(h2));
#else
#error internal error in defines
#endif
duk_hobject_compact_props(thr, h1);
DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL])); /* no need to decref */
thr->builtins[DUK_BIDX_GLOBAL] = h1;
DUK_HOBJECT_INCREF(thr, h1);
DUK_D(DUK_DPRINT("duplicated global object: %!O", h1));
/* Create a fresh object environment for the global scope. This is
* needed so that the global scope points to the newly created RAM-based
* global object.
*/
duk_push_object_helper(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV),
-1); /* no prototype */
h1 = duk_get_hobject(ctx, -1);
DUK_ASSERT(h1 != NULL);
duk_dup_m2(ctx);
duk_dup_top(ctx); /* -> [ ... new_global new_globalenv new_global new_global ] */
duk_xdef_prop_stridx(thr, -3, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE);
duk_xdef_prop_stridx(thr, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); /* always provideThis=true */
duk_hobject_compact_props(thr, h1);
DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL_ENV] != NULL);
DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL_ENV])); /* no need to decref */
thr->builtins[DUK_BIDX_GLOBAL_ENV] = h1;
DUK_HOBJECT_INCREF(thr, h1);
DUK_D(DUK_DPRINT("duplicated global env: %!O", h1));
duk_pop_2(ctx);
}
#endif /* DUK_USE_ROM_GLOBAL_CLONE || DUK_USE_ROM_GLOBAL_INHERIT */
DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
/* Setup builtins from ROM objects. All heaps/threads will share
* the same readonly objects.
*/
duk_small_uint_t i;
for (i = 0; i < DUK_NUM_BUILTINS; i++) {
duk_hobject *h;
h = (duk_hobject *) DUK_LOSE_CONST(duk_rom_builtins_bidx[i]);
DUK_ASSERT(h != NULL);
thr->builtins[i] = h;
}
#if defined(DUK_USE_ROM_GLOBAL_CLONE) || defined(DUK_USE_ROM_GLOBAL_INHERIT)
/* By default the global object is read-only which is often much
* more of an issue than having read-only built-in objects (like
* RegExp, Date, etc). Use a RAM-based copy of the global object
* and the global environment object for convenience.
*/
duk__duplicate_ram_global_object(thr);
#endif
}
#else /* DUK_USE_ROM_OBJECTS */
DUK_LOCAL void duk__push_stridx(duk_context *ctx, duk_bitdecoder_ctx *bd) {
duk_small_uint_t n;
n = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS);
DUK_ASSERT_DISABLE(n >= 0); /* unsigned */
DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS);
duk_push_hstring_stridx(ctx, n);
}
DUK_LOCAL void duk__push_string(duk_context *ctx, duk_bitdecoder_ctx *bd) {
duk_small_uint_t n;
duk_small_uint_t i;
duk_uint8_t *p;
n = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRING_LENGTH_BITS);
p = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, n);
for (i = 0; i < n; i++) {
*p++ = (duk_uint8_t) duk_bd_decode(bd, DUK__STRING_CHAR_BITS);
}
(void) duk_buffer_to_string(ctx, -1);
}
DUK_LOCAL void duk__push_stridx_or_string(duk_context *ctx, duk_bitdecoder_ctx *bd) {
if (duk_bd_decode_flag(bd)) {
duk__push_string(ctx, bd);
} else {
duk__push_stridx(ctx, bd);
}
}
DUK_LOCAL void duk__push_double(duk_context *ctx, duk_bitdecoder_ctx *bd) {
duk_double_union du;
duk_small_uint_t i;
for (i = 0; i < 8; i++) {
/* Encoding endianness must match target memory layout,
* build scripts and genbuiltins.py must ensure this.
*/
du.uc[i] = (duk_uint8_t) duk_bd_decode(bd, 8);
}
duk_push_number(ctx, du.d); /* push operation normalizes NaNs */
}
DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
duk_context *ctx = (duk_context *) thr;
duk_bitdecoder_ctx bd_ctx;
duk_bitdecoder_ctx *bd = &bd_ctx; /* convenience */
duk_hobject *h;
duk_small_uint_t i, j;
DUK_D(DUK_DPRINT("INITBUILTINS BEGIN: DUK_NUM_BUILTINS=%d, DUK_NUM_BUILTINS_ALL=%d", (int) DUK_NUM_BUILTINS, (int) DUK_NUM_ALL_BUILTINS));
DUK_MEMZERO(&bd_ctx, sizeof(bd_ctx));
bd->data = (const duk_uint8_t *) duk_builtins_data;
11 years ago
bd->length = (duk_size_t) DUK_BUILTINS_DATA_LENGTH;
/*
* First create all built-in bare objects on the empty valstack.
*
* Built-ins in the index range [0,DUK_NUM_BUILTINS-1] have value
* stack indices matching their eventual thr->builtins[] index.
*
* Built-ins in the index range [DUK_NUM_BUILTINS,DUK_NUM_ALL_BUILTINS]
* will exist on the value stack during init but won't be placed
* into thr->builtins[]. These are objects referenced in some way
* from thr->builtins[] roots but which don't need to be indexed by
* Duktape through thr->builtins[] (e.g. user custom objects).
*/
duk_require_stack(ctx, DUK_NUM_ALL_BUILTINS);
DUK_DD(DUK_DDPRINT("create empty built-ins"));
DUK_ASSERT_TOP(ctx, 0);
for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
duk_small_uint_t class_num;
duk_small_int_t len = -1; /* must be signed */
class_num = (duk_small_uint_t) duk_bd_decode(bd, DUK__CLASS_BITS);
len = (duk_small_int_t) duk_bd_decode_flagged(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/);
if (class_num == DUK_HOBJECT_CLASS_FUNCTION) {
duk_small_uint_t natidx;
duk_int_t c_nargs; /* must hold DUK_VARARGS */
duk_c_function c_func;
duk_int16_t magic;
DUK_DDD(DUK_DDDPRINT("len=%ld", (long) len));
DUK_ASSERT(len >= 0);
natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS);
DUK_ASSERT(natidx != 0);
c_func = duk_bi_native_functions[natidx];
DUK_ASSERT(c_func != NULL);
c_nargs = (duk_small_uint_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, len /*def_value*/);
if (c_nargs == DUK__NARGS_VARARGS_MARKER) {
c_nargs = DUK_VARARGS;
}
11 years ago
/* XXX: set magic directly here? (it could share the c_nargs arg) */
duk_push_c_function_noexotic(ctx, c_func, c_nargs);
h = duk_require_hobject(ctx, -1);
DUK_ASSERT(h != NULL);
/* Currently all built-in native functions are strict.
* duk_push_c_function() now sets strict flag, so
* assert for it.
*/
DUK_ASSERT(DUK_HOBJECT_HAS_STRICT(h));
11 years ago
/* XXX: function properties */
/* Built-in 'name' is not writable by default. Function '.name'
* is writable to allow user code to set a '.name' on a native
* function.
*/
duk__push_stridx_or_string(ctx, bd);
duk_xdef_prop_stridx(ctx,
-2,
DUK_STRIDX_NAME,
(i == DUK_BIDX_FUNCTION_PROTOTYPE) ?
DUK_PROPDESC_FLAGS_W : DUK_PROPDESC_FLAGS_NONE);
/* Almost all global level Function objects are constructable
* but not all: Function.prototype is a non-constructable,
* callable Function.
*/
if (duk_bd_decode_flag(bd)) {
DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(h));
} else {
DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h);
}
/* Cast converts magic to 16-bit signed value */
magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0 /*def_value*/);
((duk_hnatfunc *) h)->magic = magic;
} else if (class_num == DUK_HOBJECT_CLASS_ARRAY) {
duk_push_array(ctx);
} else {
duk_push_object_helper(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE,
-1); /* no prototype or class yet */
}
h = duk_require_hobject(ctx, -1);
DUK_ASSERT(h != NULL);
DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num);
if (i < DUK_NUM_BUILTINS) {
thr->builtins[i] = h;
DUK_HOBJECT_INCREF(thr, &h->hdr);
}
if (len >= 0) {
/*
* For top-level objects, 'length' property has the following
* default attributes: non-writable, non-enumerable, non-configurable
* (E5 Section 15).
*
* However, 'length' property for Array.prototype has attributes
* expected of an Array instance which are different: writable,
* non-enumerable, non-configurable (E5 Section 15.4.5.2).
*
* Because Array .length is virtual, it's not encoded in the init
* data separately.
*/
DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_ARRAY); /* .length is virtual */
duk_push_int(ctx, len);
duk_xdef_prop_stridx(ctx,
-2,
DUK_STRIDX_LENGTH,
DUK_PROPDESC_FLAGS_NONE);
}
/* enable exotic behaviors last */
if (class_num == DUK_HOBJECT_CLASS_ARRAY) {
DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)); /* set by duk_push_array() */
}
if (class_num == DUK_HOBJECT_CLASS_STRING) {
DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h);
}
/* some assertions */
DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h));
/* DUK_HOBJECT_FLAG_CONSTRUCTABLE varies */
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(h));
DUK_ASSERT(!DUK_HOBJECT_HAS_COMPFUNC(h));
/* DUK_HOBJECT_FLAG_NATFUNC varies */
DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(h));
DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h) || class_num == DUK_HOBJECT_CLASS_ARRAY);
/* DUK_HOBJECT_FLAG_STRICT varies */
DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(h) || /* all native functions have NEWENV */
DUK_HOBJECT_HAS_NEWENV(h));
DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(h));
DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(h));
DUK_ASSERT(!DUK_HOBJECT_HAS_ENVRECCLOSED(h));
/* DUK_HOBJECT_FLAG_EXOTIC_ARRAY varies */
/* DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ varies */
DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h));
DUK_DDD(DUK_DDDPRINT("created built-in %ld, class=%ld, length=%ld", (long) i, (long) class_num, (long) len));
}
/*
* Then decode the builtins init data (see genbuiltins.py) to
* init objects
*/
DUK_DD(DUK_DDPRINT("initialize built-in object properties"));
for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
duk_small_uint_t t;
duk_small_uint_t num;
DUK_DDD(DUK_DDDPRINT("initializing built-in object at index %ld", (long) i));
h = duk_require_hobject(ctx, i);
DUK_ASSERT(h != NULL);
t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS);
if (t != DUK__NO_BIDX_MARKER) {
DUK_DDD(DUK_DDDPRINT("set internal prototype: built-in %ld", (long) t));
DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, duk_require_hobject(ctx, t));
}
t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS);
if (t != DUK__NO_BIDX_MARKER) {
/* 'prototype' property for all built-in objects (which have it) has attributes:
* [[Writable]] = false,
* [[Enumerable]] = false,
* [[Configurable]] = false
*/
DUK_DDD(DUK_DDDPRINT("set external prototype: built-in %ld", (long) t));
duk_xdef_prop_stridx_builtin(ctx, i, DUK_STRIDX_PROTOTYPE, t, DUK_PROPDESC_FLAGS_NONE);
}
t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS);
if (t != DUK__NO_BIDX_MARKER) {
/* 'constructor' property for all built-in objects (which have it) has attributes:
* [[Writable]] = true,
* [[Enumerable]] = false,
* [[Configurable]] = true
*/
DUK_DDD(DUK_DDDPRINT("set external constructor: built-in %ld", (long) t));
duk_xdef_prop_stridx_builtin(ctx, i, DUK_STRIDX_CONSTRUCTOR, t, DUK_PROPDESC_FLAGS_WC);
}
/* normal valued properties */
num = (duk_small_uint_t) duk_bd_decode(bd, DUK__NUM_NORMAL_PROPS_BITS);
DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld normal valued properties", (long) i, (long) num));
for (j = 0; j < num; j++) {
duk_small_uint_t defprop_flags;
duk__push_stridx_or_string(ctx, bd);
/*
* Property attribute defaults are defined in E5 Section 15 (first
* few pages); there is a default for all properties and a special
* default for 'length' properties. Variation from the defaults is
* signaled using a single flag bit in the bitstream.
*/
if (duk_bd_decode_flag(bd)) {
defprop_flags = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_FLAGS_BITS);
} else {
defprop_flags = DUK_PROPDESC_FLAGS_WC;
}
defprop_flags |= DUK_DEFPROP_FORCE |
DUK_DEFPROP_HAVE_VALUE |
DUK_DEFPROP_HAVE_WRITABLE |
DUK_DEFPROP_HAVE_ENUMERABLE |
DUK_DEFPROP_HAVE_CONFIGURABLE; /* Defaults for data properties. */
/* The writable, enumerable, configurable flags in prop_flags
* match both duk_def_prop() and internal property flags.
*/
DUK_ASSERT(DUK_PROPDESC_FLAG_WRITABLE == DUK_DEFPROP_WRITABLE);
DUK_ASSERT(DUK_PROPDESC_FLAG_ENUMERABLE == DUK_DEFPROP_ENUMERABLE);
DUK_ASSERT(DUK_PROPDESC_FLAG_CONFIGURABLE == DUK_DEFPROP_CONFIGURABLE);
t = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_TYPE_BITS);
DUK_DDD(DUK_DDDPRINT("built-in %ld, normal-valued property %ld, key %!T, flags 0x%02lx, type %ld",
(long) i, (long) j, duk_get_tval(ctx, -1), (unsigned long) defprop_flags, (long) t));
switch (t) {
case DUK__PROP_TYPE_DOUBLE: {
duk__push_double(ctx, bd);
break;
}
case DUK__PROP_TYPE_STRING: {
duk__push_string(ctx, bd);
break;
}
case DUK__PROP_TYPE_STRIDX: {
duk__push_stridx(ctx, bd);
break;
}
case DUK__PROP_TYPE_BUILTIN: {
duk_small_uint_t bidx;
bidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS);
DUK_ASSERT(bidx != DUK__NO_BIDX_MARKER);
duk_dup(ctx, (duk_idx_t) bidx);
break;
}
case DUK__PROP_TYPE_UNDEFINED: {
duk_push_undefined(ctx);
break;
}
case DUK__PROP_TYPE_BOOLEAN_TRUE: {
duk_push_true(ctx);
break;
}
case DUK__PROP_TYPE_BOOLEAN_FALSE: {
duk_push_false(ctx);
break;
}
case DUK__PROP_TYPE_ACCESSOR: {
duk_small_uint_t natidx_getter = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS);
duk_small_uint_t natidx_setter = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS);
duk_small_uint_t accessor_magic = (duk_small_uint_t) duk_bd_decode(bd, DUK__ACCESSOR_MAGIC_BITS);
duk_c_function c_func_getter;
duk_c_function c_func_setter;
DUK_DDD(DUK_DDDPRINT("built-in accessor property: objidx=%ld, key=%!T, getteridx=%ld, setteridx=%ld, flags=0x%04lx",
(long) i, duk_get_tval(ctx, -1), (long) natidx_getter, (long) natidx_setter, (unsigned long) defprop_flags));
c_func_getter = duk_bi_native_functions[natidx_getter];
if (c_func_getter != NULL) {
duk_push_c_function_noconstruct_noexotic(ctx, c_func_getter, 0); /* always 0 args */
duk_set_magic(ctx, -1, (duk_int_t) accessor_magic);
defprop_flags |= DUK_DEFPROP_HAVE_GETTER;
}
c_func_setter = duk_bi_native_functions[natidx_setter];
if (c_func_setter != NULL) {
duk_push_c_function_noconstruct_noexotic(ctx, c_func_setter, 1); /* always 1 arg */
duk_set_magic(ctx, -1, (duk_int_t) accessor_magic);
defprop_flags |= DUK_DEFPROP_HAVE_SETTER;
}
/* Writable flag doesn't make sense for an accessor. */
DUK_ASSERT((defprop_flags & DUK_PROPDESC_FLAG_WRITABLE) == 0); /* genbuiltins.py ensures */
defprop_flags &= ~(DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE);
defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE;
break;
}
default: {
/* exhaustive */
DUK_UNREACHABLE();
}
}
duk_def_prop(ctx, i, defprop_flags);
DUK_ASSERT_TOP(ctx, DUK_NUM_ALL_BUILTINS);
}
/* native function properties */
num = (duk_small_uint_t) duk_bd_decode(bd, DUK__NUM_FUNC_PROPS_BITS);
DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld function valued properties", (long) i, (long) num));
for (j = 0; j < num; j++) {
duk_hstring *h_key;
duk_small_uint_t natidx;
duk_int_t c_nargs; /* must hold DUK_VARARGS */
duk_small_uint_t c_length;
duk_int16_t magic;
duk_c_function c_func;
duk_hnatfunc *h_func;
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 &#39;length&#39; property of a bound function now comes out wrong. We could simply look up the virtual &#39;length&#39; 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 &#39;magic&#39; to activation for lightfuncs. It will be needed for lightweight functions: we don&#39;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
11 years ago
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
duk_small_int_t lightfunc_eligible;
#endif
duk__push_stridx_or_string(ctx, bd);
h_key = duk_get_hstring(ctx, -1);
DUK_ASSERT(h_key != NULL);
DUK_UNREF(h_key);
natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS);
c_length = (duk_small_uint_t) duk_bd_decode(bd, DUK__LENGTH_PROP_BITS);
c_nargs = (duk_int_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, (duk_int32_t) c_length /*def_value*/);
if (c_nargs == DUK__NARGS_VARARGS_MARKER) {
c_nargs = DUK_VARARGS;
}
c_func = duk_bi_native_functions[natidx];
DUK_DDD(DUK_DDDPRINT("built-in %ld, function-valued property %ld, key %!O, natidx %ld, length %ld, nargs %ld",
(long) i, (long) j, (duk_heaphdr *) h_key, (long) natidx, (long) c_length,
(c_nargs == DUK_VARARGS ? (long) -1 : (long) c_nargs)));
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 &#39;length&#39; property of a bound function now comes out wrong. We could simply look up the virtual &#39;length&#39; 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 &#39;magic&#39; to activation for lightfuncs. It will be needed for lightweight functions: we don&#39;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
11 years ago
/* 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)
lightfunc_eligible =
((c_nargs >= DUK_LFUNC_NARGS_MIN && c_nargs <= DUK_LFUNC_NARGS_MAX) || (c_nargs == DUK_VARARGS)) &&
(c_length <= DUK_LFUNC_LENGTH_MAX) &&
(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX);
if (h_key == DUK_HTHREAD_STRING_EVAL(thr) ||
h_key == DUK_HTHREAD_STRING_YIELD(thr) ||
h_key == DUK_HTHREAD_STRING_RESUME(thr)) {
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 &#39;length&#39; property of a bound function now comes out wrong. We could simply look up the virtual &#39;length&#39; 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 &#39;magic&#39; to activation for lightfuncs. It will be needed for lightweight functions: we don&#39;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
11 years ago
/* 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: key=%!O, i=%d, j=%d", (duk_heaphdr *) h_key, (int) i, (int) j));
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 &#39;length&#39; property of a bound function now comes out wrong. We could simply look up the virtual &#39;length&#39; 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 &#39;magic&#39; to activation for lightfuncs. It will be needed for lightweight functions: we don&#39;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
11 years ago
lightfunc_eligible = 0;
}
if (lightfunc_eligible) {
duk_tval tv_lfunc;
duk_small_uint_t lf_nargs = (c_nargs == DUK_VARARGS ? DUK_LFUNC_NARGS_VARARGS : c_nargs);
duk_small_uint_t lf_flags = DUK_LFUNC_FLAGS_PACK(magic, c_length, lf_nargs);
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 &#39;length&#39; property of a bound function now comes out wrong. We could simply look up the virtual &#39;length&#39; 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 &#39;magic&#39; to activation for lightfuncs. It will be needed for lightweight functions: we don&#39;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
11 years ago
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 -> %!iT", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic, duk_get_tval(ctx, -1)));
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 &#39;length&#39; property of a bound function now comes out wrong. We could simply look up the virtual &#39;length&#39; 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 &#39;magic&#39; to activation for lightfuncs. It will be needed for lightweight functions: we don&#39;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
11 years ago
goto lightfunc_skip;
}
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));
#endif /* DUK_USE_LIGHTFUNC_BUILTINS */
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 &#39;length&#39; property of a bound function now comes out wrong. We could simply look up the virtual &#39;length&#39; 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 &#39;magic&#39; to activation for lightfuncs. It will be needed for lightweight functions: we don&#39;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
11 years ago
/* [ (builtin objects) name ] */
duk_push_c_function_noconstruct_noexotic(ctx, c_func, c_nargs);
h_func = duk_require_hnatfunc(ctx, -1);
DUK_UNREF(h_func);
/* Currently all built-in native functions are strict.
* This doesn't matter for many functions, but e.g.
* String.prototype.charAt (and other string functions)
* rely on being strict so that their 'this' binding is
* not automatically coerced.
*/
DUK_HOBJECT_SET_STRICT((duk_hobject *) h_func);
/* No built-in functions are constructable except the top
* level ones (Number, etc).
*/
DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_func));
11 years ago
/* XXX: any way to avoid decoding magic bit; there are quite
* many function properties and relatively few with magic values.
*/
h_func->magic = magic;
/* [ (builtin objects) name func ] */
duk_push_int(ctx, c_length);
duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);
duk_dup_m2(ctx);
duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);
11 years ago
/* XXX: other properties of function instances; 'arguments', 'caller'. */
DUK_DD(DUK_DDPRINT("built-in object %ld, function property %ld -> %!T",
(long) i, (long) j, (duk_tval *) duk_get_tval(ctx, -1)));
/* [ (builtin objects) name func ] */
/*
* The default property attributes are correct for all
* function valued properties of built-in objects now.
*/
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 &#39;length&#39; property of a bound function now comes out wrong. We could simply look up the virtual &#39;length&#39; 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 &#39;magic&#39; to activation for lightfuncs. It will be needed for lightweight functions: we don&#39;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
11 years ago
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
lightfunc_skip:
#endif
duk_xdef_prop(ctx, i, DUK_PROPDESC_FLAGS_WC);
/* [ (builtin objects) ] */
}
}
/*
* Special post-tweaks, for cases not covered by the init data format.
*
* - Set Date.prototype.toGMTString to Date.prototype.toUTCString.
* toGMTString is required to have the same Function object as
* toUTCString in E5 Section B.2.6. Note that while Smjs respects
* this, V8 does not (the Function objects are distinct).
*
* - Make DoubleError non-extensible.
*
* - Add info about most important effective compile options to Duktape.
*
* - Possibly remove some properties (values or methods) which are not
* desirable with current feature options but are not currently
* conditional in init data.
*/
#if defined(DUK_USE_DATE_BUILTIN)
duk_get_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING);
duk_xdef_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC);
#endif
h = duk_require_hobject(ctx, DUK_BIDX_DOUBLE_ERROR);
DUK_ASSERT(h != NULL);
DUK_HOBJECT_CLEAR_EXTENSIBLE(h);
#if !defined(DUK_USE_ES6_OBJECT_PROTO_PROPERTY)
DUK_DD(DUK_DDPRINT("delete Object.prototype.__proto__ built-in which is not enabled in features"));
(void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_PROTOTYPE], DUK_HTHREAD_STRING___PROTO__(thr), DUK_DELPROP_FLAG_THROW);
#endif
#if !defined(DUK_USE_ES6_OBJECT_SETPROTOTYPEOF)
DUK_DD(DUK_DDPRINT("delete Object.setPrototypeOf built-in which is not enabled in features"));
(void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_CONSTRUCTOR], DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr), DUK_DELPROP_FLAG_THROW);
#endif
/* XXX: relocate */
duk_push_string(ctx,
/* Endianness indicator */
#if defined(DUK_USE_INTEGER_LE)
"l"
#elif defined(DUK_USE_INTEGER_BE)
"b"
#elif defined(DUK_USE_INTEGER_ME) /* integer mixed endian not really used now */
"m"
#else
"?"
#endif
#if defined(DUK_USE_DOUBLE_LE)
"l"
#elif defined(DUK_USE_DOUBLE_BE)
"b"
#elif defined(DUK_USE_DOUBLE_ME)
"m"
#else
"?"
#endif
" "
/* Packed or unpacked tval */
#if defined(DUK_USE_PACKED_TVAL)
"p"
#else
"u"
#endif
#if defined(DUK_USE_FASTINT)
"f"
16-bit fields and heap pointer compression work Memory optimization work for very low memory devices (96 to 256kB system RAM). Overall changes are: - 16-bit fields for various internal structures to reduce their size - Heap pointer compression to reduce pointer size to 16 bits When DUK_OPT_LIGHTFUNC_BUILTINS and the new low memory options are enabled, Duktape initial heap memory usage is about 23kB (compared to baseline of about 45kB) on x86. Unless low memory feature options are enabled, there should be no visible changes to Duktape behavior. More detailed changes: - 16-bit changes for duk_heaphdr: pointer compression, refcount - 16-bit changes for duk_hstring: hash, blen, and clen can all be 16 bits, use 0xFFFF as string byte length limit (call sites ensure this limit is never exceeded) - 16-bit changes for duk_hbuffer, use 0xFFFF as buffer length limit - 16-bit fields for hobject size (entry part, array part), drop hash part since it&#39;s not usually needed for extremely low memory environments - 16-bit changes for duk_hcompiledfunction - Heap pointer packing for stringtable - Heap pointer packing for &#39;strs&#39; built-in strings list (saves around 600 to 700 bytes but may not be a good tradeoff because call site size will increase) Other changes: - Heaphdr NULL init fix. The original macros were broken: the double/single linked macro variants were the wrong way around. Now sets through macro to work properly with compressed pointers. - Rename duk_hbuffer CURR_DATA_PTR -&gt; DATA_PTR to reduce macro length (previous name was tediously long) - Rename buffer &#34;usable_size&#34; to &#34;alloc_size&#34; throughout as they have been the same for a while now (they used to differ when buffer had an extra NUL). - Add memory optimization markers to Duktape.env (pointer compression and individual 16-bit field options) - Rename a few internal fields for clarity: duk_hobject &#39;p&#39; to &#39;props&#39;, heap-&gt;st to heap-&gt;strtable - Add a safety check for buffer alloc size (should not be triggered but prevents wrapping if call sites don&#39;t properly check for sizes) - Other minor cleanups
10 years ago
#endif
" "
/* Low memory options */
#if defined(DUK_USE_STRTAB_CHAIN)
"c" /* chain */
#elif defined(DUK_USE_STRTAB_PROBE)
"p" /* probe */
#else
"?"
#endif
16-bit fields and heap pointer compression work Memory optimization work for very low memory devices (96 to 256kB system RAM). Overall changes are: - 16-bit fields for various internal structures to reduce their size - Heap pointer compression to reduce pointer size to 16 bits When DUK_OPT_LIGHTFUNC_BUILTINS and the new low memory options are enabled, Duktape initial heap memory usage is about 23kB (compared to baseline of about 45kB) on x86. Unless low memory feature options are enabled, there should be no visible changes to Duktape behavior. More detailed changes: - 16-bit changes for duk_heaphdr: pointer compression, refcount - 16-bit changes for duk_hstring: hash, blen, and clen can all be 16 bits, use 0xFFFF as string byte length limit (call sites ensure this limit is never exceeded) - 16-bit changes for duk_hbuffer, use 0xFFFF as buffer length limit - 16-bit fields for hobject size (entry part, array part), drop hash part since it&#39;s not usually needed for extremely low memory environments - 16-bit changes for duk_hcompiledfunction - Heap pointer packing for stringtable - Heap pointer packing for &#39;strs&#39; built-in strings list (saves around 600 to 700 bytes but may not be a good tradeoff because call site size will increase) Other changes: - Heaphdr NULL init fix. The original macros were broken: the double/single linked macro variants were the wrong way around. Now sets through macro to work properly with compressed pointers. - Rename duk_hbuffer CURR_DATA_PTR -&gt; DATA_PTR to reduce macro length (previous name was tediously long) - Rename buffer &#34;usable_size&#34; to &#34;alloc_size&#34; throughout as they have been the same for a while now (they used to differ when buffer had an extra NUL). - Add memory optimization markers to Duktape.env (pointer compression and individual 16-bit field options) - Rename a few internal fields for clarity: duk_hobject &#39;p&#39; to &#39;props&#39;, heap-&gt;st to heap-&gt;strtable - Add a safety check for buffer alloc size (should not be triggered but prevents wrapping if call sites don&#39;t properly check for sizes) - Other minor cleanups
10 years ago
#if !defined(DUK_USE_HEAPPTR16) && !defined(DUK_DATAPTR16) && !defined(DUK_FUNCPTR16)
"n"
#endif
#if defined(DUK_USE_HEAPPTR16)
"h"
#endif
#if defined(DUK_USE_DATAPTR16)
"d"
#endif
#if defined(DUK_USE_FUNCPTR16)
"f"
#endif
#if defined(DUK_USE_REFCOUNT16)
"R"
#endif
#if defined(DUK_USE_STRHASH16)
"H"
#endif
#if defined(DUK_USE_STRLEN16)
"S"
#endif
#if defined(DUK_USE_BUFLEN16)
"B"
#endif
#if defined(DUK_USE_OBJSIZES16)
"O"
#endif
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
"L"
#endif
#if defined(DUK_USE_ROM_STRINGS) || defined(DUK_USE_ROM_OBJECTS)
/* XXX: This won't be shown in practice now
* because this code is not run when builtins
* are in ROM.
*/
"Z"
#endif
" "
/* Object property allocation layout */
#if defined(DUK_USE_HOBJECT_LAYOUT_1)
"p1"
#elif defined(DUK_USE_HOBJECT_LAYOUT_2)
"p2"
#elif defined(DUK_USE_HOBJECT_LAYOUT_3)
"p3"
#else
"p?"
#endif
" "
/* Alignment guarantee */
#if (DUK_USE_ALIGN_BY == 4)
"a4"
#elif (DUK_USE_ALIGN_BY == 8)
"a8"
#elif (DUK_USE_ALIGN_BY == 1)
"a1"
#else
#error invalid DUK_USE_ALIGN_BY
#endif
" "
/* Architecture, OS, and compiler strings */
DUK_USE_ARCH_STRING
" "
DUK_USE_OS_STRING
" "
DUK_USE_COMPILER_STRING);
duk_xdef_prop_stridx(ctx, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC);
/*
* Since built-ins are not often extended, compact them.
*/
DUK_DD(DUK_DDPRINT("compact built-ins"));
for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
duk_hobject_compact_props(thr, duk_require_hobject(ctx, i));
}
DUK_D(DUK_DPRINT("INITBUILTINS END"));
#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1)
for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
DUK_DD(DUK_DDPRINT("built-in object %ld after initialization and compacting: %!@iO",
(long) i, (duk_heaphdr *) duk_require_hobject(ctx, i)));
}
#endif
/*
* Pop built-ins from stack: they are now INCREF'd and
* reachable from the builtins[] array or indirectly
* through builtins[].
*/
duk_set_top(ctx, 0);
DUK_ASSERT_TOP(ctx, 0);
}
#endif /* DUK_USE_ROM_OBJECTS */
DUK_INTERNAL void duk_hthread_copy_builtin_objects(duk_hthread *thr_from, duk_hthread *thr_to) {
duk_small_uint_t i;
for (i = 0; i < DUK_NUM_BUILTINS; i++) {
thr_to->builtins[i] = thr_from->builtins[i];
DUK_HOBJECT_INCREF_ALLOWNULL(thr_to, thr_to->builtins[i]); /* side effect free */
}
}