http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/a9485aeb/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_error.c ---------------------------------------------------------------------- diff --git a/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_error.c b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_error.c new file mode 100644 index 0000000..b575fff --- /dev/null +++ b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_error.c @@ -0,0 +1,383 @@ +/* + * Error built-ins + */ + +#include "duk_internal.h" + +DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx) { + /* Behavior for constructor and non-constructor call is + * the same except for augmenting the created error. When + * called as a constructor, the caller (duk_new()) will handle + * augmentation; when called as normal function, we need to do + * it here. + */ + + duk_hthread *thr = (duk_hthread *) ctx; + duk_small_int_t bidx_prototype = duk_get_current_magic(ctx); + + /* same for both error and each subclass like TypeError */ + duk_uint_t flags_and_class = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR); + + DUK_UNREF(thr); + + duk_push_object_helper(ctx, flags_and_class, bidx_prototype); + + /* If message is undefined, the own property 'message' is not set at + * all to save property space. An empty message is inherited anyway. + */ + if (!duk_is_undefined(ctx, 0)) { + duk_to_string(ctx, 0); + duk_dup(ctx, 0); /* [ message error message ] */ + duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); + } + + /* Augment the error if called as a normal function. __FILE__ and __LINE__ + * are not desirable in this case. + */ + +#ifdef DUK_USE_AUGMENT_ERROR_CREATE + if (!duk_is_constructor_call(ctx)) { + duk_err_augment_error_create(thr, thr, NULL, 0, 1 /*noblame_fileline*/); + } +#endif + + return 1; +} + +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx) { + /* XXX: optimize with more direct internal access */ + + duk_push_this(ctx); + (void) duk_require_hobject_or_lfunc_coerce(ctx, -1); + + /* [ ... this ] */ + + duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME); + if (duk_is_undefined(ctx, -1)) { + duk_pop(ctx); + duk_push_string(ctx, "Error"); + } else { + duk_to_string(ctx, -1); + } + + /* [ ... this name ] */ + + /* XXX: Are steps 6 and 7 in E5 Section 15.11.4.4 duplicated by + * accident or are they actually needed? The first ToString() + * could conceivably return 'undefined'. + */ + duk_get_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE); + if (duk_is_undefined(ctx, -1)) { + duk_pop(ctx); + duk_push_string(ctx, ""); + } else { + duk_to_string(ctx, -1); + } + + /* [ ... this name message ] */ + + if (duk_get_length(ctx, -2) == 0) { + /* name is empty -> return message */ + return 1; + } + if (duk_get_length(ctx, -1) == 0) { + /* message is empty -> return name */ + duk_pop(ctx); + return 1; + } + duk_push_string(ctx, ": "); + duk_insert(ctx, -2); /* ... name ': ' message */ + duk_concat(ctx, 3); + + return 1; +} + +#if defined(DUK_USE_TRACEBACKS) + +/* + * Traceback handling + * + * The unified helper decodes the traceback and produces various requested + * outputs. It should be optimized for size, and may leave garbage on stack, + * only the topmost return value matters. For instance, traceback separator + * and decoded strings are pushed even when looking for filename only. + * + * NOTE: although _Tracedata is an internal property, user code can currently + * write to the array (or replace it with something other than an array). + * The code below must tolerate arbitrary _Tracedata. It can throw errors + * etc, but cannot cause a segfault or memory unsafe behavior. + */ + +/* constants arbitrary, chosen for small loads */ +#define DUK__OUTPUT_TYPE_TRACEBACK (-1) +#define DUK__OUTPUT_TYPE_FILENAME 0 +#define DUK__OUTPUT_TYPE_LINENUMBER 1 + +DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t output_type) { + duk_hthread *thr = (duk_hthread *) ctx; + duk_idx_t idx_td; + duk_small_int_t i; /* traceback depth fits into 16 bits */ + duk_small_int_t t; /* stack type fits into 16 bits */ + duk_small_int_t count_func = 0; /* traceback depth ensures fits into 16 bits */ + const char *str_tailcall = " tailcall"; + const char *str_strict = " strict"; + const char *str_construct = " construct"; + const char *str_prevyield = " preventsyield"; + const char *str_directeval = " directeval"; + const char *str_empty = ""; + + DUK_ASSERT_TOP(ctx, 0); /* fixed arg count */ + DUK_UNREF(thr); + + duk_push_this(ctx); + duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TRACEDATA); + idx_td = duk_get_top_index(ctx); + + duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_4SPACE); + duk_push_this(ctx); + + /* [ ... this tracedata sep this ] */ + + /* XXX: skip null filename? */ + + if (duk_check_type(ctx, idx_td, DUK_TYPE_OBJECT)) { + /* Current tracedata contains 2 entries per callstack entry. */ + for (i = 0; ; i += 2) { + duk_int_t pc; + duk_int_t line; + duk_int_t flags; + duk_double_t d; + const char *funcname; + const char *filename; + duk_hobject *h_func; + duk_hstring *h_name; + + duk_require_stack(ctx, 5); + duk_get_prop_index(ctx, idx_td, i); + duk_get_prop_index(ctx, idx_td, i + 1); + d = duk_to_number(ctx, -1); + pc = (duk_int_t) DUK_FMOD(d, DUK_DOUBLE_2TO32); + 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 || t == DUK_TYPE_LIGHTFUNC) { + /* + * Ecmascript/native function call or lightfunc call + */ + + count_func++; + + /* [ ... v1(func) v2(pc+flags) ] */ + + 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); + +#if defined(DUK_USE_PC2LINE) + line = duk_hobject_pc2line_query(ctx, -4, (duk_uint_fast32_t) pc); +#else + line = 0; +#endif + + /* [ ... v1 v2 name filename ] */ + + /* When looking for .fileName/.lineNumber, blame first + * function which has a .fileName. + */ + if (duk_is_string(ctx, -1)) { + if (output_type == DUK__OUTPUT_TYPE_FILENAME) { + return 1; + } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { + duk_push_int(ctx, line); + return 1; + } + } + + /* XXX: Change 'anon' handling here too, to use empty string for anonymous functions? */ + /* XXX: Could be improved by coercing to a readable duk_tval (especially string escaping) */ + h_name = duk_get_hstring(ctx, -2); /* may be NULL */ + funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ? + "[anon]" : (const char *) DUK_HSTRING_GET_DATA(h_name); + filename = duk_get_string(ctx, -1); + filename = filename ? filename : ""; + DUK_ASSERT(funcname != NULL); + DUK_ASSERT(filename != NULL); + + if (h_func == NULL) { + duk_push_sprintf(ctx, "at %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_tailcall : 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, "at %s (%s) native%s%s%s%s%s", + (const char *) funcname, + (const char *) filename, + (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), + (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : 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 { + duk_push_sprintf(ctx, "at %s (%s:%ld)%s%s%s%s%s", + (const char *) funcname, + (const char *) filename, + (long) line, + (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), + (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : 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)); + } + duk_replace(ctx, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */ + duk_pop_n(ctx, 3); /* -> [ ... str ] */ + } else if (t == DUK_TYPE_STRING) { + /* + * __FILE__ / __LINE__ entry, here 'pc' is line number directly. + * Sometimes __FILE__ / __LINE__ is reported as the source for + * the error (fileName, lineNumber), sometimes not. + */ + + /* [ ... v1(filename) v2(line+flags) ] */ + + /* When looking for .fileName/.lineNumber, blame compilation + * or C call site unless flagged not to do so. + */ + if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) { + if (output_type == DUK__OUTPUT_TYPE_FILENAME) { + duk_pop(ctx); + return 1; + } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { + duk_push_int(ctx, pc); + return 1; + } + } + + duk_push_sprintf(ctx, "at [anon] (%s:%ld) internal", + (const char *) duk_get_string(ctx, -2), (long) pc); + duk_replace(ctx, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */ + duk_pop(ctx); /* -> [ ... str ] */ + } else { + /* unknown, ignore */ + duk_pop_2(ctx); + break; + } + } + + if (count_func >= DUK_USE_TRACEBACK_DEPTH) { + /* Possibly truncated; there is no explicit truncation + * marker so this is the best we can do. + */ + + duk_push_hstring_stridx(ctx, DUK_STRIDX_BRACKETED_ELLIPSIS); + } + } + + /* [ ... this tracedata sep this str1 ... strN ] */ + + if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) { + return 0; + } else { + /* The 'this' after 'sep' will get ToString() coerced by + * duk_join() automatically. We don't want to do that + * coercion when providing .fileName or .lineNumber (GH-254). + */ + duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/); + return 1; + } +} + +/* XXX: Output type could be encoded into native function 'magic' value to + * save space. For setters the stridx could be encoded into 'magic'. + */ + +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx) { + return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_TRACEBACK); +} + +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx) { + return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_FILENAME); +} + +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx) { + return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_LINENUMBER); +} + +#undef DUK__OUTPUT_TYPE_TRACEBACK +#undef DUK__OUTPUT_TYPE_FILENAME +#undef DUK__OUTPUT_TYPE_LINENUMBER + +#else /* DUK_USE_TRACEBACKS */ + +/* + * Traceback handling when tracebacks disabled. + * + * The fileName / lineNumber stubs are now necessary because built-in + * data will include the accessor properties in Error.prototype. If those + * are removed for builds without tracebacks, these can also be removed. + * 'stack' should still be present and produce a ToString() equivalent: + * this is useful for user code which prints a stacktrace and expects to + * see something useful. A normal stacktrace also begins with a ToString() + * of the error so this makes sense. + */ + +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx) { + /* XXX: remove this native function and map 'stack' accessor + * to the toString() implementation directly. + */ + return duk_bi_error_prototype_to_string(ctx); +} + +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx) { + DUK_UNREF(ctx); + return 0; +} + +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx) { + DUK_UNREF(ctx); + return 0; +} + +#endif /* DUK_USE_TRACEBACKS */ + +DUK_LOCAL duk_ret_t duk__error_setter_helper(duk_context *ctx, duk_small_uint_t stridx_key) { + /* Attempt to write 'stack', 'fileName', 'lineNumber' works as if + * user code called Object.defineProperty() to create an overriding + * own property. This allows user code to overwrite .fileName etc + * intuitively as e.g. "err.fileName = 'dummy'" as one might expect. + * See https://github.com/svaarala/duktape/issues/387. + */ + + DUK_ASSERT_TOP(ctx, 1); /* fixed arg count: value */ + + duk_push_this(ctx); + duk_push_hstring_stridx(ctx, (duk_small_int_t) stridx_key); + duk_dup(ctx, 0); + + /* [ ... obj key value ] */ + + DUK_DD(DUK_DDPRINT("error setter: %!T %!T %!T", + duk_get_tval(ctx, -3), duk_get_tval(ctx, -2), duk_get_tval(ctx, -1))); + + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | + DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE | + DUK_DEFPROP_HAVE_ENUMERABLE | /*not enumerable*/ + DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE); + return 0; +} + +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_setter(duk_context *ctx) { + return duk__error_setter_helper(ctx, DUK_STRIDX_STACK); +} + +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_setter(duk_context *ctx) { + return duk__error_setter_helper(ctx, DUK_STRIDX_FILE_NAME); +} + +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_context *ctx) { + return duk__error_setter_helper(ctx, DUK_STRIDX_LINE_NUMBER); +}
http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/a9485aeb/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_function.c ---------------------------------------------------------------------- diff --git a/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_function.c b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_function.c new file mode 100644 index 0000000..2596a4a --- /dev/null +++ b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_function.c @@ -0,0 +1,346 @@ +/* + * Function built-ins + */ + +#include "duk_internal.h" + +DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) { + duk_hthread *thr = (duk_hthread *) ctx; + duk_hstring *h_sourcecode; + duk_idx_t nargs; + duk_idx_t i; + duk_small_uint_t comp_flags; + duk_hcompiledfunction *func; + duk_hobject *outer_lex_env; + duk_hobject *outer_var_env; + + /* normal and constructor calls have identical semantics */ + + nargs = duk_get_top(ctx); + for (i = 0; i < nargs; i++) { + duk_to_string(ctx, i); + } + + if (nargs == 0) { + duk_push_string(ctx, ""); + duk_push_string(ctx, ""); + } else if (nargs == 1) { + /* XXX: cover this with the generic >1 case? */ + duk_push_string(ctx, ""); + } else { + duk_insert(ctx, 0); /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */ + duk_push_string(ctx, ","); + duk_insert(ctx, 1); + duk_join(ctx, nargs - 1); + } + + /* [ body formals ], formals is comma separated list that needs to be parsed */ + + DUK_ASSERT_TOP(ctx, 2); + + /* XXX: this placeholder is not always correct, but use for now. + * It will fail in corner cases; see test-dev-func-cons-args.js. + */ + duk_push_string(ctx, "function("); + duk_dup(ctx, 1); + duk_push_string(ctx, "){"); + duk_dup(ctx, 0); + duk_push_string(ctx, "}"); + duk_concat(ctx, 5); + + /* [ body formals source ] */ + + DUK_ASSERT_TOP(ctx, 3); + + /* strictness is not inherited, intentional */ + comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR; + + duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE); /* XXX: copy from caller? */ /* XXX: ignored now */ + h_sourcecode = duk_require_hstring(ctx, -2); + duk_js_compile(thr, + (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode), + (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode), + comp_flags); + func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); + DUK_ASSERT(func != NULL); + DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func)); + + /* [ body formals source template ] */ + + /* only outer_lex_env matters, as functions always get a new + * variable declaration environment. + */ + + outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; + outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; + + duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 1 /*add_auto_proto*/); + + /* [ body formals source template closure ] */ + + return 1; +} + +DUK_INTERNAL duk_ret_t duk_bi_function_prototype(duk_context *ctx) { + /* ignore arguments, return undefined (E5 Section 15.3.4) */ + DUK_UNREF(ctx); + return 0; +} + +DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) { + duk_tval *tv; + + /* + * E5 Section 15.3.4.2 places few requirements on the output of + * this function: + * + * - The result is an implementation dependent representation + * of the function; in particular + * + * - The result must follow the syntax of a FunctionDeclaration. + * In particular, the function must have a name (even in the + * case of an anonymous function or a function with an empty + * name). + * + * - Note in particular that the output does NOT need to compile + * into anything useful. + */ + + + /* XXX: 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); + const char *func_name; + + /* Function name: missing/undefined is mapped to empty string, + * otherwise coerce to string. + */ + /* XXX: currently no handling for non-allowed identifier characters, + * e.g. a '{' in the function name. + */ + duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME); + if (duk_is_undefined(ctx, -1)) { + func_name = ""; + } else { + func_name = duk_to_string(ctx, -1); + DUK_ASSERT(func_name != NULL); + } + + /* Indicate function type in the function body using a dummy + * directive. + */ + if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj)) { + 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\"}", (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; + } + + return 1; + + type_error: + return DUK_RET_TYPE_ERROR; +} + +DUK_INTERNAL duk_ret_t duk_bi_function_prototype_apply(duk_context *ctx) { + duk_idx_t len; + duk_idx_t i; + + DUK_ASSERT_TOP(ctx, 2); /* not a vararg function */ + + duk_push_this(ctx); + if (!duk_is_callable(ctx, -1)) { + DUK_DDD(DUK_DDDPRINT("func is not callable")); + goto type_error; + } + duk_insert(ctx, 0); + DUK_ASSERT_TOP(ctx, 3); + + DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argArray=%!iT", + (duk_tval *) duk_get_tval(ctx, 0), + (duk_tval *) duk_get_tval(ctx, 1), + (duk_tval *) duk_get_tval(ctx, 2))); + + /* [ func thisArg argArray ] */ + + if (duk_is_null_or_undefined(ctx, 2)) { + DUK_DDD(DUK_DDDPRINT("argArray is null/undefined, no args")); + len = 0; + } else if (!duk_is_object(ctx, 2)) { + goto type_error; + } else { + DUK_DDD(DUK_DDDPRINT("argArray is an object")); + + /* XXX: make this an internal helper */ + duk_get_prop_stridx(ctx, 2, DUK_STRIDX_LENGTH); + len = (duk_idx_t) duk_to_uint32(ctx, -1); /* ToUint32() coercion required */ + duk_pop(ctx); + + duk_require_stack(ctx, len); + + DUK_DDD(DUK_DDDPRINT("argArray length is %ld", (long) len)); + for (i = 0; i < len; i++) { + duk_get_prop_index(ctx, 2, i); + } + } + duk_remove(ctx, 2); + DUK_ASSERT_TOP(ctx, 2 + len); + + /* [ func thisArg arg1 ... argN ] */ + + DUK_DDD(DUK_DDDPRINT("apply, func=%!iT, thisArg=%!iT, len=%ld", + (duk_tval *) duk_get_tval(ctx, 0), + (duk_tval *) duk_get_tval(ctx, 1), + (long) len)); + duk_call_method(ctx, len); + return 1; + + type_error: + return DUK_RET_TYPE_ERROR; +} + +DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx) { + duk_idx_t 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_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argcount=%ld, top=%ld", + (duk_tval *) duk_get_tval(ctx, 0), + (duk_tval *) duk_get_tval(ctx, 1), + (long) (nargs - 1), + (long) duk_get_top(ctx))); + duk_call_method(ctx, nargs - 1); + return 1; +} + +/* XXX: 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. + */ +DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx) { + duk_hobject *h_bound; + duk_hobject *h_target; + duk_idx_t nargs; + duk_idx_t i; + + /* 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_DDD(DUK_DDDPRINT("func is not callable")); + goto type_error; + } + + /* [ thisArg arg1 ... argN func ] (thisArg+args == nargs total) */ + DUK_ASSERT_TOP(ctx, nargs + 1); + + /* create bound function object */ + duk_push_object_helper(ctx, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BOUND | + DUK_HOBJECT_FLAG_CONSTRUCTABLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION), + DUK_BIDX_FUNCTION_PROTOTYPE); + h_bound = duk_get_hobject(ctx, -1); + DUK_ASSERT(h_bound != NULL); + + /* [ thisArg arg1 ... argN func boundFunc ] */ + duk_dup(ctx, -2); /* func */ + duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); + + duk_dup(ctx, 0); /* thisArg */ + duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); + + duk_push_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_xdef_prop_stridx(ctx, -2, DUK_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); + 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 */ + duk_pop(ctx); + duk_push_int(ctx, (tmp < 0 ? 0 : tmp)); + } else { + duk_push_int(ctx, 0); + } + duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); /* attrs in E5 Section 15.3.5.1 */ + + /* caller and arguments must use the same thrower, [[ThrowTypeError]] */ + duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE); + duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_LC_ARGUMENTS, DUK_PROPDESC_FLAGS_NONE); + + /* these non-standard properties are copied for convenience */ + /* XXX: 'copy properties' API call? */ + duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); + duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_WC); + duk_get_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME); + duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC); + + /* The 'strict' flag is copied to get the special [[Get]] of E5.1 + * Section 15.3.5.4 to apply when a 'caller' value is a strict bound + * function. Not sure if this is correct, because the specification + * is a bit ambiguous on this point but it would make sense. + */ + if (h_target == NULL) { + /* Lightfuncs are always 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))); + + return 1; + + type_error: + return DUK_RET_TYPE_ERROR; +} http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/a9485aeb/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_global.c ---------------------------------------------------------------------- diff --git a/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_global.c b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_global.c new file mode 100644 index 0000000..c01f7c1 --- /dev/null +++ b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_bi_global.c @@ -0,0 +1,1288 @@ +/* + * Global object built-ins + */ + +#include "duk_internal.h" + +/* + * Encoding/decoding helpers + */ + +/* XXX: Could add fast path (for each transform callback) with direct byte + * lookups (no shifting) and no explicit check for x < 0x80 before table + * lookup. + */ + +/* Macros for creating and checking bitmasks for character encoding. + * Bit number is a bit counterintuitive, but minimizes code size. + */ +#define DUK__MKBITS(a,b,c,d,e,f,g,h) ((duk_uint8_t) ( \ + ((a) << 0) | ((b) << 1) | ((c) << 2) | ((d) << 3) | \ + ((e) << 4) | ((f) << 5) | ((g) << 6) | ((h) << 7) \ + )) +#define DUK__CHECK_BITMASK(table,cp) ((table)[(cp) >> 3] & (1 << ((cp) & 0x07))) + +/* E5.1 Section 15.1.3.3: uriReserved + uriUnescaped + '#' */ +DUK_LOCAL const duk_uint8_t duk__encode_uriunescaped_table[16] = { + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */ + DUK__MKBITS(0, 1, 0, 1, 1, 0, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x20-0x2f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 0, 1, 0, 1), /* 0x30-0x3f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x40-0x4f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1), /* 0x50-0x5f */ + DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x60-0x6f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 1, 0), /* 0x70-0x7f */ +}; + +/* E5.1 Section 15.1.3.4: uriUnescaped */ +DUK_LOCAL const duk_uint8_t duk__encode_uricomponent_unescaped_table[16] = { + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */ + DUK__MKBITS(0, 1, 0, 0, 0, 0, 0, 1), DUK__MKBITS(1, 1, 1, 0, 0, 1, 1, 0), /* 0x20-0x2f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 0, 0, 0, 0, 0, 0), /* 0x30-0x3f */ + DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x40-0x4f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1), /* 0x50-0x5f */ + DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x60-0x6f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 1, 0), /* 0x70-0x7f */ +}; + +/* E5.1 Section 15.1.3.1: uriReserved + '#' */ +DUK_LOCAL const duk_uint8_t duk__decode_uri_reserved_table[16] = { + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */ + DUK__MKBITS(0, 0, 0, 1, 1, 0, 1, 0), DUK__MKBITS(0, 0, 0, 1, 1, 0, 0, 1), /* 0x20-0x2f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 1, 1, 0, 1, 0, 1), /* 0x30-0x3f */ + DUK__MKBITS(1, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x40-0x4f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x50-0x5f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x60-0x6f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x70-0x7f */ +}; + +/* E5.1 Section 15.1.3.2: empty */ +DUK_LOCAL const duk_uint8_t duk__decode_uri_component_reserved_table[16] = { + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x20-0x2f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x30-0x3f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x40-0x4f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x50-0x5f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x60-0x6f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x70-0x7f */ +}; + +#ifdef DUK_USE_SECTION_B +/* E5.1 Section B.2.2, step 7. */ +DUK_LOCAL const duk_uint8_t duk__escape_unescaped_table[16] = { + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */ + DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 1, 1, 0, 1, 1, 1), /* 0x20-0x2f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 0, 0, 0, 0, 0, 0), /* 0x30-0x3f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x40-0x4f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1), /* 0x50-0x5f */ + DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x60-0x6f */ + DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 0) /* 0x70-0x7f */ +}; +#endif /* DUK_USE_SECTION_B */ + +#undef DUK__MKBITS + +typedef struct { + duk_hthread *thr; + duk_hstring *h_str; + duk_bufwriter_ctx bw; + const duk_uint8_t *p; + const duk_uint8_t *p_start; + const duk_uint8_t *p_end; +} duk__transform_context; + +typedef void (*duk__transform_callback)(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp); + +/* XXX: refactor and share with other code */ +DUK_LOCAL duk_small_int_t duk__decode_hex_escape(const duk_uint8_t *p, duk_small_int_t n) { + duk_small_int_t ch; + duk_small_int_t t = 0; + + while (n > 0) { + t = t * 16; + ch = (duk_small_int_t) duk_hex_dectab[*p++]; + if (DUK_LIKELY(ch >= 0)) { + t += ch; + } else { + return -1; + } + n--; + } + return t; +} + +DUK_LOCAL int duk__transform_helper(duk_context *ctx, duk__transform_callback callback, const void *udata) { + duk_hthread *thr = (duk_hthread *) ctx; + duk__transform_context tfm_ctx_alloc; + duk__transform_context *tfm_ctx = &tfm_ctx_alloc; + duk_codepoint_t cp; + + tfm_ctx->thr = thr; + + tfm_ctx->h_str = duk_to_hstring(ctx, 0); + DUK_ASSERT(tfm_ctx->h_str != NULL); + + DUK_BW_INIT_PUSHBUF(thr, &tfm_ctx->bw, DUK_HSTRING_GET_BYTELEN(tfm_ctx->h_str)); /* initial size guess */ + + tfm_ctx->p_start = DUK_HSTRING_GET_DATA(tfm_ctx->h_str); + tfm_ctx->p_end = tfm_ctx->p_start + DUK_HSTRING_GET_BYTELEN(tfm_ctx->h_str); + tfm_ctx->p = tfm_ctx->p_start; + + while (tfm_ctx->p < tfm_ctx->p_end) { + cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &tfm_ctx->p, tfm_ctx->p_start, tfm_ctx->p_end); + callback(tfm_ctx, udata, cp); + } + + DUK_BW_COMPACT(thr, &tfm_ctx->bw); + + duk_to_string(ctx, -1); + return 1; +} + +DUK_LOCAL void duk__transform_callback_encode_uri(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { + duk_uint8_t xutf8_buf[DUK_UNICODE_MAX_XUTF8_LENGTH]; + duk_small_int_t len; + duk_codepoint_t cp1, cp2; + duk_small_int_t i, t; + const duk_uint8_t *unescaped_table = (const duk_uint8_t *) udata; + + /* UTF-8 encoded bytes escaped as %xx%xx%xx... -> 3 * nbytes. + * Codepoint range is restricted so this is a slightly too large + * but doesn't matter. + */ + DUK_BW_ENSURE(tfm_ctx->thr, &tfm_ctx->bw, 3 * DUK_UNICODE_MAX_XUTF8_LENGTH); + + if (cp < 0) { + goto uri_error; + } else if ((cp < 0x80L) && DUK__CHECK_BITMASK(unescaped_table, cp)) { + DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) cp); + return; + } else if (cp >= 0xdc00L && cp <= 0xdfffL) { + goto uri_error; + } else if (cp >= 0xd800L && cp <= 0xdbffL) { + /* Needs lookahead */ + if (duk_unicode_decode_xutf8(tfm_ctx->thr, &tfm_ctx->p, tfm_ctx->p_start, tfm_ctx->p_end, (duk_ucodepoint_t *) &cp2) == 0) { + goto uri_error; + } + if (!(cp2 >= 0xdc00L && cp2 <= 0xdfffL)) { + goto uri_error; + } + cp1 = cp; + cp = ((cp1 - 0xd800L) << 10) + (cp2 - 0xdc00L) + 0x10000L; + } else if (cp > 0x10ffffL) { + /* Although we can allow non-BMP characters (they'll decode + * back into surrogate pairs), we don't allow extended UTF-8 + * characters; they would encode to URIs which won't decode + * back because of strict UTF-8 checks in URI decoding. + * (However, we could just as well allow them here.) + */ + goto uri_error; + } else { + /* Non-BMP characters within valid UTF-8 range: encode as is. + * They'll decode back into surrogate pairs if the escaped + * output is decoded. + */ + ; + } + + len = duk_unicode_encode_xutf8((duk_ucodepoint_t) cp, xutf8_buf); + for (i = 0; i < len; i++) { + t = (int) xutf8_buf[i]; + DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr, + &tfm_ctx->bw, + DUK_ASC_PERCENT, + (duk_uint8_t) duk_uc_nybbles[t >> 4], + (duk_uint8_t) duk_uc_nybbles[t & 0x0f]); + } + + return; + + uri_error: + DUK_ERROR(tfm_ctx->thr, DUK_ERR_URI_ERROR, "invalid input"); +} + +DUK_LOCAL void duk__transform_callback_decode_uri(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { + const duk_uint8_t *reserved_table = (const duk_uint8_t *) udata; + duk_small_uint_t utf8_blen; + duk_codepoint_t min_cp; + duk_small_int_t t; /* must be signed */ + duk_small_uint_t i; + + /* Maximum write size: XUTF8 path writes max DUK_UNICODE_MAX_XUTF8_LENGTH, + * percent escape path writes max two times CESU-8 encoded BMP length. + */ + DUK_BW_ENSURE(tfm_ctx->thr, + &tfm_ctx->bw, + (DUK_UNICODE_MAX_XUTF8_LENGTH >= 2 * DUK_UNICODE_MAX_CESU8_BMP_LENGTH ? + DUK_UNICODE_MAX_XUTF8_LENGTH : DUK_UNICODE_MAX_CESU8_BMP_LENGTH)); + + if (cp == (duk_codepoint_t) '%') { + const duk_uint8_t *p = tfm_ctx->p; + duk_size_t left = (duk_size_t) (tfm_ctx->p_end - p); /* bytes left */ + + DUK_DDD(DUK_DDDPRINT("percent encoding, left=%ld", (long) left)); + + if (left < 2) { + goto uri_error; + } + + t = duk__decode_hex_escape(p, 2); + DUK_DDD(DUK_DDDPRINT("first byte: %ld", (long) t)); + if (t < 0) { + goto uri_error; + } + + if (t < 0x80) { + if (DUK__CHECK_BITMASK(reserved_table, t)) { + /* decode '%xx' to '%xx' if decoded char in reserved set */ + DUK_ASSERT(tfm_ctx->p - 1 >= tfm_ctx->p_start); + DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr, + &tfm_ctx->bw, + DUK_ASC_PERCENT, + p[0], + p[1]); + } else { + DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) t); + } + tfm_ctx->p += 2; + return; + } + + /* Decode UTF-8 codepoint from a sequence of hex escapes. The + * first byte of the sequence has been decoded to 't'. + * + * Note that UTF-8 validation must be strict according to the + * specification: E5.1 Section 15.1.3, decode algorithm step + * 4.d.vii.8. URIError from non-shortest encodings is also + * specifically noted in the spec. + */ + + DUK_ASSERT(t >= 0x80); + if (t < 0xc0) { + /* continuation byte */ + goto uri_error; + } else if (t < 0xe0) { + /* 110x xxxx; 2 bytes */ + utf8_blen = 2; + min_cp = 0x80L; + cp = t & 0x1f; + } else if (t < 0xf0) { + /* 1110 xxxx; 3 bytes */ + utf8_blen = 3; + min_cp = 0x800L; + cp = t & 0x0f; + } else if (t < 0xf8) { + /* 1111 0xxx; 4 bytes */ + utf8_blen = 4; + min_cp = 0x10000L; + cp = t & 0x07; + } else { + /* extended utf-8 not allowed for URIs */ + goto uri_error; + } + + if (left < utf8_blen * 3 - 1) { + /* '%xx%xx...%xx', p points to char after first '%' */ + goto uri_error; + } + + p += 3; + for (i = 1; i < utf8_blen; i++) { + /* p points to digit part ('%xy', p points to 'x') */ + t = duk__decode_hex_escape(p, 2); + DUK_DDD(DUK_DDDPRINT("i=%ld utf8_blen=%ld cp=%ld t=0x%02lx", + (long) i, (long) utf8_blen, (long) cp, (unsigned long) t)); + if (t < 0) { + goto uri_error; + } + if ((t & 0xc0) != 0x80) { + goto uri_error; + } + cp = (cp << 6) + (t & 0x3f); + p += 3; + } + p--; /* p overshoots */ + tfm_ctx->p = p; + + DUK_DDD(DUK_DDDPRINT("final cp=%ld, min_cp=%ld", (long) cp, (long) min_cp)); + + if (cp < min_cp || cp > 0x10ffffL || (cp >= 0xd800L && cp <= 0xdfffL)) { + goto uri_error; + } + + /* The E5.1 algorithm checks whether or not a decoded codepoint + * is below 0x80 and perhaps may be in the "reserved" set. + * This seems pointless because the single byte UTF-8 case is + * handled separately, and non-shortest encodings are rejected. + * So, 'cp' cannot be below 0x80 here, and thus cannot be in + * the reserved set. + */ + + /* utf-8 validation ensures these */ + DUK_ASSERT(cp >= 0x80L && cp <= 0x10ffffL); + + if (cp >= 0x10000L) { + cp -= 0x10000L; + DUK_ASSERT(cp < 0x100000L); + + DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp >> 10) + 0xd800L)); + DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp & 0x03ffUL) + 0xdc00L)); + } else { + DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp); + } + } else { + DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp); + } + return; + + uri_error: + DUK_ERROR(tfm_ctx->thr, DUK_ERR_URI_ERROR, "invalid input"); +} + +#ifdef DUK_USE_SECTION_B +DUK_LOCAL void duk__transform_callback_escape(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { + DUK_UNREF(udata); + + DUK_BW_ENSURE(tfm_ctx->thr, &tfm_ctx->bw, 6); + + if (cp < 0) { + goto esc_error; + } else if ((cp < 0x80L) && DUK__CHECK_BITMASK(duk__escape_unescaped_table, cp)) { + DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) cp); + } else if (cp < 0x100L) { + DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr, + &tfm_ctx->bw, + (duk_uint8_t) DUK_ASC_PERCENT, + (duk_uint8_t) duk_uc_nybbles[cp >> 4], + (duk_uint8_t) duk_uc_nybbles[cp & 0x0f]); + } else if (cp < 0x10000L) { + DUK_BW_WRITE_RAW_U8_6(tfm_ctx->thr, + &tfm_ctx->bw, + (duk_uint8_t) DUK_ASC_PERCENT, + (duk_uint8_t) DUK_ASC_LC_U, + (duk_uint8_t) duk_uc_nybbles[cp >> 12], + (duk_uint8_t) duk_uc_nybbles[(cp >> 8) & 0x0f], + (duk_uint8_t) duk_uc_nybbles[(cp >> 4) & 0x0f], + (duk_uint8_t) duk_uc_nybbles[cp & 0x0f]); + } else { + /* Characters outside BMP cannot be escape()'d. We could + * encode them as surrogate pairs (for codepoints inside + * valid UTF-8 range, but not extended UTF-8). Because + * escape() and unescape() are legacy functions, we don't. + */ + goto esc_error; + } + + return; + + esc_error: + DUK_ERROR_TYPE(tfm_ctx->thr, "invalid input"); +} + +DUK_LOCAL void duk__transform_callback_unescape(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { + duk_small_int_t t; + + DUK_UNREF(udata); + + if (cp == (duk_codepoint_t) '%') { + const duk_uint8_t *p = tfm_ctx->p; + duk_size_t left = (duk_size_t) (tfm_ctx->p_end - p); /* bytes left */ + + if (left >= 5 && p[0] == 'u' && + ((t = duk__decode_hex_escape(p + 1, 4)) >= 0)) { + cp = (duk_codepoint_t) t; + tfm_ctx->p += 5; + } else if (left >= 2 && + ((t = duk__decode_hex_escape(p, 2)) >= 0)) { + cp = (duk_codepoint_t) t; + tfm_ctx->p += 2; + } + } + + DUK_BW_WRITE_ENSURE_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp); +} +#endif /* DUK_USE_SECTION_B */ + +/* + * Eval + * + * Eval needs to handle both a "direct eval" and an "indirect eval". + * Direct eval handling needs access to the caller's activation so that its + * lexical environment can be accessed. A direct eval is only possible from + * Ecmascript code; an indirect eval call is possible also from C code. + * When an indirect eval call is made from C code, there may not be a + * calling activation at all which needs careful handling. + */ + +DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { + duk_hthread *thr = (duk_hthread *) ctx; + duk_hstring *h; + duk_activation *act_caller; + duk_activation *act_eval; + duk_activation *act; + duk_hcompiledfunction *func; + duk_hobject *outer_lex_env; + duk_hobject *outer_var_env; + duk_bool_t this_to_global = 1; + duk_small_uint_t comp_flags; + duk_int_t level = -2; + + DUK_ASSERT(duk_get_top(ctx) == 1 || duk_get_top(ctx) == 2); /* 2 when called by debugger */ + DUK_ASSERT(thr->callstack_top >= 1); /* at least this function exists */ + DUK_ASSERT(((thr->callstack + thr->callstack_top - 1)->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0 || /* indirect eval */ + (thr->callstack_top >= 2)); /* if direct eval, calling activation must exist */ + + /* + * callstack_top - 1 --> this function + * callstack_top - 2 --> caller (may not exist) + * + * If called directly from C, callstack_top might be 1. If calling + * activation doesn't exist, call must be indirect. + */ + + h = duk_get_hstring(ctx, 0); + if (!h) { + return 1; /* return arg as-is */ + } + +#if defined(DUK_USE_DEBUGGER_SUPPORT) + /* NOTE: level is used only by the debugger and should never be present + * for an Ecmascript eval(). + */ + DUK_ASSERT(level == -2); /* by default, use caller's environment */ + if (duk_get_top(ctx) >= 2 && duk_is_number(ctx, 1)) { + level = duk_get_int(ctx, 1); + } + DUK_ASSERT(level <= -2); /* This is guaranteed by debugger code. */ +#endif + + /* [ source ] */ + + comp_flags = DUK_JS_COMPILE_FLAG_EVAL; + act_eval = thr->callstack + thr->callstack_top - 1; /* this function */ + if (thr->callstack_top >= (duk_size_t) -level) { + /* Have a calling activation, check for direct eval (otherwise + * assume indirect eval. + */ + act_caller = thr->callstack + thr->callstack_top + level; /* caller */ + if ((act_caller->flags & DUK_ACT_FLAG_STRICT) && + (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL)) { + /* Only direct eval inherits strictness from calling code + * (E5.1 Section 10.1.1). + */ + comp_flags |= DUK_JS_COMPILE_FLAG_STRICT; + } + } else { + DUK_ASSERT((act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0); + } + act_caller = NULL; /* avoid dereference after potential callstack realloc */ + act_eval = NULL; + + duk_push_hstring_stridx(ctx, DUK_STRIDX_INPUT); /* XXX: copy from caller? */ + duk_js_compile(thr, + (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h), + (duk_size_t) DUK_HSTRING_GET_BYTELEN(h), + comp_flags); + func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); + DUK_ASSERT(func != NULL); + DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func)); + + /* [ source template ] */ + + /* E5 Section 10.4.2 */ + DUK_ASSERT(thr->callstack_top >= 1); + act = thr->callstack + thr->callstack_top - 1; /* this function */ + if (act->flags & DUK_ACT_FLAG_DIRECT_EVAL) { + DUK_ASSERT(thr->callstack_top >= 2); + act = thr->callstack + thr->callstack_top + level; /* caller */ + if (act->lex_env == NULL) { + DUK_ASSERT(act->var_env == NULL); + DUK_DDD(DUK_DDDPRINT("delayed environment initialization")); + + /* this may have side effects, so re-lookup act */ + duk_js_init_activation_environment_records_delayed(thr, act); + act = thr->callstack + thr->callstack_top + level; + } + DUK_ASSERT(act->lex_env != NULL); + DUK_ASSERT(act->var_env != NULL); + + this_to_global = 0; + + if (DUK_HOBJECT_HAS_STRICT((duk_hobject *) func)) { + duk_hobject *new_env; + duk_hobject *act_lex_env; + + DUK_DDD(DUK_DDDPRINT("direct eval call to a strict function -> " + "var_env and lex_env to a fresh env, " + "this_binding to caller's this_binding")); + + act = thr->callstack + thr->callstack_top + level; /* caller */ + act_lex_env = act->lex_env; + act = NULL; /* invalidated */ + + (void) duk_push_object_helper_proto(ctx, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV), + act_lex_env); + new_env = duk_require_hobject(ctx, -1); + DUK_ASSERT(new_env != NULL); + DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", + (duk_heaphdr *) new_env)); + + outer_lex_env = new_env; + outer_var_env = new_env; + + duk_insert(ctx, 0); /* stash to bottom of value stack to keep new_env reachable for duration of eval */ + + /* compiler's responsibility */ + DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func)); + } else { + DUK_DDD(DUK_DDDPRINT("direct eval call to a non-strict function -> " + "var_env and lex_env to caller's envs, " + "this_binding to caller's this_binding")); + + outer_lex_env = act->lex_env; + outer_var_env = act->var_env; + + /* compiler's responsibility */ + DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func)); + } + } else { + DUK_DDD(DUK_DDDPRINT("indirect eval call -> var_env and lex_env to " + "global object, this_binding to global object")); + + this_to_global = 1; + outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; + outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; + } + act = NULL; + + /* Eval code doesn't need an automatic .prototype object. */ + duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 0 /*add_auto_proto*/); + + /* [ source template closure ] */ + + if (this_to_global) { + DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); + duk_push_hobject_bidx(ctx, DUK_BIDX_GLOBAL); + } else { + duk_tval *tv; + DUK_ASSERT(thr->callstack_top >= 2); + act = thr->callstack + thr->callstack_top + level; /* caller */ + tv = thr->valstack + act->idx_bottom - 1; /* this is just beneath bottom */ + DUK_ASSERT(tv >= thr->valstack); + duk_push_tval(ctx, tv); + } + + DUK_DDD(DUK_DDDPRINT("eval -> lex_env=%!iO, var_env=%!iO, this_binding=%!T", + (duk_heaphdr *) outer_lex_env, + (duk_heaphdr *) outer_var_env, + duk_get_tval(ctx, -1))); + + /* [ source template closure this ] */ + + duk_call_method(ctx, 0); + + /* [ source template result ] */ + + return 1; +} + +/* + * Parsing of ints and floats + */ + +DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_int(duk_context *ctx) { + duk_int32_t radix; + duk_small_uint_t s2n_flags; + + DUK_ASSERT_TOP(ctx, 2); + duk_to_string(ctx, 0); + + radix = duk_to_int32(ctx, 1); + + s2n_flags = DUK_S2N_FLAG_TRIM_WHITE | + DUK_S2N_FLAG_ALLOW_GARBAGE | + DUK_S2N_FLAG_ALLOW_PLUS | + DUK_S2N_FLAG_ALLOW_MINUS | + DUK_S2N_FLAG_ALLOW_LEADING_ZERO | + DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT; + + /* Specification stripPrefix maps to DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT. + * + * Don't autodetect octals (from leading zeroes), require user code to + * provide an explicit radix 8 for parsing octal. See write-up from Mozilla: + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt#ECMAScript_5_Removes_Octal_Interpretation + */ + + if (radix != 0) { + if (radix < 2 || radix > 36) { + goto ret_nan; + } + if (radix != 16) { + s2n_flags &= ~DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT; + } + } else { + radix = 10; + } + + duk_dup(ctx, 0); + duk_numconv_parse(ctx, radix, s2n_flags); + return 1; + + ret_nan: + duk_push_nan(ctx); + return 1; +} + +DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx) { + duk_small_uint_t s2n_flags; + duk_int32_t radix; + + DUK_ASSERT_TOP(ctx, 1); + duk_to_string(ctx, 0); + + radix = 10; + + /* XXX: check flags */ + s2n_flags = DUK_S2N_FLAG_TRIM_WHITE | + DUK_S2N_FLAG_ALLOW_EXP | + DUK_S2N_FLAG_ALLOW_GARBAGE | + DUK_S2N_FLAG_ALLOW_PLUS | + DUK_S2N_FLAG_ALLOW_MINUS | + DUK_S2N_FLAG_ALLOW_INF | + DUK_S2N_FLAG_ALLOW_FRAC | + DUK_S2N_FLAG_ALLOW_NAKED_FRAC | + DUK_S2N_FLAG_ALLOW_EMPTY_FRAC | + DUK_S2N_FLAG_ALLOW_LEADING_ZERO; + + duk_numconv_parse(ctx, radix, s2n_flags); + return 1; +} + +/* + * Number checkers + */ + +DUK_INTERNAL duk_ret_t duk_bi_global_object_is_nan(duk_context *ctx) { + duk_double_t d = duk_to_number(ctx, 0); + duk_push_boolean(ctx, DUK_ISNAN(d)); + return 1; +} + +DUK_INTERNAL duk_ret_t duk_bi_global_object_is_finite(duk_context *ctx) { + duk_double_t d = duk_to_number(ctx, 0); + duk_push_boolean(ctx, DUK_ISFINITE(d)); + return 1; +} + +/* + * URI handling + */ + +DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri(duk_context *ctx) { + return duk__transform_helper(ctx, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_reserved_table); +} + +DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri_component(duk_context *ctx) { + return duk__transform_helper(ctx, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_component_reserved_table); +} + +DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri(duk_context *ctx) { + return duk__transform_helper(ctx, duk__transform_callback_encode_uri, (const void *) duk__encode_uriunescaped_table); +} + +DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri_component(duk_context *ctx) { + return duk__transform_helper(ctx, duk__transform_callback_encode_uri, (const void *) duk__encode_uricomponent_unescaped_table); +} + +#ifdef DUK_USE_SECTION_B +DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_context *ctx) { + return duk__transform_helper(ctx, duk__transform_callback_escape, (const void *) NULL); +} + +DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_context *ctx) { + return duk__transform_helper(ctx, duk__transform_callback_unescape, (const void *) NULL); +} +#else /* DUK_USE_SECTION_B */ +DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_context *ctx) { + DUK_UNREF(ctx); + return DUK_RET_UNSUPPORTED_ERROR; +} + +DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_context *ctx) { + DUK_UNREF(ctx); + return DUK_RET_UNSUPPORTED_ERROR; +} +#endif /* DUK_USE_SECTION_B */ + +#if defined(DUK_USE_BROWSER_LIKE) && (defined(DUK_USE_FILE_IO) || defined(DUK_USE_DEBUGGER_SUPPORT)) +DUK_INTERNAL duk_ret_t duk_bi_global_object_print_helper(duk_context *ctx) { + duk_hthread *thr = (duk_hthread *) ctx; + duk_int_t magic; + duk_idx_t nargs; + const duk_uint8_t *buf; + duk_size_t sz_buf; + const char nl = (const char) DUK_ASC_LF; +#ifndef DUK_USE_PREFER_SIZE + duk_uint8_t buf_stack[256]; +#endif +#ifdef DUK_USE_FILE_IO + duk_file *f_out; +#endif + + DUK_UNREF(thr); + + magic = duk_get_current_magic(ctx); + DUK_UNREF(magic); + + nargs = duk_get_top(ctx); + + /* If argument count is 1 and first argument is a buffer, write the buffer + * as raw data into the file without a newline; this allows exact control + * over stdout/stderr without an additional entrypoint (useful for now). + * + * Otherwise current print/alert semantics are to ToString() coerce + * arguments, join them with a single space, and append a newline. + */ + + if (nargs == 1 && duk_is_buffer(ctx, 0)) { + buf = (const duk_uint8_t *) duk_get_buffer(ctx, 0, &sz_buf); + DUK_ASSERT(buf != NULL); + } else if (nargs > 0) { +#ifdef DUK_USE_PREFER_SIZE + /* Compact but lots of churn. */ + duk_push_hstring_stridx(thr, DUK_STRIDX_SPACE); + duk_insert(ctx, 0); + duk_join(ctx, nargs); + duk_push_string(thr, "\n"); + duk_concat(ctx, 2); + buf = (const duk_uint8_t *) duk_get_lstring(ctx, -1, &sz_buf); + DUK_ASSERT(buf != NULL); +#else /* DUK_USE_PREFER_SIZE */ + /* Higher footprint, less churn. */ + duk_idx_t i; + duk_size_t sz_str; + const duk_uint8_t *p_str; + duk_uint8_t *p; + + sz_buf = (duk_size_t) nargs; /* spaces (nargs - 1) + newline */ + for (i = 0; i < nargs; i++) { + (void) duk_to_lstring(ctx, i, &sz_str); + sz_buf += sz_str; + } + + if (sz_buf <= sizeof(buf_stack)) { + p = (duk_uint8_t *) buf_stack; + } else { + p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, sz_buf); + DUK_ASSERT(p != NULL); + } + + buf = (const duk_uint8_t *) p; + for (i = 0; i < nargs; i++) { + p_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &sz_str); + DUK_ASSERT(p_str != NULL); + DUK_MEMCPY((void *) p, (const void *) p_str, sz_str); + p += sz_str; + *p++ = (duk_uint8_t) (i == nargs - 1 ? DUK_ASC_LF : DUK_ASC_SPACE); + } + DUK_ASSERT((const duk_uint8_t *) p == buf + sz_buf); +#endif /* DUK_USE_PREFER_SIZE */ + } else { + buf = (const duk_uint8_t *) &nl; + sz_buf = 1; + } + + /* 'buf' contains the string to write, 'sz_buf' contains the length + * (which may be zero). + */ + DUK_ASSERT(buf != NULL); + + if (sz_buf == 0) { + return 0; + } + +#ifdef DUK_USE_FILE_IO + f_out = (magic ? DUK_STDERR : DUK_STDOUT); + DUK_FWRITE((const void *) buf, 1, (size_t) sz_buf, f_out); + DUK_FFLUSH(f_out); +#endif + +#if defined(DUK_USE_DEBUGGER_SUPPORT) && defined(DUK_USE_DEBUGGER_FWD_PRINTALERT) + if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + duk_debug_write_notify(thr, magic ? DUK_DBG_CMD_ALERT : DUK_DBG_CMD_PRINT); + duk_debug_write_string(thr, (const char *) buf, sz_buf); + duk_debug_write_eom(thr); + } +#endif + return 0; +} +#elif defined(DUK_USE_BROWSER_LIKE) /* print provider */ +DUK_INTERNAL duk_ret_t duk_bi_global_object_print_helper(duk_context *ctx) { + DUK_UNREF(ctx); + return 0; +} +#else /* print provider */ +DUK_INTERNAL duk_ret_t duk_bi_global_object_print_helper(duk_context *ctx) { + DUK_UNREF(ctx); + return DUK_RET_UNSUPPORTED_ERROR; +} +#endif /* print provider */ + +/* + * CommonJS require() and modules support + */ + +#if defined(DUK_USE_COMMONJS_MODULES) +DUK_LOCAL void duk__bi_global_resolve_module_id(duk_context *ctx, const char *req_id, const char *mod_id) { + duk_hthread *thr = (duk_hthread *) ctx; + duk_uint8_t buf[DUK_BI_COMMONJS_MODULE_ID_LIMIT]; + duk_uint8_t *p; + duk_uint8_t *q; + duk_uint8_t *q_last; /* last component */ + duk_int_t int_rc; + + DUK_ASSERT(req_id != NULL); + /* mod_id may be NULL */ + + /* + * A few notes on the algorithm: + * + * - Terms are not allowed to begin with a period unless the term + * is either '.' or '..'. This simplifies implementation (and + * is within CommonJS modules specification). + * + * - There are few output bound checks here. This is on purpose: + * the resolution input is length checked and the output is never + * longer than the input. The resolved output is written directly + * over the input because it's never longer than the input at any + * point in the algorithm. + * + * - Non-ASCII characters are processed as individual bytes and + * need no special treatment. However, U+0000 terminates the + * algorithm; this is not an issue because U+0000 is not a + * desirable term character anyway. + */ + + /* + * Set up the resolution input which is the requested ID directly + * (if absolute or no current module path) or with current module + * ID prepended (if relative and current module path exists). + * + * Suppose current module is 'foo/bar' and relative path is './quux'. + * The 'bar' component must be replaced so the initial input here is + * 'foo/bar/.././quux'. + */ + + if (mod_id != NULL && req_id[0] == '.') { + int_rc = DUK_SNPRINTF((char *) buf, sizeof(buf), "%s/../%s", mod_id, req_id); + } else { + int_rc = DUK_SNPRINTF((char *) buf, sizeof(buf), "%s", req_id); + } + if (int_rc >= (duk_int_t) sizeof(buf) || int_rc < 0) { + /* Potentially truncated, NUL not guaranteed in any case. + * The (int_rc < 0) case should not occur in practice. + */ + DUK_DD(DUK_DDPRINT("resolve error: temporary working module ID doesn't fit into resolve buffer")); + goto resolve_error; + } + DUK_ASSERT(DUK_STRLEN((const char *) buf) < sizeof(buf)); /* at most sizeof(buf) - 1 */ + + DUK_DDD(DUK_DDDPRINT("input module id: '%s'", (const char *) buf)); + + /* + * Resolution loop. At the top of the loop we're expecting a valid + * term: '.', '..', or a non-empty identifier not starting with a period. + */ + + p = buf; + q = buf; + for (;;) { + duk_uint_fast8_t c; + + /* Here 'p' always points to the start of a term. + * + * We can also unconditionally reset q_last here: if this is + * the last (non-empty) term q_last will have the right value + * on loop exit. + */ + + DUK_ASSERT(p >= q); /* output is never longer than input during resolution */ + + DUK_DDD(DUK_DDDPRINT("resolve loop top: p -> '%s', q=%p, buf=%p", + (const char *) p, (void *) q, (void *) buf)); + + q_last = q; + + c = *p++; + if (DUK_UNLIKELY(c == 0)) { + DUK_DD(DUK_DDPRINT("resolve error: requested ID must end with a non-empty term")); + goto resolve_error; + } else if (DUK_UNLIKELY(c == '.')) { + c = *p++; + if (c == '/') { + /* Term was '.' and is eaten entirely (including dup slashes). */ + goto eat_dup_slashes; + } + if (c == '.' && *p == '/') { + /* Term was '..', backtrack resolved name by one component. + * q[-1] = previous slash (or beyond start of buffer) + * q[-2] = last char of previous component (or beyond start of buffer) + */ + p++; /* eat (first) input slash */ + DUK_ASSERT(q >= buf); + if (q == buf) { + DUK_DD(DUK_DDPRINT("resolve error: term was '..' but nothing to backtrack")); + goto resolve_error; + } + DUK_ASSERT(*(q - 1) == '/'); + q--; /* backtrack to last output slash (dups already eliminated) */ + for (;;) { + /* Backtrack to previous slash or start of buffer. */ + DUK_ASSERT(q >= buf); + if (q == buf) { + break; + } + if (*(q - 1) == '/') { + break; + } + q--; + } + goto eat_dup_slashes; + } + DUK_DD(DUK_DDPRINT("resolve error: term begins with '.' but is not '.' or '..' (not allowed now)")); + goto resolve_error; + } else if (DUK_UNLIKELY(c == '/')) { + /* e.g. require('/foo'), empty terms not allowed */ + DUK_DD(DUK_DDPRINT("resolve error: empty term (not allowed now)")); + goto resolve_error; + } else { + for (;;) { + /* Copy term name until end or '/'. */ + *q++ = c; + c = *p++; + if (DUK_UNLIKELY(c == 0)) { + /* This was the last term, and q_last was + * updated to match this term at loop top. + */ + goto loop_done; + } else if (DUK_UNLIKELY(c == '/')) { + *q++ = '/'; + break; + } else { + /* write on next loop */ + } + } + } + + eat_dup_slashes: + for (;;) { + /* eat dup slashes */ + c = *p; + if (DUK_LIKELY(c != '/')) { + break; + } + p++; + } + } + loop_done: + /* Output #1: resolved absolute name */ + DUK_ASSERT(q >= buf); + duk_push_lstring(ctx, (const char *) buf, (size_t) (q - buf)); + + /* Output #2: last component name */ + DUK_ASSERT(q >= q_last); + DUK_ASSERT(q_last >= buf); + duk_push_lstring(ctx, (const char *) q_last, (size_t) (q - q_last)); + + DUK_DD(DUK_DDPRINT("after resolving module name: buf=%p, q_last=%p, q=%p", + (void *) buf, (void *) q_last, (void *) q)); + return; + + resolve_error: + DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "cannot resolve module id: %s", (const char *) req_id); +} +#endif /* DUK_USE_COMMONJS_MODULES */ + +#if defined(DUK_USE_COMMONJS_MODULES) +/* Stack indices for better readability */ +#define DUK__IDX_REQUESTED_ID 0 /* Module id requested */ +#define DUK__IDX_REQUIRE 1 /* Current require() function */ +#define DUK__IDX_REQUIRE_ID 2 /* The base ID of the current require() function, resolution base */ +#define DUK__IDX_RESOLVED_ID 3 /* Resolved, normalized absolute module ID */ +#define DUK__IDX_LASTCOMP 4 /* Last component name in resolved path */ +#define DUK__IDX_DUKTAPE 5 /* Duktape object */ +#define DUK__IDX_MODLOADED 6 /* Duktape.modLoaded[] module cache */ +#define DUK__IDX_UNDEFINED 7 /* 'undefined', artifact of lookup */ +#define DUK__IDX_FRESH_REQUIRE 8 /* New require() function for module, updated resolution base */ +#define DUK__IDX_EXPORTS 9 /* Default exports table */ +#define DUK__IDX_MODULE 10 /* Module object containing module.exports, etc */ + +DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) { + const char *str_req_id; /* requested identifier */ + const char *str_mod_id; /* require.id of current module */ + duk_int_t pcall_rc; + + /* NOTE: we try to minimize code size by avoiding unnecessary pops, + * so the stack looks a bit cluttered in this function. DUK_ASSERT_TOP() + * assertions are used to ensure stack configuration is correct at each + * step. + */ + + /* + * Resolve module identifier into canonical absolute form. + */ + + str_req_id = duk_require_string(ctx, DUK__IDX_REQUESTED_ID); + duk_push_current_function(ctx); + duk_get_prop_stridx(ctx, -1, DUK_STRIDX_ID); + str_mod_id = duk_get_string(ctx, DUK__IDX_REQUIRE_ID); /* ignore non-strings */ + DUK_DDD(DUK_DDDPRINT("resolve module id: requested=%!T, currentmodule=%!T", + duk_get_tval(ctx, DUK__IDX_REQUESTED_ID), + duk_get_tval(ctx, DUK__IDX_REQUIRE_ID))); + duk__bi_global_resolve_module_id(ctx, str_req_id, str_mod_id); + str_req_id = NULL; + str_mod_id = NULL; + DUK_DDD(DUK_DDDPRINT("resolved module id: requested=%!T, currentmodule=%!T, result=%!T, lastcomp=%!T", + duk_get_tval(ctx, DUK__IDX_REQUESTED_ID), + duk_get_tval(ctx, DUK__IDX_REQUIRE_ID), + duk_get_tval(ctx, DUK__IDX_RESOLVED_ID), + duk_get_tval(ctx, DUK__IDX_LASTCOMP))); + + /* [ requested_id require require.id resolved_id last_comp ] */ + DUK_ASSERT_TOP(ctx, DUK__IDX_LASTCOMP + 1); + + /* + * Cached module check. + * + * If module has been loaded or its loading has already begun without + * finishing, return the same cached value ('exports'). The value is + * registered when module load starts so that circular references can + * be supported to some extent. + */ + + duk_push_hobject_bidx(ctx, DUK_BIDX_DUKTAPE); + duk_get_prop_stridx(ctx, DUK__IDX_DUKTAPE, DUK_STRIDX_MOD_LOADED); /* Duktape.modLoaded */ + (void) duk_require_hobject(ctx, DUK__IDX_MODLOADED); + DUK_ASSERT_TOP(ctx, DUK__IDX_MODLOADED + 1); + + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + if (duk_get_prop(ctx, DUK__IDX_MODLOADED)) { + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded Duktape.modLoaded[id] ] */ + DUK_DD(DUK_DDPRINT("module already loaded: %!T", + duk_get_tval(ctx, DUK__IDX_RESOLVED_ID))); + duk_get_prop_stridx(ctx, -1, DUK_STRIDX_EXPORTS); /* return module.exports */ + return 1; + } + DUK_ASSERT_TOP(ctx, DUK__IDX_UNDEFINED + 1); + + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined ] */ + + /* + * Module not loaded (and loading not started previously). + * + * Create a new require() function with 'id' set to resolved ID + * of module being loaded. Also create 'exports' and 'module' + * tables but don't register exports to the loaded table yet. + * We don't want to do that unless the user module search callbacks + * succeeds in finding the module. + */ + + DUK_D(DUK_DPRINT("loading module %!T, resolution base %!T, requested ID %!T -> resolved ID %!T, last component %!T", + duk_get_tval(ctx, DUK__IDX_RESOLVED_ID), + duk_get_tval(ctx, DUK__IDX_REQUIRE_ID), + duk_get_tval(ctx, DUK__IDX_REQUESTED_ID), + duk_get_tval(ctx, DUK__IDX_RESOLVED_ID), + duk_get_tval(ctx, DUK__IDX_LASTCOMP))); + + /* Fresh require: require.id is left configurable (but not writable) + * so that is not easy to accidentally tweak it, but it can still be + * done with Object.defineProperty(). + * + * XXX: require.id could also be just made non-configurable, as there + * is no practical reason to touch it. + */ + duk_push_c_function(ctx, duk_bi_global_object_require, 1 /*nargs*/); + duk_push_hstring_stridx(ctx, DUK_STRIDX_REQUIRE); + duk_xdef_prop_stridx(ctx, DUK__IDX_FRESH_REQUIRE, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + duk_xdef_prop_stridx(ctx, DUK__IDX_FRESH_REQUIRE, DUK_STRIDX_ID, DUK_PROPDESC_FLAGS_C); /* a fresh require() with require.id = resolved target module id */ + + /* Module table: + * - module.exports: initial exports table (may be replaced by user) + * - module.id is non-writable and non-configurable, as the CommonJS + * spec suggests this if possible + * - module.filename: not set, defaults to resolved ID if not explicitly + * set by modSearch() (note capitalization, not .fileName, matches Node.js) + * - module.name: not set, defaults to last component of resolved ID if + * not explicitly set by modSearch() + */ + duk_push_object(ctx); /* exports */ + duk_push_object(ctx); /* module */ + duk_dup(ctx, DUK__IDX_EXPORTS); + duk_xdef_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_EXPORTS, DUK_PROPDESC_FLAGS_WC); /* module.exports = exports */ + duk_dup(ctx, DUK__IDX_RESOLVED_ID); /* resolved id: require(id) must return this same module */ + duk_xdef_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_ID, DUK_PROPDESC_FLAGS_NONE); /* module.id = resolved_id */ + duk_compact(ctx, DUK__IDX_MODULE); /* module table remains registered to modLoaded, minimize its size */ + DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 1); + + DUK_DD(DUK_DDPRINT("module table created: %!T", duk_get_tval(ctx, DUK__IDX_MODULE))); + + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module ] */ + + /* Register the module table early to modLoaded[] so that we can + * support circular references even in modSearch(). If an error + * is thrown, we'll delete the reference. + */ + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + duk_dup(ctx, DUK__IDX_MODULE); + duk_put_prop(ctx, DUK__IDX_MODLOADED); /* Duktape.modLoaded[resolved_id] = module */ + + /* + * Call user provided module search function and build the wrapped + * module source code (if necessary). The module search function + * can be used to implement pure Ecmacsript, pure C, and mixed + * Ecmascript/C modules. + * + * The module search function can operate on the exports table directly + * (e.g. DLL code can register values to it). It can also return a + * string which is interpreted as module source code (if a non-string + * is returned the module is assumed to be a pure C one). If a module + * cannot be found, an error must be thrown by the user callback. + * + * Because Duktape.modLoaded[] already contains the module being + * loaded, circular references for C modules should also work + * (although expected to be quite rare). + */ + + duk_push_string(ctx, "(function(require,exports,module){"); + + /* Duktape.modSearch(resolved_id, fresh_require, exports, module). */ + duk_get_prop_stridx(ctx, DUK__IDX_DUKTAPE, DUK_STRIDX_MOD_SEARCH); /* Duktape.modSearch */ + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); + duk_dup(ctx, DUK__IDX_EXPORTS); + duk_dup(ctx, DUK__IDX_MODULE); /* [ ... Duktape.modSearch resolved_id last_comp fresh_require exports module ] */ + pcall_rc = duk_pcall(ctx, 4 /*nargs*/); /* -> [ ... source ] */ + DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 3); + + if (pcall_rc != DUK_EXEC_SUCCESS) { + /* Delete entry in Duktape.modLoaded[] and rethrow. */ + goto delete_rethrow; + } + + /* If user callback did not return source code, module loading + * is finished (user callback initialized exports table directly). + */ + if (!duk_is_string(ctx, -1)) { + /* User callback did not return source code, so module loading + * is finished: just update modLoaded with final module.exports + * and we're done. + */ + goto return_exports; + } + + /* Finish the wrapped module source. Force module.filename as the + * function .fileName so it gets set for functions defined within a + * module. This also ensures loggers created within the module get + * the module ID (or overridden filename) as their default logger name. + * (Note capitalization: .filename matches Node.js while .fileName is + * used elsewhere in Duktape.) + */ + duk_push_string(ctx, "})"); + duk_concat(ctx, 3); + if (!duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_FILENAME)) { + /* module.filename for .fileName, default to resolved ID if + * not present. + */ + duk_pop(ctx); + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + } + duk_eval_raw(ctx, NULL, 0, DUK_COMPILE_EVAL); + + /* Module has now evaluated to a wrapped module function. Force its + * .name to match module.name (defaults to last component of resolved + * ID) so that it is shown in stack traces too. Note that we must not + * introduce an actual name binding into the function scope (which is + * usually the case with a named function) because it would affect the + * scope seen by the module and shadow accesses to globals of the same name. + * This is now done by compiling the function as anonymous and then forcing + * its .name without setting a "has name binding" flag. + */ + + duk_push_hstring_stridx(ctx, DUK_STRIDX_NAME); + if (!duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_NAME)) { + /* module.name for .name, default to last component if + * not present. + */ + duk_pop(ctx); + duk_dup(ctx, DUK__IDX_LASTCOMP); + } + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); + + /* + * Call the wrapped module function. + * + * Use a protected call so that we can update Duktape.modLoaded[resolved_id] + * even if the module throws an error. + */ + + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func ] */ + DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 2); + + duk_dup(ctx, DUK__IDX_EXPORTS); /* exports (this binding) */ + duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); /* fresh require (argument) */ + duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_EXPORTS); /* relookup exports from module.exports in case it was changed by modSearch */ + duk_dup(ctx, DUK__IDX_MODULE); /* module (argument) */ + DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 6); + + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func exports fresh_require exports module ] */ + + pcall_rc = duk_pcall_method(ctx, 3 /*nargs*/); + if (pcall_rc != DUK_EXEC_SUCCESS) { + /* Module loading failed. Node.js will forget the module + * registration so that another require() will try to load + * the module again. Mimic that behavior. + */ + goto delete_rethrow; + } + + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module result(ignored) ] */ + DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 2); + + /* fall through */ + + return_exports: + duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_EXPORTS); + duk_compact(ctx, -1); /* compact the exports table */ + return 1; /* return module.exports */ + + delete_rethrow: + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + duk_del_prop(ctx, DUK__IDX_MODLOADED); /* delete Duktape.modLoaded[resolved_id] */ + duk_throw(ctx); /* rethrow original error */ + return 0; /* not reachable */ +} + +#undef DUK__IDX_REQUESTED_ID +#undef DUK__IDX_REQUIRE +#undef DUK__IDX_REQUIRE_ID +#undef DUK__IDX_RESOLVED_ID +#undef DUK__IDX_LASTCOMP +#undef DUK__IDX_DUKTAPE +#undef DUK__IDX_MODLOADED +#undef DUK__IDX_UNDEFINED +#undef DUK__IDX_FRESH_REQUIRE +#undef DUK__IDX_EXPORTS +#undef DUK__IDX_MODULE +#else +DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) { + DUK_UNREF(ctx); + return DUK_RET_UNSUPPORTED_ERROR; +} +#endif /* DUK_USE_COMMONJS_MODULES */
