From 137454629e8dc5a9208d674b51baafb5c433cbfb Mon Sep 17 00:00:00 2001 From: Ge Wang Date: Sun, 29 Oct 2023 14:01:42 -0700 Subject: [PATCH] fixed memory management for overloaded ops --- VERSIONS | 6 ++- src/core/chuck_emit.cpp | 63 +++++++++++++++++++++-- src/core/chuck_type.cpp | 27 ++++++++++ src/test/01-Basic/231-op-overload-refs.ck | 50 ++++++++++++++++++ 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 src/test/01-Basic/231-op-overload-refs.ck diff --git a/VERSIONS b/VERSIONS index 965351de2..854d45b4f 100644 --- a/VERSIONS +++ b/VERSIONS @@ -2,12 +2,16 @@ ChucK VERSIONS log ------------------ -1.5.1.8 (October 2023) +1.5.1.8 (October 2023) patch release ======= +- (fixed) an issue with memory management / reference counting across + overloaded operations that return Object - (fixed) an issue with reference count cleanup on recursive functions that return Objects - (fixed) an issue with reference count cleanup on 'new' operator, e.g., (new Foo).f(); +- (updated) reduced memory management between language-defined objects + and their parent shreds - (updated) --color:ON and --color now will always output ANSI color codes, regardless of TTY status; (FYI --color:AUTO is unchanged and is the same as default, i.e., enable color terminal if TTY diff --git a/src/core/chuck_emit.cpp b/src/core/chuck_emit.cpp index 5aec28dbd..715ee82e7 100644 --- a/src/core/chuck_emit.cpp +++ b/src/core/chuck_emit.cpp @@ -3073,6 +3073,36 @@ t_CKBOOL emit_engine_emit_op( Chuck_Emitter * emit, ae_Operator op, a_Exp lhs, a +//----------------------------------------------------------------------------- +// name: emit_engine_emit_stmt_remember_object() | 1.5.1.8 +// desc: as needed (if type is Object), emit a Stmt_Remember_Object instruction +// assuming that the greater Stmt_Start to Stmt_Cleanup mechanism is +// handled correctly elsewhere +//----------------------------------------------------------------------------- +t_CKBOOL emit_engine_emit_stmt_remember_object( Chuck_Emitter * emit, Chuck_Type * type ) +{ + // if the function returns an Object + if( !isobj( emit->env, type ) ) + return FALSE; + + // the return needs to be released (later at the end of the stmt that contains this) + Chuck_Instr_Stmt_Start * onStack = emit->stmt_stack.size() ? emit->stmt_stack.back() : NULL; + // check it + if( onStack ) + { + t_CKUINT offset = 0; + // acquire next offset + if( !onStack->nextOffset( offset ) ) return FALSE; + // append instruction + emit->append( new Chuck_Instr_Stmt_Remember_Object( onStack, offset ) ); + } + + return TRUE; +} + + + + //----------------------------------------------------------------------------- // name: emit_engine_emit_op_overload_binary() | 1.5.1.5 (ge) added // desc: emit binary operator overload @@ -3083,7 +3113,16 @@ t_CKBOOL emit_engine_emit_op_overload_binary( Chuck_Emitter * emit, a_Exp_Binary // push function pointer emit->append( new Chuck_Instr_Reg_Push_Imm( (t_CKUINT)binary->ck_overload_func ) ); // emit the function call - return emit_engine_emit_exp_func_call( emit, binary->ck_overload_func, binary->self->type, binary->line, binary->where ); + if( !emit_engine_emit_exp_func_call( emit, binary->ck_overload_func, binary->self->type, binary->line, binary->where ) ) + return FALSE; + + // return type + Chuck_Type * rtype = binary->ck_overload_func->type(); + // emit remember instr | 1.5.1.8 + if( isobj( emit->env, rtype ) ) emit_engine_emit_stmt_remember_object( emit, rtype ); + + // done + return TRUE; } @@ -3099,7 +3138,16 @@ t_CKBOOL emit_engine_emit_op_overload_unary( Chuck_Emitter * emit, a_Exp_Unary u // push function pointer emit->append( new Chuck_Instr_Reg_Push_Imm( (t_CKUINT)unary->ck_overload_func ) ); // emit the function call - return emit_engine_emit_exp_func_call( emit, unary->ck_overload_func, unary->self->type, unary->line, unary->where ); + if( !emit_engine_emit_exp_func_call( emit, unary->ck_overload_func, unary->self->type, unary->line, unary->where ) ) + return FALSE; + + // return type + Chuck_Type * rtype = unary->ck_overload_func->type(); + // emit remember instr | 1.5.1.8 + if( isobj( emit->env, rtype ) ) emit_engine_emit_stmt_remember_object( emit, rtype ); + + // done + return TRUE; } @@ -3115,7 +3163,16 @@ t_CKBOOL emit_engine_emit_op_overload_postfix( Chuck_Emitter * emit, a_Exp_Postf // push function pointer emit->append( new Chuck_Instr_Reg_Push_Imm( (t_CKUINT)postfix->ck_overload_func ) ); // emit the function call - return emit_engine_emit_exp_func_call( emit, postfix->ck_overload_func, postfix->self->type, postfix->line, postfix->where ); + if( !emit_engine_emit_exp_func_call( emit, postfix->ck_overload_func, postfix->self->type, postfix->line, postfix->where ) ) + return FALSE; + + // return type + Chuck_Type * rtype = postfix->ck_overload_func->type(); + // emit remember instr | 1.5.1.8 + if( isobj( emit->env, rtype ) ) emit_engine_emit_stmt_remember_object( emit, rtype ); + + // done + return TRUE; } diff --git a/src/core/chuck_type.cpp b/src/core/chuck_type.cpp index e10662c47..f8edeeea8 100644 --- a/src/core/chuck_type.cpp +++ b/src/core/chuck_type.cpp @@ -2471,6 +2471,15 @@ t_CKTYPE type_engine_check_op_overload_binary( Chuck_Env * env, ae_Operator op, return NULL; } + // get return type | 1.5.1.8 (ge & andrew) we are back + Chuck_Type * rtype = binary->ck_overload_func->type(); + // check if return type is an Obj + if( rtype && isobj( env, rtype ) && env->stmt_stack.size() ) + { + // increment # of objects in this stmt that needs release + env->stmt_stack.back()->numObjsToRelease++; + } + // the return type return binary->ck_overload_func->type(); } @@ -2506,6 +2515,15 @@ t_CKTYPE type_engine_check_op_overload_unary( Chuck_Env * env, ae_Operator op, return NULL; } + // get return type | 1.5.1.8 (ge & andrew) we are back + Chuck_Type * rtype = unary->ck_overload_func->type(); + // check if return type is an Obj + if( rtype && isobj( env, rtype ) && env->stmt_stack.size() ) + { + // increment # of objects in this stmt that needs release + env->stmt_stack.back()->numObjsToRelease++; + } + // the return type return unary->ck_overload_func->type(); } @@ -2541,6 +2559,15 @@ t_CKTYPE type_engine_check_op_overload_postfix( Chuck_Env * env, Chuck_Type * lh return NULL; } + // get return type | 1.5.1.8 (ge & andrew) we are back + Chuck_Type * rtype = postfix->ck_overload_func->type(); + // check if return type is an Obj + if( rtype && isobj( env, rtype ) && env->stmt_stack.size() ) + { + // increment # of objects in this stmt that needs release + env->stmt_stack.back()->numObjsToRelease++; + } + // the return type return postfix->ck_overload_func->type(); } diff --git a/src/test/01-Basic/231-op-overload-refs.ck b/src/test/01-Basic/231-op-overload-refs.ck new file mode 100644 index 000000000..a7ae75b62 --- /dev/null +++ b/src/test/01-Basic/231-op-overload-refs.ck @@ -0,0 +1,50 @@ +// verify that binary, unary-pre, and unary-post overloadings that +// return Objects are correctly ref-counted across op func calls + +// let's say we define a custom class... +public class Foo { int num; } + +// persistent operator overloading (trascends contexts) +// NOTE the use of 'public' instead of 'fun' -- it's fun for all! +public Foo @operator =^( Foo lhs, Foo rhs ) +{ /* do stuff for Foo =^ Foo */ return rhs; } + +// LOCAL binary operator overload for '=>' (local to this context) +fun Foo @operator =>( Foo lhs, Foo rhs ) +{ /* do stuff for Foo => Foo */ return rhs; } + +// define binary operator overload for '+' +fun Foo @operator +( Foo lhs, Foo rhs ) +{ Foo retval; lhs.num + rhs.num => retval.num; return retval; } + +// define binary operator overload for '*' +fun Foo @operator *( Foo lhs, Foo rhs ) +{ Foo retval; lhs.num * rhs.num => retval.num; return retval; } + +// define unary operator overload for '!' +fun int @operator !( Foo foo ) +{ return !foo.num; } + +// define postfix operator overload for '++' +fun Foo @operator ( Foo foo ) ++ +{ foo.num++; return foo; } + +//----------------------------------------------------------- +// using the new overloaded operators +//----------------------------------------------------------- +// create 2 Foos +Foo a, b; 1 => a.num; 2 => b.num; +// operator in action (follows default ck operator precedence) +a + b * b + a @=> Foo @ c; // note c is not a new instance +// post ++ on c +c++; +// another instance +Foo d; +// post ++ on d +d++; + +// should print 1s +if( Machine.refcount(a) == 1 && + Machine.refcount(b) == 1 && + Machine.refcount(c) == 1 && + Machine.refcount(d) == 1 ) <<< "success" >>>;