diff --git a/src/core/chuck_dl.cpp b/src/core/chuck_dl.cpp index 2846c3b84..b7c436d49 100644 --- a/src/core/chuck_dl.cpp +++ b/src/core/chuck_dl.cpp @@ -70,7 +70,7 @@ void CK_DLL_CALL ck_remove_all_shreds( Chuck_VM * vm ); Chuck_DL_Api::Type CK_DLL_CALL ck_type_lookup( Chuck_VM * vm, const char * name ); t_CKBOOL CK_DLL_CALL ck_type_isequal( Chuck_Type * lhs, Chuck_Type * rhs ); t_CKBOOL CK_DLL_CALL ck_type_isa( Chuck_Type * lhs, Chuck_Type * rhs ); - +void CK_DLL_CALL ck_callback_on_instantiate( f_callback_on_instantiate callback, Chuck_Type * base_type, Chuck_VM * vm, t_CKBOOL shouldSetShredOrigin ); @@ -1952,7 +1952,8 @@ Chuck_DL_Api::Api::TypeApi::TypeApi() : lookup(ck_type_lookup), get_vtable_offset(ck_get_vtable_offset), is_equal(ck_type_isequal), -isa(ck_type_isa) +isa(ck_type_isa), +callback_on_instantiate(ck_callback_on_instantiate) { } @@ -2116,6 +2117,21 @@ t_CKBOOL CK_DLL_CALL ck_type_isa( Chuck_Type * lhs, Chuck_Type * rhs ) +//----------------------------------------------------------------------------- +// name: ck_callback_on_instantiate() +// desc: register a callback to be invoked whenever a base-type (or its subclass) +// is instantiated, with option for type system to auto-set shred origin if available +//----------------------------------------------------------------------------- +void CK_DLL_CALL ck_callback_on_instantiate( f_callback_on_instantiate callback, + Chuck_Type * base_type, Chuck_VM * vm, t_CKBOOL shouldSetShredOrigin ) +{ + // register the callback with chuck type + base_type->add_instantiate_cb( callback, shouldSetShredOrigin ); +} + + + + //----------------------------------------------------------------------------- // windows translation //----------------------------------------------------------------------------- diff --git a/src/core/chuck_dl.h b/src/core/chuck_dl.h index f16671ea4..309f78bbb 100644 --- a/src/core/chuck_dl.h +++ b/src/core/chuck_dl.h @@ -334,6 +334,8 @@ typedef t_CKBOOL (CK_DLL_CALL * f_mainthreadhook)( void * bindle ); typedef t_CKBOOL (CK_DLL_CALL * f_mainthreadquit)( void * bindle ); // shreds watcher callback typedef void (CK_DLL_CALL * f_shreds_watcher)( Chuck_VM_Shred * SHRED, t_CKINT CODE, t_CKINT PARAM, Chuck_VM * VM, void * BINDLE ); +// type instantiation callback +typedef void (CK_DLL_CALL * f_callback_on_instantiate)( Chuck_Type * typeInstantiated, Chuck_VM_Shred * originShred, Chuck_VM * VM ); } @@ -908,6 +910,8 @@ struct Api t_CKBOOL (CK_DLL_CALL * const is_equal)(Type lhs, Type rhs); // test if lhs is a type of rhs (e.g., SinOsc is a type of UGen) t_CKBOOL (CK_DLL_CALL * const isa)(Type lhs, Type rhs); + // register a callback to be invoked whenever a base-type (or its subclass) is instantiated, with option for type system to auto-set shred origin if available + void (CK_DLL_CALL * const callback_on_instantiate)( f_callback_on_instantiate callback, Type base_type, Chuck_VM * vm, t_CKBOOL shouldSetShredOrigin ); } * const type; // constructor diff --git a/src/core/chuck_instr.cpp b/src/core/chuck_instr.cpp index 96e52c61f..7ae713226 100644 --- a/src/core/chuck_instr.cpp +++ b/src/core/chuck_instr.cpp @@ -3991,7 +3991,7 @@ void Chuck_Instr_Pre_Constructor::execute( Chuck_VM * vm, Chuck_VM_Shred * shred // name: instantiate_object() // desc: ... //----------------------------------------------------------------------------- -t_CKBOOL initialize_object( Chuck_Object * object, Chuck_Type * type, Chuck_VM_Shred * shred, Chuck_VM * vm ) +t_CKBOOL initialize_object( Chuck_Object * object, Chuck_Type * type, Chuck_VM_Shred * shred, Chuck_VM * vm, t_CKBOOL setShredOrigin ) { // check if already initialized | 1.5.1.5 if( object->vtable != NULL ) return TRUE; @@ -4009,7 +4009,8 @@ t_CKBOOL initialize_object( Chuck_Object * object, Chuck_Type * type, Chuck_VM_S // UGens: needs shred for auto-disconnect when shred is removed // user-defined classes (that refer to global-scope variables): // ...needs shred to access the global-scope variables across sporking - if( type->ugen_info || type->originHint == te_originUserDefined ) + // setShredOrigin: if true, this is likely a registered callback_on_instantiate + if( type->ugen_info || type->originHint == te_originUserDefined || setShredOrigin ) { // set origin shred | 1.5.1.5 (ge) was: ugen->shred = shred; object->setOriginShred( shred ); @@ -4130,6 +4131,8 @@ Chuck_Object * instantiate_and_initialize_object( Chuck_Type * type, Chuck_VM_Sh Chuck_Object * object = NULL; Chuck_UGen * ugen = NULL; Chuck_UAna * uana = NULL; + vector instance_cbs; + t_CKBOOL setShredOrigin = FALSE; // sanity assert( type != NULL ); @@ -4184,9 +4187,24 @@ Chuck_Object * instantiate_and_initialize_object( Chuck_Type * type, Chuck_VM_Sh // check to see enough memory if( !object ) goto out_of_memory; + // check for callback + setShredOrigin = type->cbs_on_instantiate( instance_cbs ); + // initialize - if( !initialize_object( object, type, shred, vm ) ) goto error; + if( !initialize_object( object, type, shred, vm, setShredOrigin ) ) goto error; + + // check for callback + if( instance_cbs.size() ) + { + // loop over callbacks to call + for( t_CKUINT i = 0; i < instance_cbs.size(); i++ ) + { + // call it + instance_cbs[i].callback( type, shred, vm ); + } + } + // return the instantiated object return object; out_of_memory: diff --git a/src/core/chuck_instr.h b/src/core/chuck_instr.h index aedf4b67a..c1c7e796c 100644 --- a/src/core/chuck_instr.h +++ b/src/core/chuck_instr.h @@ -4232,8 +4232,8 @@ struct Chuck_Instr_Gack : public Chuck_Instr Chuck_Object * instantiate_and_initialize_object( Chuck_Type * type, Chuck_VM_Shred * shred ); Chuck_Object * instantiate_and_initialize_object( Chuck_Type * type, Chuck_VM * vm ); Chuck_Object * instantiate_and_initialize_object( Chuck_Type * type, Chuck_VM_Shred * shred, Chuck_VM * vm ); -// initialize object using Type -t_CKBOOL initialize_object( Chuck_Object * obj, Chuck_Type * type, Chuck_VM_Shred * shred, Chuck_VM * vm ); +// initialize object using Type | 1.5.1.5 (ge) added setShredOrigin flag +t_CKBOOL initialize_object( Chuck_Object * obj, Chuck_Type * type, Chuck_VM_Shred * shred, Chuck_VM * vm, t_CKBOOL setShredOrigin = FALSE ); // "throw exception" (halt current shred, print message) void ck_throw_exception(Chuck_VM_Shred * shred, const char * name); diff --git a/src/core/chuck_type.cpp b/src/core/chuck_type.cpp index 1817a81fe..51a686f1e 100644 --- a/src/core/chuck_type.cpp +++ b/src/core/chuck_type.cpp @@ -10393,3 +10393,90 @@ bool Chuck_Op_Overload::operator <( const Chuck_Op_Overload & other ) const // (aL, aR) < (bL, bR) iff (aL < bL) OR ( aL == bL && aR < bR ) return (aL < bL) || (aL == bL && aR < bR); } + + + + +//----------------------------------------------------------------------------- +// name: add_instantiate_cb() +// desc: register type instantiation callback +//----------------------------------------------------------------------------- +void Chuck_Type::add_instantiate_cb( f_callback_on_instantiate cb, t_CKBOOL setShredOrigin ) +{ + // avoid duplicate + for( t_CKUINT i = 0; i < m_cbs_on_instantiate.size(); i++ ) + { + // compare callback pointer + if( cb == m_cbs_on_instantiate[i].callback ) return; + } + // append + m_cbs_on_instantiate.push_back( CallbackOnInstantiate(cb, setShredOrigin) ); +} + + + + +//----------------------------------------------------------------------------- +// name: remove_instantiate_cb() +// desc: unregister type instantiation callback +//----------------------------------------------------------------------------- +void Chuck_Type::remove_instantiate_cb( f_callback_on_instantiate cb ) +{ + // iterator + vector::iterator it = m_cbs_on_instantiate.begin(); + // iterate + while( it != m_cbs_on_instantiate.end() ) + { + // check the callback + if( (*it).callback == cb ) + { + // erase while iterating | c++11 or higher + it = m_cbs_on_instantiate.erase( it ); + // m_cbs_on_instantiate.erase( it++ ); // before c++11 + } + else + { + it++; + } + } +} + + + + +//----------------------------------------------------------------------------- +// name: cbs_on_instantiate() +// desc: get vector of callbacks (including this and parents), return whether any requires setShredOrigin +//----------------------------------------------------------------------------- +t_CKBOOL Chuck_Type::cbs_on_instantiate( std::vector & results ) +{ + // clear + results.clear(); + // process this + return this->do_cbs_on_instantiate( results ); +} + + + + +//----------------------------------------------------------------------------- +// name: do_cbs_on_instantiate() +// desc: internal get vector of callbacks (including this and parents), return whether any requires setShredOrigin +//----------------------------------------------------------------------------- +t_CKBOOL Chuck_Type::do_cbs_on_instantiate( std::vector & results ) +{ + // number of callbacks in total + t_CKBOOL retval = 0; + // process parents + if( this->parent ) retval = this->parent->do_cbs_on_instantiate( results ); + // process this + for( t_CKUINT i = 0; i < m_cbs_on_instantiate.size(); i++ ) + { + // copy + results.push_back( m_cbs_on_instantiate[i] ); + // if and set shred origin + if( m_cbs_on_instantiate[i].shouldSetShredOrigin ) retval = TRUE; + } + // done + return retval; +} diff --git a/src/core/chuck_type.h b/src/core/chuck_type.h index 15d41bc92..160b5aa31 100644 --- a/src/core/chuck_type.h +++ b/src/core/chuck_type.h @@ -1009,6 +1009,32 @@ struct Chuck_Type : public Chuck_Object void apropos_vars( std::string & output, const std::string & prefix, t_CKBOOL inherited ); // dump info about examples void apropos_examples( std::string & output, const std::string & prefix ); + +public: + // struct to hold callback on instantiate + struct CallbackOnInstantiate + { + // whether to auto-set shred origin at instantiation; + // see t_CKBOOL initialize_object( ... ) + t_CKBOOL shouldSetShredOrigin; + // the callback + f_callback_on_instantiate callback; + // constructor + CallbackOnInstantiate( f_callback_on_instantiate cb = NULL, t_CKBOOL setShredOrigin = FALSE ) + : callback(cb), shouldSetShredOrigin(setShredOrigin) { } + }; + // register type instantiation callback + void add_instantiate_cb( f_callback_on_instantiate cb, t_CKBOOL setShredOrigin ); + // unregister type instantiation callback + void remove_instantiate_cb( f_callback_on_instantiate cb ); + // get vector of callbacks (including this and parents), return whether any requires setShredOrigin + t_CKBOOL cbs_on_instantiate( std::vector & results ); + +protected: + // vector of callbacks on instantiation of this type (or its subclass) + std::vector m_cbs_on_instantiate; + // internal get vector of callbacks (including this and parents), return whether any requires setShredOrigin + t_CKBOOL do_cbs_on_instantiate( std::vector & results ); }; diff --git a/src/test/01-Basic/213-spork-nested-var.ck b/src/test/01-Basic/213-spork-nested-var.ck new file mode 100644 index 000000000..573d3bdd9 --- /dev/null +++ b/src/test/01-Basic/213-spork-nested-var.ck @@ -0,0 +1,40 @@ +// test nested sporking, and accessing file-scope vars from +// nested sporked member function + +// accessing this var +5 => int a; + +// a (non-public) class +// (public classes wouldn't be able to access file-scope vars) +class Foo +{ + // a member function + fun void update( float dt ) + { + while( true ) + { + // access global-scope var a + a++; + // print + <<< a, dt >>>; + // advance time + 200::ms => now; + } + } + + fun void go() + { + // spork + spork ~ this.update(2); + // wait a long time (until the parent shred is done) + eon => now; + } +} + +// instantiate a Foo +Foo foo; +// call go +spork ~ foo.go(); +// let time pass +2.1::second => now; +// (should remove child and grandchild shreds as this shred exits) diff --git a/src/test/01-Basic/213-spork-nested-var.txt b/src/test/01-Basic/213-spork-nested-var.txt new file mode 100644 index 000000000..3fe444fca --- /dev/null +++ b/src/test/01-Basic/213-spork-nested-var.txt @@ -0,0 +1,11 @@ +6 2.000000 +7 2.000000 +8 2.000000 +9 2.000000 +10 2.000000 +11 2.000000 +12 2.000000 +13 2.000000 +14 2.000000 +15 2.000000 +16 2.000000 diff --git a/src/test/01-Basic/214-num-types.ck b/src/test/01-Basic/214-num-types.ck new file mode 100644 index 000000000..c1ac62cde --- /dev/null +++ b/src/test/01-Basic/214-num-types.ck @@ -0,0 +1,14 @@ +// test basic numerical type literals + +// int +<<< 1 >>>; +// float +<<< 2.0 >>>; +// complex +<<< #(3,4) >>>; +// polar +<<< %(1,pi/2) >>>; +// vec3 +<<< @(5,6,7) >>>; +// vec4 +<<< @(5,6,7,8) >>>; diff --git a/src/test/01-Basic/214-num-types.txt b/src/test/01-Basic/214-num-types.txt new file mode 100644 index 000000000..a96a15b5e --- /dev/null +++ b/src/test/01-Basic/214-num-types.txt @@ -0,0 +1,6 @@ +1 :(int) +2.000000 :(float) +#(3.0000,4.0000) :(complex) +%(1.0000,0.5000*pi) :(polar) +@(5.0000,6.0000,7.0000) :(vec3) +@(5.0000,6.0000,7.0000,8.0000) :(vec4) diff --git a/src/test/06-Errors/error-depend-var2.ck b/src/test/06-Errors/error-depend-var2.ck new file mode 100644 index 000000000..c9783d984 --- /dev/null +++ b/src/test/06-Errors/error-depend-var2.ck @@ -0,0 +1,7 @@ +// error case using a global-scope var before declaration + +// the use +<<< a >>>; + +// the decl +int a; diff --git a/src/test/06-Errors/error-depend-var2.txt b/src/test/06-Errors/error-depend-var2.txt new file mode 100644 index 000000000..dbf9e1d1b --- /dev/null +++ b/src/test/06-Errors/error-depend-var2.txt @@ -0,0 +1,3 @@ +error-depend-var2.ck:4:5: error: variable/member 'a' is used before declaration +[4] <<< a >>>; + ^