diff options
author | Jo-Philipp Wich <jo@mein.io> | 2021-05-04 16:01:13 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-04 16:01:13 +0200 |
commit | 799c9f4dbe123536e1dde1639a122f48dcf75a05 (patch) | |
tree | edcf626dc7a68398e8ac9a9793aa0ebb9a9cf042 | |
parent | a5aead5161821b2f5be9ae76f1bd76e8814147bd (diff) | |
parent | a36e0dfd8432a0c345ab3a710280f6d4f663bddc (diff) |
Merge pull request #8 from jow-/introduce-use-strict
syntax: implement support for 'use strict' pragma
-rw-r--r-- | compiler.c | 53 | ||||
-rw-r--r-- | tests/custom/00_syntax/22_strict_mode | 92 | ||||
-rw-r--r-- | types.h | 4 | ||||
-rw-r--r-- | vm.c | 14 |
4 files changed, 151 insertions, 12 deletions
@@ -94,9 +94,10 @@ static ssize_t uc_compiler_initialize_local(uc_compiler *compiler); static void -uc_compiler_init(uc_compiler *compiler, const char *name, size_t srcpos, uc_source *source) +uc_compiler_init(uc_compiler *compiler, const char *name, size_t srcpos, uc_source *source, bool strict) { uc_value_t *varname = ucv_string_new("(callee)"); + uc_function_t *fn; compiler->scope_depth = 0; @@ -114,6 +115,9 @@ uc_compiler_init(uc_compiler *compiler, const char *name, size_t srcpos, uc_sour compiler->current_srcpos = srcpos; + fn = (uc_function_t *)compiler->function; + fn->strict = strict; + /* reserve stack slot 0 */ uc_compiler_declare_local(compiler, varname); uc_compiler_initialize_local(compiler); @@ -625,6 +629,14 @@ uc_compiler_leave_scope(uc_compiler *compiler) } } +static bool +uc_compiler_is_strict(uc_compiler *compiler) +{ + uc_function_t *fn = (uc_function_t *)compiler->function; + + return fn->strict; +} + static ssize_t uc_compiler_declare_local(uc_compiler *compiler, uc_value_t *name) { @@ -653,8 +665,7 @@ uc_compiler_declare_local(uc_compiler *compiler, uc_value_t *name) len2 = ucv_string_length(locals->entries[i - 1].name); if (len1 == len2 && !strcmp(str1, str2)) { - if (compiler->parser->config && - compiler->parser->config->strict_declarations) { + if (uc_compiler_is_strict(compiler)) { uc_compiler_syntax_error(compiler, 0, "Variable '%s' redeclared", str2); return -1; @@ -1047,7 +1058,8 @@ uc_compiler_compile_arrowfn(uc_compiler *compiler, uc_value_t *args, bool restar pos = compiler->parser->prev.pos; uc_compiler_init(&fncompiler, NULL, compiler->parser->prev.pos, - uc_compiler_current_source(compiler)); + uc_compiler_current_source(compiler), + uc_compiler_is_strict(compiler)); fncompiler.parent = compiler; fncompiler.parser = compiler->parser; @@ -1322,9 +1334,26 @@ uc_compiler_compile_post_inc(uc_compiler *compiler, bool assignable) uc_compiler_emit_inc_dec(compiler, compiler->parser->prev.type, true); } +static bool +uc_compiler_is_use_strict_pragma(uc_compiler *compiler) +{ + uc_value_t *v; + + if (uc_compiler_current_chunk(compiler)->count > 0) + return false; + + if (compiler->parser->lex.block != STATEMENTS) + return false; + + v = compiler->parser->prev.uv; + + return (strcmp(ucv_string_get(v), "use strict") == 0); +} + static void uc_compiler_compile_constant(uc_compiler *compiler, bool assignable) { + uc_function_t *fn; int64_t n; switch (compiler->parser->prev.type) { @@ -1341,8 +1370,16 @@ uc_compiler_compile_constant(uc_compiler *compiler, bool assignable) ucv_boolean_get(compiler->parser->prev.uv) ? I_LTRUE : I_LFALSE); break; - case TK_DOUBLE: case TK_STRING: + if (uc_compiler_is_use_strict_pragma(compiler)) { + fn = (uc_function_t *)compiler->function; + fn->strict = true; + break; + } + + /* fall through */ + + case TK_DOUBLE: uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, compiler->parser->prev.uv); break; @@ -1429,7 +1466,8 @@ uc_compiler_compile_function(uc_compiler *compiler, bool assignable) uc_compiler_init(&fncompiler, name ? ucv_string_get(name) : NULL, compiler->parser->prev.pos, - uc_compiler_current_source(compiler)); + uc_compiler_current_source(compiler), + uc_compiler_is_strict(compiler)); fncompiler.parent = compiler; fncompiler.parser = compiler->parser; @@ -2706,7 +2744,8 @@ uc_compile(uc_parse_config *config, uc_source *source, char **errp) uc_function_t *fn; uc_lexer_init(&parser.lex, config, source); - uc_compiler_init(&compiler, "main", 0, source); + uc_compiler_init(&compiler, "main", 0, source, + config && config->strict_declarations); uc_compiler_parse_advance(&compiler); diff --git a/tests/custom/00_syntax/22_strict_mode b/tests/custom/00_syntax/22_strict_mode new file mode 100644 index 0000000..73f399c --- /dev/null +++ b/tests/custom/00_syntax/22_strict_mode @@ -0,0 +1,92 @@ +Ucode borrows the `"use strict";` statement from ECMA script to enable +strict variable semantics for the entire script or for the enclosing +function. + +With strict mode enabled, attempts to use undeclared local variables +or attempts to read global variables which have not been assigned yet +will raise an exception. + + +1. To enable strict mode for the entire script, it should be the first +statement of the program. + +-- Expect stderr -- +Reference error: access to undeclared variable x +In line 4, byte 8: + + ` print(x);` + ^-- Near here + + +-- End -- + +-- Testcase -- +{% + "use strict"; + + print(x); +%} +-- End -- + + +2. To enable strict mode for a single function, the "use strict" expression +should be the first statement of the function body. + +-- Expect stdout -- +a() = null +-- End -- + +-- Expect stderr -- +Reference error: access to undeclared variable x +In b(), line 9, byte 24: + called from anonymous function ([stdin]:13:4) + + ` printf("b() = %J\n", x);` + Near here -------------------^ + + +-- End -- + +-- Testcase -- +{% + function a() { + printf("a() = %J\n", x); + } + + function b() { + "use strict"; + + printf("b() = %J\n", x); + } + + a(); + b(); +%} +-- End -- + + +3. When "use strict" is not the first statement, it has no effect. + +-- Expect stdout -- +b=null +c=null +-- End -- + +-- Testcase -- +{% + function t() { + a = 1; + + "use strict"; + + printf("b=%J\n", b); + } + + t(); + + "use strict"; + + printf("c=%J\n", c); + +%} +-- End -- @@ -150,7 +150,7 @@ typedef struct { typedef struct { uc_value_t header; - bool arrow, vararg; + bool arrow, vararg, strict; size_t nargs; size_t nupvals; size_t srcpos; @@ -231,7 +231,7 @@ typedef struct { uc_cfunction_t *cfunction; size_t stackframe; uc_value_t *ctx; - bool mcall; + bool mcall, strict; } uc_callframe; uc_declare_vector(uc_callframes, uc_callframe); @@ -158,6 +158,12 @@ uc_vm_current_chunk(uc_vm *vm) return uc_vm_frame_chunk(uc_vm_current_frame(vm)); } +static bool +uc_vm_is_strict(uc_vm *vm) +{ + return uc_vm_current_frame(vm)->strict; +} + static enum insn_type uc_vm_decode_insn(uc_vm *vm, uc_callframe *frame, uc_chunk *chunk) { @@ -503,6 +509,7 @@ uc_vm_call_function(uc_vm *vm, uc_value_t *ctx, uc_value_t *fno, bool mcall, siz frame->ctx = ctx; frame->ip = function->chunk.entries; frame->mcall = mcall; + frame->strict = function->strict; if (vm->trace) uc_vm_frame_dump(vm, frame); @@ -919,7 +926,7 @@ uc_vm_insn_load_var(uc_vm *vm, enum insn_type insn) next = ucv_prototype_get(scope); if (!next) { - if (vm->config->strict_declarations) { + if (uc_vm_is_strict(vm)) { uc_vm_raise_exception(vm, EXCEPTION_REFERENCE, "access to undeclared variable %s", ucv_string_get(name)); @@ -1092,7 +1099,7 @@ uc_vm_insn_store_var(uc_vm *vm, enum insn_type insn) next = ucv_prototype_get(scope); if (!next) { - if (vm->config->strict_declarations) { + if (uc_vm_is_strict(vm)) { uc_vm_raise_exception(vm, EXCEPTION_REFERENCE, "access to undeclared variable %s", ucv_string_get(name)); @@ -1335,7 +1342,7 @@ uc_vm_insn_update_var(uc_vm *vm, enum insn_type insn) next = ucv_prototype_get(scope); if (!next) { - if (vm->config->strict_declarations) { + if (uc_vm_is_strict(vm)) { uc_vm_raise_exception(vm, EXCEPTION_REFERENCE, "access to undeclared variable %s", ucv_string_get(name)); @@ -2250,6 +2257,7 @@ uc_vm_execute(uc_vm *vm, uc_function_t *fn, uc_value_t *globals, uc_value_t *mod frame->closure = closure; frame->stackframe = 0; frame->ip = uc_vm_frame_chunk(frame)->entries; + frame->strict = fn->strict; if (vm->trace) { buf = xprintbuf_new(); |