Browse Source

more incomplete builtins

pull/1/head
Sami Vaarala 12 years ago
parent
commit
51bcd2ca1b
  1. 216
      src/duk_builtin_array.c
  2. 412
      src/duk_builtin_duk.c
  3. 214
      src/duk_builtin_function.c
  4. 44
      src/duk_builtin_json.c

216
src/duk_builtin_array.c

@ -0,0 +1,216 @@
/*
* Array built-ins
*
* Note that most Array built-ins are intentionally generic and work even
* when the 'this' binding is not an Array instance. To ensure this,
* Array algorithms do not assume "magical" Array behavior for the "length"
* property, for instance.
*/
#include "duk_internal.h"
int duk_builtin_array_constructor(duk_context *ctx) {
if (duk_is_constructor_call(ctx)) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
} else {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
}
int duk_builtin_array_constructor_is_array(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR;
}
int duk_builtin_array_prototype_to_string(duk_context *ctx) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_push_this(ctx);
DUK_DDDPRINT("this: %!T", duk_get_tval(ctx, -1));
duk_to_object(ctx, -1);
duk_get_prop_stridx(ctx, -1, DUK_HEAP_STRIDX_JOIN);
/* FIXME: uneven stack would reduce code size */
/* [ ... this func ] */
if (!duk_is_callable(ctx, -1)) {
DUK_DDDPRINT("this.join is not callable, fall back to Object.toString");
duk_pop(ctx);
duk_push_hobject(ctx, thr->builtins[DUK_BIDX_OBJECT_PROTOTYPE]);
duk_get_prop_stridx(ctx, -1, DUK_HEAP_STRIDX_TO_STRING);
duk_remove(ctx, -2);
}
/* [ ... this func ] */
duk_insert(ctx, -2);
/* [ ... func this ] */
DUK_DDDPRINT("calling: func=%!iT, this=%!iT", duk_get_tval(ctx, -2), duk_get_tval(ctx, -1));
duk_call_method(ctx, 0);
return 1;
}
int duk_builtin_array_prototype_to_locale_string(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_concat(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
/* Note: checking valstack is necessary, but only in the per-element loop */
/* FIXME: This placeholder does not work for a large number of elements.
* Provide proper hierarchical concat/join primitives in the API and use
* them here.
*/
int duk_builtin_array_prototype_join(duk_context *ctx) {
duk_u32 len;
duk_u32 i;
DUK_ASSERT(duk_get_top(ctx) == 1); /* nargs is 1 */
if (duk_is_undefined(ctx, 0)) {
duk_pop(ctx);
duk_push_hstring_stridx(ctx, DUK_HEAP_STRIDX_COMMA);
} else {
duk_to_string(ctx, 0);
}
duk_push_this(ctx);
duk_to_object(ctx, -1); /* FIXME: common enough to warrant an internal helper? push_this_to_object */
/* [ sep ToObject(this) ] */
duk_get_prop_stridx(ctx, -1, DUK_HEAP_STRIDX_LENGTH);
len = duk_to_uint32(ctx, -1);
DUK_DDDPRINT("sep=%!T, this=%!T, len=%d", duk_get_tval(ctx, 0), duk_get_tval(ctx, 1), len);
if (len == 0) {
duk_push_hstring_stridx(ctx, DUK_HEAP_STRIDX_EMPTY_STRING);
return 1;
}
duk_require_stack(ctx, 2*len - 1);
for (i = 0; i < len; i++) {
/* FIXME: primitive C API call for join */
if (i > 0) {
duk_dup(ctx, 0);
}
duk_get_prop_index(ctx, 1, i);
if (duk_is_null_or_undefined(ctx, -1)) {
duk_pop(ctx);
duk_push_hstring_stridx(ctx, DUK_HEAP_STRIDX_EMPTY_STRING);
} else {
duk_to_string(ctx, -1);
}
}
/* [ sep ToObject(this) str0 sep str1 sep ... sep str(len-1) ] */
duk_concat(ctx, 2*len - 1);
return 1;
}
int duk_builtin_array_prototype_pop(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_push(duk_context *ctx) {
/* Note: 'this' is not necessarily an Array object. The push()
* algorithm is supposed to work for other kinds of objects too,
* so the algorithm has e.g. an explicit update for the 'length'
* property which is normally "magical" in arrays.
*/
duk_u32 len;
int i, n;
n = duk_get_top(ctx);
duk_push_this(ctx);
duk_to_object(ctx, -1);
duk_get_prop_stridx(ctx, -1, DUK_HEAP_STRIDX_LENGTH);
len = duk_to_uint32(ctx, -1);
/* [ arg1 ... argN obj length ] */
for (i = 0; i < n; i++) {
duk_dup(ctx, i);
duk_put_prop_index(ctx, -3, len); /* FIXME: "Throw" is true for this [[Put]] call, needs API support */
len++;
}
duk_push_number(ctx, (double) len); /* FIXME: duk_push_u32 */
duk_dup_top(ctx);
duk_put_prop_stridx(ctx, -3, DUK_HEAP_STRIDX_LENGTH);
/* [ arg1 ... argN obj length new_length ] */
return 1;
}
int duk_builtin_array_prototype_reverse(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_shift(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_slice(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_sort(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_splice(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_unshift(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_index_of(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_last_index_of(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_every(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_some(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_for_each(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_map(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_filter(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_reduce(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_array_prototype_reduce_right(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}

412
src/duk_builtin_duk.c

@ -0,0 +1,412 @@
/*
* __duk__ built-ins
*/
#include <sys/time.h>
#include "duk_internal.h"
int duk_builtin_duk_object_addr(duk_context *ctx) {
duk_tval *tv;
void *p;
tv = duk_get_tval(ctx, 0);
if (!tv || !DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
return 0; /* undefined */
}
p = (void *) DUK_TVAL_GET_HEAPHDR(tv);
/* any heap allocated value (string, object, buffer) has a stable pointer */
duk_push_sprintf(ctx, "%p", p);
return 1;
}
int duk_builtin_duk_object_refc(duk_context *ctx) {
#ifdef DUK_USE_REFERENCE_COUNTING
duk_tval *tv = duk_get_tval(ctx, 0);
duk_heaphdr *h;
if (!tv) {
return 0;
}
if (!DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
return 0;
}
h = DUK_TVAL_GET_HEAPHDR(tv);
duk_push_int(ctx, DUK_HEAPHDR_GET_REFCOUNT(h));
return 1;
#else
return 0;
#endif
}
int duk_builtin_duk_object_gc(duk_context *ctx) {
#ifdef DUK_USE_MARK_AND_SWEEP
duk_hthread *thr = (duk_hthread *) ctx;
int flags;
int rc;
flags = duk_get_int(ctx, 0);
rc = duk_heap_mark_and_sweep(thr->heap, flags);
duk_push_int(ctx, rc);
return 1;
#else
return 0;
#endif
}
int duk_builtin_duk_object_get_finalizer(duk_context *ctx) {
(void) duk_require_hobject(ctx, 0);
duk_get_prop_stridx(ctx, 0, DUK_HEAP_STRIDX_INT_FINALIZER);
return 1;
}
int duk_builtin_duk_object_set_finalizer(duk_context *ctx) {
DUK_ASSERT(duk_get_top(ctx) == 2);
(void) duk_put_prop_stridx(ctx, 0, DUK_HEAP_STRIDX_INT_FINALIZER); /* XXX: check value? */
return 0;
}
/*
* Spawn a thread.
*/
int duk_builtin_duk_object_spawn(duk_context *ctx) {
duk_hthread *new_thr;
duk_hobject *func;
if (!duk_is_callable(ctx, 0)) {
return DUK_RET_TYPE_ERROR;
}
func = duk_get_hobject(ctx, 0);
DUK_ASSERT(func != NULL);
duk_push_new_thread(ctx);
new_thr = (duk_hthread *) duk_get_hobject(ctx, -1);
DUK_ASSERT(new_thr != NULL);
new_thr->state = DUK_HTHREAD_STATE_INACTIVE;
/* push initial function call to new thread stack; this is
* picked up by resume().
*/
duk_push_hobject((duk_context *) new_thr, func);
return 1; /* return thread */
}
/*
* Resume a thread.
*
* The thread must be in resumable state, either (a) new thread which hasn't
* yet started, or (b) a thread which has previously yielded. This method
* must be called from an Ecmascript function.
*
* Args:
* - thread
* - value
* - isError (defaults to false)
*
* Note: yield and resume handling is currently asymmetric.
*/
int duk_builtin_duk_object_resume(duk_context *ctx) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_hthread *thr_resume;
duk_tval tv_tmp;
duk_tval *tv;
duk_hobject *func;
int is_error;
DUK_DDDPRINT("__duk__.resume(): thread=%!T, value=%!T, is_error=%!T",
duk_get_tval(ctx, 0),
duk_get_tval(ctx, 1),
duk_get_tval(ctx, 2));
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
DUK_ASSERT(thr->heap->curr_thread == thr);
thr_resume = duk_require_hthread(ctx, 0);
is_error = duk_to_boolean(ctx, 2);
/*
* Thread state and calling context checks
*/
if (thr->callstack_top < 2) {
DUK_DDPRINT("resume state invalid: callstack should contain at least 2 entries (caller and __duk__.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 */
if (!DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)) {
DUK_DDPRINT("resume state invalid: caller must be Ecmascript code");
goto state_error;
}
/* Note: there is no requirement that: 'thr->callstack_preventcount == 1'
* like for yield.
*/
if (thr_resume->state != DUK_HTHREAD_STATE_INACTIVE &&
thr_resume->state != DUK_HTHREAD_STATE_YIELDED) {
DUK_DDPRINT("resume state invalid: target thread must be INACTIVE or YIELDED");
goto state_error;
}
DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE ||
thr_resume->state == DUK_HTHREAD_STATE_YIELDED);
/* Further state-dependent pre-checks */
if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
/* no pre-checks now, assume a previous yield() has left things in
* tip-top shape (longjmp handler will assert for these).
*/
} else {
DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE);
if ((thr_resume->callstack_top != 0) ||
(thr_resume->valstack_top - thr_resume->valstack != 1)) {
goto state_invalid_initial;
}
tv = &thr_resume->valstack_top[-1];
DUK_ASSERT(tv >= thr_resume->valstack && tv < thr_resume->valstack_top);
if (!DUK_TVAL_IS_OBJECT(tv)) {
goto state_invalid_initial;
}
func = DUK_TVAL_GET_OBJECT(tv);
DUK_ASSERT(func != NULL);
if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
/* Note: cannot be a bound function either right now,
* this would be easy to relax though.
*/
goto state_invalid_initial;
}
}
/*
* The error object could conceivably contain either the resumer
* or the resumee callstack. Here we use the resumee's callstack
* which is more logical for code catching the error in the resumee.
*/
#ifdef DUK_USE_AUGMENT_ERRORS
if (is_error) {
/* Note: may throw an error */
duk_err_augment_error(thr, thr_resume, 1);
}
#endif
#ifdef DUK_USE_DEBUG /* debug logging */
if (is_error) {
DUK_DDDPRINT("RESUME ERROR: thread=%!T, value=%!T",
duk_get_tval(ctx, 0),
duk_get_tval(ctx, 1));
} else if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
DUK_DDDPRINT("RESUME NORMAL: thread=%!T, value=%!T",
duk_get_tval(ctx, 0),
duk_get_tval(ctx, 1));
} else {
DUK_DDDPRINT("RESUME INITIAL: thread=%!T, value=%!T",
duk_get_tval(ctx, 0),
duk_get_tval(ctx, 1));
}
#endif
thr->heap->lj.type = DUK_LJ_TYPE_RESUME;
/* lj value2: thread */
DUK_ASSERT(thr->valstack_bottom < thr->valstack_top);
DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value2);
DUK_TVAL_SET_TVAL(&thr->heap->lj.value2, &thr->valstack_bottom[0]);
DUK_TVAL_INCREF(thr, &thr->heap->lj.value2);
DUK_TVAL_DECREF(thr, &tv_tmp);
/* lj value1: value */
DUK_ASSERT(thr->valstack_bottom + 1 < thr->valstack_top);
DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value1);
DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, &thr->valstack_bottom[1]);
DUK_TVAL_INCREF(thr, &thr->heap->lj.value1);
DUK_TVAL_DECREF(thr, &tv_tmp);
thr->heap->lj.iserror = is_error;
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* call is from executor, so we know we have a jmpbuf */
duk_err_longjmp(thr); /* execution resumes in bytecode executor */
return 0; /* never here */
state_invalid_initial:
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid initial thread state/stack");
return 0; /* never here */
state_error:
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid state for resume");
return 0; /* never here */
}
/*
* Yield the current thread.
*
* The thread must be in yieldable state: it must have a resumer, and there
* must not be any yield-preventing calls (native calls and constructor calls,
* currently) in the thread's call stack (otherwise a resume would not be
* possible later). This method must be called from an Ecmascript function.
*
* Args:
* - value
* - isError (defaults to false)
*
* Note: yield and resume handling is currently asymmetric.
*/
int duk_builtin_duk_object_yield(duk_context *ctx) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_tval tv_tmp;
int is_error;
DUK_DDDPRINT("__duk__.yield(): value=%!T, is_error=%!T",
duk_get_tval(ctx, 0),
duk_get_tval(ctx, 1));
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
DUK_ASSERT(thr->heap->curr_thread == thr);
is_error = duk_to_boolean(ctx, 1);
/*
* Thread state and calling context checks
*/
if (!thr->resumer) {
DUK_DDPRINT("yield state invalid: current thread must have a resumer");
goto state_error;
}
DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED);
if (thr->callstack_top < 2) {
DUK_DDPRINT("yield state invalid: callstack should contain at least 2 entries (caller and __duk__.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 */
if (!DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)) {
DUK_DDPRINT("yield state invalid: caller must be Ecmascript code");
goto state_error;
}
DUK_ASSERT(thr->callstack_preventcount >= 1); /* should never be zero, because we (__duk__.yield) are on the stack */
if (thr->callstack_preventcount != 1) {
/* Note: the only yield-preventing call is __duk__.yield(), hence check for 1, not 0 */
DUK_DDPRINT("yield state invalid: there must be no yield-preventing calls in current thread callstack (preventcount is %d)",
thr->callstack_preventcount);
goto state_error;
}
/*
* The error object could conceivably contain either the yielder
* or the resumer callstack. Here we use the resumer's callstack.
*/
#ifdef DUK_USE_AUGMENT_ERRORS
if (is_error) {
/* Note: may throw an error */
DUK_ASSERT(thr->resumer != NULL);
duk_err_augment_error(thr, thr->resumer, 0);
}
#endif
#ifdef DUK_USE_DEBUG
if (is_error) {
DUK_DDDPRINT("YIELD ERROR: value=%!T",
duk_get_tval(ctx, 0));
} else {
DUK_DDDPRINT("YIELD NORMAL: value=%!T",
duk_get_tval(ctx, 0));
}
#endif
/*
* Process yield
*
* After longjmp(), processing continues in bytecode executor longjmp
* handler, which will e.g. update thr->resumer to NULL.
*/
thr->heap->lj.type = DUK_LJ_TYPE_YIELD;
/* lj value1: value */
DUK_ASSERT(thr->valstack_bottom < thr->valstack_top);
DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value1);
DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, &thr->valstack_bottom[0]);
DUK_TVAL_INCREF(thr, &thr->heap->lj.value1);
DUK_TVAL_DECREF(thr, &tv_tmp);
thr->heap->lj.iserror = is_error;
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* call is from executor, so we know we have a jmpbuf */
duk_err_longjmp(thr); /* execution resumes in bytecode executor */
return 0; /* never here */
state_error:
DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid state for yield");
return 0; /* never here */
}
int duk_builtin_duk_object_curr(duk_context *ctx) {
duk_push_current_thread(ctx);
return 1;
}
int duk_builtin_duk_object_print(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_duk_object_time(duk_context *ctx) {
struct timeval tv;
memset(&tv, 0, sizeof(tv));
if (gettimeofday(&tv, NULL) != 0) {
return DUK_RET_ERROR;
}
duk_push_number(ctx, (double) tv.tv_sec + ((double) tv.tv_usec) / 1000000.0);
return 1;
}
int duk_builtin_duk_object_enc(duk_context *ctx) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_hstring *h_str;
h_str = duk_to_hstring(ctx, 0);
if (h_str == DUK_HTHREAD_STRING_HEX(thr)) {
duk_hex_encode(ctx, 1);
DUK_ASSERT_TOP(ctx, 2);
return 1;
} else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) {
duk_base64_encode(ctx, 1);
DUK_ASSERT_TOP(ctx, 2);
return 1;
} else {
return DUK_RET_TYPE_ERROR;
}
}
int duk_builtin_duk_object_dec(duk_context *ctx) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_hstring *h_str;
h_str = duk_to_hstring(ctx, 0);
if (h_str == DUK_HTHREAD_STRING_HEX(thr)) {
duk_hex_decode(ctx, 1);
DUK_ASSERT_TOP(ctx, 2);
return 1;
} else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) {
duk_base64_decode(ctx, 1);
DUK_ASSERT_TOP(ctx, 2);
return 1;
} else {
return DUK_RET_TYPE_ERROR;
}
}

214
src/duk_builtin_function.c

@ -0,0 +1,214 @@
/*
* Function built-ins
*/
#include "duk_internal.h"
int duk_builtin_function_constructor(duk_context *ctx) {
if (duk_is_constructor_call(ctx)) {
return DUK_RET_UNIMPLEMENTED_ERROR;
} else {
return DUK_RET_UNIMPLEMENTED_ERROR;
}
}
int duk_builtin_function_prototype(duk_context *ctx) {
/* ignore arguments, return undefined (E5 Section 15.3.4) */
return 0;
}
int duk_builtin_function_prototype_to_string(duk_context *ctx) {
duk_tval *tv;
/* FIXME: faster internal way to get this */
duk_push_this(ctx);
tv = duk_get_tval(ctx, -1);
DUK_ASSERT(tv != NULL);
if (DUK_TVAL_IS_OBJECT(tv)) {
duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv);
if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj)) {
/* FIXME: actual source, if available */
/* FIXME: function name */
duk_push_string(ctx, "function name() {/* source code */}");
} else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(obj)) {
/* FIXME: function name */
duk_push_string(ctx, "function name() {/* native code */}");
} else if (DUK_HOBJECT_HAS_BOUND(obj)) {
duk_push_string(ctx, "function name() {/* bound */}");
} else {
goto type_error;
}
} else {
goto type_error;
}
return 1;
type_error:
return DUK_RET_TYPE_ERROR;
}
int duk_builtin_function_prototype_apply(duk_context *ctx) {
unsigned int len;
unsigned int i;
/* FIXME: stack checks */
DUK_ASSERT(duk_get_top(ctx) == 2); /* not a vararg function */
duk_push_this(ctx);
if (!duk_is_callable(ctx, -1)) {
DUK_DDDPRINT("func is not callable");
goto type_error;
}
duk_insert(ctx, 0);
DUK_ASSERT(duk_get_top(ctx) == 3);
DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argArray=%!iT",
duk_get_tval(ctx, 0), duk_get_tval(ctx, 1), duk_get_tval(ctx, 2));
/* [ func thisArg argArray ] */
if (duk_is_null_or_undefined(ctx, 2)) {
DUK_DDDPRINT("argArray is null/undefined, no args");
len = 0;
} else if (!duk_is_object(ctx, 2)) {
goto type_error;
} else {
DUK_DDDPRINT("argArray is an object");
/* FIXME: make this an internal helper */
duk_get_prop_stridx(ctx, 2, DUK_HEAP_STRIDX_LENGTH);
len = duk_to_uint32(ctx, -1);
duk_pop(ctx);
duk_require_stack(ctx, len); /* FIXME: more? */
DUK_DDDPRINT("argArray length is %d", len);
for (i = 0; i < len; i++) {
duk_get_prop_index(ctx, 2, i);
}
}
duk_remove(ctx, 2);
DUK_ASSERT(duk_get_top(ctx) == 2 + len);
/* [ func thisArg arg1 ... argN ] */
DUK_DDDPRINT("apply, func=%!iT, thisArg=%!iT, len=%d",
duk_get_tval(ctx, 0), duk_get_tval(ctx, 1), len);
duk_call_method(ctx, len);
return 1;
type_error:
return DUK_RET_TYPE_ERROR;
}
int duk_builtin_function_prototype_call(duk_context *ctx) {
int nargs;
/* Step 1 is not necessary because duk_call_method() will take
* care of it.
*/
/* vararg function, thisArg needs special handling */
nargs = duk_get_top(ctx); /* = 1 + arg count */
if (nargs == 0) {
duk_push_undefined(ctx);
nargs++;
}
DUK_ASSERT(nargs >= 1);
/* [ thisArg arg1 ... argN ] */
duk_push_this(ctx); /* 'func' in the algorithm */
duk_insert(ctx, 0);
/* [ func thisArg arg1 ... argN ] */
DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argcount=%d, top=%d",
duk_get_tval(ctx, 0), duk_get_tval(ctx, 1), nargs - 1, duk_get_top(ctx));
duk_call_method(ctx, nargs - 1);
return 1;
}
/* FIXME: the implementation now assumes "chained" bound functions,
* whereas "collapsed" bound functions (where there is ever only
* one bound function which directly points to a non-bound, final
* function) would require a "collapsing" implementation which
* merges argument lists etc here.
*/
int duk_builtin_function_prototype_bind(duk_context *ctx) {
duk_hobject *h_target;
int nargs;
int i;
/* FIXME: stack checks */
/* vararg function, careful arg handling (e.g. thisArg may not be present) */
nargs = duk_get_top(ctx); /* = 1 + arg count */
if (nargs == 0) {
duk_push_undefined(ctx);
nargs++;
}
DUK_ASSERT(nargs >= 1);
duk_push_this(ctx);
if (!duk_is_callable(ctx, -1)) {
DUK_DDDPRINT("func is not callable");
goto type_error;
}
/* [ thisArg arg1 ... argN func ] (thisArg+args == nargs total) */
DUK_ASSERT(duk_get_top(ctx) == nargs + 1);
/* create bound function object */
duk_push_new_object_helper(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
DUK_HOBJECT_FLAG_BOUND |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION),
DUK_BIDX_FUNCTION_PROTOTYPE);
/* FIXME: check hobject flags (e.g. strict) */
/* [ thisArg arg1 ... argN func boundFunc ] */
duk_dup(ctx, -2); /* func */
duk_def_prop_stridx(ctx, -2, DUK_HEAP_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE);
duk_dup(ctx, 0); /* thisArg */
duk_def_prop_stridx(ctx, -2, DUK_HEAP_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE);
duk_push_new_array(ctx);
/* [ thisArg arg1 ... argN func boundFunc argArray ] */
for (i = 0; i < nargs - 1; i++) {
duk_dup(ctx, 1 + i);
duk_put_prop_index(ctx, -2, i);
}
duk_def_prop_stridx(ctx, -2, DUK_HEAP_STRIDX_INT_ARGS, DUK_PROPDESC_FLAGS_NONE);
/* [ thisArg arg1 ... argN func boundFunc ] */
/* 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) {
int tmp;
duk_get_prop_stridx(ctx, -2, DUK_HEAP_STRIDX_LENGTH);
tmp = duk_to_int(ctx, -1) - (nargs - 1); /* step 15.a */
duk_pop(ctx);
duk_push_int(ctx, (tmp < 0 ? 0 : tmp));
} else {
duk_push_int(ctx, 0);
}
duk_def_prop_stridx(ctx, -2, DUK_HEAP_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); /* attrs in E5 Section 15.3.5.1 */
DUK_DDDPRINT("created bound function: %!iT", duk_get_tval(ctx, -1));
return 1;
type_error:
return DUK_RET_TYPE_ERROR;
}

44
src/duk_builtin_json.c

@ -0,0 +1,44 @@
/*
* JSON built-ins
*/
/*
* Serialization notes:
*
* - It would be nice to change the standard algorithm to be based around
* a "serializeValue()" primitive. The standard algorithm assumes access
* to the "holder" of the value, especially in E5 Section 15.12.3, Str()
* algoritm, step 3.a: the holder is passed to the ReplacerFunction.
* So, the implementation here is based on the standard algorithm set.
*
* - Similarly, serialization of a value 'val' begins from a dummy wrapper
* object: { "": val }. This seems to be quite awkward and unnecessary.
* However, the wrapper object is accessible to the ReplacerFunction!
*
* - String serialization should be fast for pure ASCII strings as they
* are very common. Unfortunately we may still need to escape characters
* in them, so there is no explicit fast path now. We could use ordinary
* character lookups during serialization (note that ASCII string lookups
* would not affect the stringcache). This would be quite slow, so we
* decode the extended UTF-8 directly instead.
*
* - Strings may contain non-BMP characters. These don't really need to be
* supported from a specification standpoint. We could encode them as
* surrogate pairs, but that would only work up to U+10FFFF, whereas e.g.
* regexp bytecode may contain much higher values.
*
* FIXME: surrogate pair + \uXXXX ?
* FIXME: custom escape: \UXXXXXXXX ?
* FIXME: non-escape hack: <U+XXXXXXXX> or something (not decodable)
*/
#include "duk_internal.h"
int duk_builtin_json_object_parse(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
int duk_builtin_json_object_stringify(duk_context *ctx) {
return DUK_RET_UNIMPLEMENTED_ERROR; /*FIXME*/
}
Loading…
Cancel
Save