| /* |
| * Compilation and evaluation |
| */ |
| |
| #include "duk_internal.h" |
| |
| typedef struct duk__compile_raw_args duk__compile_raw_args; |
| struct duk__compile_raw_args { |
| duk_size_t src_length; /* should be first on 64-bit platforms */ |
| const duk_uint8_t *src_buffer; |
| duk_uint_t flags; |
| }; |
| |
| /* Eval is just a wrapper now. */ |
| DUK_EXTERNAL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) { |
| duk_uint_t comp_flags; |
| duk_int_t rc; |
| |
| DUK_ASSERT_CTX_VALID(ctx); |
| |
| /* Note: strictness is *not* inherited from the current Duktape/C. |
| * This would be confusing because the current strictness state |
| * depends on whether we're running inside a Duktape/C activation |
| * (= strict mode) or outside of any activation (= non-strict mode). |
| * See tests/api/test-eval-strictness.c for more discussion. |
| */ |
| |
| /* [ ... source? filename? ] (depends on flags) */ |
| |
| comp_flags = flags; |
| comp_flags |= DUK_COMPILE_EVAL; |
| rc = duk_compile_raw(ctx, src_buffer, src_length, comp_flags); /* may be safe, or non-safe depending on flags */ |
| |
| /* [ ... closure/error ] */ |
| |
| if (rc != DUK_EXEC_SUCCESS) { |
| rc = DUK_EXEC_ERROR; |
| goto got_rc; |
| } |
| |
| duk_push_global_object(ctx); /* explicit 'this' binding, see GH-164 */ |
| |
| if (flags & DUK_COMPILE_SAFE) { |
| rc = duk_pcall_method(ctx, 0); |
| } else { |
| duk_call_method(ctx, 0); |
| rc = DUK_EXEC_SUCCESS; |
| } |
| |
| /* [ ... result/error ] */ |
| |
| got_rc: |
| if (flags & DUK_COMPILE_NORESULT) { |
| duk_pop(ctx); |
| } |
| |
| return rc; |
| } |
| |
| /* Helper which can be called both directly and with duk_safe_call(). */ |
| DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx) { |
| duk_hthread *thr = (duk_hthread *) ctx; |
| duk__compile_raw_args *comp_args; |
| duk_uint_t flags; |
| duk_small_uint_t comp_flags; |
| duk_hcompiledfunction *h_templ; |
| |
| DUK_ASSERT_CTX_VALID(ctx); |
| |
| /* Note: strictness is not inherited from the current Duktape/C |
| * context. Otherwise it would not be possible to compile |
| * non-strict code inside a Duktape/C activation (which is |
| * always strict now). See tests/api/test-eval-strictness.c |
| * for discussion. |
| */ |
| |
| /* [ ... source? filename? &comp_args ] (depends on flags) */ |
| |
| comp_args = (duk__compile_raw_args *) duk_require_pointer(ctx, -1); |
| flags = comp_args->flags; |
| duk_pop(ctx); |
| |
| /* [ ... source? filename? ] */ |
| |
| if (flags & DUK_COMPILE_NOFILENAME) { |
| /* Automatic filename: 'eval' or 'input'. */ |
| duk_push_hstring_stridx(ctx, (flags & DUK_COMPILE_EVAL) ? DUK_STRIDX_EVAL : DUK_STRIDX_INPUT); |
| } |
| |
| /* [ ... source? filename ] */ |
| |
| if (!comp_args->src_buffer) { |
| duk_hstring *h_sourcecode; |
| |
| h_sourcecode = duk_get_hstring(ctx, -2); |
| if ((flags & DUK_COMPILE_NOSOURCE) || /* args incorrect */ |
| (h_sourcecode == NULL)) { /* e.g. duk_push_string_file_raw() pushed undefined */ |
| /* XXX: when this error is caused by a nonexistent |
| * file given to duk_peval_file() or similar, the |
| * error message is not the best possible. |
| */ |
| DUK_ERROR_API(thr, DUK_STR_NO_SOURCECODE); |
| } |
| DUK_ASSERT(h_sourcecode != NULL); |
| comp_args->src_buffer = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode); |
| comp_args->src_length = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode); |
| } |
| DUK_ASSERT(comp_args->src_buffer != NULL); |
| |
| /* XXX: unnecessary translation of flags */ |
| comp_flags = 0; |
| if (flags & DUK_COMPILE_EVAL) { |
| comp_flags |= DUK_JS_COMPILE_FLAG_EVAL; |
| } |
| if (flags & DUK_COMPILE_FUNCTION) { |
| comp_flags |= DUK_JS_COMPILE_FLAG_EVAL | |
| DUK_JS_COMPILE_FLAG_FUNCEXPR; |
| } |
| if (flags & DUK_COMPILE_STRICT) { |
| comp_flags |= DUK_JS_COMPILE_FLAG_STRICT; |
| } |
| |
| /* [ ... source? filename ] */ |
| |
| duk_js_compile(thr, comp_args->src_buffer, comp_args->src_length, comp_flags); |
| |
| /* [ ... source? func_template ] */ |
| |
| if (flags & DUK_COMPILE_NOSOURCE) { |
| ; |
| } else { |
| duk_remove(ctx, -2); |
| } |
| |
| /* [ ... func_template ] */ |
| |
| h_templ = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); |
| DUK_ASSERT(h_templ != NULL); |
| duk_js_push_closure(thr, |
| h_templ, |
| thr->builtins[DUK_BIDX_GLOBAL_ENV], |
| thr->builtins[DUK_BIDX_GLOBAL_ENV], |
| 1 /*add_auto_proto*/); |
| duk_remove(ctx, -2); /* -> [ ... closure ] */ |
| |
| /* [ ... closure ] */ |
| |
| return 1; |
| } |
| |
| DUK_EXTERNAL duk_int_t duk_compile_raw(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) { |
| duk__compile_raw_args comp_args_alloc; |
| duk__compile_raw_args *comp_args = &comp_args_alloc; |
| |
| DUK_ASSERT_CTX_VALID(ctx); |
| |
| if ((flags & DUK_COMPILE_STRLEN) && (src_buffer != NULL)) { |
| /* String length is computed here to avoid multiple evaluation |
| * of a macro argument in the calling side. |
| */ |
| src_length = DUK_STRLEN(src_buffer); |
| } |
| |
| comp_args->src_buffer = (const duk_uint8_t *) src_buffer; |
| comp_args->src_length = src_length; |
| comp_args->flags = flags; |
| duk_push_pointer(ctx, (void *) comp_args); |
| |
| /* [ ... source? filename? &comp_args ] (depends on flags) */ |
| |
| if (flags & DUK_COMPILE_SAFE) { |
| duk_int_t rc; |
| duk_int_t nargs; |
| duk_int_t nrets = 1; |
| |
| /* Arguments can be: [ source? filename? &comp_args] so that |
| * nargs is 1 to 3. Call site encodes the correct nargs count |
| * directly into flags. |
| */ |
| nargs = flags & 0x07; |
| DUK_ASSERT(nargs == (1 + |
| ((flags & DUK_COMPILE_NOSOURCE) ? 0 : 1) + |
| ((flags & DUK_COMPILE_NOFILENAME) ? 0 : 1))); |
| rc = duk_safe_call(ctx, duk__do_compile, nargs, nrets); |
| |
| /* [ ... closure ] */ |
| return rc; |
| } |
| |
| (void) duk__do_compile(ctx); |
| |
| /* [ ... closure ] */ |
| return DUK_EXEC_SUCCESS; |
| } |