const char * abc_def::get_class_from_constructor( int method ) { for( int instance_index = 0; instance_index < m_instance.size(); ++instance_index ) { if( m_instance[ instance_index ]->m_iinit == method ) { return get_multiname( m_instance[ instance_index ]->m_name ); } } return NULL; }
class_info* abc_def::get_class_info(const tu_string& full_class_name) const { //TODO: implement namespace // find name tu_string class_name = full_class_name; const char* dot = strrchr(full_class_name.c_str(), '.'); if (dot) { class_name = dot + 1; } // maybe use hash instead of array for m_instance ? for (int i = 0; i < m_instance.size(); i++) { const tu_string& name = get_multiname(m_instance[i]->m_name); if (class_name == name) { return m_class[i].get(); } } return NULL; }
// interperate action script bytecode void as_3_function::execute ( array<as_value>& lregister, as_environment *env, as_value *result ) { // m_abc may be destroyed assert ( m_abc != NULL ); vm_stack &stack = *env; vm_stack &scope = env->m_scope; // some method have no body if ( m_code.size() == 0 ) { return; } int ip = 0; do { Uint8 opcode = m_code[ip++]; switch ( opcode ) { case 0x11: // iftrue { bool taken; //Follows ECMA-262 11.9.3 taken = stack.top ( 0 ).to_bool(); stack.drop ( 1 ); if ( taken ) { int offset = m_code[ip] | m_code[ip+1]<<8 | m_code[ip+2]<<16; ip += offset; } ip += 3; IF_VERBOSE_ACTION ( log_msg ( "EX: iftrue\t %s\n", taken? "taken": "not taken" ) ); } break; case 0x12: // iffalse { bool taken; //Follows ECMA-262 11.9.3 taken = !stack.top ( 0 ).to_bool(); stack.drop ( 1 ); if ( taken ) { int offset = m_code[ip] | m_code[ip+1]<<8 | m_code[ip+2]<<16; ip += offset; } ip += 3; IF_VERBOSE_ACTION ( log_msg ( "EX: iffalse\t %s\n", taken? "taken": "not taken" ) ); } break; case 0x14: // ifne { bool taken; //Follows ECMA-262 11.9.3 taken = !as_value::abstract_equality_comparison ( scope[ scope.size() - 2 ], scope[ scope.size() - 1 ] ); if ( taken ) { int offset = m_code[ip] | m_code[ip+1]<<8 | m_code[ip+2]<<16; ip += offset; } ip += 3; IF_VERBOSE_ACTION ( log_msg ( "EX: ifne\t %s\n", taken? "taken": "not taken" ) ); } break; case 0x1D: // popscope { scope.pop(); IF_VERBOSE_ACTION ( log_msg ( "EX: popscope\n" ) ); break; } case 0x20: // pushnull { as_value value; value.set_null(); stack.push ( value ); IF_VERBOSE_ACTION ( log_msg ( "EX: pushnull\n" ) ); } break; case 0x24: // pushbyte { int byte_value; ip += read_vu30 ( byte_value, &m_code[ip] ); stack.push ( byte_value ); IF_VERBOSE_ACTION ( log_msg ( "EX: pushbyte\t %d\n", byte_value ) ); break; } case 0x25: // pushshort { int val; ip += read_vu30 ( val, &m_code[ip] ); stack.push ( val ); IF_VERBOSE_ACTION ( log_msg ( "EX: pushshort\t %d\n", val ) ); break; } case 0x26: // pushtrue { stack.push ( true ); IF_VERBOSE_ACTION ( log_msg ( "EX: pushtrue\n" ) ); } break; case 0x27: // pushfalse { stack.push ( false ); IF_VERBOSE_ACTION ( log_msg ( "EX: pushfalse\n" ) ); } break; case 0x29: // pop the value from stack and discard it { stack.pop(); IF_VERBOSE_ACTION ( log_msg ( "EX: pop\n" ) ); break; } case 0x2A: // dup { IF_VERBOSE_ACTION ( log_msg ( "EX: dup %s\n", stack.top ( 0 ).to_xstring() ) ); stack.push ( stack.top ( 0 ) ); } break; case 0x2D: // pushint { int index; ip += read_vu30 ( index, &m_code[ip] ); int val = m_abc->get_integer ( index ); stack.push ( val ); IF_VERBOSE_ACTION ( log_msg ( "EX: pushint\t %d\n", val ) ); break; } case 0x2C: // pushstring { int index; ip += read_vu30 ( index, &m_code[ip] ); const char *val = m_abc->get_string ( index ); stack.push ( val ); IF_VERBOSE_ACTION ( log_msg ( "EX: pushstring\t '%s'\n", val ) ); break; } case 0x2F: // pushdouble { int index; ip += read_vu30 ( index, &m_code[ip] ); double val = m_abc->get_double ( index ); stack.push ( val ); IF_VERBOSE_ACTION ( log_msg ( "EX: pushdouble\t %f\n", val ) ); break; } case 0x30: // pushscope { as_value val = stack.pop(); scope.push ( val ); IF_VERBOSE_ACTION ( log_msg ( "EX: pushscope\t %s\n", val.to_xstring() ) ); break; } case 0x46: // callproperty { int index; ip += read_vu30 ( index, &m_code[ip] ); const char *name = m_abc->get_multiname ( index ); int arg_count; ip += read_vu30 ( arg_count, &m_code[ip] ); as_environment env ( get_player() ); for ( int i = 0; i < arg_count; i++ ) { env.push ( stack.top ( i ) ); } stack.drop ( arg_count ); as_value result; if ( stack.top ( 0 ).is_object() ) { as_object *obj = stack.top ( 0 ).to_object(); as_value func, func2; result.set_undefined(); if ( obj && obj->get_member ( name, &func ) ) { if ( func.is_function() ) { result = call_method ( func, &env, obj, arg_count, env.get_top_index() ); } else if ( func.to_object()->get_member ( "__call__", &func2 ) ) { //todo patch scope result = call_method ( func2, &env, obj, arg_count, env.get_top_index() ); } } } else { as_value func; if ( stack.top ( 0 ).find_property ( name, &func ) ) { result = call_method ( func, &env, stack.top ( 0 ), arg_count, env.get_top_index() ); } } IF_VERBOSE_ACTION ( log_msg ( "EX: callproperty\t 0x%p.%s(args:%d), result %s\n", stack.top ( 0 ).to_xstring(), name, arg_count, result.to_xstring() ) ); stack.drop ( 1 ); stack.push ( result ); } break; case 0x47: // returnvoid { IF_VERBOSE_ACTION ( log_msg ( "EX: returnvoid\t\n" ) ); result->set_undefined(); return; } case 0x48: // returnvalue { IF_VERBOSE_ACTION ( log_msg ( "EX: returnvalue \t%s\n", stack.top ( 0 ).to_xstring() ) ); *result = stack.pop(); return; } case 0x49: // constructsuper { // stack: object, arg1, arg2, ..., argn int arg_count; ip += read_vu30 ( arg_count, &m_code[ip] ); as_environment env ( get_player() ); for ( int i = 0; i < arg_count; i++ ) { env.push ( stack.pop() ); } gc_ptr<as_object> obj = stack.pop().to_object(); // Assume we are in a constructor tu_string class_name = m_abc->get_class_from_constructor ( m_method ); tu_string super_class_name = m_abc->get_super_class ( class_name ); as_object *super = obj.get_ptr(); while ( super->get_proto() ) { super = super->get_proto(); } as_function *function = m_abc->get_class_constructor ( super_class_name ); if ( !function ) { as_value value; if ( get_player()->get_global()->get_member ( super_class_name, &value ) ) { function = cast_to<as_function> ( value.to_object() ); } } assert ( function ); as_object *proto = super->create_proto ( function ); UNUSED ( proto ); call_method ( function, &env, obj.get_ptr(), arg_count, 0 ); //stack.top(0) = obj.get_ptr(); IF_VERBOSE_ACTION ( log_msg ( "EX: constructsuper\t 0x%p(args:%d)\n", obj.get_ptr(), arg_count ) ); break; } case 0x4A: //constructprop // Stack ..., obj, [ns], [name], arg1,...,argn => ..., value { int index; ip += read_vu30 ( index, &m_code[ip] ); const char *name = m_abc->get_multiname ( index ); const char *name_space = m_abc->get_multiname_namespace ( index ); UNUSED ( name_space ); int arg_count; ip += read_vu30 ( arg_count, &m_code[ip] ); as_environment env ( get_player() ); for ( int i = 0; i < arg_count; i++ ) { env.push ( stack.top ( i ) ); } stack.drop ( arg_count ); as_object *obj = stack.pop().to_object(); as_value func, func2; gc_ptr<as_object> new_object; if ( obj && obj->get_member ( name, &func ) ) { instance_info *ii = m_abc->get_instance_info ( name ); new_object = new as_object ( get_player() ); //:TODO: create prototype .... ( move instanciate class from character -> as_object ) new_object->set_instance ( ii ); new_object->create_traits ( m_abc.get_ptr(), ii ); call_method ( m_abc->get_class_constructor ( name ), &env, new_object.get_ptr(), arg_count, 0 ); } IF_VERBOSE_ACTION ( log_msg ( "EX: constructprop\t 0x%p.%s(args:%d)\n", obj, name, arg_count ) ); stack.push ( new_object.get_ptr() ); } break; case 0x4F: // callpropvoid, Call a property, discarding the return value. // Stack: ..., obj, [ns], [name], arg1,...,argn => ... { int index; ip += read_vu30 ( index, &m_code[ip] ); const char *name = m_abc->get_multiname ( index ); int arg_count; ip += read_vu30 ( arg_count, &m_code[ip] ); as_environment env ( get_player() ); for ( int i = 0; i < arg_count; i++ ) { env.push ( stack.top ( i ) ); } stack.drop ( arg_count ); as_object *obj = stack.pop().to_object(); as_value func, func2; if ( obj && obj->get_member ( name, &func ) ) { if ( func.is_function() ) { call_method ( func, &env, obj, arg_count, env.get_top_index() ); } else if ( func.to_object()->get_member ( "__call__", &func2 ) ) { //todo patch scope call_method ( func2, &env, obj, arg_count, env.get_top_index() ); } } else { if ( !obj ) { log_msg ( "Error #1010: A term is undefined and has no properties (%s call)\n", name ); } else { log_msg ( "Error #1006: value is not a function (%s call)\n", name ); } } IF_VERBOSE_ACTION ( log_msg ( "EX: callpropvoid\t 0x%p.%s(args:%d)\n", obj, name, arg_count ) ); break; } case 0x56: //newarray { int arg_count; ip += read_vu30 ( arg_count, &m_code[ip] ); as_array *array = new as_array ( get_player() ); int offset = stack.size() - arg_count; for ( int arg_index = 0; arg_index < arg_count; ++arg_index ) { array->push ( stack[ offset + arg_index ] ); } stack.resize ( offset + 1 ); stack.top ( 0 ) = array; IF_VERBOSE_ACTION ( log_msg ( "EX: newarray\t arg_count:%i\n", arg_count ) ); } break; case 0x58: // newclass { // stack: ..., basetype => ..., newclass int class_index; ip += read_vu30 ( class_index, &m_code[ip] ); IF_VERBOSE_ACTION ( log_msg ( "EX: newclass\t class index:%i\n", class_index ) ); // as_object* basetype = stack.top(0).to_object(); gc_ptr<as_class> new_class = new as_class ( get_player() ); //new_class->set_proto(basetype); new_class->set_class ( m_abc->get_class_info ( class_index ) ); as_environment env ( get_player() ); call_method ( m_abc->get_class_function ( class_index ), &env, new_class.get_ptr(), 0, 0 ); stack.top ( 0 ).set_as_object ( new_class.get_ptr() ); break; } case 0x5D: // findpropstrict { int index; ip += read_vu30 ( index, &m_code[ip] ); const char *name = m_abc->get_multiname ( index ); // search property in scope as_object *obj = scope.find_property ( name ); //Search for a script entry to execute as_function *func = m_abc->get_script_function ( name ); if ( obj == NULL && func != NULL ) { get_global()->set_member ( name, new as_object ( get_player() ) ); as_environment env ( get_player() ); call_method ( func, &env, get_global(), 0, 0 ); obj = get_global(); } IF_VERBOSE_ACTION ( log_msg ( "EX: findpropstrict\t %s, obj=0x%p\n", name, obj ) ); stack.push ( obj ); break; } case 0x5E: // findproperty, Search the scope stack for a property { int index; ip += read_vu30 ( index, &m_code[ip] ); const char *name = m_abc->get_multiname ( index ); const char *name_space = m_abc->get_multiname_namespace ( index ); UNUSED ( name_space ); as_object *obj = scope.find_property ( name ); if ( obj ) { IF_VERBOSE_ACTION ( log_msg ( "EX: findproperty\t '%s', obj=0x%p\n", name, obj ) ); stack.push ( obj ); } else { IF_VERBOSE_ACTION ( log_msg ( "EX: findproperty\t '%s', obj=global\n", name ) ); stack.push ( get_global() ); } break; } case 0x60: // getlex, Find and get a property. { int index; ip += read_vu30 ( index, &m_code[ip] ); const char *name = m_abc->get_multiname ( index ); // search and get property in scope as_value val; scope.get_property ( name, &val ); if ( val.is_undefined() ) { as_function *func = m_abc->get_script_function ( name ); if ( func != NULL ) { gc_ptr<as_object> object = new as_object ( get_player() ); get_global()->set_member ( name, object.get() ); as_environment env ( get_player() ); call_method ( func, &env, get_global(), 0, 0 ); val.set_as_object ( object ); } } IF_VERBOSE_ACTION ( log_msg ( "EX: getlex\t %s, value=%s\n", name, val.to_xstring() ) ); stack.push ( val ); break; } case 0x61: // setproperty { int index; ip += read_vu30 ( index, &m_code[ip] ); const char *name = m_abc->get_multiname ( index ); IF_VERBOSE_ACTION ( log_msg ( "EX: setproperty\t %s.%s, value=%s\n", stack.top ( 1 ).to_xstring(), name, stack.top ( 0 ).to_xstring() ) ); as_object *object = stack.top ( 1 ).to_object(); if ( object ) { object->set_member ( name, stack.top ( 0 ) ); } stack.drop ( 2 ); } break; case 0x62: // getlocal { int index; ip += read_vu30 ( index, &m_code[ip] ); IF_VERBOSE_ACTION ( log_msg ( "EX: getlocal\t index=%i, value=%s\n", index, lregister[index].to_xstring() ) ); stack.push ( lregister[index] ); } break; case 0x63: // setlocal { int index; ip += read_vu30 ( index, &m_code[ip] ); IF_VERBOSE_ACTION ( log_msg ( "EX: setlocal\t index=%i, value=%s\n", index, stack.top ( 0 ).to_xstring() ) ); lregister[index] = stack.pop(); } break; case 0x65: // getscopeobject { int index = m_code[ip]; ++ip; assert ( index < scope.size() ); stack.push ( scope[index] ); IF_VERBOSE_ACTION ( log_msg ( "EX: getscopeobject\t index=%i, value=%s\n", index, stack.top ( 0 ).to_xstring() ) ); } break; case 0x66: // getproperty { int index; ip += read_vu30 ( index, &m_code[ip] ); tu_string name = get_multiname ( index, stack ); as_object *obj = stack.top ( 0 ).to_object(); if ( obj ) { obj->get_member ( name, &stack.top ( 0 ) ); } else { stack.top ( 0 ).set_undefined(); } IF_VERBOSE_ACTION ( log_msg ( "EX: getproperty\t %s, value=%s\n", name.c_str(), stack.top ( 0 ).to_xstring() ) ); break; } case 0x68: // initproperty, Initialize a property. { int index; ip += read_vu30 ( index, &m_code[ip] ); const char *name = m_abc->get_multiname ( index ); as_value &val = stack.top ( 0 ); as_object *obj = stack.top ( 1 ).to_object(); if ( obj ) { obj->set_member ( name, val ); } IF_VERBOSE_ACTION ( log_msg ( "EX: initproperty\t 0x%p.%s=%s\n", obj, name, val.to_xstring() ) ); stack.drop ( 2 ); break; } case 0x73: //convert_i { stack.top ( 0 ).set_int ( stack.top ( 0 ).to_int() ); IF_VERBOSE_ACTION ( log_msg ( "EX: convert_i : %i \n", stack.top ( 0 ).to_int() ) ); } break; case 0x80: // coerce { int index; ip += read_vu30 ( index, &m_code[ip] ); const char *type_name = m_abc->get_multiname ( index ); // stack.push( stack.top( index ) ); IF_VERBOSE_ACTION ( log_msg ( "EX: coerce : %s todo\n", type_name ) ); } break; case 0x85: // coerce_s { stack.top ( 0 ).set_string ( stack.top ( 0 ).to_string() ); IF_VERBOSE_ACTION ( log_msg ( "EX: coerce_s : %s\n", stack.top ( 0 ).to_string() ) ); } break; case 0x96: // not { stack.top ( 0 ).set_bool ( !stack.top ( 0 ).to_bool() ); IF_VERBOSE_ACTION ( log_msg ( "EX: not\n" ) ); } break; case 0xA0: // Add two values { if ( stack.top ( 0 ).is_string() || stack.top ( 1 ).is_string() ) { tu_string str = stack.top ( 1 ).to_string(); str += stack.top ( 0 ).to_string(); stack.top ( 1 ).set_tu_string ( str ); } else { stack.top ( 1 ) += stack.top ( 0 ).to_number(); } stack.drop ( 1 ); break; } case 0xA2: // multiply { stack.top ( 1 ) = stack.top ( 1 ).to_number() * stack.top ( 0 ).to_number(); stack.drop ( 1 ); IF_VERBOSE_ACTION ( log_msg ( "EX: multiply\n" ) ); break; } case 0xAB: // equals { bool result = as_value::abstract_equality_comparison ( stack.top ( 1 ), stack.top ( 0 ) ); IF_VERBOSE_ACTION ( log_msg ( "EX: equals %s & %s : %s\n", stack.top ( 0 ).to_xstring(), stack.top ( 1 ).to_xstring(), result? "true":"false" ) ); stack.drop ( 1 ); stack.top ( 0 ).set_bool ( result ); } break; case 0xAD: //lessthan { as_value result = as_value::abstract_relational_comparison ( stack.top ( 1 ), stack.top ( 0 ) ); IF_VERBOSE_ACTION ( log_msg ( "EX: lessthan %s & %s : %s\n", stack.top ( 1 ).to_xstring(), stack.top ( 0 ).to_xstring(), result.to_string() ) ); stack.drop ( 1 ); stack.top ( 0 ) = result; } break; case 0xC2: // inclocal_i { int index; ip += read_vu30 ( index, &m_code[ip] ); as_value ® = lregister[ index ]; reg.set_int ( reg.to_int() + 1 ); IF_VERBOSE_ACTION ( log_msg ( "EX: inclocal_i %i\n", index ) ); } break; case 0xD0: // getlocal_0 case 0xD1: // getlocal_1 case 0xD2: // getlocal_2 case 0xD3: // getlocal_3 { as_value &val = lregister[opcode & 0x03]; stack.push ( val ); IF_VERBOSE_ACTION ( log_msg ( "EX: getlocal_%d\t %s\n", opcode & 0x03, val.to_xstring() ) ); break; } case 0xD4: // setlocal_0 case 0xD5: // setlocal_1 case 0xD6: // setlocal_2 case 0xD7: // setlocal_3 { lregister[opcode & 0x03] = stack.pop(); IF_VERBOSE_ACTION ( log_msg ( "EX: setlocal_%d\t %s\n", opcode & 0x03, lregister[opcode & 0x03].to_xstring() ) ); break; } default: log_msg ( "TODO opcode 0x%02X\n", opcode ); return; } } while ( ip < m_code.size() ); }