diff --git a/include/deemon/asm.h b/include/deemon/asm.h index d22a7e47a..d64a9fe8a 100644 --- a/include/deemon/asm.h +++ b/include/deemon/asm.h @@ -956,10 +956,10 @@ #define ASM_CALLATTR_THIS_C_TUPLE 0xd5 /* [2][-1,+1] `push callattr this, const , pop...' - Pop a Tuple and lookup and call an attribute of `this', using a string in constant slot `' (Only valid for code with the `CODE_FTHISCALL' flag set) */ #define ASM_CALLATTR_C_SEQ 0xd6 /* [3][-1-n,+1] `callattr top, const , [#]' - Call an attribute with a single sequence-like argument packed from the top # stack-items. */ #define ASM_CALLATTR_C_MAP 0xd7 /* [3][-1-n,+1] `callattr top, const , {#*2}' - Call an attribute with a single mapping-like argument packed from the top #*2 stack-items. */ -/* ASM_ 0xd8 * -------- - ------------------ */ -#define ASM_GETMEMBER_THIS_R 0xd9 /* [3][-0,+1] `push getmember this, ref , $' - Same as `ASM_GETMEMBER_THIS', but use a referenced variable `' as class type. */ -#define ASM_DELMEMBER_THIS_R 0xda /* [3][-0,+0] `delmember this, ref , $' - Same as `ASM_DELMEMBER_THIS', but use a referenced variable `' as class type. */ -#define ASM_SETMEMBER_THIS_R 0xdb /* [3][-1,+0] `setmember this, ref , $, pop' - Same as `ASM_SETMEMBER_THIS', but use a referenced variable `' as class type. */ +#define ASM_GETMEMBER_THIS_R 0xd8 /* [3][-0,+1] `push getmember this, ref , $' - Same as `ASM_GETMEMBER_THIS', but use a referenced variable `' as class type. */ +#define ASM_DELMEMBER_THIS_R 0xd9 /* [3][-0,+0] `delmember this, ref , $' - Same as `ASM_DELMEMBER_THIS', but use a referenced variable `' as class type. */ +#define ASM_SETMEMBER_THIS_R 0xda /* [3][-1,+0] `setmember this, ref , $, pop' - Same as `ASM_SETMEMBER_THIS', but use a referenced variable `' as class type. */ +#define ASM_SETMEMBERI_THIS_R 0xdb /* [3][-1,+0] `setmemberi this, ref , $, pop' - Same as `ASM_SETMEMBER_THIS_R', but does `err_cant_access_attribute_string_c()' if the member already has a value (the "i" stands for "initial"). */ #define ASM_BOUNDMEMBER_THIS_R 0xdc/* [3][-0,+1] `push boundmember this, ref , $' - Same as `ASM_BOUNDMEMBER_THIS', but use a referenced variable `' as class type. */ #define ASM_CALL_EXTERN 0xdd /* [4][-n,+1] `push call extern :, #' - Pop # (second) values from the stack, pack then into a Tuple, then call an external function referenced by : (first). */ #define ASM_CALL_GLOBAL 0xde /* [3][-n,+1] `push call global , #' - Pop # (second) values from the stack, pack then into a Tuple, then call a function in global slot (first). */ @@ -1411,10 +1411,10 @@ #define ASM16_CALLATTR_THIS_C_TUPLE 0xf0d5 /* [4][-1,+1] `callattr this, const , pop' - Pop a Tuple and lookup and call an attribute of `this', using a string in constant slot `' (Only valid for code with the `CODE_FTHISCALL' flag set) */ #define ASM16_CALLATTR_C_SEQ 0xf0d6 /* [5][-1-n,+1] `callattr top, const , [#]' - Call an attribute with a single sequence-like argument packed from the top # stack-items. */ #define ASM16_CALLATTR_C_MAP 0xf0d7 /* [5][-1-n,+1] `callattr top, const , {#*2}' - Call an attribute with a single mapping-like argument packed from the top #*2 stack-items. */ -/* ASM_ 0xf0d8 * -------- - ------------------ */ -#define ASM16_GETMEMBER_THIS_R 0xf0d9/* [6][-0,+1] `push getmember this, ref , $' - Same as `ASM16_GETMEMBER_THIS', but use a referenced variable `' as class type. */ -#define ASM16_DELMEMBER_THIS_R 0xf0da/* [6][-0,+0] `delmember this, ref , $' - Same as `ASM16_DELMEMBER_THIS', but use a referenced variable `' as class type. */ -#define ASM16_SETMEMBER_THIS_R 0xf0db/* [6][-1,+0] `setmember this, ref , $, pop' - Same as `ASM16_SETMEMBER_THIS', but use a referenced variable `' as class type. */ +#define ASM16_GETMEMBER_THIS_R 0xf0d8/* [6][-0,+1] `push getmember this, ref , $' - Same as `ASM16_GETMEMBER_THIS', but use a referenced variable `' as class type. */ +#define ASM16_DELMEMBER_THIS_R 0xf0d9/* [6][-0,+0] `delmember this, ref , $' - Same as `ASM16_DELMEMBER_THIS', but use a referenced variable `' as class type. */ +#define ASM16_SETMEMBER_THIS_R 0xf0da/* [6][-1,+0] `setmember this, ref , $, pop' - Same as `ASM16_SETMEMBER_THIS', but use a referenced variable `' as class type. */ +#define ASM16_SETMEMBERI_THIS_R 0xf0db/*[6][-1,+0] `setmemberi this, ref , $, pop' - Same as `ASM16_SETMEMBER_THIS_R', but does `err_cant_access_attribute_string_c()' if the member already has a value (the "i" stands for "initial"). */ #define ASM16_BOUNDMEMBER_THIS_R 0xf0dc/*[6][-0,+1] `push boundmember this, ref , $' - Same as `ASM16_BOUNDMEMBER_THIS', but use a referenced variable `' as class type. */ #define ASM16_CALL_EXTERN 0xf0dd /* [7][-n,+1] `push call extern :, #' - Pop # values from the stack, pack then into a Tuple, then call an external function referenced by :. */ #define ASM16_CALL_GLOBAL 0xf0de /* [5][-n,+1] `push call global , #' - Pop # values from the stack, pack then into a Tuple, then call a function in global slot . */ diff --git a/include/deemon/class.h b/include/deemon/class.h index 1e2b1b1a0..250e35d89 100644 --- a/include/deemon/class.h +++ b/include/deemon/class.h @@ -673,10 +673,12 @@ DFUNDEF WUNUSED NONNULL((1, 2)) DREF DeeObject *DCALL DeeInstance_GetMember(/*Cl DFUNDEF WUNUSED NONNULL((1, 2)) bool DCALL DeeInstance_BoundMember(/*Class*/ DeeTypeObject *__restrict tp_self, /*Instance*/ DeeObject *__restrict self, uint16_t addr); DFUNDEF WUNUSED NONNULL((1, 2)) int DCALL DeeInstance_DelMember(/*Class*/ DeeTypeObject *__restrict tp_self, /*Instance*/ DeeObject *__restrict self, uint16_t addr); DFUNDEF NONNULL((1, 2, 4)) void DCALL DeeInstance_SetMember(/*Class*/ DeeTypeObject *tp_self, /*Instance*/ DeeObject *self, uint16_t addr, DeeObject *value); +DFUNDEF WUNUSED NONNULL((1, 2, 4)) int DCALL DeeInstance_SetMemberInitial(/*Class*/ DeeTypeObject *tp_self, /*Instance*/ DeeObject *self, uint16_t addr, DeeObject *value); DFUNDEF WUNUSED NONNULL((1, 2)) DREF DeeObject *DCALL DeeInstance_GetMemberSafe(DeeTypeObject *tp_self, DeeObject *__restrict self, uint16_t addr); DFUNDEF WUNUSED NONNULL((1, 2)) int DCALL DeeInstance_BoundMemberSafe(DeeTypeObject *tp_self, DeeObject *__restrict self, uint16_t addr); DFUNDEF WUNUSED NONNULL((1, 2)) int DCALL DeeInstance_DelMemberSafe(DeeTypeObject *tp_self, DeeObject *__restrict self, uint16_t addr); DFUNDEF WUNUSED NONNULL((1, 2, 4)) int DCALL DeeInstance_SetMemberSafe(DeeTypeObject *tp_self, DeeObject *self, uint16_t addr, DeeObject *value); +DFUNDEF WUNUSED NONNULL((1, 2, 4)) int DCALL DeeInstance_SetMemberInitialSafe(DeeTypeObject *tp_self, DeeObject *self, uint16_t addr, DeeObject *value); /* Class member access (by addr) */ DFUNDEF NONNULL((1, 3)) void DCALL DeeClass_SetMember(DeeTypeObject *self, uint16_t addr, DeeObject *value); @@ -721,25 +723,14 @@ DeeClass_FindClassInstanceAttribute(DeeTypeObject *tp_invoker, DeeTypeObject *se struct attribute_info *__restrict result, struct attribute_lookup_rules const *__restrict rules); -/* @return: 2: Attribute isn't a basic one +/* Used to initialize attributes in deault constructor calls. * @return: 0: Basic attribute successfully set * @return: -1: An error occurred. */ -#ifdef __INTELLISENSE__ -INTDEF WUNUSED NONNULL((1, 2, 3, 4, 5)) int DCALL +INTDEF WUNUSED NONNULL((1, 2, 3, 4)) int DCALL DeeInstance_SetBasicAttribute(struct Dee_class_desc *__restrict desc, struct Dee_instance_desc *__restrict self, - DeeObject *this_arg, struct Dee_class_attribute const *__restrict attr, DeeObject *value); -#else /* __INTELLISENSE__ */ -#define DeeInstance_SetBasicAttribute(desc, self, this_arg, attr, value) \ - DeeInstance_SetBasicAttribute_(desc, self, attr, value) -INTDEF WUNUSED NONNULL((1, 2, 3, 4)) int -(DCALL DeeInstance_SetBasicAttribute_)(struct Dee_class_desc *__restrict desc, - struct Dee_instance_desc *__restrict self, - struct Dee_class_attribute const *__restrict attr, - DeeObject *__restrict value); -#endif /* !__INTELLISENSE__ */ /* Get/Call/Del/Set a class attribute, as acquired * through `DeeClassDescriptor_QueryClassAttribute()'. */ diff --git a/include/deemon/compiler/assembler.h b/include/deemon/compiler/assembler.h index 576900d13..241194ea9 100644 --- a/include/deemon/compiler/assembler.h +++ b/include/deemon/compiler/assembler.h @@ -1443,6 +1443,7 @@ INTDEF WUNUSED int DCALL asm_gunwind(void); #define asm_ggetmember_this_r(rid, iid) (asm_incsp(), asm_put881616(ASM_GETMEMBER_THIS_R, rid, iid)) #define asm_gdelmember_this_r(rid, iid) (asm_put881616(ASM_DELMEMBER_THIS_R, rid, iid)) #define asm_gsetmember_this_r(rid, iid) (asm_decsp(), asm_put881616(ASM_SETMEMBER_THIS_R, rid, iid)) +#define asm_gsetmemberi_this_r(rid, iid) (asm_decsp(), asm_put881616(ASM_SETMEMBERI_THIS_R, rid, iid)) #define asm_gboundmember_this_r(rid, iid) (asm_incsp(), asm_put881616(ASM_BOUNDMEMBER_THIS_R, rid, iid)) #define asm_goperator(oid, n) \ (asm_subsp((n) + 1), asm_incsp(), \ diff --git a/include/deemon/compiler/lexer.def b/include/deemon/compiler/lexer.def index bb122ffce..b5dc97e09 100644 --- a/include/deemon/compiler/lexer.def +++ b/include/deemon/compiler/lexer.def @@ -423,6 +423,8 @@ DEF_WARNING(W_EXPECTED_CALLBACK_NAME_IN_PROPERTY, (WG_SYNTAX), WSTATE_ERROR, WAR DEF_WARNING(W_PROPERTY_CALLBACK_ALREADY_DEFINED, (WG_SYMBOL), WSTATE_ERROR, WARNF("Property callback `%s' has already been defined", ARG(char *))) DEF_WARNING(W_CLASS_MEMBER_TYPE_NOT_ASSIGNED, (WG_SYMBOL), WSTATE_WARN, WARNF("Explicitly typed member `%s' was not initialized", KWDNAME())) DEF_WARNING(W_CLASS_MEMBER_TYPE_NOT_MATCHED, (WG_SYMBOL), WSTATE_WARN, WARNF("Explicitly typed member `%s' expectation was not met", KWDNAME())) +DEF_WARNING(W_CLASS_MEMBER_VARYING_IGNORED_FOR_PROPERTY, (WG_SYMBOL), WSTATE_WARN, WARNF("`varying' modifier ignored for `property %s'", KWDNAME())) +DEF_WARNING(W_CLASS_MEMBER_VARYING_IGNORED_FOR_FUNCTION, (WG_SYMBOL), WSTATE_WARN, WARNF("`varying' modifier ignored for `function %s'", KWDNAME())) DEF_WARNING(W_DEPRECATED_PROPERTY_MODIFIER, (WG_SYNTAX), WSTATE_ERROR, WARNF("Property modifier " TOK_S " is deprecated and ignored", TOK_A)) DEF_WARNING(W_DEPRECATED_PROPERTY_NAME, (WG_SYNTAX), WSTATE_ERROR, WARNF("Property name " TOK_S " is deprecated. Use `%s' instead", TOK_A, ARG(char *))) DEF_WARNING(W_EXPECTED_PROPERTY_NAME_AFTER_TYPE_PREFIX, (WG_SYNTAX), WSTATE_ERROR, WARNF("Expected a property callback name, but got " TOK_S, TOK_A)) diff --git a/lib/doc.dee b/lib/doc.dee index 42253390e..7ce3ce7dd 100644 --- a/lib/doc.dee +++ b/lib/doc.dee @@ -802,7 +802,7 @@ class TypeRef: WeakRefAble { return m_docstring; } } - public final member m_docstring: string | Type; + private member m_docstring: string | Type; @@The doc node that contains a reference to @this TypeRef public final member docnode: WeakRef with Doc; diff --git a/lib/rt/bytecode.dee b/lib/rt/bytecode.dee index 5a611fe4b..e524f3e24 100644 --- a/lib/rt/bytecode.dee +++ b/lib/rt/bytecode.dee @@ -230,9 +230,10 @@ final global ASM_CALLATTR_THIS_C = 0xd4; final global ASM_CALLATTR_THIS_C_TUPLE = 0xd5; final global ASM_CALLATTR_C_SEQ = 0xd6; final global ASM_CALLATTR_C_MAP = 0xd7; -final global ASM_GETMEMBER_THIS_R = 0xd9; -final global ASM_DELMEMBER_THIS_R = 0xda; -final global ASM_SETMEMBER_THIS_R = 0xdb; +final global ASM_GETMEMBER_THIS_R = 0xd8; +final global ASM_DELMEMBER_THIS_R = 0xd9; +final global ASM_SETMEMBER_THIS_R = 0xda; +final global ASM_SETMEMBERI_THIS_R = 0xdb; final global ASM_BOUNDMEMBER_THIS_R = 0xdc; final global ASM_CALL_EXTERN = 0xdd; final global ASM_CALL_GLOBAL = 0xde; @@ -381,9 +382,10 @@ final global ASM16_CALLATTR_THIS_C = 0xf0d4; final global ASM16_CALLATTR_THIS_C_TUPLE = 0xf0d5; final global ASM16_CALLATTR_C_SEQ = 0xf0d6; final global ASM16_CALLATTR_C_MAP = 0xf0d7; -final global ASM16_GETMEMBER_THIS_R = 0xf0d9; -final global ASM16_DELMEMBER_THIS_R = 0xf0da; -final global ASM16_SETMEMBER_THIS_R = 0xf0db; +final global ASM16_GETMEMBER_THIS_R = 0xf0d8; +final global ASM16_DELMEMBER_THIS_R = 0xf0d9; +final global ASM16_SETMEMBER_THIS_R = 0xf0da; +final global ASM16_SETMEMBERI_THIS_R = 0xf0db; final global ASM16_BOUNDMEMBER_THIS_R = 0xf0dc; final global ASM16_CALL_EXTERN = 0xf0dd; final global ASM16_CALL_GLOBAL = 0xf0de; diff --git a/src/deemon/compiler/asm/assembler.c b/src/deemon/compiler/asm/assembler.c index 79c069c1e..628aa9abe 100644 --- a/src/deemon/compiler/asm/assembler.c +++ b/src/deemon/compiler/asm/assembler.c @@ -128,6 +128,7 @@ STATIC_ASSERT((ASM16_CALLATTR_THIS_C_TUPLE & 0xff) == ASM_CALLATTR_THIS_C_TUPLE) STATIC_ASSERT((ASM16_GETMEMBER_THIS_R & 0xff) == ASM_GETMEMBER_THIS_R); STATIC_ASSERT((ASM16_DELMEMBER_THIS_R & 0xff) == ASM_DELMEMBER_THIS_R); STATIC_ASSERT((ASM16_SETMEMBER_THIS_R & 0xff) == ASM_SETMEMBER_THIS_R); +STATIC_ASSERT((ASM16_SETMEMBERI_THIS_R & 0xff) == ASM_SETMEMBERI_THIS_R); STATIC_ASSERT((ASM16_CALL_EXTERN & 0xff) == ASM_CALL_EXTERN); STATIC_ASSERT((ASM16_CALL_GLOBAL & 0xff) == ASM_CALL_GLOBAL); STATIC_ASSERT((ASM16_CALL_LOCAL & 0xff) == ASM_CALL_LOCAL); diff --git a/src/deemon/compiler/asm/genstore.c b/src/deemon/compiler/asm/genstore.c index f10df1ea5..163ebb117 100644 --- a/src/deemon/compiler/asm/genstore.c +++ b/src/deemon/compiler/asm/genstore.c @@ -643,7 +643,6 @@ asm_set_cattr_symbol(struct symbol *__restrict sym, struct ast *__restrict value, struct ast *__restrict ddi_ast, unsigned int gflags) { -#if 1 struct symbol *class_sym, *this_sym; struct class_attribute *attr; int32_t symid; @@ -652,16 +651,12 @@ asm_set_cattr_symbol(struct symbol *__restrict sym, this_sym = sym->s_attr.a_this; attr = sym->s_attr.a_attr; SYMBOL_INPLACE_UNWIND_ALIAS(class_sym); - if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) { - /* TODO: Dedicated warning. */ - if (ASM_WARN(W_ASM_CANNOT_WRITE_SYMBOL, sym)) - goto err; - return ast_genasm(value, gflags & ASM_G_FPUSHRES); - } if (!this_sym) { set_class_attribute: if (attr->ca_flag & CLASS_ATTRIBUTE_FGETSET) { - /* Must invoke the getter callback. */ + if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) + goto fallback; + /* Must invoke the setter callback. */ if (PUSH_RESULT && ast_genasm(value, ASM_G_FPUSHRES)) goto err; if (asm_putddi(ddi_ast)) @@ -724,15 +719,20 @@ asm_set_cattr_symbol(struct symbol *__restrict sym, if (asm_putddi(ddi_ast)) goto err; } + if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) { + /* XXX: Assert that not already bound? */ + } if (asm_gdefcmember(attr->ca_addr)) goto err; /* [result], class */ return asm_gpop(); /* [result] */ } - /* The attribute must be accessed as virtual. */ + + /* Check if the attribute must be accessed as virtual. */ if unlikely(asm_check_thiscall(sym, ddi_ast)) goto err; SYMBOL_INPLACE_UNWIND_ALIAS(this_sym); if (!(attr->ca_flag & (CLASS_ATTRIBUTE_FPRIVATE | CLASS_ATTRIBUTE_FFINAL))) { +do_virtual_access: symid = asm_newconst((DeeObject *)attr->ca_name); if unlikely(symid < 0) goto err; @@ -767,8 +767,11 @@ asm_set_cattr_symbol(struct symbol *__restrict sym, } return asm_gsetattr_const((uint16_t)symid); /* [result] */ } + /* Regular, old member variable. */ if (attr->ca_flag & CLASS_ATTRIBUTE_FGETSET) { + if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) + goto fallback; /* Call the setter function of the attribute. */ if (PUSH_RESULT && ast_genasm(value, ASM_G_FPUSHRES)) goto err; /* value */ @@ -862,6 +865,8 @@ asm_set_cattr_symbol(struct symbol *__restrict sym, goto set_class_attribute; if (this_sym->s_type != SYMBOL_TYPE_THIS || SYMBOL_MUST_REFERENCE_THIS(this_sym)) { + if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) + goto do_virtual_access; /* There is no `setmemberi pop, pop, $, pop' instruction, so use fallback */ if (PUSH_RESULT) { if (ast_genasm(value, ASM_G_FPUSHRES)) goto err; /* result */ @@ -897,9 +902,13 @@ asm_set_cattr_symbol(struct symbol *__restrict sym, goto err; if (PUSH_RESULT && asm_gdup()) goto err; /* [result], value */ - if (asm_gsetmember_this_r((uint16_t)symid, attr->ca_addr)) + if ((attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) + ? asm_gsetmemberi_this_r((uint16_t)symid, attr->ca_addr) + : asm_gsetmember_this_r((uint16_t)symid, attr->ca_addr)) goto err; /* [result] */ } else { + if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) + goto do_virtual_access; /* There is no `setmemberi this, pop, $, pop' instruction, so use fallback */ if (PUSH_RESULT) { if (ast_genasm(value, ASM_G_FPUSHRES)) goto err; /* result */ @@ -923,9 +932,7 @@ asm_set_cattr_symbol(struct symbol *__restrict sym, goto err; /* [result] */ } return 0; -err: - return -1; -#else +fallback: if (ast_genasm(value, ASM_G_FPUSHRES)) goto err; if (asm_putddi(ddi_ast)) @@ -935,7 +942,6 @@ asm_set_cattr_symbol(struct symbol *__restrict sym, return asm_gpop_symbol(sym, ddi_ast); err: return -1; -#endif } diff --git a/src/deemon/compiler/asm/userdb.def b/src/deemon/compiler/asm/userdb.def index 84f9283bf..47a0ee409 100644 --- a/src/deemon/compiler/asm/userdb.def +++ b/src/deemon/compiler/asm/userdb.def @@ -1112,6 +1112,12 @@ INSTR("setmember", ( O4(ASM_SETMEMBER_THIS_R, FRELABS | FF0 | FF0_IMM, (THIS)(REF)(IMM16)(POP)) /* `setmember this, ref , $, pop' */ )) +/* CONSTRAINTS: `setmemberi , , ..., ' */ +INSTR("setmemberi", ( + O4(ASM_SETMEMBERI_THIS_R, FRELABS | FF0 | FF0_IMM, (THIS)(REF)(IMM8)(POP)) /* `setmemberi this, ref , $, pop' */ + O4(ASM_SETMEMBERI_THIS_R, FRELABS | FF0 | FF0_IMM, (THIS)(REF)(IMM16)(POP)) /* `setmemberi this, ref , $, pop' */ +)) + /* CONSTRAINTS: `reduce , ...' */ INSTR("reduce", ( O2(ASM_REDUCE_MIN, FNORMAL, (TOP)(MIN)) /* `reduce top, min' */ diff --git a/src/deemon/compiler/asm/util.c b/src/deemon/compiler/asm/util.c index 81ef88141..6857e4469 100644 --- a/src/deemon/compiler/asm/util.c +++ b/src/deemon/compiler/asm/util.c @@ -2081,12 +2081,12 @@ INTERN WUNUSED NONNULL((1, 2)) int this_sym = sym->s_attr.a_this; attr = sym->s_attr.a_attr; SYMBOL_INPLACE_UNWIND_ALIAS(class_sym); - if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) - break; /* TODO: Dedicated warning. */ if (!this_sym) { set_class_attribute: if (attr->ca_flag & CLASS_ATTRIBUTE_FGETSET) { - /* Must invoke the getter callback. */ + if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) + break; + /* Must invoke the setter callback. */ if (ASM_SYMBOL_MAY_REFERENCE(class_sym)) { symid = asm_rsymid(class_sym); if unlikely(symid < 0) @@ -2118,16 +2118,20 @@ INTERN WUNUSED NONNULL((1, 2)) int goto err; /* value, class */ if (asm_gswap()) goto err; /* class, value */ + if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) { + /* XXX: Assert that not already bound? */ + } if (asm_gdefcmember(attr->ca_addr)) goto err; /* class */ return asm_gpop(); /* - */ } - /* The attribute must be accessed as virtual. */ + /* Check if the attribute must be accessed as virtual. */ if unlikely(asm_check_thiscall(sym, warn_ast)) goto err; SYMBOL_INPLACE_UNWIND_ALIAS(this_sym); if (!(attr->ca_flag & (CLASS_ATTRIBUTE_FPRIVATE | CLASS_ATTRIBUTE_FFINAL))) { +do_virtual_access: symid = asm_newconst((DeeObject *)attr->ca_name); if unlikely(symid < 0) goto err; @@ -2143,7 +2147,9 @@ INTERN WUNUSED NONNULL((1, 2)) int /* Regular, old member variable. */ if (attr->ca_flag & CLASS_ATTRIBUTE_FGETSET) { - /* Call the delete function of the attribute. */ + if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) + break; + /* Call the setter function of the attribute. */ if (attr->ca_flag & CLASS_ATTRIBUTE_FCLASSMEM) { if (ASM_SYMBOL_MAY_REFERENCE(class_sym)) { symid = asm_rsymid(class_sym); @@ -2153,7 +2159,7 @@ INTERN WUNUSED NONNULL((1, 2)) int (CLASS_ATTRIBUTE_FGETSET | CLASS_ATTRIBUTE_FMETHOD) && this_sym->s_type == SYMBOL_TYPE_THIS && !SYMBOL_MUST_REFERENCE_THIS(this_sym)) { - /* Invoke the delete callback. */ + /* Invoke the setter callback. */ if (asm_gcallcmember_this_r((uint16_t)symid, attr->ca_addr + CLASS_GETSET_SET, 0)) goto err; goto pop_unused_result; @@ -2209,6 +2215,8 @@ INTERN WUNUSED NONNULL((1, 2)) int goto set_class_attribute; if (this_sym->s_type != SYMBOL_TYPE_THIS || SYMBOL_MUST_REFERENCE_THIS(this_sym)) { + if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) + goto do_virtual_access; /* There is no `setmemberi pop, pop, $, pop' instruction, so use fallback */ if (asm_gpush_symbol(this_sym, warn_ast)) goto err; /* value, this */ if (asm_gpush_symbol(class_sym, warn_ast)) @@ -2221,9 +2229,13 @@ INTERN WUNUSED NONNULL((1, 2)) int symid = asm_rsymid(class_sym); if unlikely(symid < 0) goto err; - if (asm_gsetmember_this_r((uint16_t)symid, attr->ca_addr)) + if ((attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) + ? asm_gsetmemberi_this_r((uint16_t)symid, attr->ca_addr) + : asm_gsetmember_this_r((uint16_t)symid, attr->ca_addr)) goto err; } else { + if (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY) + goto do_virtual_access; /* There is no `setmemberi this, pop, $, pop' instruction, so use fallback */ if (asm_gpush_symbol(class_sym, warn_ast)) goto err; /* value, class */ if (asm_gswap()) diff --git a/src/deemon/compiler/instrlen.c b/src/deemon/compiler/instrlen.c index 3398b2f08..335f86c8b 100644 --- a/src/deemon/compiler/instrlen.c +++ b/src/deemon/compiler/instrlen.c @@ -359,10 +359,10 @@ PRIVATE uint8_t const intr_len[256] = { /* 0xd5 */ 2, /* `ASM_CALLATTR_THIS_C_TUPLE': `push callattr this, const , pop...' */ /* 0xd6 */ 3, /* `ASM_CALLATTR_C_SEQ': `callattr top, const , [#]' */ /* 0xd7 */ 3, /* `ASM_CALLATTR_C_MAP': `callattr top, const , {#*2}' */ - /* 0xd8 */ 1, /* --- */ - /* 0xd9 */ 3, /* `ASM_GETMEMBER_THIS_R': `push getmember this, ref , $' */ - /* 0xda */ 3, /* `ASM_DELMEMBER_THIS_R': `delmember this, ref , $' */ - /* 0xdb */ 3, /* `ASM_SETMEMBER_THIS_R': `setmember this, ref , $, pop' */ + /* 0xd8 */ 3, /* `ASM_GETMEMBER_THIS_R': `push getmember this, ref , $' */ + /* 0xd9 */ 3, /* `ASM_DELMEMBER_THIS_R': `delmember this, ref , $' */ + /* 0xda */ 3, /* `ASM_SETMEMBER_THIS_R': `setmember this, ref , $, pop' */ + /* 0xdb */ 3, /* `ASM_SETMEMBERI_THIS_R': `setmemberi this, ref , $, pop' */ /* 0xdc */ 3, /* `ASM_BOUNDMEMBER_THIS_R': `push boundmember this, ref , $' */ /* 0xdd */ 4, /* `ASM_CALL_EXTERN': `push call extern :, #' */ /* 0xde */ 3, /* `ASM_CALL_GLOBAL': `push call global , #' */ @@ -618,10 +618,10 @@ PRIVATE uint8_t const intr_len_f0[256] = { /* 0xd5 */ 4, /* `ASM16_CALLATTR_THIS_C_TUPLE': `callattr this, const , pop' */ /* 0xd6 */ 5, /* `ASM16_CALLATTR_C_SEQ': `callattr top, const , [#]' */ /* 0xd7 */ 5, /* `ASM16_CALLATTR_C_MAP': `callattr top, const , {#*2}' */ - /* 0xd8 */ 2, /* --- */ - /* 0xd9 */ 6, /* `ASM16_GETMEMBER_THIS_R': `push getmember this, ref , $' */ - /* 0xda */ 6, /* `ASM16_DELMEMBER_THIS_R': `delmember this, ref , $' */ - /* 0xdb */ 6, /* `ASM16_SETMEMBER_THIS_R': `setmember this, ref , $, pop' */ + /* 0xd8 */ 6, /* `ASM16_GETMEMBER_THIS_R': `push getmember this, ref , $' */ + /* 0xd9 */ 6, /* `ASM16_DELMEMBER_THIS_R': `delmember this, ref , $' */ + /* 0xda */ 6, /* `ASM16_SETMEMBER_THIS_R': `setmember this, ref , $, pop' */ + /* 0xdb */ 6, /* `ASM16_SETMEMBERI_THIS_R': `setmemberi this, ref , $, pop' */ /* 0xdc */ 6, /* `ASM16_BOUNDMEMBER_THIS_R': `push boundmember this, ref , $' */ /* 0xdd */ 7, /* `ASM16_CALL_EXTERN': `push call extern :, #' */ /* 0xde */ 5, /* `ASM16_CALL_GLOBAL': `push call global , #' */ @@ -877,10 +877,10 @@ PRIVATE uint8_t const stack_effect[256] = { /* 0xd5 */ STACK_EFFECT(1, 1), /* `ASM_CALLATTR_THIS_C_TUPLE': `push callattr this, const , pop...' */ /* 0xd6 */ STACK_EFFECT_UNDEF, /* `ASM_CALLATTR_C_SEQ': `callattr top, const , [#]' */ /* 0xd7 */ STACK_EFFECT_UNDEF, /* `ASM_CALLATTR_C_MAP': `callattr top, const , {#*2}' */ - /* 0xd8 */ STACK_EFFECT_UNDEF, /* --- */ - /* 0xd9 */ STACK_EFFECT(0, 1), /* `ASM_GETMEMBER_THIS_R': `push getmember this, ref , $' */ - /* 0xda */ STACK_EFFECT(0, 0), /* `ASM_DELMEMBER_THIS_R': `delmember this, ref , $' */ - /* 0xdb */ STACK_EFFECT(1, 0), /* `ASM_SETMEMBER_THIS_R': `setmember this, ref , $, pop' */ + /* 0xd8 */ STACK_EFFECT(0, 1), /* `ASM_GETMEMBER_THIS_R': `push getmember this, ref , $' */ + /* 0xd9 */ STACK_EFFECT(0, 0), /* `ASM_DELMEMBER_THIS_R': `delmember this, ref , $' */ + /* 0xda */ STACK_EFFECT(1, 0), /* `ASM_SETMEMBER_THIS_R': `setmember this, ref , $, pop' */ + /* 0xdb */ STACK_EFFECT(1, 0), /* `ASM_SETMEMBERI_THIS_R': `setmemberi this, ref , $, pop' */ /* 0xdc */ STACK_EFFECT(0, 1), /* `ASM_BOUNDMEMBER_THIS_R': `push boundmember this, ref , $' */ /* 0xdd */ STACK_EFFECT_UNDEF, /* `ASM_CALL_EXTERN': `push call extern :, #' */ /* 0xde */ STACK_EFFECT_UNDEF, /* `ASM_CALL_GLOBAL': `push call global , #' */ @@ -1136,10 +1136,10 @@ PRIVATE uint8_t const stack_effect_f0[256] = { /* 0xd5 */ STACK_EFFECT(1, 1), /* `ASM16_CALLATTR_THIS_C_TUPLE': `callattr this, const , pop' */ /* 0xd6 */ STACK_EFFECT_UNDEF, /* `ASM16_CALLATTR_C_SEQ': `callattr top, const , [#]' */ /* 0xd7 */ STACK_EFFECT_UNDEF, /* `ASM16_CALLATTR_C_MAP': `callattr top, const , {#*2}' */ - /* 0xd8 */ STACK_EFFECT_UNDEF, /* --- */ - /* 0xd9 */ STACK_EFFECT(0, 1), /* `ASM16_GETMEMBER_THIS_R': `push getmember this, ref , $' */ - /* 0xda */ STACK_EFFECT(0, 0), /* `ASM16_DELMEMBER_THIS_R': `delmember this, ref , $' */ - /* 0xdb */ STACK_EFFECT(1, 0), /* `ASM16_SETMEMBER_THIS_R': `setmember this, ref , $, pop' */ + /* 0xd8 */ STACK_EFFECT(0, 1), /* `ASM16_GETMEMBER_THIS_R': `push getmember this, ref , $' */ + /* 0xd9 */ STACK_EFFECT(0, 0), /* `ASM16_DELMEMBER_THIS_R': `delmember this, ref , $' */ + /* 0xda */ STACK_EFFECT(1, 0), /* `ASM16_SETMEMBER_THIS_R': `setmember this, ref , $, pop' */ + /* 0xdb */ STACK_EFFECT(1, 0), /* `ASM16_SETMEMBERI_THIS_R': `setmemberi this, ref , $, pop' */ /* 0xdc */ STACK_EFFECT(0, 1), /* `ASM16_BOUNDMEMBER_THIS_R': `push boundmember this, ref , $' */ /* 0xdd */ STACK_EFFECT_UNDEF, /* `ASM16_CALL_EXTERN': `push call extern :, #' */ /* 0xde */ STACK_EFFECT_UNDEF, /* `ASM16_CALL_GLOBAL': `push call global , #' */ diff --git a/src/deemon/compiler/lexer/class.c b/src/deemon/compiler/lexer/class.c index e1355e153..666d37759 100644 --- a/src/deemon/compiler/lexer/class.c +++ b/src/deemon/compiler/lexer/class.c @@ -1686,11 +1686,13 @@ ast_parse_class_impl(uint16_t class_flags, struct TPPKeyword *name, uint16_t *p_usage_counter; struct ast_loc loc; bool modifiers_encountered; + bool varying_encountered; next_member: member_class = MEMBER_CLASS_AUTO; member_flags = default_member_flags; is_class_member = false; modifiers_encountered = false; + varying_encountered = false; /* Reset member tags. */ if unlikely(ast_tags_clear()) @@ -1756,6 +1758,16 @@ ast_parse_class_impl(uint16_t class_flags, struct TPPKeyword *name, if unlikely(yield() < 0) goto err; member_flags |= CLASS_ATTRIBUTE_FFINAL; + if (!varying_encountered) + member_flags |= CLASS_ATTRIBUTE_FREADONLY; + modifiers_encountered = true; + goto next_modifier; + + case KWD_varying: + if unlikely(yield() < 0) + goto err; + member_flags &= ~CLASS_ATTRIBUTE_FREADONLY; + varying_encountered = true; modifiers_encountered = true; goto next_modifier; @@ -2511,6 +2523,9 @@ ast_parse_class_impl(uint16_t class_flags, struct TPPKeyword *name, member_class != MEMBER_CLASS_GETSET && WARNAT(&loc, W_CLASS_MEMBER_TYPE_NOT_MATCHED, member_name)) goto err_decl; + if (varying_encountered && + WARNAT(&loc, W_CLASS_MEMBER_VARYING_IGNORED_FOR_PROPERTY, member_name)) + goto err_decl; if unlikely(yield() < 0) goto err_decl; @@ -2634,6 +2649,9 @@ ast_parse_class_impl(uint16_t class_flags, struct TPPKeyword *name, member_class != MEMBER_CLASS_METHOD && WARNAT(&loc, W_CLASS_MEMBER_TYPE_NOT_MATCHED, member_name)) goto err_decl; + if (varying_encountered && + WARNAT(&loc, W_CLASS_MEMBER_VARYING_IGNORED_FOR_FUNCTION, member_name)) + goto err_decl; /* Everything else is parsed as a member function. * NOTE: We mark such members are read-only because the intention is to never overwrite them. */ @@ -2753,6 +2771,7 @@ ast_parse_class_impl(uint16_t class_flags, struct TPPKeyword *name, goto err; } } break; + } } done_class_modal: diff --git a/src/deemon/execute/asm/exec.gas-386.S b/src/deemon/execute/asm/exec.gas-386.S index 8d6364571..84785609d 100644 --- a/src/deemon/execute/asm/exec.gas-386.S +++ b/src/deemon/execute/asm/exec.gas-386.S @@ -4394,6 +4394,40 @@ target_setmember_this_r: +target16_setmemberi_this_r: + /* `setmemberi this, ref , $, pop' */ + lodsw + movl %eax, %ecx + lodsw + jmp 1f +target_setmemberi_this_r: + /* `setmemberi this, ref , $, pop' */ + lodsb + movl %eax, %ecx + lodsb +1: +#ifndef NDEBUG + LOAD_CODE(%edx) + ASSERT_BELOW(%cx,co_refc(CODE_OR_EDX)) +#endif /* !NDEBUG */ + pushl TOP /* DeeInstance_SetMemberInitial:value */ + ADJUST_CFA_OFFSET(4) + pushl %eax /* DeeInstance_SetMemberInitial:index */ + ADJUST_CFA_OFFSET(4) + pushl FRAME_THIS /* DeeInstance_SetMemberInitial:self */ + ADJUST_CFA_OFFSET(4) + movl FRAME_FUNC, %edx + pushl fo_refv(%edx,%ecx,4) /* DeeInstance_SetMemberInitial:tp_self */ + ADJUST_CFA_OFFSET(4) + call fSYM(DeeInstance_SetMemberInitial,16) + ADJUST_CFA_OFFSET(-16) + testl %eax, %eax + jnz .except /* DeeInstance_SetMemberInitial can throw errors... */ + sp_popref + jmp .disp + + + target16_getmember: /* `getmember top, pop, $' */ lodsw @@ -8964,6 +8998,9 @@ prefix_target_delmember_this_r = prefix_invop .ifndef prefix_target_setmember_this_r prefix_target_setmember_this_r = prefix_invop .endif +.ifndef prefix_target_setmemberi_this_r +prefix_target_setmemberi_this_r = prefix_invop +.endif .ifndef prefix_target_boundmember_this_r prefix_target_boundmember_this_r = prefix_invop .endif @@ -9405,6 +9442,9 @@ prefix_target16_delmember_this_r = prefix_invop .ifndef prefix_target16_setmember_this_r prefix_target16_setmember_this_r = prefix_invop .endif +.ifndef prefix_target16_setmemberi_this_r +prefix_target16_setmemberi_this_r = prefix_invop +.endif .ifndef prefix_target16_boundmember_this_r prefix_target16_boundmember_this_r = prefix_invop .endif @@ -10035,6 +10075,9 @@ repr_target_delmember_this_r = repr_invop .ifndef repr_target_setmember_this_r repr_target_setmember_this_r = repr_invop .endif +.ifndef repr_target_setmemberi_this_r +repr_target_setmemberi_this_r = repr_invop +.endif .ifndef repr_target_boundmember_this_r repr_target_boundmember_this_r = repr_invop .endif @@ -10303,10 +10346,10 @@ inst_table: .long target_callattr_this_c_tuple /* 0xd5 : `push callattr this, const , pop...' */ .long target_callattr_c_seq /* 0xd6 : `callattr top, const , [#]' */ .long target_callattr_c_map /* 0xd7 : `callattr top, const , {#*2}' */ - .long invop - .long target_getmember_this_r /* 0xd9 : `push getmember this, ref , $' */ - .long target_delmember_this_r /* 0xda : `delmember this, ref , $' */ - .long target_setmember_this_r /* 0xdb : `setmember this, ref , $, pop' */ + .long target_getmember_this_r /* 0xd8 : `push getmember this, ref , $' */ + .long target_delmember_this_r /* 0xd9 : `delmember this, ref , $' */ + .long target_setmember_this_r /* 0xda : `setmember this, ref , $, pop' */ + .long target_setmemberi_this_r /* 0xdb : `setmemberi this, ref , $, pop' */ .long target_boundmember_this_r /* 0xdc : `push boundmember this, ref , $' */ .long target_call_extern /* 0xdd : `push call extern :, #' */ .long target_call_global /* 0xde : `push call global , #' */ @@ -10561,10 +10604,10 @@ inst_table_f0: .long target16_callattr_this_c_tuple /* 0xd5 : `callattr this, const , pop' */ .long target16_callattr_c_seq /* 0xd6 : `callattr top, const , [#]' */ .long target16_callattr_c_map /* 0xd7 : `callattr top, const , {#*2}' */ - .long invop - .long target16_getmember_this_r /* 0xd9 : `push getmember this, ref , $' */ - .long target16_delmember_this_r /* 0xda : `delmember this, ref , $' */ - .long target16_setmember_this_r /* 0xdb : `setmember this, ref , $, pop' */ + .long target16_getmember_this_r /* 0xd8 : `push getmember this, ref , $' */ + .long target16_delmember_this_r /* 0xd9 : `delmember this, ref , $' */ + .long target16_setmember_this_r /* 0xda : `setmember this, ref , $, pop' */ + .long target16_setmemberi_this_r /* 0xdb : `setmemberi this, ref , $, pop' */ .long target16_boundmember_this_r /* 0xdc : `push boundmember this, ref , $' */ .long target16_call_extern /* 0xdd : `push call extern :, #' */ .long target16_call_global /* 0xde : `push call global , #' */ @@ -10819,10 +10862,10 @@ prefix_inst_table: .long prefix_target_callattr_this_c_tuple /* 0xd5 : `push callattr this, const , pop...' */ .long prefix_target_callattr_c_seq /* 0xd6 : `callattr top, const , [#]' */ .long prefix_target_callattr_c_map /* 0xd7 : `callattr top, const , {#*2}' */ - .long prefix_invop - .long prefix_target_getmember_this_r /* 0xd9 : `push getmember this, ref , $' */ - .long prefix_target_delmember_this_r /* 0xda : `delmember this, ref , $' */ - .long prefix_target_setmember_this_r /* 0xdb : `setmember this, ref , $, pop' */ + .long prefix_target_getmember_this_r /* 0xd8 : `push getmember this, ref , $' */ + .long prefix_target_delmember_this_r /* 0xd9 : `delmember this, ref , $' */ + .long prefix_target_setmember_this_r /* 0xda : `setmember this, ref , $, pop' */ + .long prefix_target_setmemberi_this_r /* 0xdb : `setmemberi this, ref , $, pop' */ .long prefix_target_boundmember_this_r /* 0xdc : `push boundmember this, ref , $' */ .long prefix_target_call_extern /* 0xdd : `push call extern :, #' */ .long prefix_target_call_global /* 0xde : `push call global , #' */ @@ -11077,10 +11120,10 @@ prefix_inst_table_f0: .long prefix_target16_callattr_this_c_tuple /* 0xd5 : `callattr this, const , pop' */ .long prefix_target16_callattr_c_seq /* 0xd6 : `callattr top, const , [#]' */ .long prefix_target16_callattr_c_map /* 0xd7 : `callattr top, const , {#*2}' */ - .long prefix_invop - .long prefix_target16_getmember_this_r /* 0xd9 : `push getmember this, ref , $' */ - .long prefix_target16_delmember_this_r /* 0xda : `delmember this, ref , $' */ - .long prefix_target16_setmember_this_r /* 0xdb : `setmember this, ref , $, pop' */ + .long prefix_target16_getmember_this_r /* 0xd8 : `push getmember this, ref , $' */ + .long prefix_target16_delmember_this_r /* 0xd9 : `delmember this, ref , $' */ + .long prefix_target16_setmember_this_r /* 0xda : `setmember this, ref , $, pop' */ + .long prefix_target16_setmemberi_this_r /* 0xdb : `setmemberi this, ref , $, pop' */ .long prefix_target16_boundmember_this_r /* 0xdc : `push boundmember this, ref , $' */ .long prefix_target16_call_extern /* 0xdd : `push call extern :, #' */ .long prefix_target16_call_global /* 0xde : `push call global , #' */ @@ -11335,10 +11378,10 @@ repr_inst_table: .long repr_target_callattr_this_c_tuple /* 0xd5 : `push callattr this, const , pop...' */ .long repr_target_callattr_c_seq /* 0xd6 : `callattr top, const , [#]' */ .long repr_target_callattr_c_map /* 0xd7 : `callattr top, const , {#*2}' */ - .long repr_invop - .long repr_target_getmember_this_r /* 0xd9 : `push getmember this, ref , $' */ - .long repr_target_delmember_this_r /* 0xda : `delmember this, ref , $' */ - .long repr_target_setmember_this_r /* 0xdb : `setmember this, ref , $, pop' */ + .long repr_target_getmember_this_r /* 0xd8 : `push getmember this, ref , $' */ + .long repr_target_delmember_this_r /* 0xd9 : `delmember this, ref , $' */ + .long repr_target_setmember_this_r /* 0xda : `setmember this, ref , $, pop' */ + .long repr_target_setmemberi_this_r /* 0xdb : `setmemberi this, ref , $, pop' */ .long repr_target_boundmember_this_r /* 0xdc : `push boundmember this, ref , $' */ .long repr_target_call_extern /* 0xdd : `push call extern :, #' */ .long repr_target_call_global /* 0xde : `push call global , #' */ diff --git a/src/deemon/execute/code-exec-targets.c.inl b/src/deemon/execute/code-exec-targets.c.inl index 1d1e0bfea..4afa4a11f 100644 --- a/src/deemon/execute/code-exec-targets.c.inl +++ b/src/deemon/execute/code-exec-targets.c.inl @@ -277,10 +277,10 @@ static void *const basic_targets[256] = { /* 0xd5 */ &&target_ASM_CALLATTR_THIS_C_TUPLE, /* 0xd6 */ &&target_ASM_CALLATTR_C_SEQ, /* 0xd7 */ &&target_ASM_CALLATTR_C_MAP, - /* 0xd8 */ &&unknown_instruction, - /* 0xd9 */ &&target_ASM_GETMEMBER_THIS_R, - /* 0xda */ &&target_ASM_DELMEMBER_THIS_R, - /* 0xdb */ &&target_ASM_SETMEMBER_THIS_R, + /* 0xd8 */ &&target_ASM_GETMEMBER_THIS_R, + /* 0xd9 */ &&target_ASM_DELMEMBER_THIS_R, + /* 0xda */ &&target_ASM_SETMEMBER_THIS_R, + /* 0xdb */ &&target_ASM_SETMEMBERI_THIS_R, /* 0xdc */ &&target_ASM_BOUNDMEMBER_THIS_R, /* 0xdd */ &&target_ASM_CALL_EXTERN, /* 0xde */ &&target_ASM_CALL_GLOBAL, @@ -535,10 +535,10 @@ static void *const f0_targets[256] = { /* 0xd5 */ &&target_ASM16_CALLATTR_THIS_C_TUPLE, /* 0xd6 */ &&target_ASM16_CALLATTR_C_SEQ, /* 0xd7 */ &&target_ASM16_CALLATTR_C_MAP, - /* 0xd8 */ &&unknown_instruction, - /* 0xd9 */ &&target_ASM16_GETMEMBER_THIS_R, - /* 0xda */ &&target_ASM16_DELMEMBER_THIS_R, - /* 0xdb */ &&target_ASM16_SETMEMBER_THIS_R, + /* 0xd8 */ &&target_ASM16_GETMEMBER_THIS_R, + /* 0xd9 */ &&target_ASM16_DELMEMBER_THIS_R, + /* 0xda */ &&target_ASM16_SETMEMBER_THIS_R, + /* 0xdb */ &&target_ASM16_SETMEMBERI_THIS_R, /* 0xdc */ &&target_ASM16_BOUNDMEMBER_THIS_R, /* 0xdd */ &&target_ASM16_CALL_EXTERN, /* 0xde */ &&target_ASM16_CALL_GLOBAL, diff --git a/src/deemon/execute/code-exec.c.inl b/src/deemon/execute/code-exec.c.inl index d134b291f..49ed8d5d8 100644 --- a/src/deemon/execute/code-exec.c.inl +++ b/src/deemon/execute/code-exec.c.inl @@ -3760,6 +3760,22 @@ do_setmember_r: DISPATCH(); } + TARGET(ASM_SETMEMBERI_THIS_R, -1, +0) { + imm_val = READ_imm8(); + imm_val2 = READ_imm8(); +do_setmemberi_r: + ASSERT_THISCALL(); + ASSERT_REFimm(); +#ifdef EXEC_SAFE + if unlikely(DeeInstance_SetMemberInitialSafe((DeeTypeObject *)REFimm, THIS, imm_val2, TOP)) + HANDLE_EXCEPT(); +#else /* EXEC_SAFE */ + DeeInstance_SetMemberInitial((DeeTypeObject *)REFimm, THIS, + imm_val2, TOP); +#endif /* !EXEC_SAFE */ + POPREF(); + DISPATCH(); + } { DREF DeeObject *call_object, *call_result; @@ -5020,6 +5036,12 @@ do_setmember_this: goto do_setmember_r; } + TARGET(ASM16_SETMEMBERI_THIS_R, -1, +0) { + imm_val = READ_imm16(); + imm_val2 = READ_imm16(); + goto do_setmemberi_r; + } + TARGET(ASM16_BOUNDMEMBER_THIS_R, -1, +0) { imm_val = READ_imm16(); imm_val2 = READ_imm16(); diff --git a/src/deemon/linker-scripts/link-deemon-gcc-i386-cygwin.def b/src/deemon/linker-scripts/link-deemon-gcc-i386-cygwin.def index 8f0535449..b39749909 100644 --- a/src/deemon/linker-scripts/link-deemon-gcc-i386-cygwin.def +++ b/src/deemon/linker-scripts/link-deemon-gcc-i386-cygwin.def @@ -337,6 +337,8 @@ EXPORTS _DeeInstance_GetMemberSafe@12=DeeInstance_GetMemberSafe@12 _DeeInstance_SetAttribute@20=DeeInstance_SetAttribute@20 _DeeInstance_SetMember@16=DeeInstance_SetMember@16 + _DeeInstance_SetMemberInitial@16=DeeInstance_SetMemberInitial@16 + _DeeInstance_SetMemberInitialSafe@16=DeeInstance_SetMemberInitialSafe@16 _DeeInstance_SetMemberSafe@16=DeeInstance_SetMemberSafe@16 _DeeInstance_VCallAttributef@24=DeeInstance_VCallAttributef@24 _DeeInt_AsBytes@20=DeeInt_AsBytes@20 diff --git a/src/deemon/linker-scripts/link-deemon-msvc-i386-win32.def b/src/deemon/linker-scripts/link-deemon-msvc-i386-win32.def index 30811e851..837f316fe 100644 --- a/src/deemon/linker-scripts/link-deemon-msvc-i386-win32.def +++ b/src/deemon/linker-scripts/link-deemon-msvc-i386-win32.def @@ -337,6 +337,8 @@ EXPORTS DeeInstance_GetMemberSafe@12=_DeeInstance_GetMemberSafe@12 DeeInstance_SetAttribute@20=_DeeInstance_SetAttribute@20 DeeInstance_SetMember@16=_DeeInstance_SetMember@16 + DeeInstance_SetMemberInitial@16=_DeeInstance_SetMemberInitial@16 + DeeInstance_SetMemberInitialSafe@16=_DeeInstance_SetMemberInitialSafe@16 DeeInstance_SetMemberSafe@16=_DeeInstance_SetMemberSafe@16 DeeInstance_VCallAttributef@24=_DeeInstance_VCallAttributef@24 DeeInt_AsBytes@20=_DeeInt_AsBytes@20 diff --git a/src/deemon/objects/class.c b/src/deemon/objects/class.c index 912e6aa92..549bf8c04 100644 --- a/src/deemon/objects/class.c +++ b/src/deemon/objects/class.c @@ -2974,11 +2974,11 @@ find_next_attribute(DeeClassDescriptorObject *__restrict self, return result; } -LOCAL WUNUSED NONNULL((1, 2, 3, 4)) int DCALL +LOCAL WUNUSED NONNULL((1, 2, 3)) int DCALL instance_autoload_members(DeeTypeObject *tp_self, struct class_desc *__restrict desc, struct instance_desc *__restrict instance, - DeeObject *self, size_t argc, DeeObject *const *argv) { + size_t argc, DeeObject *const *argv) { size_t i; uint16_t next_table_index = 0; for (i = 0; i < argc; ++i) { @@ -2986,8 +2986,7 @@ instance_autoload_members(DeeTypeObject *tp_self, at = find_next_attribute(desc->cd_desc, &next_table_index); if unlikely(!at) goto err_argc; - if unlikely(DeeInstance_SetAttribute(desc, instance, - self, at, argv[i])) + if unlikely(DeeInstance_SetBasicAttribute(desc, instance, at, argv[i])) goto err; } return 0; @@ -2997,16 +2996,15 @@ instance_autoload_members(DeeTypeObject *tp_self, return -1; } -PRIVATE WUNUSED NONNULL((1, 2, 3, 4)) int DCALL +PRIVATE WUNUSED NONNULL((1, 2, 3)) int DCALL instance_autoload_members_kw(DeeTypeObject *tp_self, struct class_desc *__restrict desc, struct instance_desc *__restrict instance, - DeeObject *self, size_t argc, - DeeObject *const *argv, DeeObject *kw) { + size_t argc, DeeObject *const *argv, DeeObject *kw) { uint16_t next_table_index; DREF DeeObject *iterator, *elem, *data[2]; if (!kw) - return instance_autoload_members(tp_self, desc, instance, self, argc, argv); + return instance_autoload_members(tp_self, desc, instance, argc, argv); next_table_index = 0; if (DeeKwds_Check(kw)) { DeeKwdsObject *kwds = (DeeKwdsObject *)kw; @@ -3024,7 +3022,7 @@ instance_autoload_members_kw(DeeTypeObject *tp_self, err_invalid_argc(tp_self->tp_name, argc, 0, i); goto err; } - if unlikely(DeeInstance_SetAttribute(desc, instance, self, at, argv[i])) + if unlikely(DeeInstance_SetBasicAttribute(desc, instance, at, argv[i])) goto err; } for (i = 0; i <= kwds->kw_mask; ++i) { @@ -3045,8 +3043,8 @@ instance_autoload_members_kw(DeeTypeObject *tp_self, err_keywords_shadows_positional(DeeString_STR(kwds->kw_map[i].ke_name)); goto err; } - if unlikely(DeeInstance_SetAttribute(desc, instance, self, at, - kw_argv[kwds->kw_map[i].ke_index])) + if unlikely(DeeInstance_SetBasicAttribute(desc, instance, at, + kw_argv[kwds->kw_map[i].ke_index])) goto err; } } else { @@ -3059,8 +3057,7 @@ instance_autoload_members_kw(DeeTypeObject *tp_self, err_invalid_argc(tp_self->tp_name, argc, 0, i); goto err; } - if unlikely(DeeInstance_SetAttribute(desc, instance, - self, at, argv[i])) + if unlikely(DeeInstance_SetBasicAttribute(desc, instance, at, argv[i])) goto err; } iterator = DeeObject_IterSelf((DeeObject *)kw); @@ -3085,8 +3082,7 @@ instance_autoload_members_kw(DeeTypeObject *tp_self, err_keywords_shadows_positional(DeeString_STR(data[0])); goto err_iter_data; } - if unlikely(DeeInstance_SetAttribute(desc, instance, - self, at, data[1])) + if unlikely(DeeInstance_SetBasicAttribute(desc, instance, at, data[1])) goto err_iter_data; Dee_Decref(data[1]); Dee_Decref(data[0]); @@ -3208,8 +3204,7 @@ instance_auto_tinit(DeeTypeObject *tp_self, DeeObject *__restrict self, Dee_Decref(result); /* Auto-initialize members. */ - if unlikely(instance_autoload_members(tp_self, desc, instance, - self, argc, argv)) + if unlikely(instance_autoload_members(tp_self, desc, instance, argc, argv)) goto err_super; Dee_Decref(func); return 0; @@ -3263,8 +3258,7 @@ instance_auto_tinitkw(DeeTypeObject *tp_self, Dee_Decref(result); /* Auto-initialize members. */ - if unlikely(instance_autoload_members_kw(tp_self, desc, instance, - self, argc, argv, kw)) + if unlikely(instance_autoload_members_kw(tp_self, desc, instance, argc, argv, kw)) goto err_super; Dee_Decref(func); return 0; @@ -3303,8 +3297,7 @@ instance_builtin_auto_tinit(DeeTypeObject *tp_self, DeeObject *__restrict self, } /* Auto-initialize members. */ - if unlikely(instance_autoload_members(tp_self, desc, instance, - self, argc, argv)) + if unlikely(instance_autoload_members(tp_self, desc, instance, argc, argv)) goto err_super; return 0; err_super: @@ -3341,8 +3334,7 @@ instance_builtin_auto_tinitkw(DeeTypeObject *tp_self, } /* Auto-initialize members. */ - if unlikely(instance_autoload_members_kw(tp_self, desc, instance, - self, argc, argv, kw)) + if unlikely(instance_autoload_members_kw(tp_self, desc, instance, argc, argv, kw)) goto err_super; return 0; err_super: @@ -3408,8 +3400,7 @@ instance_auto_nobase_tinit(DeeTypeObject *tp_self, DeeObject *__restrict self, Dee_Decref(result); /* Auto-initialize members. */ - if unlikely(instance_autoload_members(tp_self, desc, instance, - self, argc, argv)) + if unlikely(instance_autoload_members(tp_self, desc, instance, argc, argv)) goto err_members; Dee_Decref(func); return 0; @@ -3448,8 +3439,7 @@ instance_auto_nobase_tinitkw(DeeTypeObject *tp_self, Dee_Decref(result); /* Auto-initialize members. */ - if unlikely(instance_autoload_members_kw(tp_self, desc, instance, - self, argc, argv, kw)) + if unlikely(instance_autoload_members_kw(tp_self, desc, instance, argc, argv, kw)) goto err_members; Dee_Decref(func); return 0; @@ -3473,8 +3463,7 @@ instance_builtin_auto_nobase_tinit(DeeTypeObject *tp_self, DeeObject *__restrict sizeof(DREF DeeObject *)); /* Auto-initialize members. */ - if unlikely(instance_autoload_members(tp_self, desc, instance, - self, argc, argv)) + if unlikely(instance_autoload_members(tp_self, desc, instance, argc, argv)) goto err_members; return 0; err_members: @@ -3497,9 +3486,8 @@ instance_builtin_auto_nobase_tinitkw(DeeTypeObject *tp_self, sizeof(DREF DeeObject *)); /* Auto-initialize members. */ - if unlikely(instance_autoload_members_kw(tp_self, desc, instance, - self, argc, argv, kw)) - goto err_members; + if unlikely(instance_autoload_members_kw(tp_self, desc, instance, argc, argv, kw)) + goto err_members; return 0; err_members: instance_clear_members(instance, desc->cd_desc->cd_imemb_size); diff --git a/src/deemon/objects/class_desc.c b/src/deemon/objects/class_desc.c index 163d849ee..9bca247ed 100644 --- a/src/deemon/objects/class_desc.c +++ b/src/deemon/objects/class_desc.c @@ -4333,35 +4333,38 @@ DeeInstance_SetAttribute(struct class_desc *__restrict desc, } INTERN WUNUSED NONNULL((1, 2, 3, 4)) int -(DCALL DeeInstance_SetBasicAttribute_)(struct class_desc *__restrict desc, - struct instance_desc *__restrict self, - struct class_attribute const *__restrict attr, - DeeObject *__restrict value) { +(DCALL DeeInstance_SetBasicAttribute)(struct class_desc *__restrict desc, + struct instance_desc *__restrict self, + struct class_attribute const *__restrict attr, + DeeObject *__restrict value) { DREF DeeObject *old_value; if (attr->ca_flag & CLASS_ATTRIBUTE_FCLASSMEM) self = class_desc_as_instance(desc); if (attr->ca_flag & CLASS_ATTRIBUTE_FGETSET) - return 2; /* Not a basic attribute. */ + goto illegal; /* Not a basic attribute. */ /* Simply override the field in the attr table. */ + Dee_Incref(value); Dee_instance_desc_lock_write(self); old_value = self->id_vtab[attr->ca_addr]; +#if 0 /* Special case: `this = default' constructors are allowed to override default initializers of "final" members! */ if (old_value && (attr->ca_flag & CLASS_ATTRIBUTE_FREADONLY)) { Dee_instance_desc_lock_endwrite(self); - goto illegal; /* readonly fields can only be set once. */ - } else { - Dee_Incref(value); - self->id_vtab[attr->ca_addr] = value; + goto illegal; + } +#endif + self->id_vtab[attr->ca_addr] = value; + if unlikely(old_value) { + Dee_instance_desc_lock_endwrite(self); + Dee_Decref_unlikely(old_value); + return 0; } Dee_instance_desc_lock_endwrite(self); - - /* Drop a reference from the old value. */ - Dee_XDecref(old_value); return 0; illegal: return err_cant_access_attribute_string_c(desc, - DeeString_STR(attr->ca_name), - ATTR_ACCESS_SET); + DeeString_STR(attr->ca_name), + ATTR_ACCESS_SET); } @@ -4655,6 +4658,57 @@ PUBLIC NONNULL((1, 2, 4)) void Dee_XDecref(old_value); } +PRIVATE ATTR_COLD NONNULL((1)) int +(DCALL err_readonly_member_already_bound)(struct class_desc *__restrict desc, + uint16_t addr) { + /* Check if we can find the proper member so we can pass its name. */ + size_t i; + char const *name = "??" "?"; + for (i = 0; i <= desc->cd_desc->cd_iattr_mask; ++i) { + struct class_attribute *attr; + attr = &desc->cd_desc->cd_iattr_list[i]; + if (!attr->ca_name) + continue; + if (addr < attr->ca_addr) + continue; + if (addr >= (attr->ca_addr + ((attr->ca_flag & CLASS_ATTRIBUTE_FGETSET) ? 3 : 1))) + continue; + if (attr->ca_flag & CLASS_ATTRIBUTE_FCLASSMEM) + continue; + name = DeeString_STR(attr->ca_name); + break; + } + + /* Throw the error. */ + return err_cant_access_attribute_string_c(desc, name, ATTR_ACCESS_SET); +} + +PUBLIC WUNUSED NONNULL((1, 2, 4)) int +(DCALL DeeInstance_SetMemberInitial)(/*Class*/ DeeTypeObject *tp_self, + /*Instance*/ DeeObject *self, + uint16_t addr, DeeObject *value) { + struct class_desc *desc; + struct instance_desc *inst; + ASSERT_OBJECT_TYPE(tp_self, &DeeType_Type); + ASSERT(DeeType_IsClass(tp_self)); + ASSERT_OBJECT_TYPE_A(self, tp_self); + desc = DeeClass_DESC(tp_self); + ASSERT(addr < desc->cd_desc->cd_imemb_size); + inst = DeeInstance_DESC(desc, self); + + /* Lock and extract the member. */ + Dee_Incref(value); + Dee_instance_desc_lock_write(inst); + if unlikely(inst->id_vtab[addr]) { + Dee_instance_desc_lock_endwrite(inst); + Dee_DecrefNokill(value); + return err_readonly_member_already_bound(desc, addr); + } + inst->id_vtab[addr] = value; + Dee_instance_desc_lock_endwrite(inst); + return 0; +} + PUBLIC WUNUSED NONNULL((1, 2)) DREF DeeObject * (DCALL DeeInstance_GetMemberSafe)(DeeTypeObject *tp_self, @@ -4742,6 +4796,27 @@ PUBLIC WUNUSED NONNULL((1, 2, 4)) int return -1; } +PUBLIC WUNUSED NONNULL((1, 2, 4)) int +(DCALL DeeInstance_SetMemberInitialSafe)(DeeTypeObject *tp_self, + DeeObject *self, + uint16_t addr, DeeObject *value) { + if (DeeObject_AssertType(tp_self, &DeeType_Type)) + goto err; + if (DeeObject_AssertType(self, tp_self)) + goto err; + if (!DeeType_IsClass(tp_self)) + goto err_req_class; + if (addr >= DeeClass_DESC(tp_self)->cd_desc->cd_imemb_size) + goto err_bad_index; + return DeeInstance_SetMemberInitial(tp_self, self, addr, value); +err_bad_index: + return err_invalid_instance_addr(tp_self, self, addr); +err_req_class: + return err_requires_class(tp_self); +err: + return -1; +} + /* Class member access (by addr) */ PUBLIC NONNULL((1, 3)) void (DCALL DeeClass_SetMember)(DeeTypeObject *self, diff --git a/src/dex/_hostasm/generator-deemon.c b/src/dex/_hostasm/generator-deemon.c index 7df26b5ff..12f1cc90a 100644 --- a/src/dex/_hostasm/generator-deemon.c +++ b/src/dex/_hostasm/generator-deemon.c @@ -2342,6 +2342,21 @@ fg_geninstr(struct fungen *__restrict self, return fg_vpop_imember(self, addr, FG_CIMEMBER_F_NORMAL); } break; + //TODO:TARGET(ASM_SETMEMBERI_THIS_R) { + //TODO: uint16_t type_rid, addr; + //TODO: type_rid = instr[1]; + //TODO: addr = instr[2]; + //TODO: __IF0 { + //TODO:TARGET(ASM16_SETMEMBERI_THIS_R) + //TODO: type_rid = UNALIGNED_GETLE16(instr + 2); + //TODO: addr = UNALIGNED_GETLE16(instr + 4); + //TODO: } + //TODO: DO(fg_vpush_this(self, instr)); /* value, this */ + //TODO: DO(fg_vpush_rid(self, type_rid)); /* value, this, type */ + //TODO: DO(fg_vlrot(self, 3)); /* this, type, value */ + //TODO: return fg_vpop_imember(self, addr, FG_CIMEMBER_F_NORMAL); + //TODO:} break; + TARGET(ASM_BOUNDMEMBER_THIS_R) { uint16_t type_rid, addr; type_rid = instr[1]; diff --git a/src/dex/_hostasm/loader-locuse.c b/src/dex/_hostasm/loader-locuse.c index c631b7d8e..f84ad3ad0 100644 --- a/src/dex/_hostasm/loader-locuse.c +++ b/src/dex/_hostasm/loader-locuse.c @@ -160,6 +160,8 @@ asm_get_locuse(struct function_assembler *__restrict self, case ASM16_DELMEMBER_THIS_R: case ASM_SETMEMBER_THIS_R: case ASM16_SETMEMBER_THIS_R: + case ASM_SETMEMBERI_THIS_R: + case ASM16_SETMEMBERI_THIS_R: case ASM_BOUNDMEMBER_THIS_R: case ASM16_BOUNDMEMBER_THIS_R: case ASM_PUSH_THIS: diff --git a/src/dex/disassembler/printinstr.c b/src/dex/disassembler/printinstr.c index 761ad5097..f347f849a 100644 --- a/src/dex/disassembler/printinstr.c +++ b/src/dex/disassembler/printinstr.c @@ -394,10 +394,10 @@ PRIVATE char const mnemonic_names[256][31] = { /* 0xd5 */ "push callattr this, ", /* `ASM_CALLATTR_THIS_C_TUPLE' */ /* 0xd6 */ "callattr top, ", /* `ASM_CALLATTR_C_SEQ' */ /* 0xd7 */ "callattr top, ", /* `ASM_CALLATTR_C_MAP' */ - /* 0xd8 */ UNKNOWN_MNEMONIC, /* --- */ - /* 0xd9 */ "push getmember this, ", /* `ASM_GETMEMBER_THIS_R' */ - /* 0xda */ "delmember this, ", /* `ASM_DELMEMBER_THIS_R' */ - /* 0xdb */ "setmember this, ", /* `ASM_SETMEMBER_THIS_R' */ + /* 0xd8 */ "push getmember this, ", /* `ASM_GETMEMBER_THIS_R' */ + /* 0xd9 */ "delmember this, ", /* `ASM_DELMEMBER_THIS_R' */ + /* 0xda */ "setmember this, ", /* `ASM_SETMEMBER_THIS_R' */ + /* 0xdb */ "setmemberi this, ", /* `ASM_SETMEMBERI_THIS_R' */ /* 0xdc */ "push boundmember this, ", /* `ASM_BOUNDMEMBER_THIS_R' */ /* 0xdd */ "push call ", /* `ASM_CALL_EXTERN' */ /* 0xde */ "push call ", /* `ASM_CALL_GLOBAL' */ @@ -653,10 +653,10 @@ PRIVATE char const mnemonic_names_f0[256][32] = { /* 0xf0d5 */ "callattr this, ", /* `ASM16_CALLATTR_THIS_C_TUPLE' */ /* 0xf0d6 */ "callattr top, ", /* `ASM16_CALLATTR_C_SEQ' */ /* 0xf0d7 */ "callattr top, ", /* `ASM16_CALLATTR_C_MAP' */ - /* 0xf0d8 */ UNKNOWN_MNEMONIC, /* --- */ - /* 0xf0d9 */ "push getmember this, ", /* `ASM16_GETMEMBER_THIS_R' */ - /* 0xf0da */ "delmember this, ", /* `ASM16_DELMEMBER_THIS_R' */ - /* 0xf0db */ "setmember this, ", /* `ASM16_SETMEMBER_THIS_R' */ + /* 0xf0d8 */ "push getmember this, ", /* `ASM16_GETMEMBER_THIS_R' */ + /* 0xf0d9 */ "delmember this, ", /* `ASM16_DELMEMBER_THIS_R' */ + /* 0xf0da */ "setmember this, ", /* `ASM16_SETMEMBER_THIS_R' */ + /* 0xf0db */ "setmemberi this, ", /* `ASM16_SETMEMBERI_THIS_R' */ /* 0xf0dc */ "push boundmember this, ", /* `ASM16_BOUNDMEMBER_THIS_R' */ /* 0xf0dd */ "push call ", /* `ASM16_CALL_EXTERN' */ /* 0xf0de */ "push call ", /* `ASM16_CALL_GLOBAL' */ @@ -2297,6 +2297,7 @@ libdisasm_printinstr(dformatprinter printer, void *arg, case ASM16_BOUNDMEMBER_THIS_R: case ASM16_DELMEMBER_THIS_R: case ASM16_SETMEMBER_THIS_R: + case ASM16_SETMEMBERI_THIS_R: case ASM16_GETCMEMBER_R: case ASM16_CALLCMEMBER_THIS_R: imm = READ_imm16(iter); @@ -2308,6 +2309,7 @@ libdisasm_printinstr(dformatprinter printer, void *arg, case ASM_BOUNDMEMBER_THIS_R: case ASM_DELMEMBER_THIS_R: case ASM_SETMEMBER_THIS_R: + case ASM_SETMEMBERI_THIS_R: case ASM_GETCMEMBER_R: case ASM_CALLCMEMBER_THIS_R: imm = READ_imm8(iter); @@ -2319,6 +2321,7 @@ libdisasm_printinstr(dformatprinter printer, void *arg, case ASM_BOUNDMEMBER_THIS_R: case ASM_DELMEMBER_THIS_R: case ASM_SETMEMBER_THIS_R: + case ASM_SETMEMBERI_THIS_R: case ASM_GETCMEMBER_R: case ASM_CALLCMEMBER_THIS_R: PRINT(", " PREFIX_INTEGERAL); @@ -2326,7 +2329,7 @@ libdisasm_printinstr(dformatprinter printer, void *arg, INVOKE(libdisasm_printmembername(printer, arg, imm, imm2, code, flags, opcode == ASM_GETCMEMBER_R || opcode == ASM_CALLCMEMBER_THIS_R)); - if (opcode == ASM_SETMEMBER_THIS_R) + if (opcode == ASM_SETMEMBER_THIS_R || opcode == ASM_SETMEMBERI_THIS_R) PRINT(", pop"); if (opcode == ASM_CALLCMEMBER_THIS_R) printf(", " PREFIX_STACKEFFECT "%" PRFu8, READ_imm8(iter)); @@ -2336,6 +2339,7 @@ libdisasm_printinstr(dformatprinter printer, void *arg, case ASM16_BOUNDMEMBER_THIS_R: case ASM16_DELMEMBER_THIS_R: case ASM16_SETMEMBER_THIS_R: + case ASM16_SETMEMBERI_THIS_R: case ASM16_GETCMEMBER_R: case ASM16_CALLCMEMBER_THIS_R: PRINT(", " PREFIX_INTEGERAL); @@ -2343,7 +2347,7 @@ libdisasm_printinstr(dformatprinter printer, void *arg, INVOKE(libdisasm_printmembername(printer, arg, imm, imm2, code, flags, opcode == ASM16_GETCMEMBER_R || opcode == ASM16_CALLCMEMBER_THIS_R)); - if (opcode == ASM16_SETMEMBER_THIS_R) + if (opcode == ASM16_SETMEMBER_THIS_R || opcode == ASM16_SETMEMBERI_THIS_R) PRINT(", pop"); if (opcode == ASM16_CALLCMEMBER_THIS_R) printf(", " PREFIX_STACKEFFECT "%" PRFu8, READ_imm8(iter)); diff --git a/util/test/compiler-class-final-member.dee b/util/test/compiler-class-final-member.dee new file mode 100644 index 000000000..17396fcff --- /dev/null +++ b/util/test/compiler-class-final-member.dee @@ -0,0 +1,96 @@ +#!/usr/bin/deemon +/* Copyright (c) 2018-2024 Griefer@Work * + * * + * This software is provided 'as-is', without any express or implied * + * warranty. In no event will the authors be held liable for any damages * + * arising from the use of this software. * + * * + * Permission is granted to anyone to use this software for any purpose, * + * including commercial applications, and to alter it and redistribute it * + * freely, subject to the following restrictions: * + * * + * 1. The origin of this software must not be misrepresented; you must not * + * claim that you wrote the original software. If you use this software * + * in a product, an acknowledgement (see the following) in the product * + * documentation is required: * + * Portions Copyright (c) 2018-2024 Griefer@Work * + * 2. Altered source versions must be plainly marked as such, and must not be * + * misrepresented as being the original software. * + * 3. This notice may not be removed or altered from any source distribution. * + */ + +import * from errors; + +/* Rules for "final" class members: + * - If a sub-class tries to override the member, it is undefined if said override + * is actually used during some given access (iow: the compiler doesn't need to + * emit a dynamic attribute lookup). + * The same effect applies when the member is "private", or the class is "final" + * - The member can only be assigned its value *once*. Trying to assign a value + * when the member is already bound results in an AttributeError. (This behavior + * mirrors java semantics). + * This second part of the behavior can be suppressed by adding "varying", which + * results in a member that can't be overwritten, but can be re-assigned. + * - The "varying" modifier can also only be used on members (not properties or functions) + */ +class MyClass { + public final member foo = 42; + public final member bar; + public final varying member baz; + this = default; + + setFoo() { + foo = 10; + return this; + } + + setBar() { + bar = 20; + return this; + } + + setBaz() { + baz = 30; + return this; + } +} + +function setFoo(x) { + x.foo = 10; + return x; +} + +function setBar(x) { + x.bar = 20; + return x; +} + +function setBaz(x) { + x.baz = 30; + return x; +} + +assert MyClass().foo == 42; +assert MyClass().bar !is bound; +assert MyClass().baz !is bound; +assert MyClass(foo: 10).foo == 10; +assert MyClass(foo: 10).bar !is bound; +assert MyClass(bar: 20).foo == 42; +assert MyClass(bar: 20).bar == 20; +assert MyClass(baz: 30).foo == 42; +assert MyClass(baz: 30).bar !is bound; +assert MyClass(baz: 30).baz == 30; +assert (try setFoo(MyClass()).foo catch (e...) e) is AttributeError; +assert (try setBar(MyClass()).bar catch (e...) e) == 20; +assert (try setBaz(MyClass()).baz catch (e...) e) == 30; +assert (try MyClass().setFoo().foo catch (e...) e) is AttributeError; +assert (try MyClass().setBar().bar catch (e...) e) == 20; +assert (try MyClass().setBaz().baz catch (e...) e) == 30; +assert (try setFoo(setFoo(MyClass())).foo catch (e...) e) is AttributeError; +assert (try setBar(setBar(MyClass())).bar catch (e...) e) is AttributeError; +assert (try setBaz(setBaz(MyClass())).baz catch (e...) e) == 30; +assert (try MyClass().setFoo().setFoo().foo catch (e...) e) is AttributeError; +assert (try MyClass().setBar().setBar().bar catch (e...) e) is AttributeError; +assert (try MyClass().setBaz().setBaz().baz catch (e...) e) == 30; + + diff --git a/util/test/compiler-range-expressions.dee b/util/test/compiler-range-expressions.dee index b9e0089f5..022ae7864 100644 --- a/util/test/compiler-range-expressions.dee +++ b/util/test/compiler-range-expressions.dee @@ -35,8 +35,8 @@ function assert_range(r, e) { assert x in r; } -class IntWrapper: Numeric { - public final member i: int = 0; +final class IntWrapper: Numeric { + public member i: int = 0; this = default; operator ++ () { return IntWrapper(i + 1); } operator -- () { return IntWrapper(i - 1); }