History

memory system upgrade (part 1); scope-sensitive cleanup for each return statement; clarified reference counting semantics; function argument release for builtin functions; improved post-function call object cleanup; auto-reference-increment on returned objects from all builtin/imported functions; absyn tree printing for instruction dumping abysn2str(); Machine.refcount() and refcount printing for object references

Ge Wang committed 3 years ago

Commit beef6541 parent 2eec4e717 files · +835 −72Browse files

Unified · Split

17 files
  1. src/core/chuck_emit.cpp +99 −24
  2. src/core/chuck_emit.h +3 −1
  3. src/core/chuck_frame.cpp +7 −2
  4. src/core/chuck_frame.h +1 −1
  5. src/core/chuck_instr.cpp +197 −12
  6. src/core/chuck_instr.h +44 −5
  7. src/core/chuck_lang.cpp +6 −1
  8. src/core/chuck_oo.cpp +11 −2
  9. src/core/chuck_parse.cpp +404 −0
  10. src/core/chuck_parse.h +2 −0
  11. src/core/ulib_machine.cpp +34 −2
  12. src/core/ulib_machine.h +1 −17
  13. src/test/01-Basic/120.ck +20 −0
  14. src/test/01-Basic/120.txt +1 −0
  15. src/test/05-Global/01b.txt +1 −1
  16. src/test/05-Global/102a.txt +3 −3
  17. src/test/test.py +1 −1
src/core/chuck_emit.cpp+99−24
@@ -35,6 +35,7 @@ · 34 unmodified lines
3535 #include "chuck_errmsg.h"
3636 #include "chuck_instr.h"
3737 #include "chuck_globals.h" // added 1.4.1.0
38+#include "chuck_parse.h" // added 1.4.2.1
3839 #include <sstream>
3940 #include <iostream>
4041
@@ -73,7 +74,7 @@ t_CKBOOL emit_engine_emit_exp_postfix( Chuck_Emitter * emit, a_Exp_Postfix postf · 32 unmodified lines
7374 t_CKBOOL emit_engine_emit_exp_dur( Chuck_Emitter * emit, a_Exp_Dur dur );
7475 t_CKBOOL emit_engine_emit_exp_array( Chuck_Emitter * emit, a_Exp_Array array );
7576 t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, Chuck_Func * func,
76− Chuck_Type * type, int linepos, t_CKBOOL spork = FALSE );
77+ a_Exp args, Chuck_Type * type, int linepos, t_CKBOOL spork = FALSE );
7778 t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, a_Exp_Func_Call func_call,
7879 t_CKBOOL spork = FALSE );
7980 t_CKBOOL emit_engine_emit_func_args( Chuck_Emitter * emit, a_Exp_Func_Call func_call );
@@ -386,6 +387,10 @@ t_CKBOOL emit_engine_emit_stmt( Chuck_Emitter * emit, a_Stmt stmt, t_CKBOOL pop · 306 unmodified lines
386387 ret = emit_engine_emit_exp( emit, stmt->stmt_exp );
387388 if( !ret )
388389 return FALSE;
390+
391+ // 1.4.2.1
392+ // cerr << absyn2str( stmt->stmt_exp ) << endl;
393+
389394 // need to pop the final value from stack
390395 if( ret && pop && stmt->stmt_exp->type->size > 0 )
391396 {
@@ -398,17 +403,25 @@ t_CKBOOL emit_engine_emit_stmt( Chuck_Emitter * emit, a_Stmt stmt, t_CKBOOL pop · 6 unmodified lines
398403 if( exp->s_type == ae_exp_primary && exp->primary.s_type == ae_primary_hack )
399404 exp = exp->primary.exp;
400405
401− // HACK!
406+ // loop over the expression that is the statement (prior to 1.4.2.1 comment: "HACK" hmmm...)
402407 while( exp )
403408 {
404409 // if decl, then expect only one word per var
405410 // added 1.3.1.0: iskindofint -- since on some 64-bit systems sz_INT == sz_FLOAT
406411 if( exp->s_type == ae_exp_decl )
412+ {
407413 // (added 1.3.1.0 -- multiply by type size; #64-bit)
408414 // (fixed 1.3.1.2 -- uh... decl should also be int-sized, so changed to INT/WORD)
409415 emit->append( new Chuck_Instr_Reg_Pop_Word4( exp->decl.num_var_decls * sz_INT / sz_WORD ) );
416+ }
410417 else if( exp->type->size == sz_INT && iskindofint(emit->env, exp->type) ) // ISSUE: 64-bit (fixed 1.3.1.0)
411− emit->append( new Chuck_Instr_Reg_Pop_Word );
418+ {
419+ // is an object left on the stack for the stmt
420+ if( exp->s_type == ae_exp_func_call && isobj(emit->env, exp->type) )
421+ { emit->append( new Chuck_Instr_Release_Object3_Pop_Word ); }
422+ else // not an object
423+ { emit->append( new Chuck_Instr_Reg_Pop_Word ); }
424+ }
412425 else if( exp->type->size == sz_FLOAT ) // ISSUE: 64-bit (fixed 1.3.1.0)
413426 emit->append( new Chuck_Instr_Reg_Pop_Word2 );
414427 else if( exp->type->size == sz_COMPLEX ) // ISSUE: 64-bit (fixed 1.3.1.0)
@@ -1291,12 +1304,27 @@ t_CKBOOL emit_engine_emit_continue( Chuck_Emitter * emit, a_Stmt_Continue cont ) · 876 unmodified lines
12911304 //-----------------------------------------------------------------------------
12921305 t_CKBOOL emit_engine_emit_return( Chuck_Emitter * emit, a_Stmt_Return stmt )
12931306 {
1307+ // emit the value
12941308 if( !emit_engine_emit_exp( emit, stmt->val ) )
12951309 return FALSE;
12961310
1311+ // if return is an object type
1312+ if( isobj( emit->env, stmt->val->type ) )
1313+ {
1314+ // add reference (to be released by the function caller)
1315+ emit->append( new Chuck_Instr_Reg_AddRef_Object3 );
1316+ }
1317+
1318+ // 1.4.2.1 (ge) | traverse and unwind current scope frames, release object
1319+ // references given the current state of the stack frames; every `return`
1320+ // statement needs its own scope unwinding; FYI: this does not change the
1321+ // scope frames, which is needed to continue compilation for the rest
1322+ // of the function; this does emit instructions (e.g., Release_Object2)
1323+ // associated with this traversal action (also see 'break' and 'continue')
1324+ emit->traverse_scope_on_jump();
1325+
12971326 // emit return
12981327 // emit->append( new Chuck_Instr_Func_Return );
1299−
13001328 // determine where later
13011329 Chuck_Instr_Goto * instr = new Chuck_Instr_Goto( 0 );
13021330 emit->append( instr );
@@ -2537,7 +2565,7 @@ t_CKBOOL emit_engine_emit_op_chuck( Chuck_Emitter * emit, a_Exp lhs, a_Exp rhs, · 1234 unmodified lines
25372565 assert( binary->ck_func != NULL );
25382566
25392567 // emit
2540− return emit_engine_emit_exp_func_call( emit, binary->ck_func, binary->self->type, binary->linepos );
2568+ return emit_engine_emit_exp_func_call( emit, binary->ck_func, binary->lhs, binary->self->type, binary->linepos );
25412569 }
25422570
25432571 // assignment or something else
@@ -3401,6 +3429,7 @@ t_CKBOOL emit_engine_emit_exp_array( Chuck_Emitter * emit, a_Exp_Array array ) · 857 unmodified lines
34013429 //-----------------------------------------------------------------------------
34023430 t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit,
34033431 Chuck_Func * func,
3432+ a_Exp args,
34043433 Chuck_Type * type,
34053434 int linepos,
34063435 t_CKBOOL spork )
@@ -3408,13 +3437,6 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, · 1 unmodified line
34083437 // is a member?
34093438 t_CKBOOL is_member = func->is_member;
34103439
3411− // if member
3412− //if( is_member )
3413− //{
3414− // // this
3415− // emit->append( new Chuck_Instr_Reg_Push_This );
3416− //}
3417−
34183440 // translate to code
34193441 emit->append( new Chuck_Instr_Func_To_Code );
34203442 // emit->append( new Chuck_Instr_Reg_Push_Imm( (t_CKUINT)func->code ) );
@@ -3436,9 +3458,9 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, · 15 unmodified lines
34363458 {
34373459 // is member (1.3.1.0: changed to use kind instead of size)
34383460 if( is_member )
3439− emit->append( instr = new Chuck_Instr_Func_Call_Member( kind ) );
3461+ emit->append( instr = new Chuck_Instr_Func_Call_Member( kind, func ) );
34403462 else
3441− emit->append( instr = new Chuck_Instr_Func_Call_Static( kind ) );
3463+ emit->append( instr = new Chuck_Instr_Func_Call_Static( kind, func ) );
34423464 }
34433465 else
34443466 {
@@ -3509,8 +3531,8 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, · 64 unmodified lines
35093531 }
35103532
35113533 // the rest
3512− return emit_engine_emit_exp_func_call( emit, func_call->ck_func, func_call->ret_type,
3513− func_call->linepos, spork );
3534+ return emit_engine_emit_exp_func_call( emit, func_call->ck_func, func_call->args,
3535+ func_call->ret_type, func_call->linepos, spork );
35143536 }
35153537
35163538
@@ -4126,7 +4148,7 @@ t_CKBOOL emit_engine_emit_exp_decl( Chuck_Emitter * emit, a_Exp_Decl decl, · 609 unmodified lines
41264148 is_ref = ( var_decl->array->exp_list == NULL );
41274149 // ...and only instantiate if NOT empty
41284150 // REFACTOR-2017 TODO: do we want to
4129− // avoid doing this if the array is global?
4151+ // avoid doing this if the array is global?
41304152 if( !is_ref )
41314153 {
41324154 // set
@@ -4795,13 +4817,22 @@ t_CKBOOL emit_engine_emit_spork( Chuck_Emitter * emit, a_Exp_Func_Call exp ) · 662 unmodified lines
47954817 emit->append( op );
47964818
47974819 // call the func on sporkee shred
4798− if( !emit_engine_emit_exp_func_call(
4799− emit,
4800− exp->ck_func,
4801− exp->ret_type,
4802− exp->linepos,
4803− TRUE ) )
4804− return FALSE;
4820+ if( !emit_engine_emit_exp_func_call( emit,
4821+ exp->ck_func,
4822+ exp->args,
4823+ exp->ret_type,
4824+ exp->linepos,
4825+ TRUE ) ) return FALSE;
4826+
4827+ // 1.4.2.1 (ge) | added to pop/release the returned value, which could be an object
4828+ if( iskindofint(emit->env, exp->ret_type) )
4829+ {
4830+ // is an object?
4831+ if( isobj( emit->env, exp->ret_type) )
4832+ { emit->append( new Chuck_Instr_Release_Object3_Pop_Word ); }
4833+ else // not an object
4834+ { emit->append( new Chuck_Instr_Reg_Pop_Word ); }
4835+ }
48054836
48064837 // emit the function call, with special flag
48074838 // if( !emit_engine_emit_exp_func_call( emit, exp, TRUE ) )
@@ -5087,6 +5118,50 @@ void Chuck_Emitter::addref_on_scope() · 279 unmodified lines
50875118
50885119
50895120
5121+//-----------------------------------------------------------------------------
5122+// name: traverse_scope_on_jump() | 1.4.2.1 (ge) added
5123+// desc: traverse the scope and emit instructions on a jump statement:
5124+// 'return', 'break', 'continue'
5125+// this is done to ensure proper cleanup of objects on scope frames
5126+//-----------------------------------------------------------------------------
5127+void Chuck_Emitter::traverse_scope_on_jump( )
5128+{
5129+ // this gotta be the case
5130+ assert( code != NULL );
5131+
5132+ // clear locals
5133+ locals.clear();
5134+ // get locals (localOnly=false to get all locals at this point in 'code')
5135+ code->frame->get_scope( locals, false );
5136+
5137+ // loop over
5138+ for( int i = 0; i < locals.size(); i++ )
5139+ {
5140+ // get local
5141+ Chuck_Local * local = locals[i];
5142+ // skip scope boundary (denoted by NULL)
5143+ if( !local ) continue;
5144+
5145+ // CK_STDCERR << "local: " << local->offset << " " << local->is_obj << CK_STDENDL;
5146+ // check to see if it's an object
5147+ if( local->is_obj )
5148+ {
5149+ // (REFACTOR-2017: don't release global objects)
5150+ if( !local->is_global )
5151+ {
5152+ // emit instruction to release the object
5153+ this->append( new Chuck_Instr_Release_Object2( local->offset ) );
5154+ }
5155+ }
5156+ }
5157+
5158+ // clear it
5159+ locals.clear();
5160+}
5161+
5162+
5163+
5164+
50905165 //-----------------------------------------------------------------------------
50915166 // name: pop_scope()
50925167 // desc: ...
src/core/chuck_emit.h+3−1
@@ -148,7 +148,9 @@ struct Chuck_Emitter : public Chuck_VM_Object · 147 unmodified lines
148148 // add references to locals on current scope (added 1.3.0.0)
149149 void addref_on_scope();
150150 // pop scope
151− void pop_scope( );
151+ void pop_scope();
152+ // traverse scope on jumps such as 'return' 'break' 'continue' | 1.4.2.1 (ge)
153+ void traverse_scope_on_jump();
152154
153155 // default durations
154156 t_CKBOOL find_dur( const std::string & name, t_CKDUR * out );
src/core/chuck_frame.cpp+7−2
@@ -100,7 +100,7 @@ void Chuck_Frame::push_scope( ) · 99 unmodified lines
100100 // name: get_scope()
101101 // desc: get current scope (added 1.3.0.0)
102102 //-----------------------------------------------------------------------------
103−void Chuck_Frame::get_scope( vector<Chuck_Local *> & out ) const
103+void Chuck_Frame::get_scope( vector<Chuck_Local *> & out, bool localOnly ) const
104104 {
105105 // sanity
106106 assert( this->stack.size() > 0 );
@@ -111,8 +111,13 @@ void Chuck_Frame::get_scope( vector<Chuck_Local *> & out ) const · 4 unmodified lines
111111 t_CKINT index = this->stack.size() - 1;
112112
113113 // loop
114− while( index >= 0 && this->stack[index] != NULL )
114+ while( index >= 0 )
115115 {
116+ // did we hit a stack boundary (delineated by NULL)
117+ // localOnly option added 1.4.2.1 (ge) -- to get all scope in the frame
118+ if( localOnly && this->stack[index] != NULL )
119+ break;
120+
116121 // last thing
117122 local = stack[index];
118123 // move
src/core/chuck_frame.h+1−1
@@ -92,7 +92,7 @@ public: · 91 unmodified lines
9292 Chuck_Local * alloc_local( t_CKUINT size, const std::string & name,
9393 t_CKBOOL is_ref, t_CKBOOL is_obj, t_CKBOOL is_global );
9494 // get the current scope (added 1.3.0.0)
95− void get_scope( std::vector<Chuck_Local *> & out ) const;
95+ void get_scope( std::vector<Chuck_Local *> & out, bool localOnly = true ) const;
9696 // pop scope
9797 void pop_scope( std::vector<Chuck_Local *> & out );
9898 };
src/core/chuck_instr.cpp+197−12
@@ -3777,7 +3777,7 @@ error: · 3776 unmodified lines
37773777
37783778
37793779 static Chuck_Instr_Func_Call g_func_call;
3780−static Chuck_Instr_Func_Call_Member g_func_call_member( 0 );
3780+static Chuck_Instr_Func_Call_Member g_func_call_member( 0, NULL );
37813781 //-----------------------------------------------------------------------------
37823782 // name: call_pre_constructor()
37833783 // desc: ...
@@ -3797,6 +3797,10 @@ inline void call_pre_constructor( Chuck_VM * vm, Chuck_VM_Shred * shred, · 13 unmodified lines
37973797 // push the stack offset
37983798 push_( reg_sp, stack_offset );
37993799
3800+ // NOTE (1.4.2.1): pre-constructors by their nature have no arguments...
3801+ // if they did, they would need to clean up argument list, in the built-in/native
3802+ // func case, by setting g_func_call_member.m_func_ref with the appropriate func
3803+
38003804 // call the function
38013805 if( pre_ctor->native_func != 0 )
38023806 g_func_call_member.execute( vm, shred );
@@ -4399,11 +4403,16 @@ void Chuck_Instr_AddRef_Object2::execute( Chuck_VM * vm, Chuck_VM_Shred * shred · 596 unmodified lines
43994403
44004404 //-----------------------------------------------------------------------------
44014405 // name: execute()
4402−// desc: add one reference on object (added 1.3.0.0)
4406+// desc: ref-count increment the top of the register stack (added 1.3.0.0)
4407+// 1.4.2.1 (ge) additional notes...
4408+// this is used to keep objects referenced while on stack;
4409+// relevant for cases like `return obj;` where obj may need to
4410+// released for the local stack AND need to be kept on the stack
4411+// to be return; in this case, we need this additional add_ref();
4412+// releasing obj from stack is responsibility of the caller
44034413 //-----------------------------------------------------------------------------
44044414 void Chuck_Instr_Reg_AddRef_Object3::execute( Chuck_VM * vm, Chuck_VM_Shred * shred )
44054415 {
4406− // ISSUE: 64-bit?
44074416 // NOTE: this pointer is NOT a reference pointer
44084417 t_CKUINT * reg_sp = (t_CKUINT *&)shred->reg->sp;
44094418 Chuck_VM_Object * obj = NULL;
@@ -4415,7 +4424,7 @@ void Chuck_Instr_Reg_AddRef_Object3::execute( Chuck_VM * vm, Chuck_VM_Shred * sh · 5 unmodified lines
44154424 // ge (2012 april): check for NULL (added 1.3.0.0)
44164425 if( obj != NULL )
44174426 {
4418− // release
4427+ // add reference
44194428 obj->add_ref();
44204429 }
44214430 }
@@ -4470,13 +4479,70 @@ void Chuck_Instr_Release_Object2::execute( Chuck_VM * vm, Chuck_VM_Shred * shred · 48 unmodified lines
44704479
44714480
44724481
4482+
4483+//-----------------------------------------------------------------------------
4484+// name: execute()
4485+// desc: release object reference + pop from reg stack | 1.4.2.1 (ge) added
4486+// the variant assumes object pointer directly on stack (not offset)
4487+// used to release returned object pointer after a function call;
4488+// FYI the return value is conveyed on the reg stack; this undoes
4489+// the addref before the return; see Chuck_Instr_Reg_AddRef_Object3
4490+//-----------------------------------------------------------------------------
4491+void Chuck_Instr_Release_Object3_Pop_Word::execute( Chuck_VM * vm, Chuck_VM_Shred * shred )
4492+{
4493+ t_CKUINT *& reg_sp = (t_CKUINT *&)shred->reg->sp;
4494+ Chuck_VM_Object * obj = NULL;
4495+
4496+ // pop
4497+ pop_( reg_sp, 1 );
4498+ // copy popped value into mem stack
4499+ obj = *( (Chuck_VM_Object **)(reg_sp) );
4500+ // ge (2012 april): check for NULL (added 1.3.0.0)
4501+ if( obj != NULL )
4502+ {
4503+ // release
4504+ obj->release();
4505+ }
4506+}
4507+
4508+
4509+
4510+
4511+//-----------------------------------------------------------------------------
4512+// name: execute()
4513+// desc: release object reference from reg stack | 1.4.2.1 (ge) added
4514+// the variant assumes object pointer directly on stack (not offset)
4515+// used to release objects on function arguments, specifically for
4516+// built-in / imported (chugins) functions; code-defined functions
4517+// do their own argument cleanup
4518+//-----------------------------------------------------------------------------
4519+void Chuck_Instr_Release_Object4::execute( Chuck_VM * vm, Chuck_VM_Shred * shred )
4520+{
4521+ // NOTE: this pointer is NOT a reference pointer
4522+ t_CKBYTE * mem_sp = (t_CKBYTE *&)shred->mem->sp;
4523+ Chuck_VM_Object * obj = NULL;
4524+
4525+ // move it
4526+ mem_sp += m_val;
4527+ // copy popped value into mem stack
4528+ obj = *( (Chuck_VM_Object **)(mem_sp) );
4529+ if( obj != NULL )
4530+ {
4531+ // release
4532+ obj->release();
4533+ }
4534+}
4535+
4536+
4537+
4538+
44734539 #pragma mark === Function Calls ===
44744540
44754541
44764542
44774543 //-----------------------------------------------------------------------------
44784544 // name: execute()
4479−// desc: ...
4545+// desc: converts, in place on operand stack, a Func to its Code (instructions)
44804546 //-----------------------------------------------------------------------------
44814547 void Chuck_Instr_Func_To_Code::execute( Chuck_VM * vm, Chuck_VM_Shred * shred )
44824548 {
@@ -4494,6 +4560,57 @@ void Chuck_Instr_Func_To_Code::execute( Chuck_VM * vm, Chuck_VM_Shred * shred ) · 11 unmodified lines
44944560
44954561
44964562
4563+//-----------------------------------------------------------------------------
4564+// name: func_release_args() | 1.4.2.1 (ge) added
4565+// desc: helper function to release objects an function arg list
4566+// input: VM, args list, mem stack pointer
4567+// context: this is designed for builtin/imported functions
4568+// functions defined in chuck code do their own arg list cleanup
4569+//-----------------------------------------------------------------------------
4570+t_CKBOOL func_release_args( Chuck_VM * vm, a_Arg_List args, t_CKUINT * mem_sp )
4571+{
4572+ // keep track of offset
4573+ t_CKUINT offset = 0;
4574+ // the type in question
4575+ Chuck_Type * type = NULL;
4576+ // object pointer
4577+ Chuck_VM_Object * object = NULL;
4578+
4579+ // loop over
4580+ while( args )
4581+ {
4582+ // get the type
4583+ type = args->type;
4584+ // check we have one!
4585+ if( type != NULL )
4586+ {
4587+ // is an object?
4588+ if( isobj( vm->env(), args->type) )
4589+ {
4590+ // get the object pointer
4591+ object = (Chuck_VM_Object *)(*(mem_sp+offset));
4592+ // check
4593+ if( object != NULL )
4594+ {
4595+ // release it
4596+ object->release();
4597+ }
4598+ }
4599+
4600+ // advance offset
4601+ offset += args->type->size;
4602+ }
4603+
4604+ // go
4605+ args = args->next;
4606+ }
4607+
4608+ return TRUE;
4609+}
4610+
4611+
4612+
4613+
44974614 //-----------------------------------------------------------------------------
44984615 // name: execute()
44994616 // desc: ...
@@ -4648,6 +4765,16 @@ void Chuck_Instr_Func_Call_Member::execute( Chuck_VM * vm, Chuck_VM_Shred * shre · 148 unmodified lines
46484765 // call the function (added 1.3.0.0 -- Chuck_DL_Api::Api::instance())
46494766 f( (Chuck_Object *)(*mem_sp), mem_sp + 1, &retval, vm, shred, Chuck_DL_Api::Api::instance() );
46504767 }
4768+
4769+ // if we have a func def | 1.4.2.1 (ge) added
4770+ if( m_func_ref != NULL )
4771+ {
4772+ // cleanup / release objects on the arg list
4773+ // context: this should be done here for builtin/import functions
4774+ // user-defined functions do their own arg list cleanup
4775+ func_release_args( vm, m_func_ref->def->arg_list, mem_sp+1 );
4776+ }
4777+
46514778 // pop (TODO: check if this is right)
46524779 mem_sp -= push;
46534780
@@ -4657,6 +4784,25 @@ void Chuck_Instr_Func_Call_Member::execute( Chuck_VM * vm, Chuck_VM_Shred * shre · 3 unmodified lines
46574784 {
46584785 // push the return args
46594786 push_( reg_sp, retval.v_uint );
4787+
4788+ // 1.4.2.1 (ge) | added -- ensure ref count
4789+ if( m_func_ref && isobj(vm->env(), m_func_ref->def->ret_type) )
4790+ {
4791+ Chuck_VM_Object * obj = (Chuck_VM_Object *) retval.v_uint;
4792+ if( obj )
4793+ {
4794+ // check if refcount is 0
4795+ if( obj->m_ref_count == 0 )
4796+ {
4797+ EM_log( CK_LOG_FINE, "%s(...) returned object with refcount==0", S_name(m_func_ref->def->name) );
4798+ EM_pushlog();
4799+ EM_log( CK_LOG_FINE, "auto-incrementing object refcount to 1..." );
4800+ EM_poplog();
4801+ }
4802+ // always add reference to returned objects (should release outside)
4803+ obj->add_ref();
4804+ }
4805+ }
46604806 }
46614807 else if( m_val == kindof_FLOAT ) // ISSUE: 64-bit (fixed 1.3.1.0)
46624808 {
@@ -4755,7 +4901,15 @@ void Chuck_Instr_Func_Call_Static::execute( Chuck_VM * vm, Chuck_VM_Shred * shre · 92 unmodified lines
47554901 // (added 1.3.0.0 -- Chuck_DL_Api::Api::instance())
47564902 // (added 1.4.1.0 -- base_type)
47574903 f( (Chuck_Type *)(*mem_sp), mem_sp+1, &retval, vm, shred, Chuck_DL_Api::Api::instance() );
4758− // clean up memory stack
4904+ // if we have a func def | 1.4.2.1 (ge) added
4905+ if( m_func_ref != NULL )
4906+ {
4907+ // cleanup / release objects on the arg list
4908+ // context: this should be done here for builtin/import functions
4909+ // user-defined functions do their own arg list cleanup
4910+ func_release_args( vm, m_func_ref->def->arg_list, mem_sp+1 );
4911+ }
4912+ // pop memory stack pointer
47594913 mem_sp -= push;
47604914
47614915 // push the return
@@ -4764,6 +4918,26 @@ void Chuck_Instr_Func_Call_Static::execute( Chuck_VM * vm, Chuck_VM_Shred * shre · 2 unmodified lines
47644918 {
47654919 // push the return args
47664920 push_( reg_sp, retval.v_uint );
4921+
4922+ // 1.4.2.1 (ge) | added -- ensure ref count
4923+ if( m_func_ref && isobj(vm->env(), m_func_ref->def->ret_type) )
4924+ {
4925+ // get as object pointer
4926+ Chuck_VM_Object * obj = (Chuck_VM_Object *) retval.v_uint;
4927+ if( obj )
4928+ {
4929+ // check if refcount is 0
4930+ if( obj->m_ref_count == 0 )
4931+ {
4932+ EM_log( CK_LOG_FINE, "%s(...) returned object with refcount==0", S_name(m_func_ref->def->name) );
4933+ EM_pushlog();
4934+ EM_log( CK_LOG_FINE, "auto-incrementing object refcount to 1..." );
4935+ EM_poplog();
4936+ }
4937+ // always add reference to returned objects (should release outside)
4938+ obj->add_ref();
4939+ }
4940+ }
47674941 }
47684942 else if( m_val == kindof_FLOAT ) // ISSUE: 64-bit (fixed 1.3.1.0)
47694943 {
@@ -7461,17 +7635,27 @@ void Chuck_Instr_Hack::execute( Chuck_VM * vm, Chuck_VM_Shred * shred ) · 2691 unmodified lines
74617635 if( m_type_ref->size == sz_INT && iskindofint(vm->env(), m_type_ref) ) // ISSUE: 64-bit (fixed 1.3.1.0)
74627636 {
74637637 t_CKINT * sp = (t_CKINT *)shred->reg->sp;
7464− if( !isa( m_type_ref, vm->env()->t_string ) || *(sp-1) == 0 )
7638+ Chuck_Object * obj = ((Chuck_Object *)*(sp-1));
7639+ if( !isa( m_type_ref, vm->env()->t_string ) || *(sp-1) == 0 )
74657640 {
74667641 // print it
7467− if( *(sp-1) == 0 && isa( m_type_ref, vm->env()->t_object ) )
7468− CK_FPRINTF_STDERR( "null :(%s)\n", m_type_ref->c_name() );
7642+ if( isa( m_type_ref, vm->env()->t_object ) )
7643+ {
7644+ if( obj == NULL )
7645+ {
7646+ CK_FPRINTF_STDERR( "null :(%s)\n", m_type_ref->c_name() );
7647+ }
7648+ else
7649+ {
7650+ CK_FPRINTF_STDERR( "0x%lx :(%s|refcount=%d)\n", *(sp-1), m_type_ref->c_name(), obj->m_ref_count );
7651+ }
7652+ }
74697653 else
7470− CK_FPRINTF_STDERR( "%ld :(%s)\n", *(sp-1), m_type_ref->c_name() );
7654+ CK_FPRINTF_STDERR( "%d :(%s)\n", *(sp-1), m_type_ref->c_name() );
74717655 }
74727656 else
74737657 {
7474− CK_FPRINTF_STDERR( "\"%s\" : (%s)\n", ((Chuck_String *)*(sp-1))->str().c_str(), m_type_ref->c_name() );
7658+ CK_FPRINTF_STDERR( "\"%s\" :(%s)\n", ((Chuck_String *)obj)->str().c_str(), m_type_ref->c_name() );
74757659 }
74767660 }
74777661 else if( m_type_ref->size == sz_FLOAT ) // ISSUE: 64-bit (fixed 1.3.1.0)
@@ -7593,11 +7777,12 @@ void Chuck_Instr_Gack::execute( Chuck_VM * vm, Chuck_VM_Shred * shred ) · 115 unmodified lines
75937777 {
75947778 if( isa( type, vm->env()->t_object ) )
75957779 {
7780+ Chuck_Object * obj = ((Chuck_Object *)(*sp));
75967781 // print it
75977782 if( *(sp) == 0 )
75987783 CK_FPRINTF_STDERR( "null " );
75997784 else
7600− CK_FPRINTF_STDERR( "0x%lx ", *(sp) );
7785+ CK_FPRINTF_STDERR( "0x%lx (refcount=%d)", *(sp), obj->m_ref_count );
76017786 }
76027787 else
76037788 {
src/core/chuck_instr.h+44−5
@@ -2977,6 +2977,7 @@ public: · 2976 unmodified lines
29772977 //-----------------------------------------------------------------------------
29782978 // name: struct Chuck_Instr_Reg_AddRef_Object3
29792979 // desc: added 1.3.0.0 -- does the ref add in-place
2980+// operates on top of the register stack
29802981 //-----------------------------------------------------------------------------
29812982 struct Chuck_Instr_Reg_AddRef_Object3 : public Chuck_Instr
29822983 {
@@ -2989,7 +2990,7 @@ public: · 6 unmodified lines
29892990
29902991 //-----------------------------------------------------------------------------
29912992 // name: struct Chuck_Instr_Release_Object
2992−// desc: ...
2993+// desc: release object on stack by its offset
29932994 //-----------------------------------------------------------------------------
29942995 struct Chuck_Instr_Release_Object : public Chuck_Instr
29952996 {
@@ -3018,6 +3019,38 @@ public: · 22 unmodified lines
30183019
30193020
30203021
3022+//-----------------------------------------------------------------------------
3023+// name: struct Chuck_Instr_Release_Object3_Pop_Word
3024+// desc: release object reference + pop from reg stack | 1.4.2.1 (ge) added
3025+// the variant assumes object pointer directly on stack (not offset)
3026+//-----------------------------------------------------------------------------
3027+struct Chuck_Instr_Release_Object3_Pop_Word : public Chuck_Instr
3028+{
3029+public:
3030+ virtual void execute( Chuck_VM * vm, Chuck_VM_Shred * shred );
3031+};
3032+
3033+
3034+
3035+
3036+//-----------------------------------------------------------------------------
3037+// name: struct Chuck_Instr_Release_Object4
3038+// desc: release object reference from reg stack (no pop) | 1.4.2.1 (ge) added
3039+// the variant assumes object pointer directly on stack (not offset)
3040+//-----------------------------------------------------------------------------
3041+struct Chuck_Instr_Release_Object4 : public Chuck_Instr_Unary_Op
3042+{
3043+public:
3044+ Chuck_Instr_Release_Object4( t_CKUINT offset )
3045+ { this->set( offset ); }
3046+
3047+public:
3048+ virtual void execute( Chuck_VM * vm, Chuck_VM_Shred * shred );
3049+};
3050+
3051+
3052+
3053+
30213054 //-----------------------------------------------------------------------------
30223055 // name: struct Chuck_Instr_Func_To_Code
30233056 // desc: Chuck_Func * to Chuck_VM_Code *
@@ -3051,11 +3084,14 @@ public: · 27 unmodified lines
30513084 struct Chuck_Instr_Func_Call_Member : public Chuck_Instr_Unary_Op
30523085 {
30533086 public:
3054− Chuck_Instr_Func_Call_Member( t_CKUINT ret_size )
3055− { this->set( ret_size ); }
3087+ Chuck_Instr_Func_Call_Member( t_CKUINT ret_size, Chuck_Func * func_ref )
3088+ { this->set( ret_size ); m_func_ref = func_ref; }
30563089
30573090 public:
30583091 virtual void execute( Chuck_VM * vm, Chuck_VM_Shred * shred );
3092+
3093+public:
3094+ Chuck_Func * m_func_ref; // 1.4.2.1 (ge) | added for arg list cleanup
30593095 };
30603096
30613097
@@ -3068,11 +3104,14 @@ public: · 6 unmodified lines
30683104 struct Chuck_Instr_Func_Call_Static : public Chuck_Instr_Unary_Op
30693105 {
30703106 public:
3071− Chuck_Instr_Func_Call_Static( t_CKUINT ret_size )
3072− { this->set( ret_size ); }
3107+ Chuck_Instr_Func_Call_Static( t_CKUINT ret_size, Chuck_Func * func_ref )
3108+ { this->set( ret_size ); m_func_ref = func_ref; }
30733109
30743110 public:
30753111 virtual void execute( Chuck_VM * vm, Chuck_VM_Shred * shred );
3112+
3113+public:
3114+ Chuck_Func * m_func_ref; // 1.4.2.1 (ge) | added for arg list cleanup
30763115 };
30773116
30783117
src/core/chuck_lang.cpp+6−1
@@ -1062,13 +1062,18 @@ CK_DLL_MFUN( object_toString ) · 1061 unmodified lines
10621062 RETURN->v_object = NULL;
10631063 return;
10641064 }
1065+ // 1.4.2.1 (ge) assign to object
1066+ OBJ_MEMBER_UINT(SELF, Object_offset_string) = (t_CKUINT)str;
1067+ // 1.4.2.1 (ge) | add ref
1068+ str->add_ref();
1069+
10651070 // set it
10661071 ostringstream strout( ostringstream::out );
10671072 // get the type
10681073 Chuck_Type * type = SELF->type_ref;
10691074 // write
10701075 strout.setf( ios::hex, ios::basefield );
1071− strout << ((type != NULL) ? type->c_name() : "[VOID]") << ":" << (t_CKUINT)SELF;
1076+ strout << ((type != NULL) ? type->c_name() : "[VOID]") << ":" << (t_CKUINT)SELF << " (refcount=" << SELF->m_ref_count << ")";
10721077 strout.flush();
10731078
10741079 // done
src/core/chuck_oo.cpp+11−2
@@ -125,8 +125,14 @@ void Chuck_VM_Object::add_ref() · 124 unmodified lines
125125 //-----------------------------------------------------------------------------
126126 void Chuck_VM_Object::release()
127127 {
128− // make sure there is at least one reference
129− assert( m_ref_count > 0 );
128+ // check
129+ if( m_ref_count <= 0 )
130+ {
131+ // print error
132+ EM_error3( "[chuck]: internal error: Object.release() refcount == %d", m_ref_count );
133+ // make sure there is at least one reference
134+ assert( m_ref_count > 0 );
135+ }
130136 // decrement
131137 m_ref_count--;
132138
@@ -148,6 +154,9 @@ void Chuck_VM_Object::release() · 15 unmodified lines
148154 *(int *)0 = 1;
149155 }
150156
157+ // log | 1.4.2.1 (ge) added
158+ EM_log( CK_LOG_FINEST, "reclaiming %s: 0x%08x", typeid(*this).name(), this );
159+
151160 // tell the object manager to set this free
152161 // Chuck_VM_Alloc::instance()->free_object( this );
153162 // REFACTOR-2017: doing this for now
src/core/chuck_parse.cpp+404−0
@@ -278,3 +278,407 @@ t_CKBOOL SyntaxQuery::parseLine( const std::string & line, SyntaxTokenList & tok · 277 unmodified lines
278278
279279 return TRUE;
280280 }
281+
282+
283+
284+
285+
286+//-----------------------------------------------------------------------------
287+// absyn to string functions
288+//-----------------------------------------------------------------------------
289+#include "chuck_type.h"
290+string absyn_exp2str( a_Exp exp, t_CKBOOL iterate = true );
291+string absyn_binary2str( a_Exp_Binary binary );
292+string absyn_unary2str( a_Exp_Unary unary );
293+string absyn_op2str( ae_Operator op );
294+string absyn_cast2str( a_Exp_Cast cast );
295+string absyn_postfix2str( a_Exp_Postfix postfix );
296+string absyn_dur2str( a_Exp_Dur dur );
297+string absyn_primary2str( a_Exp_Primary primary );
298+string absyn_array2str( a_Exp_Array array );
299+string absyn_func_call2str( a_Exp_Func_Call func_call );
300+string absyn_dot_member2str( a_Exp_Dot_Member dot_member );
301+string absyn_exp_if2str( a_Exp_If exp_if );
302+string absyn_decl2str( a_Exp_Decl decl );
303+
304+
305+
306+
307+//-----------------------------------------------------------------------------
308+// name: absyn2str()
309+// desc: convert abstract syntax exp to string
310+//-----------------------------------------------------------------------------
311+string absyn2str( a_Exp exp )
312+{
313+ return absyn_exp2str( exp ) + ";";
314+}
315+
316+
317+
318+
319+//-----------------------------------------------------------------------------
320+// name: absyn_exp2str()
321+// desc: convert abstract syntax exp to string
322+//-----------------------------------------------------------------------------
323+string absyn_exp2str( a_Exp exp, t_CKBOOL iterate )
324+{
325+ string str = "";
326+
327+ // loop over
328+ while( exp )
329+ {
330+ switch( exp->s_type )
331+ {
332+ case ae_exp_binary:
333+ str += absyn_binary2str( &exp->binary );
334+ break;
335+
336+ case ae_exp_unary:
337+ str += absyn_unary2str( &exp->unary );
338+ break;
339+
340+ case ae_exp_cast:
341+ str += absyn_cast2str( &exp->cast );
342+ break;
343+
344+ case ae_exp_postfix:
345+ str += absyn_postfix2str( &exp->postfix );
346+ break;
347+
348+ case ae_exp_dur:
349+ str += absyn_dur2str( &exp->dur );
350+ break;
351+
352+ case ae_exp_primary:
353+ str += absyn_primary2str( &exp->primary );
354+ break;
355+
356+ case ae_exp_array:
357+ str += absyn_array2str( &exp->array );
358+ break;
359+
360+ case ae_exp_func_call:
361+ str += absyn_func_call2str( &exp->func_call );
362+ break;
363+
364+ case ae_exp_dot_member:
365+ str += absyn_dot_member2str( &exp->dot_member );
366+ break;
367+
368+ case ae_exp_if:
369+ str += absyn_exp_if2str( &exp->exp_if );
370+ break;
371+
372+ case ae_exp_decl:
373+ str += absyn_decl2str( &exp->decl );
374+ break;
375+
376+ default:
377+ return "";
378+ }
379+
380+ // implicit cast
381+ if( exp->cast_to != NULL )
382+ break;
383+
384+ // break
385+ if( !iterate ) break;
386+
387+ // next
388+ exp = exp->next;
389+ // if more
390+ if( exp ) str += ", ";
391+ }
392+
393+ return str;
394+}
395+
396+
397+
398+
399+//-----------------------------------------------------------------------------
400+// name: absyn_op2str()
401+// desc: convert abstract syntax operator to string
402+//-----------------------------------------------------------------------------
403+string absyn_op2str( ae_Operator op )
404+{
405+ string str;
406+ // get operation string
407+ switch( op )
408+ {
409+ case ae_op_plus: str = "+"; break;
410+ case ae_op_plus_chuck: str = "+=>"; break;
411+ case ae_op_minus: str = "-"; break;
412+ case ae_op_minus_chuck: str = "-=>"; break;
413+ case ae_op_times: str = "*"; break;
414+ case ae_op_times_chuck: str = "*=>"; break;
415+ case ae_op_divide: str = "/"; break;
416+ case ae_op_divide_chuck: str = "/=>"; break;
417+ case ae_op_s_or: str = "|"; break;
418+ case ae_op_s_or_chuck: str = "|=>"; break;
419+ case ae_op_s_and: str = "&"; break;
420+ case ae_op_s_and_chuck: str = "&=>"; break;
421+ case ae_op_shift_left: str = "<<"; break;
422+ case ae_op_shift_left_chuck: str = "<<=>"; break;
423+ case ae_op_shift_right: str = ">>"; break;
424+ case ae_op_shift_right_chuck: str = ">>=>"; break;
425+ case ae_op_percent: str = "%"; break;
426+ case ae_op_percent_chuck: str = "%=>"; break;
427+ case ae_op_s_xor: str = "^"; break;
428+ case ae_op_s_xor_chuck: str = "^=>"; break;
429+ case ae_op_chuck: str = "=>"; break;
430+ case ae_op_unchuck: str = "=<"; break;
431+ case ae_op_upchuck: str = "=^"; break;
432+ case ae_op_at_chuck: str = "@=>"; break;
433+ case ae_op_s_chuck: str = ""; break;
434+ case ae_op_eq: str = "=="; break;
435+ case ae_op_neq: str = "!="; break;
436+ case ae_op_lt: str = "<"; break;
437+ case ae_op_le: str = "<="; break;
438+ case ae_op_gt: str = ">"; break;
439+ case ae_op_ge: str = ">="; break;
440+ case ae_op_and: str = "&&"; break;
441+ case ae_op_or: str = "||"; break;
442+ case ae_op_arrow_left: str = "<-"; break;
443+ case ae_op_arrow_right: str = "->"; break;
444+
445+ // unary
446+ case ae_op_plusplus: str = "++"; break;
447+ case ae_op_minusminus: str = "--"; break;
448+ case ae_op_tilda: str = "~"; break;
449+ case ae_op_exclamation: str = "!"; break;
450+ case ae_op_spork: str = "spork ~"; break;
451+ case ae_op_new: str = "new"; break;
452+
453+ // not used
454+ case ae_op_typeof: str = "typeof"; break;
455+ case ae_op_sizeof: str = "sizeof"; break;
456+ default: str = "[OP]"; break;
457+ }
458+ return str;
459+}
460+
461+
462+
463+
464+//-----------------------------------------------------------------------------
465+// name: absyn_binary2str()
466+// desc: convert abstract syntax binary to string
467+//-----------------------------------------------------------------------------
468+string absyn_binary2str( a_Exp_Binary binary )
469+{
470+ // (lhs OP rhs)
471+ return "(" + absyn_exp2str(binary->lhs)
472+ + absyn_op2str(binary->op)
473+ + absyn_exp2str(binary->rhs) + ")";
474+}
475+
476+
477+
478+
479+//-----------------------------------------------------------------------------
480+// name: absyn_unary2str()
481+// desc: convert abstract syntax unary to string
482+//-----------------------------------------------------------------------------
483+string absyn_unary2str( a_Exp_Unary unary )
484+{
485+ return absyn_op2str(unary->op) + " " + absyn_exp2str(unary->exp);
486+}
487+
488+
489+
490+
491+//-----------------------------------------------------------------------------
492+// name: absyn_cast2str()
493+// desc: convert abstract syntax cast to string
494+//-----------------------------------------------------------------------------
495+string absyn_cast2str( a_Exp_Cast cast )
496+{
497+ Chuck_Type * to = cast->self->type;
498+ return "$ " + to->name;
499+}
500+
501+
502+
503+
504+//-----------------------------------------------------------------------------
505+// name: absyn_cast2str()
506+// desc: convert abstract syntax cast to string
507+//-----------------------------------------------------------------------------
508+string absyn_postfix2str( a_Exp_Postfix postfix )
509+{
510+ return absyn_exp2str( postfix->exp ) + absyn_op2str( postfix->op );
511+}
512+
513+
514+
515+
516+//-----------------------------------------------------------------------------
517+// name: absyn_dur2str()
518+// desc: convert abstract syntax dur to string
519+//-----------------------------------------------------------------------------
520+string absyn_dur2str( a_Exp_Dur dur )
521+{
522+ return absyn_exp2str(dur->base) + "::" + absyn_exp2str(dur->unit);
523+}
524+
525+
526+
527+
528+//-----------------------------------------------------------------------------
529+// name: absyn_primary2str()
530+// desc: convert abstract syntax primary to string
531+//-----------------------------------------------------------------------------
532+string absyn_primary2str( a_Exp_Primary primary )
533+{
534+ string str;
535+
536+ // find out exp
537+ switch( primary->s_type )
538+ {
539+ case ae_primary_var: str = S_name(primary->var); break;
540+ case ae_primary_num: str = to_string(primary->num); break;
541+ case ae_primary_float: str = primary->fnum; break;
542+ case ae_primary_str: str = primary->str; break;
543+ case ae_primary_char: str = primary->chr; break;
544+ case ae_primary_array: str = "["+absyn_exp2str(primary->array->exp_list)+"]"; break;
545+ case ae_primary_complex: str = "#("+absyn_exp2str(primary->complex->re)+")"; break;
546+ case ae_primary_polar: str = "%("+absyn_exp2str(primary->polar->mod)+")"; break;
547+ case ae_primary_vec: str = "@("+absyn_exp2str(primary->vec->args)+")"; break;
548+ case ae_primary_exp: str = absyn_exp2str(primary->exp); break;
549+ case ae_primary_hack: str = "<<< " + absyn_exp2str(primary->exp) + " >>>"; break;
550+ case ae_primary_nil: str = "null"; break;
551+ default: str ="[PRIMARY]"; break;
552+ }
553+
554+ return str;
555+}
556+
557+
558+
559+
560+//-----------------------------------------------------------------------------
561+// name: absyn_array2str()
562+// desc: convert abstract syntax array to string
563+//-----------------------------------------------------------------------------
564+string absyn_array2str( a_Exp_Array array )
565+{
566+ string str;
567+
568+ t_CKUINT depth = 0;
569+ a_Array_Sub sub = NULL;
570+ t_CKBOOL is_str = FALSE;
571+
572+ // get the sub
573+ sub = array->indices;
574+ // expressions
575+ a_Exp e = sub->exp_list;
576+
577+ // base of the array
578+ str = absyn_exp2str(array->base);
579+
580+ // loop over
581+ while( e )
582+ {
583+ // each index
584+ str += "[" + absyn_exp2str(e,false) + "]";
585+ // advance
586+ e = e->next;
587+ }
588+
589+ return str;
590+}
591+
592+
593+
594+
595+//-----------------------------------------------------------------------------
596+// name: absyn_func_call2str()
597+// desc: convert abstract syntax func_call to string
598+//-----------------------------------------------------------------------------
599+string absyn_func_call2str( a_Exp_Func_Call func_call )
600+{
601+ string str;
602+ // function name
603+ str = absyn_exp2str( func_call->func );
604+ // args
605+ str += "(" + absyn_exp2str( func_call->args ) + ")";
606+ // done
607+ return str;
608+}
609+
610+
611+
612+
613+//-----------------------------------------------------------------------------
614+// name: absyn_dot_member2str()
615+// desc: convert abstract syntax dot_member to string
616+//-----------------------------------------------------------------------------
617+string absyn_dot_member2str( a_Exp_Dot_Member dot_member )
618+{
619+ return absyn_exp2str(dot_member->base) + "." + S_name(dot_member->xid);
620+}
621+
622+
623+
624+
625+//-----------------------------------------------------------------------------
626+// name: absyn_exp_if2str()
627+// desc: convert abstract syntax exp_if to string
628+//-----------------------------------------------------------------------------
629+string absyn_exp_if2str( a_Exp_If exp_if )
630+{
631+ return "("+absyn_exp2str(exp_if->cond) + "?" + absyn_exp2str(exp_if->if_exp)
632+ + ":" + absyn_exp2str(exp_if->else_exp)+")";
633+}
634+
635+
636+
637+
638+//-----------------------------------------------------------------------------
639+// name: absyn_decl2str()
640+// desc: convert abstract syntax decl to string
641+//-----------------------------------------------------------------------------
642+string absyn_decl2str( a_Exp_Decl decl )
643+{
644+ a_Var_Decl_List list = decl->var_decl_list;
645+ string str;
646+ a_Exp e = NULL;
647+
648+ if( decl->is_global ) str += "global ";
649+ else if( decl->is_static ) str += "static ";
650+
651+ while( list )
652+ {
653+ // type
654+ str += list->var_decl->value->type->name + " " + (decl->type->ref ? "@" : "") + list->var_decl->value->name;
655+ // array?
656+ if( list->var_decl->array )
657+ {
658+ e = list->var_decl->array->exp_list;
659+ if( e )
660+ {
661+ // loop over
662+ while( e )
663+ {
664+ // each index
665+ str += "[" + absyn_exp2str(e,false) + "]";
666+ // advance
667+ e = e->next;
668+ }
669+ }
670+ else
671+ {
672+ for( t_CKINT i = 0; i < list->var_decl->value->type->array_depth; i++ )
673+ {
674+ str += "[]";
675+ }
676+ }
677+ }
678+
679+ list = list->next;
680+ if( list ) str += ",";
681+ }
682+
683+ return str;
684+}
src/core/chuck_parse.h+2−0
@@ -63,6 +63,8 @@ t_CKBOOL chuck_parse( c_constr fname, FILE * fd = NULL, c_constr code = NULL ); · 62 unmodified lines
6363 // reset the parser
6464 void reset_parse( );
6565
66+// convert abstract syntax expression to string
67+std::string absyn2str( a_Exp exp );
6668
6769 // syntax highlighting tools
6870 #include <vector>
src/core/ulib_machine.cpp+34−2
@@ -40,6 +40,25 @@ · 39 unmodified lines
4040 #include <string>
4141 using namespace std;
4242
43+// exports
44+CK_DLL_SFUN( machine_crash_impl );
45+CK_DLL_SFUN( machine_add_impl );
46+CK_DLL_SFUN( machine_spork_impl );
47+CK_DLL_SFUN( machine_remove_impl );
48+CK_DLL_SFUN( machine_replace_impl );
49+CK_DLL_SFUN( machine_status_impl );
50+CK_DLL_SFUN( machine_intsize_impl );
51+CK_DLL_SFUN( machine_shreds_impl );
52+CK_DLL_SFUN( machine_realtime_impl );
53+CK_DLL_SFUN( machine_silent_impl );
54+CK_DLL_SFUN( machine_eval_impl );
55+CK_DLL_SFUN( machine_eval2_impl );
56+CK_DLL_SFUN( machine_eval3_impl );
57+CK_DLL_SFUN( machine_version_impl );
58+CK_DLL_SFUN( machine_setloglevel_impl );
59+CK_DLL_SFUN( machine_getloglevel_impl );
60+CK_DLL_SFUN( machine_refcount_impl);
61+
4362
4463
4564
@@ -154,7 +173,7 @@ DLL_QUERY machine_query( Chuck_DL_Query * QUERY ) · 108 unmodified lines
154173 QUERY->add_sfun( QUERY, machine_version_impl, "string", "version" );
155174 QUERY->doc_func( QUERY, "return version string" );
156175
157− // get version string
176+ // get set loglevel
158177 QUERY->add_sfun( QUERY, machine_setloglevel_impl, "int", "loglevel" );
159178 QUERY->add_arg( QUERY, "int", "level" );
160179 QUERY->doc_func( QUERY, "set log level\n"
@@ -170,10 +189,15 @@ DLL_QUERY machine_query( Chuck_DL_Query * QUERY ) · 9 unmodified lines
170189 " |- 9: FINEST\n"
171190 " |- 10: ALL" );
172191
173− // get version string
192+ // get log level
174193 QUERY->add_sfun( QUERY, machine_getloglevel_impl, "int", "loglevel" );
175194 QUERY->doc_func( QUERY, "get log level." );
176195
196+ // get reference count for any object
197+ QUERY->add_sfun( QUERY, machine_refcount_impl, "int", "refcount" );
198+ QUERY->add_arg( QUERY, "Object", "obj" );
199+ QUERY->doc_func( QUERY, "get an object's current internal reference count; this is intended for testing or curiosity; NOTE: this function intentionally does not take into account any reference counting related to the calling of this function (normally all functions increments the reference count for objects passed as arguments and decrements upon returning)" );
200+
177201 // end class
178202 QUERY->end_class( QUERY );
179203
@@ -399,3 +423,11 @@ CK_DLL_SFUN( machine_getloglevel_impl ) · 219 unmodified lines
399423 // return
400424 RETURN->v_int = VM->carrier()->chuck->getLogLevel();
401425 }
426+
427+CK_DLL_SFUN( machine_refcount_impl )
428+{
429+ // get the argument
430+ Chuck_Object * obj = (Chuck_Object *)GET_NEXT_OBJECT(ARGS);
431+ // return (-1 to account for the extra refcount as part of calling this function!)
432+ RETURN->v_int = obj != NULL ? obj->m_ref_count-1 : 0;
433+}
src/core/ulib_machine.h+1−17
@@ -53,23 +53,7 @@ t_CKBOOL machine_init( Chuck_Compiler * compiler, proc_msg_func func ); · 52 unmodified lines
5353 // machine exports
5454 t_CKUINT machine_intsize();
5555
56−// exports
57−CK_DLL_SFUN( machine_crash_impl );
58−CK_DLL_SFUN( machine_add_impl );
59−CK_DLL_SFUN( machine_spork_impl );
60−CK_DLL_SFUN( machine_remove_impl );
61−CK_DLL_SFUN( machine_replace_impl );
62−CK_DLL_SFUN( machine_status_impl );
63−CK_DLL_SFUN( machine_intsize_impl );
64−CK_DLL_SFUN( machine_shreds_impl );
65−CK_DLL_SFUN( machine_realtime_impl );
66−CK_DLL_SFUN( machine_silent_impl );
67−CK_DLL_SFUN( machine_eval_impl );
68−CK_DLL_SFUN( machine_eval2_impl );
69−CK_DLL_SFUN( machine_eval3_impl );
70−CK_DLL_SFUN( machine_version_impl );
71−CK_DLL_SFUN( machine_setloglevel_impl );
72−CK_DLL_SFUN( machine_getloglevel_impl );
56+
7357
7458
7559 #endif
src/test/01-Basic/120.ck+20−0
@@ -0,0 +1,20 @@
1+fun vec3 missingReturn()
2+{
3+ // missing return
4+}
5+
6+fun vec3 hasReturn()
7+{
8+ return @(1,0,0);
9+}
10+
11+// missing return statements default to 0 as appropriate
12+// for return type; this tests that and that the stack
13+// is okay afterwards, even for larger-than-int return types
14+// like vec3
15+<<< hasReturn(), missingReturn() >>>;
16+
17+// before the fix in 1.4.2.1, the above would incorrectly print
18+// #(0.0000,0.0000,1.0000) #(0.0000,0.0000,0.0000)
19+// this implies the stack pointer is off, due to missingReturn
20+// not accounting for the size of its return type, vec3
src/test/01-Basic/120.txt+1−0
@@ -0,0 +1 @@
1+#(1.0000,0.0000,0.0000) #(0.0000,0.0000,0.0000)
src/test/05-Global/01b.txt+1−1
@@ -1 +1 @@
1−"success" : (string)
1+"success" :(string)
src/test/05-Global/102a.txt+3−3
@@ -1,5 +1,5 @@
1−"no i'm foo" : (string)
1+"no i'm foo" :(string)
22 [chuck](VM): sporking incoming shred: 2 (102b.ck)...
3−"no i'm foo" : (string)
4−"no i'm foo" : (string)
3+"no i'm foo" :(string)
4+"no i'm foo" :(string)
55 3 :(int)
src/test/test.py+1−1
@@ -47,7 +47,7 @@ def run_test(exe, path, filename, attempt): · 46 unmodified lines
4747 try:
4848 result = subprocess.check_output([exe, "--silent", "%s" % path], stderr=subprocess.STDOUT).decode("utf-8")
4949
50− if result.strip().endswith(("\"success\" : (string)",)):
50+ if result.strip().endswith(("\"success\" :(string)",)):
5151 successes += 1
5252
5353 else:

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t