compiler fix to prohibit self. or super. in out-of-class method defintion nested in a normal method in a class
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
hyung-hwan 2024-05-29 23:19:25 +09:00
parent b4d435a593
commit c25f0dabdb
4 changed files with 115 additions and 27 deletions

View File

@ -51,6 +51,11 @@ enum
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_PLAIN, /* plain function */
FUN_IM, /* instance method */ FUN_IM, /* instance method */
FUN_CM, /* class 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. /* instance variables are accessible only in an instance method defintion scope.
* it is in class initialization 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; return -1;
} }
for (fi = hcl->c->fnblk.depth + 1; fi > i; ) /* TOOD: review this loop for correctness */ 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 */ /* '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*/ /* 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; return -1;
} }
/* instance methods and instantiation methods can access instance variables */ /* 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; 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->type = (i >= hcl->c->fnblk.depth? VAR_CLASS_I: VAR_CLASS_IM);
vi->ctx_offset = 0; vi->ctx_offset = 0;
vi->index_in_ctx = index; vi->index_in_ctx = index;
/*
HCL_INFO6 (hcl, "FOUND CLASS VAR [%.*js]...[%.*js]................ ===> ctx_offset %d index %d\n", 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); haystack.len, haystack.ptr, name->len, name->ptr, (int)(vi->ctx_offset), (int)vi->index_in_ctx);
*/
return 1; 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, 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_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_oow_t new_depth;
hcl_fnblk_info_t* fbi; 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. /* 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. * 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 */ * 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); 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* defun_name;
hcl_cnode_t* class_name; hcl_cnode_t* class_name;
hcl_cframe_t* cf; hcl_cframe_t* cf;
int fun_type = FUN_PLAIN; unsigned int fun_type = FUN_PLAIN;
HCL_ASSERT (hcl, HCL_CNODE_IS_CONS(src)); 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)) if (tmp && HCL_CNODE_IS_CONS(tmp))
{ {
tmp = HCL_CNODE_CONS_CAR(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_setsynerrbfmt (
hcl, HCL_SYNERR_VARNAME, 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); marker = HCL_CNODE_CONS_CAR(tmp);
if (HCL_CNODE_IS_COLON(marker) || HCL_CNODE_IS_DBLCOLONS(marker) || HCL_CNODE_IS_COLONSTAR(marker)) 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); tmp = HCL_CNODE_CONS_CDR(tmp);
if (tmp && HCL_CNODE_IS_CONS(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); cand = HCL_CNODE_CONS_CAR(tmp);
if (HCL_CNODE_IS_SYMBOL_PLAIN(cand)) if (HCL_CNODE_IS_SYMBOL_PLAIN(cand))
{ {
/* out-of-class method definition /* out-of-class method definition
* for defun String:length() { ... }, class_name is String, defun_name is length. */ * for defun String:length() { ... },
/* TODO: this must not be allowed at the in-class definition level.... */ * class_name is String, defun_name is length. */
/* TODO: can we use fun_type to indicate different types of out-of-class methods? use marker.... */
fun_type = HCL_CNODE_IS_DBLCOLONS(marker)? FUN_CM: fun_type = HCL_CNODE_IS_DBLCOLONS(marker)? FUN_CM:
HCL_CNODE_IS_COLONSTAR(marker)? FUN_CIM: FUN_IM; 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; class_name = defun_name;
defun_name = HCL_CNODE_CONS_CAR(tmp); defun_name = HCL_CNODE_CONS_CAR(tmp);
obj = tmp; obj = tmp;
@ -4243,20 +4263,45 @@ static HCL_INLINE int compile_dsymbol (hcl_t* hcl, hcl_cnode_t* obj)
hcl_oocs_t name; hcl_oocs_t name;
int x = 0; int x = 0;
hcl_var_info_t vi; hcl_var_info_t vi;
hcl_fnblk_info_t* fbi;
name = *HCL_CNODE_GET_TOK(obj); name = *HCL_CNODE_GET_TOK(obj);
fbi = &hcl->c->fnblk.info[hcl->c->fnblk.depth];
sep = hcl_find_oochar(name.ptr, name.len, '.'); sep = hcl_find_oochar(name.ptr, name.len, '.');
HCL_ASSERT (hcl, sep != HCL_NULL); HCL_ASSERT (hcl, sep != HCL_NULL);
if (hcl_comp_oochars_bcstr(name.ptr, (sep - (const hcl_ooch_t*)name.ptr), "self") == 0) if (hcl_comp_oochars_bcstr(name.ptr, (sep - (const hcl_ooch_t*)name.ptr), "self") == 0)
{ {
/* instance variable? or instance method? */ /* 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.ptr = (hcl_ooch_t*)(sep + 1);
name.len -= 5; name.len -= 5;
x = find_variable_backward_with_word(hcl, &name, HCL_CNODE_GET_LOC(obj), 1, &vi); 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) 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.ptr = (hcl_ooch_t*)(sep + 1);
name.len -= 6; 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 */ 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 else
{ {
/* single return value */ /* 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) if (block_code_size == 0)
{ {
@ -5636,7 +5681,7 @@ static HCL_INLINE int post_lambda (hcl_t* hcl)
if (x == 0) if (x == 0)
{ {
/* arrange to save to the method slot */ /* 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 */ case FUN_CM: /* class method */
SWITCH_TOP_CFRAME (hcl, COP_EMIT_CLASS_CMSTORE, defun_name); SWITCH_TOP_CFRAME (hcl, COP_EMIT_CLASS_CMSTORE, defun_name);
@ -5652,8 +5697,8 @@ static HCL_INLINE int post_lambda (hcl_t* hcl)
default: default:
/* in the class initialization scope, the type must not be other than the listed above */ /* 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_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); hcl_seterrbfmt (hcl, HCL_EINTERN, "internal error - invalid method type %d", cf->u.lambda.fun_type & 0xFF);
return -1; return -1;
} }
cf = GET_TOP_CFRAME(hcl); 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 (HCL_UNLIKELY(!lit)) return -1;
if (add_literal(hcl, lit, &index) <= -1) 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 */ case FUN_CM: /* class method */
inst = HCL_CODE_CLASS_CMSTORE; inst = HCL_CODE_CLASS_CMSTORE;
@ -5719,8 +5764,8 @@ static HCL_INLINE int post_lambda (hcl_t* hcl)
default: default:
/* in the class initialization scope, the type must not be other than the listed above */ /* 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_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); hcl_seterrbfmt (hcl, HCL_EINTERN, "internal error - invalid function type %d", cf->u.lambda.fun_type & 0xFF);
return -1; return -1;
} }

View File

@ -600,7 +600,7 @@ struct hcl_cframe_t
/* COP_POST_LAMBDA, COP_EMIT_LAMBDA */ /* COP_POST_LAMBDA, COP_EMIT_LAMBDA */
struct struct
{ {
int fun_type; unsigned int fun_type;
hcl_oow_t jump_inst_pos; hcl_oow_t jump_inst_pos;
hcl_ooi_t lfbase_pos; hcl_ooi_t lfbase_pos;
hcl_ooi_t lfsize_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 */ /* function block information for the compiler */
struct hcl_fnblk_info_t 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 tmprlen; /* accumulated length of the temporaries string including outer blocks */
hcl_oow_t tmprcnt; /* accumulated number of temporaries including outer blocks */ hcl_oow_t tmprcnt; /* accumulated number of temporaries including outer blocks */

View File

@ -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)) 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; 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)) 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; return HCL_PF_FAILURE;
} }

View File

@ -47,18 +47,61 @@ class X {
} }
} }
## this will trigger a runtime error as J isn't a class name ## this triggers a runtime error as J isn't a class name
fun J:ccc() { ##ERROR: exception not handled fun J:ccc() { ##ERROR: exception not handled - "J accessed without initialization"
return 999 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 ## this must not be very useful as Array is an index item
## and the clase instantiation method can't specify the size ## and the clase instantiation method can't specify the size
## you can't place an item in the arrya at all. ## you can't place an item in the arrya at all.
fun Array:*boom() { fun Array:*boom() {
arr.put self 0 10 ##ERROR: exception not handled arr.put self 0 10 ##ERROR: exception not handled - "array index 0 out of bounds for array of size 0"
printf "%O" self printf "%O" self
} }
Array:boom 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
}
}
}