📄 oasisscriptrubyinterpreter.cpp
字号:
#include "oasisScriptRubyInterpreter.h"
#include "oasisScriptCommon.h"
#include "oasisScriptSystem.h"
#include "oasisVector3.h"
#include "oasisQuaternion.h"
namespace Oasis {
VALUE FuncallWrap(VALUE arg);
//Spontaneous structure to deal with some of Ruby's internal's ugliness.
struct RubyArgs {
VALUE recv;
int32 id;
int32 n;
VALUE *argv;
RubyArgs(VALUE recv, int32 id, int32 n, VALUE *argv) :
recv(recv), id(id), n(n), argv(argv) { }
};
scriptRubyInterpreter::scriptRubyInterpreter( void ) {
//Start the ruby interpreter
ruby_init( );
//Load the library paths
ruby_init_loadpath( );
}
scriptRubyInterpreter::~scriptRubyInterpreter( void ) {
//kill the ruby interpreter if we need to
ruby_finalize( );
}
void scriptRubyInterpreter::reset( void ) {
//Stop and restart the script...
//PENDING: Is there a better way to do this?
ruby_finalize( );
ruby_init( );
ruby_init_loadpath( );
}
bool scriptRubyInterpreter::executeScript( string &name ) {
int error;
//ruby_script sets the internal argv[0] to whatever is passed in.
ruby_script( ( char * ) name.c_str( ) );
rb_eval_string_protect( string( "load '" + name + "'" ).c_str( ), &error );
if( error ) {
if( error == 6 )
return 0;
scriptSystem::get().scriptLog( string( "While loading - " ) + name );
printError( );
}
return 1;
}
void *scriptRubyInterpreter::executeString( string &command, uint8 &type ) {
int state;
VALUE retv_ruby;
void *retv;
char *error;
//run the string and catch all exceptions
retv_ruby = rb_eval_string_protect( command.c_str( ), &state );
retv = ( void * ) convertToC( ( void * ) retv_ruby, type );
if ( state )
printError( );
return retv;
}
void *scriptRubyInterpreter::convertFromC( void *cObject, uint8 type ) {
VALUE retv;
if( type == 'b' ) {
//'b' - bool
bool * tmp = ( bool * ) cObject;
retv = ( *tmp ? Qtrue : Qfalse );
}
else if( type == 's' ) {
//'s' - char *
retv = rb_str_new2( ( char * ) cObject );
}
else if( type == 'd' ) {
//notice that RUBY only has 31 bit fixnums
//'d' - int16
int16 * tmp = ( int16 * ) cObject;
retv = INT2FIX( *tmp );
}
else if( type == 'i' ) {
//arbitrarily sized numbers
//'i' - int32
int32 * tmp = ( int32 * ) cObject;
retv = INT2NUM( *tmp );
}
else if( type == 'c' ) {
//'c' - character - Seen as an one element string
char *hold = new char[2];
hold[0]= *( uint8 * ) cObject;
hold[1]= 0;
retv = rb_str_new2( hold );
delete [] hold;
}
else if( type == 'f' || type == 'd' || type == 'r' ) {
//'f' - real
//'d' - real
//'r' - real
real * tmp = ( real * ) cObject;
retv = rb_float_new( *tmp );
}
else if( type == 'v' ) {
//'v' - vector(3-element array)
vector3 *value = ( vector3 * ) cObject;
retv = rb_ary_new3( 3, rb_float_new( value->x ),
rb_float_new( value->y ),
rb_float_new( value->z ) );
}
else if( type == 'q' ) {
//'q' - quaternion(4-element array)
quaternion *value = ( quaternion * ) cObject;
retv = rb_ary_new3( 4, rb_float_new( value->w ),
rb_float_new( value->x ),
rb_float_new( value->y ),
rb_float_new( value->z ) );
}
// This is not the fastest way to do garbage collection in ruby
// The absolute fastest is to combine everything into an rb_ary
// and allocate/deallocate it all at once. However, this option
// is not available to us unless we change the architechure.
rb_gc_register_address(&retv);
return ( void * ) retv;
}
void *scriptRubyInterpreter::convertToC( void *scriptObject, uint8 &type ) {
void * retv;
VALUE scriptObjectTrans = ( VALUE ) scriptObject;
if( NIL_P( scriptObjectTrans ) ) {
//nil = NULL
retv = NULL;
type = '0';
}
else if(FIXNUM_P( scriptObjectTrans )) {
//'i' = int32
retv = ( void * ) new int32( FIX2INT( scriptObjectTrans ) );
type = 'i';
}
else if( scriptObjectTrans == Qtrue ) {
//'b' = bool
retv = ( void * ) new bool( true );
type = 'b';
}
else if( scriptObjectTrans == Qfalse) {
//'b' = bool
retv = ( void * ) new bool( false );
type = 'b';
}
else
{
type = TYPE( scriptObjectTrans );
if( type == T_FLOAT )
{
//'r' = real
retv = ( void * ) new real( NUM2DBL( scriptObjectTrans ) );
type = 'r';
}
else if ( type == T_STRING )
{
//'s' = string
char *ni,*cp;
uint16 len;
ni = StringValuePtr( scriptObjectTrans );
len = strlen( ni );
cp = new char[len];
strcpy( cp, ni );
retv = ( void * ) cp;
type = 's';
}
else if ( type == T_BIGNUM )
{
//'i' = uint32, other than that, we cannot hold this value
retv = ( void * ) new int32( NUM2INT( scriptObjectTrans ) );
type = 'i';
}
else if ( type == T_ARRAY )
{
uint8 len = RARRAY( scriptObjectTrans )->len;
if( len == 3 || len == 4 ) {
//we can combine both quaternions/vectors using some nifty ifs.
void **vals = new ( void * )[len];
char *types = new char[len];
uint8 i, j;
bool allInts = 1;
for( i = 0; i < len; i++ ) {
//Recursively get the value and type for each element in the array.
vals[i] = convertToC(
( void * ) ( RARRAY( scriptObjectTrans )->ptr[i] ), type );
types[i] = type;
if( type == 'i' || type == 'f' || type == 'd' || type == 'r' )
continue;
scriptSystem::get( ).scriptLog(
string( "[!] Error, arrays returned " )+
string( "of size 3 must contain only " )+
string( "ints/reals to be a vector." ) );
string strtype = " ";
strtype[0] = type;
scriptSystem::get( ).scriptLog( "[!] type: " +
scriptSystem::get( ).toString( vals[i], type ) );
allInts = 0;
scriptSystem::get( ).freeVariable( vals[i], type );
break;
}
if( allInts ) {
if(len == 3) {
//You are responsible for freeing retv, scriptSystem::freeVariable
// 'v' - vector
vector3 *nvector = new vector3( );
real xyz[3];
//make sure the void * is casted to the right value
for( j=0; j<3; j++ ) {
if( types[j] == 'i')
xyz[j] = *( int32 *) vals[j];
if( types[j] == 'd' )
xyz[j] = *( int16 *) vals[j];
if( types[j] == 'r' || types[j] == 'f' )
xyz[j] = *( real *) vals[j];
}
nvector->x = xyz[0];
nvector->y = xyz[1];
nvector->z = xyz[2];
retv = ( void * ) nvector;
type = 'v';
}
else {
// 'v' - vector
quaternion *nquat = new quaternion( );
real wxyz[4];
//make sure the void * is casted to the right value
for( j=0; j<4; j++ ) {
if( types[j] == 'i')
wxyz[j] = *( int32 *) vals[j];
if( types[j] == 'd' )
wxyz[j] = *( int16 *) vals[j];
if( types[j] == 'r' || types[j] == 'f' )
wxyz[j] = *( real *) vals[j];
}
nquat->w = wxyz[0];
nquat->x = wxyz[1];
nquat->y = wxyz[2];
nquat->z = wxyz[3];
retv = ( void * ) nquat;
type = 'q';
}
}
else
{
type = '?';
retv = scriptObject;
}
//i is from the above 'for' loop
for( j=0; j<i; j++ )
{
scriptSystem::get( ).freeVariable( vals[j], types[j] );
}
delete [] vals;
delete [] types;
}
else {
scriptSystem::get( ).scriptLog(
string( "[!] Error, arrays returned " )+
string( "must be of size 3(vectors) or " )+
string( "4(quaternions)." ) );
type = '?';
retv = scriptObject;
}
}
else
{
//Find out the string representation of an unknown object.
// retv = ( void * ) convertToC( ( void * )rb_obj_as_string(
// scriptObjectTrans ), type );
type = '?';
//scriptSystem::get( ).scriptLog( StringValuePtr( scriptObjectTrans ) );
//PENDING: This causes random crashes, but is really really cool.
//PENDING: I think the problem has to do with GC in ruby, but maybe
//PENDING: rb_obj_as_string is just not as stable as one would hope.
//PENDING: Then again, it could also be convertToC( RB_STRING )...
}
}
return retv;
}
void *scriptRubyInterpreter::callFunction( string &name, string &types, void ** arguments, uint8 &returntype ) {
VALUE *arrayArgs;
void *result;
uint8 i;
//Convert arguments to a C-style ruby array
arrayArgs = ALLOC_N( VALUE, types.length( ) );
for( i=0; i < types.length( ); i++ ) {
arrayArgs[i] = ( VALUE ) arguments[i];
}
//PENDING: Call the function inside of a class instance? Can we do that by
//just extanding name to be "instance.funct"?
//rb_cModule is ruby's globally defined namespace
void * intermediate = ( void * ) executeFunction( rb_cModule,
rb_intern( name.c_str( ) ),
types.length( ),
arrayArgs );
result = ( void * ) convertToC( intermediate, returntype );
return ( void * ) result;
}
VALUE scriptRubyInterpreter::executeFunction( VALUE recv, int ID, int argc, VALUE *argv )
{
int error;
RubyArgs *args= new RubyArgs( recv, ID, argc, argv );
//Call the rb_funcall through FuncallWrap function,
// while protected against exceptions :)
VALUE result = rb_protect( FuncallWrap, ( VALUE ) args, &error );
if( error )
printError( );
delete args;
return result;
}
void scriptRubyInterpreter::printError( ) {
uint8 buf;
char *error;
// Print the error info to stdout
rb_p( ruby_errinfo );
//error = ( char * ) convertToC( ( void * )rb_obj_as_string( rb_inspect(
// ruby_errinfo ) ),buf );
//scriptSystem::get( ).scriptLog( error );
scriptSystem::get( ).scriptLog( StringValuePtr( ruby_errinfo ) );
rb_backtrace( );
//delete [] error;
}
void scriptRubyInterpreter::freeVariable( void *script_value ) {
// dispose array and flush all elements
VALUE rb_val = ( VALUE ) script_value;
rb_gc_unregister_address(&rb_val);
}
//This is a callback function to deal with some of ruby's ugliness.
VALUE FuncallWrap(VALUE arg) {
RubyArgs *a = ( RubyArgs * ) arg ;
return rb_funcall2( a->recv, a->id, a->n, a->argv );
}
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -