"Fossies" - the Fresh Open Source Software Archive

Member "gnash-0.8.10/libcore/vm/ActionExec.cpp" (19 Jan 2012, 23266 Bytes) of package /linux/www/old/gnash-0.8.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // ActionExec.cpp:  ActionScript execution, for Gnash.
    2 //
    3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
    4 //   Free Software Foundation, Inc
    5 //
    6 // This program is free software; you can redistribute it and/or modify
    7 // it under the terms of the GNU General Public License as published by
    8 // the Free Software Foundation; either version 3 of the License, or
    9 // (at your option) any later version.
   10 //
   11 // This program is distributed in the hope that it will be useful,
   12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
   13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14 // GNU General Public License for more details.
   15 //
   16 // You should have received a copy of the GNU General Public License
   17 // along with this program; if not, write to the Free Software
   18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   19 
   20 #ifdef HAVE_CONFIG_H
   21 #include "gnashconfig.h"
   22 #endif
   23 
   24 #include "ActionExec.h"
   25 #include "action_buffer.h"
   26 #include "Function.h"
   27 #include "log.h"
   28 #include "VM.h"
   29 #include "GnashException.h"
   30 #include "DisplayObject.h"
   31 #include "movie_root.h"
   32 #include "SWF.h"
   33 #include "ASHandlers.h"
   34 #include "as_environment.h"
   35 #include "SystemClock.h"
   36 #include "CallStack.h"
   37 
   38 #include <sstream>
   39 #include <string>
   40 #include <boost/format.hpp>
   41 
   42 #ifndef DEBUG_STACK
   43 
   44 // temporarily disabled as will produce lots of output with -v
   45 // we'd need another switch maybe, as -va does also produce
   46 // too much information for my tastes. I really want just
   47 // to see how stack changes while executing actions...
   48 // --strk Fri Jun 30 02:28:46 CEST 2006
   49 # define DEBUG_STACK 1
   50 
   51 // Max number of stack item to dump. 0 for unlimited.
   52 # define STACK_DUMP_LIMIT 32
   53 
   54 // Define to get debugging messages for try / catch
   55 //#define GNASH_DEBUG_TRY 1
   56 
   57 #endif
   58 
   59 namespace gnash {
   60 
   61 ActionExec::ActionExec(const Function& func, as_environment& newEnv,
   62         as_value* nRetVal, as_object* this_ptr)
   63     :
   64     code(func.getActionBuffer()),
   65     env(newEnv),
   66     retval(nRetVal),
   67     _withStack(),
   68     _scopeStack(func.getScopeStack()),
   69     _func(&func),
   70     _this_ptr(this_ptr),
   71     _initialStackSize(0),
   72     _originalTarget(0),
   73     _origExecSWFVersion(0),
   74     _tryList(),
   75     _returning(false),
   76     _abortOnUnload(false),
   77     pc(func.getStartPC()),
   78     next_pc(pc),
   79     stop_pc(pc + func.getLength())
   80 {
   81     assert(stop_pc < code.size());
   82 
   83     // Functions defined in SWF version 6 and higher pushes
   84     // the activation object to the scope stack
   85     // NOTE: if we query env.get_version() we get version of the calling
   86     //       code, not the one we're about to call. This is because
   87     //       it is ActionExec's call operator switching the VM version!
   88     //
   89     //       TESTCASE: winterbell from orisinal.
   90     //       If we query VM version here, the movie results in:
   91     //              set local var: i=[number:0]
   92     //              push 'i' getvariable
   93     //              get var: i=[undefined] 
   94     if (code.getDefinitionVersion() > 5) {
   95         // We assume that the Function () operator already initialized
   96         // its environment so that its activation object is now in the
   97         // top element of the CallFrame stack
   98         CallFrame& topFrame = getVM(newEnv).currentCall();
   99         assert(&topFrame.function() == &func);
  100         _scopeStack.push_back(&topFrame.locals());
  101     }
  102 }
  103 
  104 ActionExec::ActionExec(const action_buffer& abuf, as_environment& newEnv,
  105         bool abortOnUnloaded)
  106     :
  107     code(abuf),
  108     env(newEnv),
  109     retval(0),
  110     _withStack(),
  111     _scopeStack(),
  112     _func(0),
  113     _this_ptr(0),
  114     _initialStackSize(0),
  115     _originalTarget(0),
  116     _origExecSWFVersion(0),
  117     _tryList(),
  118     _returning(false),
  119     _abortOnUnload(abortOnUnloaded),
  120     pc(0),
  121     next_pc(0),
  122     stop_pc(abuf.size())
  123 {
  124 }
  125 
  126 void
  127 ActionExec::operator()()
  128 {
  129     VM& vm = getVM(env);
  130 
  131     // Do not execute if scripts are disabled
  132     if (vm.getRoot().scriptsDisabled()) return;
  133 
  134     _origExecSWFVersion = vm.getSWFVersion();
  135 
  136     const int codeVersion = code.getDefinitionVersion();
  137     vm.setSWFVersion(codeVersion);
  138 
  139     static const SWF::SWFHandlers& ash = SWF::SWFHandlers::instance();
  140         
  141     _originalTarget = env.target();
  142 
  143     _initialStackSize = env.stack_size();
  144 
  145 #if DEBUG_STACK
  146     IF_VERBOSE_ACTION (
  147             log_action(_("at ActionExec operator() start, pc=%d"
  148                    ", stop_pc=%d, code.size=%d, func=%d, codeVersion=%d"),
  149                 pc, stop_pc, code.size(), _func ? _func : 0, codeVersion);
  150         std::stringstream ss;
  151         getVM(env).dumpState(ss, STACK_DUMP_LIMIT);
  152         log_action(_("%s"), ss.str());
  153     );
  154 #endif
  155 
  156     // stop_pc: set to the code boundary at which we should check
  157     // for exceptions. If there is no exception in a TryBlock, it
  158     // is set to the end of that block; all the code (including catch
  159     // and finally) should be executed.
  160     // If 'try' finds an exception, stop_pc causes execution to stop
  161     // at 'catch', while we find whether it catches the exception, and
  162     // at 'finally', where we handle any exceptions thrown in the meantime.
  163     //
  164     // The stages of our TryBlock only roughly correspond to the ActionScript
  165     // code. There may be no catch and/or finally block, but certain operations
  166     // must still be carried out. 
  167 
  168     const size_t maxTime = getRoot(vm).getTimeoutLimit() * 1000;
  169     SystemClock clock; // TODO: should we use a CPUClock here ?
  170 
  171     try {
  172 
  173         // We might not stop at stop_pc, if we are trying.
  174         while (1) {
  175 
  176             if (pc >= stop_pc) {
  177                 // No try blocks
  178                 if (_tryList.empty()) {
  179                 
  180                     // Check for any uncaught exceptions.
  181                     if (env.stack_size() && env.top(0).is_exception()) {
  182                         log_debug ("Exception on stack, no handlers left.");
  183                         // Stop execution if an exception
  184                         // is still on the stack and there is nothing
  185                         // left to catch it.
  186                         cleanupAfterRun();
  187 
  188                         // Forceably clear the stack.
  189                         // - Fixes misc-mtasc.all/exception.swf
  190                         // By commenting the line above, we get an XPASS in
  191                         // - swfdec/catch-in-caller.swf
  192                         env.drop(env.stack_size());
  193 
  194                         return;
  195                     }
  196                     break;
  197                 }
  198 
  199                 // If we are in a try block, check to see if we have thrown.
  200                 TryBlock& t = _tryList.back();
  201                 
  202                 if (!processExceptions(t)) break;
  203 
  204                 continue;
  205             }
  206 
  207             // Cleanup any expired "with" blocks.
  208             while (!_withStack.empty() && pc >= _withStack.back().end_pc()) {
  209            
  210                 // Drop last stack element
  211                 assert(_withStack.back().object() == _scopeStack.back());
  212                 _withStack.pop_back();
  213                 
  214                 // hopefully nothing gets after the 'with' stack.
  215                 _scopeStack.pop_back();
  216             }
  217 
  218             // Get the opcode.
  219             boost::uint8_t action_id = code[pc];
  220 
  221             IF_VERBOSE_ACTION (
  222                 log_action(_("PC:%d - EX: %s"), pc, code.disasm(pc));
  223             );
  224 
  225             // Set default next_pc offset, control flow action handlers
  226             // will be able to reset it.
  227             if ((action_id & 0x80) == 0) {
  228                 // action with no extra data
  229                 next_pc = pc+1;
  230             }
  231             else {
  232                 // action with extra data
  233                 // Note this converts from int to uint!
  234                 boost::uint16_t length(code.read_int16(pc + 1));
  235 
  236                 next_pc = pc + length + 3;
  237                 if (next_pc > stop_pc) {
  238                     IF_VERBOSE_MALFORMED_SWF(
  239                     log_swferror(_("Length %u (%d) of action tag"
  240                                    " id %u at pc %d"
  241                                    " overflows actions buffer size %d"),
  242                           length, static_cast<int>(length),
  243                           static_cast<unsigned>(action_id), pc,
  244                           stop_pc);
  245                     );
  246                     
  247                     // no way to recover from this actually...
  248                     // Give this action handler a chance anyway.
  249                     // Maybe it will be able to do something about
  250                     // this anyway.
  251                     break; 
  252                 }
  253             }
  254 
  255             // Do we still need this ?
  256             if (action_id == SWF::ACTION_END) {
  257                 break;
  258             }
  259 
  260             ash.execute(static_cast<SWF::ActionType>(action_id), *this);
  261 
  262             // Code round here has to do with bugs: #20974, #21069, #20996,
  263             // but since there is so much disabled code it's not clear exactly
  264             // what part.
  265             
  266             // curveball.swf and feed.swf suggest that it is the
  267             // *current* target, not the *original* one that matters.
  268             DisplayObject* guardedChar = env.target();
  269 
  270             if (_abortOnUnload && guardedChar && guardedChar->unloaded()) {
  271 
  272                 IF_VERBOSE_ACTION(
  273                     // action_execution_order_test8.c shows that the opcode
  274                     // guard is not SWF version based
  275                     std::stringstream ss;
  276                     ss << "Target of action_buffer (" <<
  277                         guardedChar->getTarget() << " of type " <<
  278                         typeName(*guardedChar) << ") unloaded "
  279                         "by execution of opcode: " << std::endl;
  280 
  281                     dumpActions(pc, next_pc, ss);
  282                     ss << "Discarding " << stop_pc-next_pc
  283                         << " bytes of remaining opcodes: " << std::endl;
  284                     dumpActions(next_pc, stop_pc, ss);
  285                     log_action(_("%s"), ss.str());
  286                 );
  287                 break;
  288             }
  289 
  290 #if DEBUG_STACK
  291             IF_VERBOSE_ACTION (
  292                 log_action(_("After execution: PC %d, next PC %d, "
  293                         "stack follows"), pc, next_pc);
  294                 std::stringstream ss;
  295                 getVM(env).dumpState(ss, STACK_DUMP_LIMIT);
  296                 log_action(_("%s"), ss.str());
  297             );
  298 #endif
  299 
  300             // Do some housecleaning on branch back
  301             if (next_pc <= pc) {
  302                 // Check for script limits hit. 
  303                 // See: http://www.gnashdev.org/wiki/index.php/ScriptLimits
  304                 if (clock.elapsed() > maxTime) {
  305                     boost::format fmt = 
  306                         boost::format(_("Time exceeded (%4% secs) while "
  307                             "executing code in %1% between pc %2% and %3%. "
  308                             "Disable scripts?")) %
  309                             code.getMovieDefinition().get_url() % next_pc %
  310                             pc % (maxTime/1000);
  311 
  312                     if (getRoot(env).queryInterface(fmt.str())) {
  313                         throw ActionLimitException(fmt.str());
  314                     } 
  315 
  316                     clock.restart();
  317                 }
  318                 // TODO: Run garbage collector ? If stack isn't too big ?
  319             }
  320 
  321             // Control flow actions will change the PC (next_pc)
  322             pc = next_pc;
  323         }
  324     }
  325     catch (const ActionLimitException&) {
  326         // Class execution should stop (for this frame only?)
  327         // Here's were we should pop-up a window to prompt user about
  328         // what to do next (abort or not ?)
  329         cleanupAfterRun(); // we expect inconsistencies here
  330         throw;
  331     }
  332 
  333     cleanupAfterRun();
  334 
  335 }
  336 
  337 
  338 // Try / catch / finally rules:
  339 //
  340 // The actionscript code looks like this:
  341 // try { }
  342 // catch { }
  343 // finally { };
  344 //
  345 // For functions:
  346 //
  347 // 1. The catch block is only executed when an exception is thrown.
  348 // 2. The finally block is *always* executed, even when there is no exception
  349 //    *and* a return in try!
  350 // 3. Unhandled executions are handled the same.
  351 // 4. If an exception is thrown in a function and not caught within that function,
  352 //    the return value is the exception, unless the 'finally' block has its own
  353 //    return. (In that case, the exception is never handled).
  354 
  355 bool
  356 ActionExec::processExceptions(TryBlock& t)
  357 {
  358 
  359     switch (t._tryState) {
  360         case TryBlock::TRY_TRY:
  361         {
  362             if (env.stack_size() && env.top(0).is_exception()) {
  363 #ifdef GNASH_DEBUG_TRY
  364                 as_value ex = env.top(0);
  365                 ex.unflag_exception();
  366                 log_debug("TRY block: Encountered exception (%s). "
  367                         "Set PC to catch.", ex);
  368 #endif
  369                 // We have an exception. Don't execute any more of the try
  370                 // block and process exception.
  371                 pc = t._catchOffset;
  372                 t._tryState = TryBlock::TRY_CATCH;
  373               
  374                 if (!t._hasName) {
  375                     // Used when exceptions are thrown in functions.
  376                     // Tests in misc-mtasc.all/exception.as
  377                     as_value ex = env.pop();
  378                     ex.unflag_exception();
  379                     
  380                     getVM(env).setRegister(t._registerIndex, ex);
  381                 }
  382             }
  383             else {
  384 #ifdef GNASH_DEBUG_TRY 
  385                 log_debug("TRY block: No exception, continuing as normal.");
  386 #endif
  387 
  388                 // No exception, so the try block should be executed,
  389                 // then finally (even if a function return is in try). There
  390                 // should be an action jump to finally at the end of try; if
  391                 // this is missing, catch must be executed as well.
  392 
  393                 // If there is a return in the try block, go straight to 
  394                 // finally.
  395                 if (_returning) pc = t._finallyOffset;
  396                 else stop_pc = t._finallyOffset;
  397 
  398                 t._tryState = TryBlock::TRY_FINALLY;
  399             }
  400             break;
  401         }
  402 
  403 
  404         case TryBlock::TRY_CATCH:
  405         {
  406 #ifdef GNASH_DEBUG_TRY
  407             log_debug("CATCH: TryBlock name = %s", t._name); 
  408 #endif               
  409             // If we are here, there should have been an exception
  410             // in 'try'. 
  411             
  412             if (env.stack_size() && env.top(0).is_exception()) {
  413                 // This was thrown in "try". Remove it from
  414                 // the stack and remember it so that
  415                 // further exceptions can be caught.
  416                 t._lastThrow = env.pop();
  417                 as_value ex = t._lastThrow;
  418                 ex.unflag_exception();
  419 
  420 #ifdef GNASH_DEBUG_TRY
  421                 log_debug("CATCH block: top of stack is an exception (%s)", ex);
  422 #endif
  423 
  424                 if (t._hasName && !t._name.empty()) {
  425                     // If name isn't empty, it means we have a catch block.
  426                     // We should set its argument to the exception value.
  427                     setLocalVariable(t._name, ex);
  428                     t._lastThrow = as_value();
  429 #ifdef GNASH_DEBUG_TRY
  430                     log_debug("CATCH block: encountered exception (%s). "
  431                               "Assigning to catch arg %d.", ex, t._name);
  432 #endif
  433                 }
  434             }
  435 
  436             // Go to finally
  437             stop_pc = t._finallyOffset;
  438             t._tryState = TryBlock::TRY_FINALLY;
  439             break;
  440         }
  441 
  442 
  443         case TryBlock::TRY_FINALLY:
  444         {
  445             // FINALLY. This may or may not exist, but these actions
  446             // are carried out anyway.
  447 #ifdef GNASH_DEBUG_TRY
  448             log_debug("FINALLY: TryBlock name = %s", t._name);                 
  449 #endif
  450             // If the exception is here, we have thrown in catch.
  451             if (env.stack_size() && env.top(0).is_exception()) {
  452                  
  453                 t._lastThrow = env.pop();
  454 #ifdef GNASH_DEBUG_TRY 
  455                 as_value ex = t._lastThrow;
  456                 ex.unflag_exception();
  457                 log_debug("FINALLY: top of stack is an exception "
  458                           "again (%s). Replaces any previous "
  459                           "uncaught exceptions", ex);
  460 #endif
  461 
  462                 // Return any exceptions thrown in catch. This
  463                 // can be overridden by a return in finally. 
  464                 // Should these be thrown to the register too
  465                 // if it is a catch-in-register case?
  466                 if (retval) *retval = t._lastThrow;
  467 
  468             }
  469             stop_pc = t._afterTriedOffset;
  470             t._tryState = TryBlock::TRY_END;
  471             break;
  472         }
  473 
  474         case TryBlock::TRY_END:
  475         {
  476             // If there's no exception here, we can execute the
  477             // rest of the code. If there is, it will be caught
  478             // by the next TryBlock or stop execution.
  479             if (env.stack_size() && env.top(0).is_exception()) {
  480                 // Check for exception handlers straight away
  481                 stop_pc = t._afterTriedOffset;
  482 #ifdef GNASH_DEBUG_TRY
  483                 as_value ex = env.top(0);
  484                 ex.unflag_exception();
  485                 log_debug("END: exception thrown in finally(%s). "
  486                           "Leaving on the stack", ex);
  487 #endif
  488                 _tryList.pop_back();
  489                 return true;
  490             }
  491             else if (t._lastThrow.is_exception()) {
  492                 // Check for exception handlers straight away
  493                 stop_pc = t._afterTriedOffset;                
  494 #ifdef GNASH_DEBUG_TRY
  495                 as_value ex = t._lastThrow;
  496                 ex.unflag_exception();
  497                 log_debug("END: no new exceptions thrown. Pushing "
  498                       "uncaught one (%s) back on stack", ex);
  499 #endif                   
  500 
  501                 env.push(t._lastThrow);
  502  
  503 
  504                 _tryList.pop_back();
  505                 return true;
  506             }
  507 #ifdef GNASH_DEBUG_TRY
  508             log_debug("END: no new exceptions thrown. Continuing");
  509 #endif
  510             // No uncaught exceptions left in TryBlock:
  511             // execute rest of code.
  512             stop_pc = t._savedEndOffset;
  513             
  514             // Finished with this TryBlock.
  515             _tryList.pop_back();
  516             
  517             // Will break out of action execution.
  518             if (_returning) return false;
  519             
  520             break;
  521         }
  522 
  523 
  524     } // end switch
  525     return true;
  526 }
  527 
  528 void
  529 ActionExec::cleanupAfterRun() 
  530 {
  531     VM& vm = getVM(env);
  532 
  533     env.set_target(_originalTarget);
  534     _originalTarget = NULL;
  535 
  536     vm.setSWFVersion(_origExecSWFVersion);
  537 
  538     IF_VERBOSE_MALFORMED_SWF(
  539         // check if the stack was smashed
  540         if (_initialStackSize > env.stack_size()) {
  541             log_swferror(_("Stack smashed (ActionScript compiler bug, or "
  542                 "obfuscated SWF). Taking no action to fix (as expected)."));
  543         }
  544         else if (_initialStackSize < env.stack_size()) {
  545            log_swferror(_("%d elements left on the stack after block "
  546                    "execution."), env.stack_size() - _initialStackSize);
  547         }
  548     );
  549 
  550     // Have movie_root flush any newly pushed actions in higher priority queues
  551     getRoot(env).flushHigherPriorityActionQueues();
  552 
  553 }
  554 
  555 void
  556 ActionExec::skip_actions(size_t offset)
  557 {
  558 
  559     for (size_t i = 0; i < offset; ++i) {
  560         // we need to check at every iteration because
  561         // an action can be longer then a single byte
  562         if (next_pc >= stop_pc) {
  563             IF_VERBOSE_MALFORMED_SWF(
  564                 log_swferror(_("End of DoAction block hit while skipping "
  565                   "%d action tags (pc:%d, stop_pc:%d) "
  566                   "(WaitForFrame, probably)"), offset, next_pc,
  567                   stop_pc);
  568             )
  569             next_pc = stop_pc;
  570             return;
  571         }
  572 
  573         // Get the opcode.
  574         const boost::uint8_t action_id = code[next_pc];
  575 
  576         // Set default next_pc offset, control flow action handlers
  577         // will be able to reset it.
  578         if ((action_id & 0x80) == 0) {
  579             // action with no extra data
  580             ++next_pc;
  581         }
  582         else {
  583             // action with extra data
  584             const boost::int16_t length = code.read_int16(next_pc + 1);
  585             assert(length >= 0);
  586             next_pc += length + 3;
  587         }
  588     }
  589 }
  590 
  591 bool
  592 ActionExec::pushWith(const With& entry)
  593 {
  594     // The maximum number of withs supported is 13, regardless of the
  595     // other documented figures. See actionscript.all/with.as.
  596     const size_t withLimit = 13;
  597 
  598     if (_withStack.size() == withLimit) {
  599         IF_VERBOSE_ASCODING_ERRORS(
  600             log_aserror("With stack limit of %s exceeded");
  601         );
  602         return false;
  603     }
  604     
  605     _withStack.push_back(entry);
  606     _scopeStack.push_back(entry.object());
  607     return true;
  608 }
  609 
  610 bool
  611 ActionExec::delVariable(const std::string& name)
  612 {
  613     return gnash::delVariable(env, name, getScopeStack());
  614 }
  615 
  616 void
  617 ActionExec::setVariable(const std::string& name, const as_value& val)
  618 {
  619     gnash::setVariable(env, name, val, getScopeStack());
  620 }
  621 
  622 as_value
  623 ActionExec::getVariable(const std::string& name, as_object** target)
  624 {
  625     return gnash::getVariable(env, name, getScopeStack(), target);
  626 }
  627 
  628 void
  629 ActionExec::setLocalVariable(const std::string& name, const as_value& val)
  630 {
  631     if (isFunction()) {
  632         // TODO: set local in the function object?
  633         setLocal(getVM(env).currentCall(), getURI(getVM(env), name), val);
  634     } else {
  635         // TODO: set target member  ?
  636         //       what about 'with' stack ?
  637         gnash::setVariable(env, name, val, getScopeStack());
  638     }
  639 }
  640 
  641 as_object*
  642 ActionExec::getTarget()
  643 {
  644     if (_withStack.empty()) {
  645         return getObject(env.target());
  646     }
  647     return _withStack.back().object();
  648 }
  649 
  650 void
  651 ActionExec::pushTryBlock(TryBlock t)
  652 {
  653     // The current block should end at the end of the try block.
  654     t._savedEndOffset = stop_pc;
  655     stop_pc = t._catchOffset;
  656 
  657     _tryList.push_back(t);
  658 }
  659 
  660 void
  661 ActionExec::pushReturn(const as_value& t)
  662 {
  663     if (retval) {
  664         *retval = t;
  665     }
  666     _returning = true;
  667 }
  668 
  669 void
  670 ActionExec::adjustNextPC(int offset)
  671 {
  672     const int tagPos = offset + static_cast<int>(pc);
  673     if (tagPos < 0) {
  674         log_unimpl(_("Jump outside DoAction tag requested (offset %d "
  675             "before tag start)"), -tagPos);
  676         return;
  677     }
  678     next_pc += offset;
  679 }
  680 
  681 void
  682 ActionExec::dumpActions(size_t from, size_t to, std::ostream& os)
  683 {
  684     size_t lpc = from;
  685     while (lpc < to) {
  686         // Get the opcode.
  687         const boost::uint8_t action_id = code[lpc];
  688 
  689         os << " PC:" << lpc << " - EX: " <<  code.disasm(lpc) << std::endl;
  690 
  691         // Set default next_pc offset, control flow action handlers
  692         // will be able to reset it.
  693         if ((action_id & 0x80) == 0) {
  694             // action with no extra data
  695             ++lpc;
  696         } 
  697         else {
  698             // action with extra data
  699             const boost::int16_t length = code.read_int16(lpc + 1);
  700             assert(length >= 0);
  701             lpc += length + 3;
  702         }
  703 
  704     }
  705 }
  706 
  707 as_object*
  708 ActionExec::getThisPointer()
  709 {
  710     return _func ? _this_ptr : getObject(env.get_original_target()); 
  711 }
  712 
  713 } // end of namespace gnash
  714 
  715 
  716 // Local Variables:
  717 // mode: C++
  718 // indent-tabs-mode: t
  719 // End: