From c25f0dabdb1173d4920dfb5aed7975bffac81266 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Wed, 29 May 2024 23:19:25 +0900 Subject: [PATCH] compiler fix to prohibit self. or super. in out-of-class method defintion nested in a normal method in a class --- lib/comp.c | 83 +++++++++++++++++++++++++++++++++++++----------- lib/hcl-prv.h | 4 +-- mod/arr.c | 4 +-- t/class-5001.err | 51 ++++++++++++++++++++++++++--- 4 files changed, 115 insertions(+), 27 deletions(-) diff --git a/lib/comp.c b/lib/comp.c index 4766df5..eac593e 100644 --- a/lib/comp.c +++ b/lib/comp.c @@ -51,6 +51,11 @@ enum enum { + /* these enumerators are stored in the lower 8 bits of + * the fun_type field of hcl_fnblk_info_t. + * the 9th bit of the field indicate a method is defined + * out of a class */ + FUN_PLAIN, /* plain function */ FUN_IM, /* instance method */ FUN_CM, /* class method */ @@ -323,22 +328,22 @@ static int find_variable_backward_with_word (hcl_t* hcl, const hcl_oocs_t* name, { /* instance variables are accessible only in an instance method defintion scope. * it is in class initialization scope */ - hcl_setsynerrbfmt (hcl, HCL_SYNERR_BANNED, loc, name, "prohibited access to an instance variable"); + hcl_setsynerrbfmt (hcl, HCL_SYNERR_BANNED, loc, name, "prohibited access to instance variable"); return -1; } for (fi = hcl->c->fnblk.depth + 1; fi > i; ) /* TOOD: review this loop for correctness */ { /* 'i' is the function level that holds the class defintion block. the check must not go past it */ - if (hcl->c->fnblk.info[--fi].fun_type == FUN_CM) + if ((hcl->c->fnblk.info[--fi].fun_type & 0xFF) == FUN_CM) { /* the function where this variable is defined is a class method or an plain function block within a class method*/ - hcl_setsynerrbfmt (hcl, HCL_SYNERR_BANNED, loc, name, "prohibited access to an instance variable in a class method context"); + hcl_setsynerrbfmt (hcl, HCL_SYNERR_BANNED, loc, name, "prohibited access to instance variable in class method context"); return -1; } /* instance methods and instantiation methods can access instance variables */ - if (hcl->c->fnblk.info[fi].fun_type != FUN_PLAIN) break; + if ((hcl->c->fnblk.info[fi].fun_type & 0xFF) != FUN_PLAIN) break; } vi->type = VAR_INST; @@ -363,8 +368,10 @@ HCL_INFO6 (hcl, "FOUND INST VAR [%.*js]...[%.*js]................ ===> ctx_offse vi->type = (i >= hcl->c->fnblk.depth? VAR_CLASS_I: VAR_CLASS_IM); vi->ctx_offset = 0; vi->index_in_ctx = index; +/* HCL_INFO6 (hcl, "FOUND CLASS VAR [%.*js]...[%.*js]................ ===> ctx_offset %d index %d\n", haystack.len, haystack.ptr, name->len, name->ptr, (int)(vi->ctx_offset), (int)vi->index_in_ctx); +*/ return 1; } } @@ -1126,7 +1133,7 @@ static void pop_clsblk (hcl_t* hcl) static int push_fnblk (hcl_t* hcl, const hcl_loc_t* errloc, hcl_oow_t tmpr_va, hcl_oow_t tmpr_nargs, hcl_oow_t tmpr_nrvars, hcl_oow_t tmpr_nlvars, - hcl_oow_t tmpr_count, hcl_oow_t tmpr_len, hcl_oow_t make_inst_pos, hcl_oow_t lfbase, int fun_type) + hcl_oow_t tmpr_count, hcl_oow_t tmpr_len, hcl_oow_t make_inst_pos, hcl_oow_t lfbase, unsigned int fun_type) { hcl_oow_t new_depth; hcl_fnblk_info_t* fbi; @@ -1240,7 +1247,7 @@ static void pop_fnblk (hcl_t* hcl) /* the temporaries mask is a bit-mask that encodes the counts of different temporary variables. * and it's split to two intruction parameters when used with MAKE_LAMBDA and MAKE_FUNCTION. * the INSTA bit is on if fbi->fun_type == FUN_CIM */ - attr_mask = ENCODE_BLK_MASK((fbi->fun_type == FUN_CIM), fbi->tmpr_va, fbi->tmpr_nargs, fbi->tmpr_nrvars, fbi->tmpr_nlvars); + attr_mask = ENCODE_BLK_MASK(((fbi->fun_type & 0xFF) == FUN_CIM), fbi->tmpr_va, fbi->tmpr_nargs, fbi->tmpr_nrvars, fbi->tmpr_nlvars); patch_double_long_params_with_oow (hcl, fbi->make_inst_pos + 1, attr_mask); } } @@ -2752,7 +2759,7 @@ static int compile_lambda (hcl_t* hcl, hcl_cnode_t* src, int defun) hcl_cnode_t* defun_name; hcl_cnode_t* class_name; hcl_cframe_t* cf; - int fun_type = FUN_PLAIN; + unsigned int fun_type = FUN_PLAIN; HCL_ASSERT (hcl, HCL_CNODE_IS_CONS(src)); @@ -2822,7 +2829,7 @@ static int compile_lambda (hcl_t* hcl, hcl_cnode_t* src, int defun) if (tmp && HCL_CNODE_IS_CONS(tmp)) { tmp = HCL_CNODE_CONS_CAR(tmp); - if (HCL_CNODE_IS_COLON(tmp) /*(HCL_CNODE_IS_SYMBOL_PLAIN(tmp)*/) + if (HCL_CNODE_IS_COLON(tmp) || HCL_CNODE_IS_DBLCOLONS(tmp) || HCL_CNODE_IS_COLONSTAR(tmp)/*(HCL_CNODE_IS_SYMBOL_PLAIN(tmp)*/) { hcl_setsynerrbfmt ( hcl, HCL_SYNERR_VARNAME, @@ -2847,6 +2854,8 @@ static int compile_lambda (hcl_t* hcl, hcl_cnode_t* src, int defun) marker = HCL_CNODE_CONS_CAR(tmp); if (HCL_CNODE_IS_COLON(marker) || HCL_CNODE_IS_DBLCOLONS(marker) || HCL_CNODE_IS_COLONSTAR(marker)) { + /* fun A:aaa A::aaa A:*aaa */ + tmp = HCL_CNODE_CONS_CDR(tmp); if (tmp && HCL_CNODE_IS_CONS(tmp)) { @@ -2854,12 +2863,23 @@ static int compile_lambda (hcl_t* hcl, hcl_cnode_t* src, int defun) cand = HCL_CNODE_CONS_CAR(tmp); if (HCL_CNODE_IS_SYMBOL_PLAIN(cand)) { - /* out-of-class method definition - * for defun String:length() { ... }, class_name is String, defun_name is length. */ -/* TODO: this must not be allowed at the in-class definition level.... */ -/* TODO: can we use fun_type to indicate different types of out-of-class methods? use marker.... */ + /* out-of-class method definition + * for defun String:length() { ... }, + * class_name is String, defun_name is length. */ fun_type = HCL_CNODE_IS_DBLCOLONS(marker)? FUN_CM: HCL_CNODE_IS_COLONSTAR(marker)? FUN_CIM: FUN_IM; + + /* indicates that this method is defined using the AAA:bbb syntax. + * the form of method defintion can still be inside a class if this + * form is place inside another normal method. + * class X { + * fun x() { + * fun J:q() { .... } ## this defintion + * } + * } + * */ + fun_type |= 0x100; + class_name = defun_name; defun_name = HCL_CNODE_CONS_CAR(tmp); obj = tmp; @@ -4243,20 +4263,45 @@ static HCL_INLINE int compile_dsymbol (hcl_t* hcl, hcl_cnode_t* obj) hcl_oocs_t name; int x = 0; hcl_var_info_t vi; + hcl_fnblk_info_t* fbi; name = *HCL_CNODE_GET_TOK(obj); + fbi = &hcl->c->fnblk.info[hcl->c->fnblk.depth]; sep = hcl_find_oochar(name.ptr, name.len, '.'); HCL_ASSERT (hcl, sep != HCL_NULL); if (hcl_comp_oochars_bcstr(name.ptr, (sep - (const hcl_ooch_t*)name.ptr), "self") == 0) { /* instance variable? or instance method? */ + if (fbi->fun_type >> 8) + { + /* if defined using A::xxx syntax, it's not possible to know the instance position of an instance variable. + * class X | a b | { + * fun a() { + * fun J::t() { + * ## J has nothing to to with X in priciple even if J may point to X when a() is executed. + * ## it's not meaningful to look up the variable `a` in the context of class X. + * ## it must be prohibited to access instance variables using the self or super prefix + * ## in this context + * return self.a + * } + * } + * } + */ + hcl_setsynerrbfmt (hcl, HCL_SYNERR_VARNAME, HCL_CNODE_GET_LOC(obj), HCL_CNODE_GET_TOK(obj), "not allowed to prefix with self in out-of-class method context"); + return -1; + } name.ptr = (hcl_ooch_t*)(sep + 1); name.len -= 5; x = find_variable_backward_with_word(hcl, &name, HCL_CNODE_GET_LOC(obj), 1, &vi); } else if (hcl_comp_oochars_bcstr(name.ptr, sep - (const hcl_ooch_t*)name.ptr, "super") == 0) { + if (fbi->fun_type >> 8) /* if defined using A::xxx syntax */ + { + hcl_setsynerrbfmt (hcl, HCL_SYNERR_VARNAME, HCL_CNODE_GET_LOC(obj), HCL_CNODE_GET_TOK(obj), "not allowed to prefix with super in out-of-class method context"); + return -1; + } name.ptr = (hcl_ooch_t*)(sep + 1); name.len -= 6; x = find_variable_backward_with_word(hcl, &name, HCL_CNODE_GET_LOC(obj), 2, &vi); /* TODO: arrange to skip the current class */ @@ -5534,7 +5579,7 @@ static HCL_INLINE int emit_lambda (hcl_t* hcl) else { /* single return value */ - if (cf->u.lambda.fun_type == FUN_PLAIN) + if ((cf->u.lambda.fun_type & 0xFF) == FUN_PLAIN) { if (block_code_size == 0) { @@ -5636,7 +5681,7 @@ static HCL_INLINE int post_lambda (hcl_t* hcl) if (x == 0) { /* arrange to save to the method slot */ - switch (cf->u.lambda.fun_type) + switch (cf->u.lambda.fun_type & 0xFF) { case FUN_CM: /* class method */ SWITCH_TOP_CFRAME (hcl, COP_EMIT_CLASS_CMSTORE, defun_name); @@ -5652,8 +5697,8 @@ static HCL_INLINE int post_lambda (hcl_t* hcl) default: /* in the class initialization scope, the type must not be other than the listed above */ - HCL_DEBUG1 (hcl, "Internal error - invalid method type %d\n", cf->u.lambda.fun_type); - hcl_seterrbfmt (hcl, HCL_EINTERN, "internal error - invalid method type %d", cf->u.lambda.fun_type); + HCL_DEBUG1 (hcl, "Internal error - invalid method type %d\n", cf->u.lambda.fun_type & 0xFF); + hcl_seterrbfmt (hcl, HCL_EINTERN, "internal error - invalid method type %d", cf->u.lambda.fun_type & 0xFF); return -1; } cf = GET_TOP_CFRAME(hcl); @@ -5703,7 +5748,7 @@ static HCL_INLINE int post_lambda (hcl_t* hcl) if (HCL_UNLIKELY(!lit)) return -1; if (add_literal(hcl, lit, &index) <= -1) return -1; - switch (cf->u.lambda.fun_type) + switch (cf->u.lambda.fun_type & 0xFF) { case FUN_CM: /* class method */ inst = HCL_CODE_CLASS_CMSTORE; @@ -5719,8 +5764,8 @@ static HCL_INLINE int post_lambda (hcl_t* hcl) default: /* in the class initialization scope, the type must not be other than the listed above */ - HCL_DEBUG1 (hcl, "Internal error - invalid function type %d\n", cf->u.lambda.fun_type); - hcl_seterrbfmt (hcl, HCL_EINTERN, "internal error - invalid function type %d", cf->u.lambda.fun_type); + HCL_DEBUG1 (hcl, "Internal error - invalid function type %d\n", cf->u.lambda.fun_type & 0xFF); + hcl_seterrbfmt (hcl, HCL_EINTERN, "internal error - invalid function type %d", cf->u.lambda.fun_type & 0xFF); return -1; } diff --git a/lib/hcl-prv.h b/lib/hcl-prv.h index 1f1457d..730c25c 100644 --- a/lib/hcl-prv.h +++ b/lib/hcl-prv.h @@ -600,7 +600,7 @@ struct hcl_cframe_t /* COP_POST_LAMBDA, COP_EMIT_LAMBDA */ struct { - int fun_type; + unsigned int fun_type; hcl_oow_t jump_inst_pos; hcl_ooi_t lfbase_pos; hcl_ooi_t lfsize_pos; @@ -648,7 +648,7 @@ typedef struct hcl_cblk_info_t hcl_cblk_info_t; /* function block information for the compiler */ struct hcl_fnblk_info_t { - int fun_type; + unsigned int fun_type; hcl_oow_t tmprlen; /* accumulated length of the temporaries string including outer blocks */ hcl_oow_t tmprcnt; /* accumulated number of temporaries including outer blocks */ diff --git a/mod/arr.c b/mod/arr.c index def7f13..c093b36 100644 --- a/mod/arr.c +++ b/mod/arr.c @@ -61,7 +61,7 @@ static hcl_pfrc_t pf_arr_get (hcl_t* hcl, hcl_mod_t* mod, hcl_ooi_t nargs) if (index >= HCL_OBJ_GET_SIZE(arr)) { - hcl_seterrbfmt (hcl, HCL_EINVAL, "array index(%zu) out of boundsfor array of size %zu", index, HCL_OBJ_GET_SIZE(arr)); + hcl_seterrbfmt (hcl, HCL_EINVAL, "array index %zu out of bounds for array of size %zu", index, HCL_OBJ_GET_SIZE(arr)); return HCL_PF_FAILURE; } @@ -89,7 +89,7 @@ static hcl_pfrc_t pf_arr_put (hcl_t* hcl, hcl_mod_t* mod, hcl_ooi_t nargs) if (index >= HCL_OBJ_GET_SIZE(arr)) { - hcl_seterrbfmt (hcl, HCL_EINVAL, "array index(%zu) out of bounds for array of size %zu", index, HCL_OBJ_GET_SIZE(arr)); + hcl_seterrbfmt (hcl, HCL_EINVAL, "array index %zu out of bounds for array of size %zu", index, HCL_OBJ_GET_SIZE(arr)); return HCL_PF_FAILURE; } diff --git a/t/class-5001.err b/t/class-5001.err index 2406964..9a0636a 100644 --- a/t/class-5001.err +++ b/t/class-5001.err @@ -47,18 +47,61 @@ class X { } } -## this will trigger a runtime error as J isn't a class name -fun J:ccc() { ##ERROR: exception not handled +## this triggers a runtime error as J isn't a class name +fun J:ccc() { ##ERROR: exception not handled - "J accessed without initialization" return 999 } --- + +X := 20 + +## this also raises a runtime error as X isn't a class name +fun X:xxx() { ##ERROR: exception not handled - "not class" + return self +} + +--- + ## this must not be very useful as Array is an index item ## and the clase instantiation method can't specify the size ## you can't place an item in the arrya at all. fun Array:*boom() { - arr.put self 0 10 ##ERROR: exception not handled - printf "%O" self + arr.put self 0 10 ##ERROR: exception not handled - "array index 0 out of bounds for array of size 0" + printf "%O" self } Array:boom + +--- + +class X | a b c | { + fun :* new () { + self.a := 20 + return self + } + + fun getA() { return self.a } +} + +## the instance variables are not accessible in out-of-class method +## defintionas there isn't a good way to know the class structure +## as X isn't known in advance and can point to anything +fun X:get_a() { + return self.a ##ERROR: syntax error - not allowed to prefix with self +} + +printf "%d\n" ((X:new):get_a) + +--- + +class F | a b c | { +} + +class X | a b c | { + fun oh() { + fun F:get_a() { + return super.a ##ERROR: syntax error - not allowed to prefix with super + } + } +}