void as_environment::set_variable_raw ( const tu_string &varname, const as_value &val, const array<with_stack_entry>& with_stack ) // No path rigamarole. { // Check the with-stack. for ( int i = with_stack.size() - 1; i >= 0; i-- ) { as_object *obj = with_stack[i].m_object.get_ptr(); as_value unused; if ( obj && obj->get_member ( varname, &unused ) ) { // This object has the member; so set it here. obj->set_member ( varname, val ); return; } } // Check locals. int local_index = find_local ( varname, true ); if ( local_index >= 0 ) { // Set local var. m_local_frames[local_index].m_value = val; return; } if ( m_target != NULL ) { m_target->set_member ( varname, val ); } else { // assume local var // This case happens for example so // class myclass // { // function myfunc() // { // for (i=0;...) should be for (var i=0; ...) // { // } // } // } add_local ( varname, val ); IF_VERBOSE_ACTION ( log_error ( "can't set_variable_raw %s=%s, target is NULL, it's assumed as local\n", varname.c_str(), val.to_string() ) ); IF_VERBOSE_ACTION ( log_error ( "probably you forgot to declare variable '%s'\n", varname.c_str() ) ); } }
as_value *as_environment::get_register ( int reg ) { as_value *val = local_register_ptr ( reg ); IF_VERBOSE_ACTION ( log_msg ( "-------------- get_register(%d): %s at %p\n", reg, val->to_string(), val->to_object() ) ); return val; }
void as_mcloader_loadclip(const fn_call& fn) { as_mcloader* mcl = cast_to<as_mcloader>(fn.this_ptr); assert(mcl); fn.result->set_bool(false); // on default if (fn.nargs == 2) { array<as_value> event_args; // for event handler args event_args.push_back(as_value()); // undefined tu_string infile = get_full_url(fn.get_player()->get_workdir(), fn.arg(0).to_string()); movie_definition* md = fn.get_player()->create_movie(infile.c_str()); if (md == NULL) { IF_VERBOSE_ACTION(log_msg("can't create movie from %s\n", fn.arg(0).to_string())); event_args.push_back("URLNotFound"); // 2-d param mcl->m_listeners.notify(event_id(event_id::ONLOAD_ERROR, &event_args)); return; } as_mcloader::loadable_movie lm; lm.m_def = cast_to<movie_def_impl>(md); lm.m_target = cast_to<character>(fn.env->find_target(fn.arg(1))); mcl->m_lm.push_back(lm); mcl->m_listeners.notify(event_id(event_id::ONLOAD_START, &event_args)); fn.result->set_bool(true); } }
void as_environment::set_variable ( const tu_string &varname, const as_value &val, const array<with_stack_entry>& with_stack ) // Given a path to variable, set its value. { IF_VERBOSE_ACTION ( log_msg ( "-------------- %s = %s\n", varname.c_str(), val.to_string() ) ); //xxxxxxxxxx // Path lookup rigamarole. character *target = get_target(); tu_string path; tu_string var; if ( parse_path ( varname, &path, &var ) ) { target = cast_to<character> ( find_target ( path.c_str() ) ); if ( target ) { target->set_member ( var, val ); } } else { set_variable_raw ( varname, val, with_stack ); } }
void sound_attach(const fn_call& fn) { if (fn.nargs < 1) { log_error("attach sound needs one argument\n"); return; } as_sound* snd = cast_to<as_sound>(fn.this_ptr); assert(snd); assert(fn.env); // find target movieclip character* target = snd->m_target.get_ptr(); if (target == NULL) { target = fn.env->get_target(); } // find resource character_def* res = NULL; if (target) { res = target->find_exported_resource(fn.arg(0).to_string()); } if (res == NULL) { IF_VERBOSE_ACTION(log_msg("import error: resource '%s' is not exported\n", fn.arg(0).to_string())); return; } int si = 0; sound_sample* ss = cast_to<sound_sample>(res); if (ss != NULL) { si = ss->m_sound_handler_id; } else { log_error("sound sample is NULL\n"); return; } snd->clear(); // sanity check assert(si >= 0 && si < 1000); snd->m_id = si; snd->m_is_loaded_sound = false; }
void as_environment::set_target ( as_value &target, character *original_target ) { if ( target.is_string() ) { tu_string path = target.to_tu_string(); IF_VERBOSE_ACTION ( log_msg ( "-------------- ActionSetTarget2: %s", path.c_str() ) ); if ( path.size() > 0 ) { character *tar = cast_to<character> ( find_target ( path.c_str() ) ); if ( tar ) { set_target ( tar ); return; } } else { set_target ( original_target ); return; } } else if ( target.is_object() ) { IF_VERBOSE_ACTION ( log_msg ( "-------------- ActionSetTarget2: %s", target.to_string() ) ); character *tar = cast_to<character> ( find_target ( target ) ); if ( tar ) { set_target ( tar ); return; } } IF_VERBOSE_ACTION ( log_msg ( "can't set target %s\n", target.to_string() ) ); }
static void call( const char * name, array<as_value> & stack ) { as_value& val = stack[stack.size() - 1]; as_object* obj = stack[stack.size() - 2].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.resize(stack.size() - 2); }
static void call( array<as_value> & stack, int arg_count ) { as_object* obj = stack.back().to_object(); stack.resize(stack.size() - 1); for (int i = 0; i < arg_count; i++) { as_value& val = stack.back(); stack.resize(stack.size() - 1); } //TODO: construct super of obj IF_VERBOSE_ACTION(log_msg("EX: constructsuper\t 0x%p(args:%d)\n", obj, arg_count)); }
bool root::goto_labeled_frame(const char* label) { int target_frame = -1; if (m_def->get_labeled_frame(label, &target_frame)) { goto_frame(target_frame); return true; } else { IF_VERBOSE_ACTION( log_error("error: movie_impl::goto_labeled_frame('%s') unknown label\n", label)); return false; } }
static void call( const char* name, array<as_value> & scope, array<as_value> &stack ) { // search property in scope as_value val; for (int i = scope.size() - 1; i >= 0; i--) { if (scope[i].find_property(name, &val)) { break; } } IF_VERBOSE_ACTION(log_msg("EX: getlex\t %s, value=%s\n", name, val.to_xstring())); stack.push_back(val); }
static void call( const char* name, array<as_value> & scope, array<as_value> & stack ) { as_object* obj = NULL; for (int i = scope.size() - 1; i >= 0; i--) { as_value val; if (scope[i].find_property(name, &val)) { obj = scope[i].to_object(); break; } } IF_VERBOSE_ACTION(log_msg("EX: findproperty\t %s, obj=0x%p\n", name, obj)); stack.push_back(obj); }
static void call( const as_object *_this, const char* name, int arg_count, array<as_value> & stack ) { as_environment env(_this->get_player()); for (int i = 0; i < arg_count; i++) { as_value& val = stack[stack.size() - 1 - i]; env.push(val); } stack.resize(stack.size() - arg_count); as_object* obj = stack.back().to_object(); stack.resize(stack.size() - 1); as_value func; if (obj && obj->get_member(name, &func)) { call_method(func, &env, obj, arg_count, env.get_top_index()); } IF_VERBOSE_ACTION(log_msg("EX: callpropvoid\t 0x%p.%s(args:%d)\n", obj, name, arg_count)); }
//static registerClass(name:String, theClass:Function) : Boolean void as_object_registerclass(const fn_call& fn) { fn.result->set_bool(false); if (fn.nargs == 2 && fn.env->get_target() != NULL) { character_def* def = fn.env->get_target()->find_exported_resource(fn.arg(0).to_tu_string()); if (def) { as_function* func = cast_to<as_function>(fn.arg(1).to_object()); if (func) { IF_VERBOSE_ACTION(log_msg("registerClass '%s'\n", fn.arg(0).to_string())); fn.result->set_bool(true); def->set_registered_class_constructor(func); } } else { log_error("can't find exported resource '%s'\n", fn.arg(0).to_string()); } } }
as_value as_environment::get_variable ( const tu_string &varname, const array<with_stack_entry>& with_stack ) const // Return the value of the given var, if it's defined. { // Path lookup rigamarole. as_object *target = get_target(); tu_string path; tu_string var; if ( parse_path ( varname, &path, &var ) ) { // @@ Use with_stack here too??? Need to test. target = find_target ( path.c_str() ); if ( target ) { as_value val; target->get_member ( var, &val ); return val; } else if ( ( target = get_player()->get_global()->find_target ( path.c_str() ) ) ) { as_value val; target->get_member ( var, &val ); return val; } else { IF_VERBOSE_ACTION ( log_msg ( "find_target(\"%s\") failed\n", path.c_str() ) ); return as_value(); } } else { return get_variable_raw ( varname, with_stack ); } }
void as_object::enumerate(as_environment* env) // retrieves members & pushes them into env { stringi_hash<as_value>::const_iterator it = m_members.begin(); while (it != m_members.end()) { if (it->second.is_enum()) { env->push(it->first); IF_VERBOSE_ACTION(log_msg("-------------- enumerate - push: %s\n", it->first.c_str())); } ++it; } // as_object_interface* proto = get_proto(); // if (proto) // { // proto->enumerate(env); // } }
void as_s_function::operator()(const fn_call& fn) // Dispatch. { assert(fn.env); // Keep target alive during execution! smart_ptr<as_object> target = m_target; // try to use caller environment // if the caller object has own environment then we use its environment as_environment* env = fn.env; if (fn.this_ptr) { if (fn.this_ptr->get_environment()) { env = fn.this_ptr->get_environment(); } } // set 'this' as_object* this_ptr = env->get_target(); if (fn.this_ptr) { this_ptr = fn.this_ptr; if (this_ptr->m_this_ptr != NULL) { this_ptr = this_ptr->m_this_ptr.get_ptr(); } } // Function has been declared in moviclip ==> we should use its environment // At the same time 'this_ptr' may refers to another object // see testcase in .h file if (m_target != NULL) { character* ch = cast_to<character>(m_target.get_ptr()); if (ch) { if (ch->is_alive()) { env = m_target->get_environment(); } } } // Set up local stack frame, for parameters and locals. int local_stack_top = env->get_local_frame_top(); env->add_frame_barrier(); if (m_is_function2 == false) { // Conventional function. // Push the arguments onto the local frame. int args_to_pass = imin(fn.nargs, m_args.size()); for (int i = 0; i < args_to_pass; i++) { assert(m_args[i].m_register == 0); env->add_local(m_args[i].m_name, fn.arg(i)); } env->set_local("this", this_ptr); // Put 'super' in a local var. if (fn.this_ptr) { env->add_local("super", fn.this_ptr->get_proto()); } } else { // function2: most args go in registers; any others get pushed. // Create local registers. env->add_local_registers(m_local_register_count); // Handle the explicit args. int args_to_pass = imin(fn.nargs, m_args.size()); for (int i = 0; i < args_to_pass; i++) { if (m_args[i].m_register == 0) { // Conventional arg passing: create a local var. env->add_local(m_args[i].m_name, fn.arg(i)); } else { // Pass argument into a register. int reg = m_args[i].m_register; env->set_register(reg, fn.arg(i)); } } // Handle the implicit args. int current_reg = 1; if (m_function2_flags & 0x01) { // preload 'this' into a register. IF_VERBOSE_ACTION(log_msg("-------------- preload this=0x%X to register %d\n", this_ptr, current_reg)); env->set_register(current_reg, this_ptr); current_reg++; } if (m_function2_flags & 0x02) { // Don't put 'this' into a local var. } else { // Put 'this' in a local var. env->add_local("this", as_value(this_ptr)); } // Init arguments array, if it's going to be needed. smart_ptr<as_array> arg_array; if ((m_function2_flags & 0x04) || ! (m_function2_flags & 0x08)) { arg_array = new as_array(env->get_player()); as_value index_number; for (int i = 0; i < fn.nargs; i++) { index_number.set_int(i); arg_array->set_member(index_number.to_string(), fn.arg(i)); } } if (m_function2_flags & 0x04) { // preload 'arguments' into a register. env->set_register(current_reg, arg_array.get_ptr()); current_reg++; } if (m_function2_flags & 0x08) { // Don't put 'arguments' in a local var. } else { // Put 'arguments' in a local var. env->add_local("arguments", as_value(arg_array.get_ptr())); } if (m_function2_flags & 0x10) { // Put 'super' in a register. IF_VERBOSE_ACTION(log_msg("-------------- preload super=0x%X to register %d\n", fn.this_ptr->get_proto(), current_reg)); env->set_register(current_reg, fn.this_ptr->get_proto()); current_reg++; } if (m_function2_flags & 0x20) { // Don't put 'super' in a local var. } else { // Put 'super' in a local var. env->add_local("super", fn.this_ptr->get_proto()); } if (m_function2_flags & 0x40) { // Put '_root' in a register. env->set_register(current_reg, env->get_root()->get_root_movie()); current_reg++; } if (m_function2_flags & 0x80) { // Put '_parent' in a register. array<with_stack_entry> dummy; as_value parent = env->get_variable("_parent", dummy); IF_VERBOSE_ACTION(log_msg("-------------- preload _parent=0x%X to register %d\n", parent, current_reg)); env->set_register(current_reg, parent); current_reg++; } if (m_function2_flags & 0x100) { // Put '_global' in a register. IF_VERBOSE_ACTION(log_msg("-------------- preload _global=0x%X to register %d\n", get_global(), current_reg)); env->set_register(current_reg, get_global()); current_reg++; } } // keep stack size int stack_size = env->m_stack.size(); // printf("***on entry*** %d\n", stack_size); // Execute the actions. m_action_buffer.execute(env, m_start_pc, m_length, fn.result, m_with_stack, m_is_function2); // restore stack size // it should not be but it happens if (stack_size != env->m_stack.size()) { // log_error("s_function: on entry stack size (%d) != on exit stack size (%d)\n", // stack_size, env->m_stack.size()); env->m_stack.resize(stack_size); } // Clean up stack frame. env->set_local_frame_top(local_stack_top); if (m_is_function2) { // Clean up the local registers. env->drop_local_registers(m_local_register_count); } }
void as_3_function::operator() ( const fn_call &fn ) // dispatch { assert ( fn.env ); // try to use caller environment // if the caller object has own environment then we use its environment as_environment *env = fn.env; if ( fn.this_ptr ) { if ( fn.this_ptr->get_environment() ) { env = fn.this_ptr->get_environment(); } } // set 'this' as_object *this_ptr = env->get_target(); if ( fn.this_ptr ) { this_ptr = fn.this_ptr; if ( this_ptr->m_this_ptr != NULL ) { this_ptr = this_ptr->m_this_ptr.get_ptr(); } } // Create local registers. array<as_value> local_register; local_register.resize ( m_local_count + 1 ); // Register 0 holds the ?this? object. This value is never null. assert ( this_ptr ); local_register[0] = this_ptr; // Registers 1 through method_info.param_count holds parameter values. // If fewer than method_body_info.local_count values are supplied to the call then // the remaining values are either the values provided by default value declarations // or the value undefined. for ( int i = 0; i < m_param_type.size(); i++ ) { // A zero value denotes the any (?*?) type. //const char* name = m_abc->get_multiname(m_param_type[i]); local_register[i + 1] = fn.arg ( i ); // hack } #ifdef __GAMESWF_ENABLE_JIT__ if ( !m_compiled_code.is_valid() ) { compile(); m_compiled_code.initialize(); } #endif if ( m_compiled_code.is_valid() ) { try { m_compiled_code.call< array<as_value>&, vm_stack &, vm_stack &, as_value * > ( local_register, *env, env->m_scope, fn.result ); } catch ( ... ) { log_msg ( "jitted code crashed" ); } } else { // keep stack size on entry int stack_size = env->size(); IF_VERBOSE_ACTION ( log_msg ( "\nEX: call method #%d\n", m_method ) ); // Execute the actions. execute ( local_register, env, fn.result ); IF_VERBOSE_ACTION ( log_msg ( "EX: ended #%d.\n\n", m_method ) ); if ( stack_size != env->size() ) { log_error ( "error: stack size on exit must be same as on entry, %d:%d \n", stack_size, env->size() ); // restore stack size env->resize ( stack_size ); } } }
void as_environment::set_register ( int reg, const as_value &val ) { IF_VERBOSE_ACTION ( log_msg ( "-------------- set_register(%d): %s at %p\n", reg, val.to_string(), val.to_object() ) ); *local_register_ptr ( reg ) = val; }
// 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() ); }
as_value as_environment::get_variable_raw ( const tu_string &varname, const array<with_stack_entry>& with_stack ) const // varname must be a plain variable name; no path parsing. { as_value val; // First check the with-stack. for ( int i = with_stack.size() - 1; i >= 0; i-- ) { as_object *obj = with_stack[i].m_object.get_ptr(); if ( obj && obj->get_member ( varname, &val ) ) { // Found the var in this context. return val; } } // Then check locals. int local_index = find_local ( varname, true ); if ( local_index >= 0 ) { return m_local_frames[local_index].m_value; } // Check movie members. if ( m_target != NULL && m_target->get_member ( varname, &val ) ) { return val; } // Check this, _global, _root as_standard_member varname_id = get_standard_member ( varname ); switch ( varname_id ) { default: break; case M_GLOBAL: val.set_as_object ( get_player()->get_global() ); return val; case MTHIS: val.set_as_object ( get_target() ); return val; case M_ROOT: case M_LEVEL0: val.set_as_object ( get_root()->get_root_movie() ); return val; } // check _global.member if ( get_player()->get_global()->get_member ( varname, &val ) ) { return val; } // Fallback. IF_VERBOSE_ACTION ( log_msg ( "get_variable_raw(\"%s\") failed, returning UNDEFINED.\n", varname.c_str() ) ); return val; }
// Implementation of: // // loadVariables(url:String, target:Object, [method:String]) : Void // loadMovie(url:String, target:Object, [method:String]) : Void // loadMovie(url:String, target:String, [method:String]) : Void // unloadMovie(target:MovieClip) : Void // unloadMovie(target:String) : Void // // url=="" means that the load_file() works as unloadMovie(target) // TODO: implement [method] character *as_environment::load_file ( const char *url, const as_value &target_value, int method ) { character *target = cast_to<character> ( find_target ( target_value ) ); if ( target == NULL ) { IF_VERBOSE_ACTION ( log_msg ( "load_file: target %s is't found\n", target_value.to_string() ) ); return NULL; } // is unloadMovie() ? if ( strlen ( url ) == 0 ) { character *parent = target->get_parent(); if ( parent ) { parent->remove_display_object ( target ); } else // target is _root, unloadMovie(_root) { target->clear_display_objects(); } return NULL; } // is path relative ? tu_string file_name = get_full_url ( get_player()->get_workdir(), url ); switch ( get_file_type ( file_name.c_str() ) ) { default: break; case TXT: { // Reads data from an external file, such as a text file and sets the values for // variables in a target movie clip. This action can also be used // to update variables in the active SWF file with new values. tu_file fi ( file_name.c_str(), "r" ); if ( fi.get_error() == TU_FILE_NO_ERROR ) { fi.go_to_end(); int len = fi.get_position(); fi.set_position ( 0 ); char *buf = ( char * ) malloc ( len ); if ( fi.read_string ( buf, len ) > 0 ) { // decode data in the standard MIME format and copy theirs into target #if TU_ENABLE_NETWORK == 1 as_loadvars lv ( get_player() ); lv.decode ( buf ); lv.copy_to ( target ); #else log_error ( "Compile gameswf with TU_ENABLE_NETWORK=1 to use loadVariable() function" ); #endif } free ( buf ); } break; } case URL: case SWF: { movie_definition *md = get_player()->create_movie ( file_name.c_str() ); if ( md && md->get_frame_count() > 0 ) { return target->replace_me ( md ); } break; } case X3DS: { #if TU_CONFIG_LINK_TO_LIB3DS == 0 log_error ( "gameswf is not linked to lib3ds -- can't load 3DS file\n" ); #else x3ds_definition *x3ds = create_3ds_definition ( get_player(), file_name.c_str() ); if ( x3ds ) { if ( x3ds->is_loaded() ) { rect bound; target->get_bound ( &bound ); x3ds->set_bound ( bound ); return target->replace_me ( x3ds ); } } #endif break; } case JPG: { #if TU_CONFIG_LINK_TO_JPEGLIB == 0 log_error ( "gameswf is not linked to jpeglib -- can't load jpeg image data!\n" ); #else image::rgb *im = image::read_jpeg ( file_name.c_str() ); if ( im ) { bitmap_info *bi = render::create_bitmap_info_rgb ( im ); delete im; movie_definition *rdef = get_root()->get_movie_definition(); assert ( rdef ); bitmap_character *jpeg = new bitmap_character ( rdef, bi ); return target->replace_me ( jpeg ); } #endif break; } } return NULL; }