gnash-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Gnash-commit] /srv/bzr/gnash/trunk r12328: Extensive cleanup of VM code


From: Benjamin Wolsey
Subject: [Gnash-commit] /srv/bzr/gnash/trunk r12328: Extensive cleanup of VM code, with no functionality changes (yet).
Date: Fri, 23 Jul 2010 12:11:11 +0200
User-agent: Bazaar (2.0.3)

------------------------------------------------------------
revno: 12328 [merge]
committer: Benjamin Wolsey <address@hidden>
branch nick: trunk
timestamp: Fri 2010-07-23 12:11:11 +0200
message:
  Extensive cleanup of VM code, with no functionality changes (yet).
  
  Drop about half of the horrible as_enviroment class. Use other, more
  appropriate classes to do the work instead of forwarding function calls
  interminably.
  
  Start documenting the various classes involved in action execution, including
  what their tasks are and why they exist, so that the VM isn't quite so
  impenetrable. This doesn't include as_environment; I'm not sure what it's for.
added:
  libcore/UserFunction.h
modified:
  libcore/Button.cpp
  libcore/ClassHierarchy.cpp
  libcore/ExternalInterface.cpp
  libcore/Makefile.am
  libcore/MovieClip.cpp
  libcore/TextField.cpp
  libcore/Video.cpp
  libcore/as_environment.cpp
  libcore/as_environment.h
  libcore/as_function.cpp
  libcore/as_object.cpp
  libcore/as_object.h
  libcore/asobj/MovieClip_as.cpp
  libcore/asobj/TextField_as.cpp
  libcore/builtin_function.h
  libcore/swf_function.cpp
  libcore/swf_function.h
  libcore/vm/ASHandlers.cpp
  libcore/vm/ActionExec.cpp
  libcore/vm/ActionExec.h
  libcore/vm/CallStack.cpp
  libcore/vm/CallStack.h
  libcore/vm/VM.cpp
  libcore/vm/VM.h
  libcore/vm/fn_call.h
=== modified file 'libcore/Button.cpp'
--- a/libcore/Button.cpp        2010-07-05 08:32:15 +0000
+++ b/libcore/Button.cpp        2010-07-23 07:18:03 +0000
@@ -31,7 +31,6 @@
 #include "MovieClip.h"
 #include "movie_root.h"
 #include "VM.h"
-#include "builtin_function.h"
 #include "NativeFunction.h"
 #include "fn_call.h" 
 #include "ExecutableCode.h"

=== modified file 'libcore/ClassHierarchy.cpp'
--- a/libcore/ClassHierarchy.cpp        2010-07-09 10:57:44 +0000
+++ b/libcore/ClassHierarchy.cpp        2010-07-23 07:18:03 +0000
@@ -22,7 +22,6 @@
 #include "namedStrings.h"
 #include "ClassHierarchy.h"
 #include "as_function.h"
-#include "builtin_function.h"
 #include "Class.h"
 #include "Global_as.h"
 #include "extension.h"

=== modified file 'libcore/ExternalInterface.cpp'
--- a/libcore/ExternalInterface.cpp     2010-07-09 07:11:32 +0000
+++ b/libcore/ExternalInterface.cpp     2010-07-23 07:18:03 +0000
@@ -28,14 +28,10 @@
 #include <algorithm>
 
 #include "StringPredicates.h"
-#include "Relay.h" // for inheritance
 #include "ExternalInterface.h"
-#include "as_object.h" // for inheritance
 #include "fn_call.h"
 #include "Global_as.h"
 #include "smart_ptr.h" // for boost intrusive_ptr
-#include "builtin_function.h" // need builtin_function
-#include "GnashException.h" // for ActionException
 #include "VM.h"
 #include "rc.h"
 #include "as_value.h"

=== modified file 'libcore/Makefile.am'
--- a/libcore/Makefile.am       2010-07-06 10:21:25 +0000
+++ b/libcore/Makefile.am       2010-07-22 11:56:51 +0000
@@ -244,6 +244,7 @@
        PropFlags.h     \
        CharacterProxy.h \
        builtin_function.h \
+       UserFunction.h \
        NativeFunction.h \
        as_function.h \
        namedStrings.h \

=== modified file 'libcore/MovieClip.cpp'
--- a/libcore/MovieClip.cpp     2010-07-13 06:16:31 +0000
+++ b/libcore/MovieClip.cpp     2010-07-23 07:18:03 +0000
@@ -36,7 +36,6 @@
 #include "swf_event.h"
 #include "sprite_definition.h"
 #include "ActionExec.h"
-#include "builtin_function.h"
 #include "smart_ptr.h" // GNASH_USE_GC
 #include "VM.h"
 #include "Range2d.h" // for getBounds
@@ -1718,7 +1717,7 @@
     // A MovieClip should always have an associated object.
     assert(mc);
 
-    if (!isAS3(getVM(*mc)) && !get_parent()) {
+    if (!get_parent()) {
         mc->init_member("$version", getVM(*mc).getPlayerVersion(), 0); 
     }
 

=== modified file 'libcore/TextField.cpp'
--- a/libcore/TextField.cpp     2010-07-21 08:35:58 +0000
+++ b/libcore/TextField.cpp     2010-07-23 08:56:49 +0000
@@ -34,7 +34,7 @@
 #include "MovieClip.h"
 #include "TextField.h"
 #include "movie_root.h"     // for killing focus
-#include "as_environment.h" // for parse_path
+#include "as_environment.h" 
 #include "Font.h" 
 #include "fontlib.h" 
 #include "namedStrings.h"

=== added file 'libcore/UserFunction.h'
--- a/libcore/UserFunction.h    1970-01-01 00:00:00 +0000
+++ b/libcore/UserFunction.h    2010-07-23 07:18:03 +0000
@@ -0,0 +1,60 @@
+// 
+//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
+//   Foundation, Inc
+// 
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#ifndef GNASH_USER_FUNCTION_H
+#define GNASH_USER_FUNCTION_H
+
+#include "as_function.h"
+
+namespace gnash {
+    class Global_as;
+}
+
+namespace gnash {
+
+/// A UserFunction is a callable function defined in ActionScript
+//
+/// Gnash has two types of UserFunction:
+//
+/// 1. swf_function: functions parsed from a SWF
+/// 2. builtin_function: functions implemented in C++ as though they were
+///    These are used to implement what the API functions that the proprietary
+///    player implements in a startup script.
+class UserFunction : public as_function
+{
+public:
+
+    /// Return the number of local registers needed
+    //
+    /// Only Function2 functions require local registers; for all others
+    /// the value should be 0.
+    virtual size_t registers() const = 0;
+
+protected:
+
+    UserFunction(Global_as& gl) : as_function(gl) {}
+
+    /// This is an abstract base class!
+    virtual ~UserFunction() = 0;
+
+};
+
+inline UserFunction::~UserFunction() {}
+
+}
+
+#endif

=== modified file 'libcore/Video.cpp'
--- a/libcore/Video.cpp 2010-07-14 10:47:13 +0000
+++ b/libcore/Video.cpp 2010-07-23 07:18:03 +0000
@@ -25,7 +25,6 @@
 #include "fn_call.h"
 #include "as_value.h"
 #include "NetStream_as.h"
-#include "builtin_function.h" // for getter/setter properties
 #include "NativeFunction.h" 
 #include "movie_root.h"
 #include "VM.h"

=== modified file 'libcore/as_environment.cpp'
--- a/libcore/as_environment.cpp        2010-07-21 10:30:55 +0000
+++ b/libcore/as_environment.cpp        2010-07-23 09:00:55 +0000
@@ -28,7 +28,6 @@
 #include "Property.h"
 #include "as_object.h"
 #include "namedStrings.h"
-#include "as_function.h" 
 #include "CallStack.h"
 #include "Global_as.h"
 
@@ -64,6 +63,19 @@
     return locals.get_member(st.find(name), &ret);
 }
 
+bool
+findLocal(as_object& locals, const std::string& varname, as_value& ret,
+        as_object** retTarget) 
+{
+
+    if (getLocal(locals, varname, ret)) {
+        if (retTarget) *retTarget = &locals;
+        return true;
+    }
+
+    return false;
+}
+
 /// Delete a local variable
 //
 /// @param varname
@@ -112,7 +124,6 @@
     :
     _vm(vm),
     _stack(_vm.getStack()),
-    _localFrames(_vm.getCallStack()),
     m_target(0),
     _original_target(0)
 {
@@ -136,7 +147,6 @@
     {
         // TODO: let find_target return generic as_objects, or use 'with' 
stack,
         //       see player2.swf or bug #18758 (strip.swf)
-        // @@ TODO: should we use scopeStack here too ?
         as_object* target = find_object(path, &scopeStack); 
 
         if (target)
@@ -239,7 +249,11 @@
 
     // Check locals for getting them
     // for SWF6 and up locals should be in the scope stack
-    if (swfVersion < 6 && findLocal(varname, val, retTarget)) return val;
+    if (swfVersion < 6 && _vm.calling()) {
+       if (findLocal(_vm.currentCall().locals(), varname, val, retTarget)) {
+           return val;
+       }
+    }
 
     // Check current target members. TODO: shouldn't target be in scope stack ?
     if (m_target)
@@ -333,7 +347,7 @@
     }
 
     // Check locals for deletion.
-    if ( delLocal(varname) )
+    if (_vm.calling() && deleteLocal(_vm.currentCall().locals(), varname))
     {
         return true;
     }
@@ -407,7 +421,7 @@
 
     // in SWF5 and lower, scope stack should just contain 'with' elements 
 
-    // Check the with-stack.
+    // Check the scope stack.
     for (size_t i = scopeStack.size(); i > 0; --i)
     {
         as_object* obj = scopeStack[i-1];
@@ -417,7 +431,9 @@
     }
     
     const int swfVersion = vm.getSWFVersion();
-    if (swfVersion < 6 && setLocal(varname, val)) return;
+    if (swfVersion < 6 && _vm.calling()) {
+       if (setLocal(_vm.currentCall().locals(), varname, val)) return;
+    }
     
     // TODO: shouldn't m_target be in the scope chain ?
     if (m_target) getObject(m_target)->set_member(varkey, val);
@@ -433,60 +449,6 @@
     }
 }
 
-// Set/initialize the value of the local variable.
-void
-as_environment::set_local(const std::string& varname, const as_value& val)
-{
-    // why would you want to set a local if there's no call frame on the
-    // stack ?
-    assert( ! _localFrames.empty() );
-
-    string_table::key varkey = _vm.getStringTable().find(varname);
-    // Is it in the current frame already?
-    if ( setLocal(varname, val) )
-    {
-        return;
-    }
-    else
-    {
-        // Not in frame; create a new local var.
-        assert(!varname.empty()); // null varnames are invalid!
-        as_object& locals = _localFrames.back().locals();
-        //locals.push_back(as_environment::frame_slot(varname, val));
-        locals.set_member(varkey, val);
-    }
-}
-    
-// Create the specified local var if it doesn't exist already.
-void
-as_environment::declare_local(const std::string& varname)
-{
-    as_value tmp;
-    if ( ! findLocal(varname, tmp) )
-    {
-        // Not in frame; create a new local var.
-        assert(!_localFrames.empty());
-        assert( ! varname.empty() );    // null varnames are invalid!
-        as_object& locals = _localFrames.back().locals();
-        locals.set_member(_vm.getStringTable().find(varname), as_value());
-    }
-}
-
-bool
-as_environment::parse_path(const std::string& var_path, as_object** target,
-        as_value& val)
-{
-    std::string path;
-    std::string var;
-    if (!parsePath(var_path, path, var)) return false;
-    as_object* target_ptr = find_object(path); 
-    if ( ! target_ptr ) return false;
-
-    target_ptr->get_member(_vm.getStringTable().find(var), &val);
-    *target = target_ptr;
-    return true;
-}
-
 // Search for next '.' or '/' DisplayObject in this word.  Return
 // a pointer to it, or to NULL if it wasn't found.
 static const char*
@@ -699,230 +661,19 @@
 }
 
 void
-as_environment::dump_local_registers(std::ostream& out) const
-{
-    if ( _localFrames.empty() ) return;
-    out << "Local registers: ";
-    for (CallStack::const_iterator it=_localFrames.begin(),
-            itEnd=_localFrames.end(); it != itEnd; ++it) {
-        if (it != _localFrames.begin()) out << " | ";
-        out << *it;
-    }
-    out << std::endl;
-}
-
-static void
-dump(as_object& locals, std::ostream& out)
-{
-    typedef std::map<std::string, as_value> PropMap;
-    PropMap props;
-    locals.dump_members(props);
-    
-    int count = 0;
-    for (PropMap::iterator i=props.begin(), e=props.end(); i!=e; ++i) {
-        if (count++) out << ", ";
-        // TODO: define output operator for as_value !
-        out << i->first << "==" << i->second;
-    }
-    out << std::endl;
-}
-
-void
-as_environment::dump_local_variables(std::ostream& out) const
-{
-    if ( _localFrames.empty() ) return;
-    out << "Local variables: ";
-    for (CallStack::iterator it=_localFrames.begin(),
-            itEnd=_localFrames.end(); it != itEnd; ++it) {
-
-        if ( it != _localFrames.begin() ) out << " | ";
-        dump(it->locals(), out);
-    }
-    out << std::endl;
-}
-
-void
-as_environment::dump_global_registers(std::ostream& out) const
-{
-    std::string registers;
-
-    std::stringstream ss;
-
-    ss << "Global registers: ";
-    int defined=0;
-    for (unsigned int i = 0; i < numGlobalRegisters; ++i)
-    {
-        if ( m_global_register[i].is_undefined() ) continue;
-
-        if ( defined++ ) ss <<  ", ";
-
-        ss << i << ":" << m_global_register[i];
-
-    }
-    if ( defined ) out << ss.str() << std::endl;
-}
-
-bool
-as_environment::findLocal(const std::string& varname, as_value& ret,
-        as_object** retTarget) const
-{
-    if (_localFrames.empty()) return false;
-
-    as_object& locals = _localFrames.back().locals();
-
-    if (getLocal(locals, varname, ret)) {
-        if (retTarget) *retTarget = &locals;
-        return true;
-    }
-
-    return false;
-}
-
-bool
-as_environment::delLocal(const std::string& varname)
-{
-    if (_localFrames.empty()) return false;
-    return deleteLocal(_localFrames.back().locals(), varname);
-}
-
-bool
-as_environment::setLocal(const std::string& varname, const as_value& val)
-{
-    if (_localFrames.empty()) return false;
-
-    // If this name is not qualified, the compiler fails to look beyond
-    // as_environment::setLocal.
-    return gnash::setLocal(_localFrames.back().locals(), varname, val);
-}
-
-void
-as_environment::pushCallFrame(as_function& func)
-{
-
-    // The stack size can be changed by the ScriptLimits
-    // tag. There is *no* difference between SWF versions.
-    // TODO: override from gnashrc.
-    
-    // A stack size of 0 is apparently legitimate.
-    const boost::uint16_t recursionLimit = getRoot(func).getRecursionLimit();
-
-    // Don't proceed if local call frames would reach the recursion limit.
-    if (_localFrames.size() + 1 >= recursionLimit) {
-
-        std::ostringstream ss;
-        ss << boost::format(_("Recursion limit reached (%u)")) % 
recursionLimit;
-
-        // throw something
-        throw ActionLimitException(ss.str()); 
-    }
-
-    _localFrames.push_back(CallFrame(&func));
-
-}
-
-void 
-as_environment::popCallFrame()
-{
-    assert(!_localFrames.empty());
-    _localFrames.pop_back();
-}
-    
-void
 as_environment::set_target(DisplayObject* target)
 {
     if (!_original_target) _original_target = target;
     m_target = target;
 }
 
-void
-as_environment::add_local(const std::string& varname, const as_value& val)
-{
-    assert(!varname.empty());   
-    assert(!_localFrames.empty());
-
-    as_object& locals = _localFrames.back().locals();
-    locals.set_member(_vm.getStringTable().find(varname), val);
-}
-
-void
-as_environment::dump_stack(std::ostream& out, unsigned int limit) const
-{
-    unsigned int si=0, n=_stack.size();
-    if ( limit && n > limit )
-    {
-        si=n-limit;
-        out << "Stack (last " << limit << " of " << n << " items): ";
-    }
-    else
-    {
-        out << "Stack: ";
-    }
-
-    for (unsigned int i=si; i<n; i++)
-    {
-        if (i!=si) out << " | ";
-        out << '"' << _stack.value(i) << '"';
-    }
-    out << std::endl;
-}
-
 #ifdef GNASH_USE_GC
 
-unsigned int
-as_environment::setRegister(unsigned int regnum, const as_value& v)
-{
-    // If there is a call frame and it has registers, the value must be
-    // set there.
-    if (!_localFrames.empty()) {
-        CallFrame& fr = _localFrames.back();
-        if (fr.hasRegisters()) {
-            if (_localFrames.back().setRegister(regnum, v)) return 2;
-            return 0;
-        }
-    }
-
-    if (regnum < numGlobalRegisters) {
-        m_global_register[regnum] = v;
-        return 1;
-    }
-
-    return 0;
-}
-
-unsigned int
-as_environment::getRegister(unsigned int regnum, as_value& v)
-{
-    // If there is a call frame and it has registers, the value must be
-    // sought there.
-    if (!_localFrames.empty()) {
-        const CallFrame& fr = _localFrames.back();
-        if (fr.hasRegisters()) {
-            if (fr.getRegister(regnum, v)) return 2;
-            return 0;
-        }
-    }
-
-    // Otherwise it can be in the global registers.
-    if (regnum < numGlobalRegisters) {
-        v = m_global_register[regnum];
-        return 1;
-    }
-
-    return 0;
-}
-
 void
 as_environment::markReachableResources() const
 {
-    for (size_t i = 0; i < 4; ++i) {
-        m_global_register[i].setReachable();
-    }
-
     if (m_target) m_target->setReachable();
     if (_original_target) _original_target->setReachable();
-
-    // _localFrames and _stack are taken care of by VM
-
 }
 #endif // GNASH_USE_GC
 

=== modified file 'libcore/as_environment.h'
--- a/libcore/as_environment.h  2010-07-21 08:35:58 +0000
+++ b/libcore/as_environment.h  2010-07-23 08:56:49 +0000
@@ -22,11 +22,9 @@
 #include "smart_ptr.h" // GNASH_USE_GC
 #include "as_value.h" // for composition (vector + frame_slot)
 #include "SafeStack.h"
-#include "CallStack.h" // for composition
 
 #include <string> // for frame_slot name
 #include <vector>
-#include <iostream> // for dump_stack inline
 
 namespace gnash {
 
@@ -36,6 +34,7 @@
 class Global_as;
 class movie_root;
 class string_table;
+class UserFunction;
 
 /// ActionScript execution environment.
 class as_environment
@@ -183,115 +182,6 @@
     void set_variable(const std::string& path, const as_value& val,
         const ScopeStack& scopeStack);
 
-    /// Set/initialize the value of the local variable.
-    //
-    /// If no *local* variable with that name is found, a new one
-    /// will be created.
-    ///
-    /// @param varname
-    /// Variable name. Can not contain path elements.
-    /// TODO: should be case-insensitive up to SWF6.
-    ///
-    /// @param val
-    /// The value to assign to the variable. 
-    ///
-    void set_local(const std::string& varname, const as_value& val);
-
-    /// \brief
-    /// Add a local var with the given name and value to our
-    /// current local frame. 
-    ///
-    /// Use this when you know the var
-    /// doesn't exist yet, since it's faster than set_local();
-    /// e.g. when setting up args for a function.
-    ///
-    void add_local(const std::string& varname, const as_value& val);
-
-    /// Create the specified local var if it doesn't exist already.
-    void declare_local(const std::string& varname);
-
-    /// Add 'count' local registers (add space to end)
-    //
-    /// Local registers are only meaningful within a function2 context.
-    ///
-    void add_local_registers(unsigned int register_count) {
-        assert(!_localFrames.empty());
-        return _localFrames.back().resizeRegisters(register_count);
-    }
-
-    /// Set value of a register (local or global).
-    //
-    /// When not in a function context the register will be
-    /// global or none (if regnum is not in the valid range
-    /// of global registers).
-    ///
-    /// When in a function context defining NO registers, 
-    /// we'll behave the same as for a non-function context.
-    ///
-    /// When in a function context defining non-zero number
-    /// of local registers, the register set will be local
-    /// or none (if regnum is not in the valid range of local
-    /// registers).
-    ///
-    /// @param regnum
-    /// Register number
-    ///
-    /// @param v
-    /// Value to assign to the register
-    ///
-    /// @return 0 if register num is invalid
-    ///         1 if a global register was set
-    ///         2 if a local register was set
-    ///
-    unsigned int setRegister(unsigned int regnum, const as_value& v);
-
-    /// Get value of a register (local or global).
-    //
-    /// When not in a function context the register will be
-    /// global or none (if regnum is not in the valid range
-    /// of global registers).
-    ///
-    /// When in a function context defining NO registers, 
-    /// we'll behave the same as for a non-function context.
-    ///
-    /// When in a function context defining non-zero number
-    /// of local registers, the register set will be local
-    /// or none (if regnum is not in the valid range of local
-    /// registers).
-    ///
-    /// @param regnum
-    /// Register number
-    ///
-    /// @param v
-    /// Output parameter, will be set to register
-    ///     value or untouched if 0 is returned.
-    ///
-    /// @return 0 if register num is invalid (v unmodified in this case)
-    ///         1 if a global register was set
-    ///         2 if a local register was set
-    ///
-    unsigned int getRegister(unsigned int regnum, as_value& v);
-
-    /// Set the Nth local register to something
-    void set_local_register(boost::uint8_t n, as_value &val) {
-        if (! _localFrames.empty()) {
-            _localFrames.back().setRegister(n, val);
-        }
-    }
-
-    /// Return a reference to the Nth global register.
-    as_value& global_register(unsigned int n) {
-        assert(n<4);
-        return m_global_register[n];
-    }
-
-    /// Set the Nth local register to something
-    void set_global_register(boost::uint8_t n, as_value &val) {
-        if (n <= 4) {
-            m_global_register[n] = val;
-        }
-    }
-
 #ifdef GNASH_USE_GC
     /// Mark all reachable resources.
     //
@@ -314,32 +204,9 @@
     /// Supports both /slash/syntax and dot.syntax
     /// Case insensitive for SWF up to 6, sensitive from 7 up
     ///
-    as_object* find_object(const std::string& path, const ScopeStack* 
scopeStack=NULL) const;
+    as_object* find_object(const std::string& path,
+            const ScopeStack* scopeStack = 0) const;
     
-    /// Dump content of the stack to a std::ostream
-    //
-    /// @param out
-    /// The output stream, standard error if omitted.
-    ///
-    /// @param limit
-    /// If > 0, limit number of printed item by the given amount (from the 
top).
-    /// Unlimited by default;
-    ///
-    void dump_stack(std::ostream& out=std::cerr, unsigned int limit=0) const;
-
-    /// Dump the local registers to a std::ostream
-    //
-    /// NOTE that nothing will be written to the stream if NO local registers
-    ///      are set
-    ///
-    void dump_local_registers(std::ostream& out=std::cerr) const;
-
-    /// Dump the global registers to a std::ostream
-    void dump_global_registers(std::ostream& out=std::cerr) const;
-
-    /// Dump the local variables to a std::ostream
-    void dump_local_variables(std::ostream& out=std::cerr) const;
-
     /// Return the SWF version we're running for.
     //
     /// NOTE: this is the version encoded in the first loaded
@@ -348,66 +215,6 @@
     ///
     int get_version() const;
 
-    /// \brief
-    /// Try to parse a string as a variable path
-    //
-    /// Variable paths come in the form:
-    ///
-    ///     /path/to/some/sprite/:varname
-    ///
-    /// or
-    ///
-    ///     /path/to/some/sprite 
-    ///
-    /// or
-    /// path.to.some.var
-    ///
-    /// If there's no dot nor colon, or if the 'path' part
-    /// does not resolve to an object, this function returns false.
-    /// Otherwise, true is returned and 'target' and 'val'
-    /// parameters are appropriaterly set.
-    ///
-    /// Note that if the parser variable name doesn't exist in the found
-    /// target, the 'val' will be undefined, but no other way to tell whether
-    /// the variable existed or not from the caller...
-    ///
-    bool parse_path(const std::string& var_path, as_object** target, as_value& 
val);
-
-    /// A class to wrap frame access.  Stack allocating a frame guard
-    /// will ensure that all CallFrame pushes have a corresponding
-    /// CallFrame pop, even in the presence of extraordinary returns.
-    class FrameGuard
-    {
-        as_environment& _env;
-
-    public:
-        FrameGuard(as_environment& env, as_function& func)
-            :
-            _env(env)
-        {
-            _env.pushCallFrame(func);
-        }
-
-        ~FrameGuard()
-        {
-            _env.popCallFrame();
-        }
-    };
-
-    /// Get top element of the call stack
-    //
-    CallFrame& topCallFrame()
-    {
-        assert(!_localFrames.empty());
-        return _localFrames.back();
-    }
-
-    /// Return the depth of call stack
-    size_t callStackDepth()
-    {
-        return _localFrames.size();
-    }
-
 private:
 
     VM& _vm;
@@ -415,37 +222,12 @@
     /// Stack of as_values in this environment
     SafeStack<as_value>& _stack;
 
-    static const short unsigned int numGlobalRegisters = 4;
-
-    CallStack& _localFrames;
-
-    as_value m_global_register[numGlobalRegisters];
-
     /// Movie target. 
     DisplayObject* m_target;
 
     /// Movie target. 
     DisplayObject* _original_target;
 
-    /// Push a frame on the calls stack.
-    //
-    /// This should happen right before calling an ActionScript
-    /// function. Function local registers and variables
-    /// must be set *after* pushCallFrame has been invoked
-    ///
-    /// Call popCallFrame() at ActionScript function return.
-    ///
-    /// @param func
-    /// The function being called
-    ///
-    void pushCallFrame(as_function& func);
-
-    /// Remove current call frame from the stack
-    //
-    /// This should happen when an ActionScript function returns.
-    ///
-    void popCallFrame();
-    
     /// Given a variable name, set its value (no support for path)
     //
     /// If no variable with that name is found, a new one
@@ -469,46 +251,6 @@
     as_value get_variable_raw(const std::string& varname,
         const ScopeStack& scopeStack, as_object** retTarget=NULL) const;
 
-    /// \brief
-    /// Get a local variable given its name,
-    //
-    /// @param varname
-    /// Name of the local variable
-    ///
-    /// @param ret
-    /// If a variable is found it's assigned to this parameter.
-    /// Untouched if the variable is not found.
-    ///
-    /// @param retTarget
-    /// If not NULL, the pointer will be set to the actual object containing 
the
-    /// found variable (if found).
-    ///
-    /// @return true if the variable was found, false otherwise
-    ///
-    bool findLocal(const std::string& varname, as_value& ret,
-            as_object** retTarget = 0) const;
-
-    /// Delete a variable from the given as_object
-    //
-    /// @param varname
-    /// Name of the local variable
-    ///
-    /// @return true if the variable was found and deleted, false otherwise
-    ///
-    bool delLocal(const std::string& varname);
-
-    /// Set a local variable, if it exists.
-    //
-    /// @param varname
-    /// Name of the local variable
-    ///
-    /// @param val
-    /// Value to assign to the variable
-    ///
-    /// @return true if the variable was found, false otherwise
-    ///
-    bool setLocal(const std::string& varname, const as_value& val);
-
     static as_value undefVal;
         
 };

=== modified file 'libcore/as_function.cpp'
--- a/libcore/as_function.cpp   2010-06-21 06:02:24 +0000
+++ b/libcore/as_function.cpp   2010-07-23 07:18:03 +0000
@@ -21,7 +21,6 @@
 #include "smart_ptr.h" // GNASH_USE_GC
 #include "log.h"
 #include "as_function.h"
-#include "builtin_function.h" // for _global.Function
 #include "as_value.h"
 #include "Array_as.h"
 #include "Global_as.h"

=== modified file 'libcore/as_object.cpp'
--- a/libcore/as_object.cpp     2010-07-09 14:25:07 +0000
+++ b/libcore/as_object.cpp     2010-07-22 12:52:31 +0000
@@ -315,13 +315,6 @@
 const std::string&
 as_object::stringValue() const
 {
-    // TODO: AS3 returns a string describing the type of object, e.g.
-    // "[object MyObject]"
-    if (isAS3(*this)) {
-        static const std::string str("[object Object]");
-        return str;
-    }
-
     static const std::string str("[object Object]");
     return str;
 }
@@ -1211,12 +1204,6 @@
     return *o.vm().getGlobal();
 }
 
-bool
-isAS3(const as_object& o)
-{
-    return isAS3(getVM(o));
-}
-
 } // end of gnash namespace
 
 // local Variables:

=== modified file 'libcore/as_object.h'
--- a/libcore/as_object.h       2010-07-09 14:25:07 +0000
+++ b/libcore/as_object.h       2010-07-22 12:52:31 +0000
@@ -910,9 +910,6 @@
 /// Get the Global object from an as_object.
 Global_as& getGlobal(const as_object& o);
 
-/// Return whether the object is an AS3 object.
-bool isAS3(const as_object& o);
-
 /// Return whether property matching is caseless
 inline bool caseless(const as_object& o) {
     return getSWFVersion(o) < 7;

=== modified file 'libcore/asobj/MovieClip_as.cpp'
--- a/libcore/asobj/MovieClip_as.cpp    2010-07-09 06:58:02 +0000
+++ b/libcore/asobj/MovieClip_as.cpp    2010-07-22 14:16:16 +0000
@@ -2228,9 +2228,8 @@
 
 
 as_value
-movieclip_as2_ctor(const fn_call& fn)
+movieclip_as2_ctor(const fn_call& /*fn*/)
 {
-    assert(!isAS3(fn));
     return as_value();
 }
 

=== modified file 'libcore/asobj/TextField_as.cpp'
--- a/libcore/asobj/TextField_as.cpp    2010-07-09 07:37:30 +0000
+++ b/libcore/asobj/TextField_as.cpp    2010-07-22 12:52:31 +0000
@@ -697,14 +697,6 @@
         return as_value();
     }
 
-    if (isAS3(fn)) {
-        // TODO: current font finding assumes we have a parent, which isn't
-        // necessarily the case in AS3. It seems the AS2 implementation is
-        // wrong anyway.
-        log_unimpl("fonts in AS3 TextField.setTextFormat");
-        return as_value();
-    }
-
     if (tf->font())
     {
         const std::string& fontName = *tf->font();
@@ -1089,13 +1081,6 @@
 textfield_ctor(const fn_call& fn)
 {
 
-    if (isAS3(fn)) {
-        as_object* obj = ensure<ValidThis>(fn);
-        SWFRect nullRect;
-        obj->setDisplayObject(new TextField(obj, 0, nullRect));
-        return as_value();
-    }
-
     as_object* obj = ensure<ValidThis>(fn);
     
     // It's not clear why this happens. Attaching a relay would have the

=== modified file 'libcore/builtin_function.h'
--- a/libcore/builtin_function.h        2010-03-13 18:00:33 +0000
+++ b/libcore/builtin_function.h        2010-07-22 12:37:56 +0000
@@ -19,7 +19,7 @@
 #ifndef GNASH_BUILTIN_FUNCTION_H
 #define GNASH_BUILTIN_FUNCTION_H
 
-#include "as_function.h" 
+#include "UserFunction.h" 
 #include "as_environment.h"
 #include "fn_call.h"
 
@@ -36,7 +36,7 @@
 //
 /// They are distinct from NativeFunctions, which are part of the player and
 /// do not go through the ActionScript interpreter.
-class builtin_function : public as_function
+class builtin_function : public UserFunction
 {
     typedef as_value (*ASFunction)(const fn_call& fn);
 
@@ -51,16 +51,22 @@
        ///     For classes, the function pointer is the constructor.
        builtin_function(Global_as& gl, ASFunction func)
                :
-               as_function(gl),
+               UserFunction(gl),
                _func(func)
        {
        }
 
+    /// Return the number of registers required for function execution
+    //
+    /// Gnash's C++ implementations of AS functions don't need any registers!
+    virtual size_t registers() const {
+        return 0;
+    }
+
        /// Invoke this function or this Class constructor
        virtual as_value call(const fn_call& fn)
        {
-        as_environment env = fn.env();
-               as_environment::FrameGuard guard(env, *this);
+               FrameGuard guard(getVM(fn), *this);
 
                assert(_func);
                return _func(fn);

=== modified file 'libcore/swf_function.cpp'
--- a/libcore/swf_function.cpp  2010-01-11 06:41:38 +0000
+++ b/libcore/swf_function.cpp  2010-07-23 08:10:09 +0000
@@ -22,11 +22,11 @@
 #include "MovieClip.h"
 #include "action_buffer.h"
 #include "ActionExec.h" // for operator()
-#include "VM.h" // for storing _global in a local register
-#include "NativeFunction.h" // for Function constructor
+#include "VM.h" 
+#include "NativeFunction.h" 
 #include "Global_as.h" 
 #include "namedStrings.h"
-
+#include "CallStack.h"
 #include <typeinfo>
 #include <iostream>
 #include <string>
@@ -46,7 +46,7 @@
 swf_function::swf_function(const action_buffer& ab, as_environment& env,
                        size_t start, const ScopeStack& scopeStack)
        :
-       as_function(getGlobal(env)),
+       UserFunction(getGlobal(env)),
        m_action_buffer(ab),
        _env(env),
        _scopeStack(scopeStack),
@@ -74,13 +74,14 @@
 /// in as_environment seems bogus, see setProperty.as, the 
 /// failure of setTarget("") construct in SWF5.
 ///
-struct TargetGuard {
+struct TargetGuard
+{
        as_environment& env;
        DisplayObject* from;
        DisplayObject* from_orig;
 
        // @param ch : target to set temporarely
-       // @param och : original target to set temporarely
+       // @param och : original target to set temporarily
        TargetGuard(as_environment& e, DisplayObject* ch, DisplayObject* och)
                :
                env(e),
@@ -105,12 +106,12 @@
 {
     // Extract caller before pushing ourself on the call stack
     VM& vm = getVM(fn); 
-    CallStack& cs = vm.getCallStack();
 
-    as_object* caller = cs.empty() ? 0 : &cs.back().function();
+    as_object* caller = vm.calling() ? &vm.currentCall().function() : 0;
 
        // Set up local stack frame, for parameters and locals.
-       as_environment::FrameGuard guard(_env, *this);
+       FrameGuard guard(getVM(fn), *this);
+    CallFrame& cf = guard.callFrame();
 
        DisplayObject* target = _env.get_target();
        DisplayObject* orig_target = _env.get_original_target();
@@ -150,43 +151,46 @@
                {
                        assert(_args[i].reg == 0);
                        if (i < fn.nargs) {
-                               _env.add_local(_args[i].name, fn.arg(i));
+                setLocal(cf, _args[i].name, fn.arg(i));
                        }
                        else {
                                // Still declare named arguments, even if
                                // they are not passed from caller
                                // See bug #22203
-                               _env.declare_local(_args[i].name);
+                declareLocal(cf, _args[i].name);
                        }
                }
 
                // Add 'this'
-               _env.set_local("this", fn.this_ptr ? fn.this_ptr : as_value());
+        setLocal(cf, NSV::PROP_THIS, fn.this_ptr ? fn.this_ptr : as_value());
 
         as_object* super = fn.super ? fn.super :
             fn.this_ptr ? fn.this_ptr->get_super() : 0;
 
                // Add 'super' (SWF6+ only)
                if (super && swfversion > 5) {
-                       _env.set_local("super", super);
+            setLocal(cf, NSV::PROP_SUPER, super);
                }
 
                // Add 'arguments'
         as_object* args = getGlobal(fn).createArray();
-               _env.set_local("arguments", getArguments(*this, *args, fn, 
caller));
+        string_table& st = getStringTable(fn);
+        // Put 'arguments' in a local var.
+        setLocal(cf, st.find("arguments"),
+                getArguments(*this, *args, fn, caller));
        }
        else
        {
                // function2: most args go in registers; any others get pushed.
-               
-               // Create local registers.
-               _env.add_local_registers(_registerCount);
 
                // Handle the implicit args.
                // @@ why start at 1 ? Note that starting at 0 makes    
                // intro.swf movie fail to play correctly.
         size_t current_reg(1);
 
+        // This is us. TODO: why do we have to query the VM to get
+        // what are effectively our own resources?
+
         // If this is not suppressed it is either placed in a register
         // or set as a local variable, but not both.
                if (!(_function2Flags & SUPPRESS_THIS)) {
@@ -194,12 +198,13 @@
                 // preload 'this' into a register.
                 // TODO: check whether it should be undefined or null
                 // if this_ptr is null.
-                _env.setRegister(current_reg, fn.this_ptr); 
+                cf.setLocalRegister(current_reg, fn.this_ptr); 
                 ++current_reg;
             }
             else {
                 // Put 'this' in a local var.
-                _env.add_local("this", fn.this_ptr ? fn.this_ptr : as_value());
+                setLocal(cf, NSV::PROP_THIS,
+                        fn.this_ptr ? fn.this_ptr : as_value());
             }
         }
 
@@ -219,12 +224,13 @@
 
             if (_function2Flags & PRELOAD_ARGUMENTS) {
                 // preload 'arguments' into a register.
-                _env.setRegister(current_reg, args);
+                cf.setLocalRegister(current_reg, args);
                 ++current_reg;
             }
             else {
+                string_table& st = getStringTable(fn);
                 // Put 'arguments' in a local var.
-                _env.add_local("arguments", args);
+                setLocal(cf, st.find("arguments"), args);
             }
 
         }
@@ -239,11 +245,11 @@
                 fn.this_ptr ? fn.this_ptr->get_super() : 0;
 
             if (super && (_function2Flags & PRELOAD_SUPER)) {
-                               _env.setRegister(current_reg, super);
+                               cf.setLocalRegister(current_reg, super);
                                current_reg++;
                        }
             else if (super) {
-                _env.add_local("super", super);
+                setLocal(cf, NSV::PROP_SUPER, super);
             }
                }
 
@@ -253,7 +259,7 @@
                        if (tgtch) {
                                // NOTE: _lockroot will be handled by 
getAsRoot()
                                as_object* r = getObject(tgtch->getAsRoot());
-                               _env.setRegister(current_reg, r);
+                               cf.setLocalRegister(current_reg, r);
                                ++current_reg;
                        }
                }
@@ -262,7 +268,7 @@
                        DisplayObject* tgtch = _env.get_target();
             if (tgtch) {
                 as_object* parent = getObject(tgtch->get_parent());
-                _env.setRegister(current_reg, parent);
+                cf.setLocalRegister(current_reg, parent);
                 ++current_reg;
             }
                }
@@ -270,7 +276,7 @@
                if (_function2Flags & PRELOAD_GLOBAL) {
                        // Put '_global' in a register.
                        as_object* global = vm.getGlobal();
-                       _env.setRegister(current_reg, global);
+                       cf.setLocalRegister(current_reg, global);
                        ++current_reg;
                }
 
@@ -283,20 +289,20 @@
                        if (!_args[i].reg) {
                                if (i < fn.nargs) {
                                        // Conventional arg passing: create a 
local var.
-                                       _env.add_local(_args[i].name, 
fn.arg(i));
+                                       setLocal(cf, _args[i].name, fn.arg(i));
                                }
                                else {
                                        // Still declare named arguments, even 
if
                                        // they are not passed from caller
                                        // See bug #22203
-                                       _env.declare_local(_args[i].name);
+                    declareLocal(cf, _args[i].name);
                                }
                        }
                        else {
                                if (i < fn.nargs) {
                                        // Pass argument into a register.
                                        const int reg = _args[i].reg;
-                                       _env.setRegister(reg, fn.arg(i));
+                                       cf.setLocalRegister(reg, fn.arg(i));
                                }
                 // If no argument was passed, no need to setup a register
                 // I guess.
@@ -307,15 +313,13 @@
        // Execute the actions.
        // Do this in a try block to proper drop the pushed call frame 
        // in case of problems (most interesting action limits)
-       try 
-       {
+       try {
         as_value result;
                ActionExec exec(*this, _env, &result, fn.this_ptr);
                exec();
         return result;
        }
-       catch (ActionLimitException& ale) // expected and sane 
-       {
+       catch (ActionLimitException& ale) {
                throw;
        }
 }

=== modified file 'libcore/swf_function.h'
--- a/libcore/swf_function.h    2010-03-12 15:42:07 +0000
+++ b/libcore/swf_function.h    2010-07-23 07:47:23 +0000
@@ -19,24 +19,23 @@
 #ifndef GNASH_SWF_FUNCTION_H
 #define GNASH_SWF_FUNCTION_H
 
-#include "as_function.h" // for inheritance
-
-#include "smart_ptr.h"
 #include <vector>
 #include <cassert>
 #include <string>
 
+#include "UserFunction.h"
+#include "smart_ptr.h"
+
 // Forward declarations
 namespace gnash {
     class action_buffer;
-    class as_environmnet;
     class as_object;
 }
 
 namespace gnash {
 
 /// SWF-defined Function 
-class swf_function : public as_function
+class swf_function : public UserFunction
 {
 
 public:
@@ -115,6 +114,10 @@
 
        void set_is_function2() { _isFunction2 = true; }
 
+    size_t registers() const {
+        return _registerCount;
+    }
+
        void set_local_register_count(boost::uint8_t ct) {
         assert(_isFunction2);
         _registerCount = ct;
@@ -125,7 +128,7 @@
         _function2Flags = flags;
     }
 
-       void add_arg(int arg_register, const char* name)
+       void add_arg(boost::uint8_t arg_register, string_table::key name)
        {
                assert(arg_register == 0 || _isFunction2 == true);
         _args.push_back(Argument(arg_register, name));
@@ -170,9 +173,9 @@
 
        struct Argument
        {
-        Argument(int r, const std::string& n) : reg(r), name(n) {}
+        Argument(int r, string_table::key n) : reg(r), name(n) {}
                int reg;
-               std::string name;
+        string_table::key name;
        };
 
        std::vector<Argument> _args;

=== modified file 'libcore/vm/ASHandlers.cpp'
--- a/libcore/vm/ASHandlers.cpp 2010-07-21 08:35:58 +0000
+++ b/libcore/vm/ASHandlers.cpp 2010-07-23 08:56:49 +0000
@@ -2045,16 +2045,17 @@
 
             case pushRegister: // 4
             {
-                unsigned int reg = code[3 + i];
+                const size_t reg = code[3 + i];
                 ++i;
-                as_value v;
-                if (!env.getRegister(reg, v)) {
+                const as_value* v = getVM(env).getRegister(reg);
+                if (!v) {
                     IF_VERBOSE_MALFORMED_SWF(
                         log_swferror(_("Invalid register %d in ActionPush"),
                             reg);
                     );
+                    env.push(as_value());
                 }
-                env.push(v);
+                else env.push(*v);
                 break;
             }
 
@@ -2273,7 +2274,7 @@
         target = env.find_target(target_path);
     }
 
-    // 4.11 would make parse_path above return true,
+    // 4.11 would make parsePath above return true,
     // we should check if a sprite named '4' is supposed to work
     // in that case
     if (!target) {
@@ -2456,7 +2457,6 @@
 {
 
     as_environment& env = thread.env;
-    std::string function_name;
 
     // Let's consider it a as a string and lookup the function.
     //
@@ -2465,8 +2465,6 @@
     // function is called. If it is undefined, nothing happens, even if
     // there is a function called 'undefined'.
     //
-    // Using to_string() would produce the wrong behaviour.
-    //
     // In all cases, even undefined, the specified number of arguments
     // is dropped from the stack.
     const std::string& funcname = env.pop().to_string();
@@ -2618,14 +2616,16 @@
     as_environment& env = thread.env;
     
     const std::string& varname = env.top(0).to_string();
-    if ( thread.isFunction() )
-    {
-       env.declare_local(varname);
+    const string_table::key name = getStringTable(env).find(varname);
+    VM& vm = getVM(env);
+
+    if (vm.calling()) {
+        declareLocal(vm.currentCall(), name);
     }
-    else
-    {
+    else {
        IF_VERBOSE_ASCODING_ERRORS(
-       log_aserror(_("The 'var whatever' syntax in timeline context is a 
no-op."));
+       log_aserror(_("The 'var whatever' syntax in timeline context is a "
+               "no-op."));
        );
     }
     env.drop(1);
@@ -3473,6 +3473,8 @@
 
     func->set_function2_flags(flags);
 
+    string_table& st = getStringTable(env);
+
     // Get the register assignments and names of the arguments.
     for (unsigned n = 0; n < nargs; n++)
     {
@@ -3480,10 +3482,10 @@
         ++i;
 
         // @@ security: watch out for possible missing terminator here!
-        const char* arg = code.read_string(i);
+        const std::string arg(code.read_string(i));
 
-        func->add_arg(arg_register, arg);
-        i += strlen(arg)+1;
+        func->add_arg(arg_register, st.find(arg));
+        i += arg.size() + 1;
     }
 
     // Get the length of the actual function code.
@@ -3691,16 +3693,18 @@
     // Get number of arguments.
     unsigned nargs = code.read_int16(i);
     i += 2;
+    
+    string_table& st = getStringTable(env);
 
     // Get the names of the arguments.
     for (unsigned n = 0; n < nargs; n++)
     {
-        const char* arg = code.read_string(i);
+        const std::string arg(code.read_string(i));
 
         // @@ security: watch out for possible missing terminator here!
-        func->add_arg(0, arg);
+        func->add_arg(0, st.find(arg));
         // wouldn't it be simpler to use strlen(arg)+1 ?
-        i += strlen(arg)+1; // func->m_args.back().m_name.length() + 1;
+        i += arg.size() + 1; 
     }
 
     // Get the length of the actual function code.
@@ -3765,35 +3769,7 @@
     const size_t reg = code[thread.getCurrentPC() + 3];
 
     // Save top of stack in specified register.
-    const int ret = env.setRegister(reg, env.top(0));
-
-    switch (ret) {
-        default:
-        case 0:
-        {
-            IF_VERBOSE_MALFORMED_SWF(
-                log_swferror(_("Invalid register %d in ActionSetRegister"),
-                    reg);
-            );
-            break;
-        }
-        case 1:
-        {
-            IF_VERBOSE_ACTION (
-                log_action(_("-------------- global register[%d] = '%s'"),
-                    reg, env.top(0));
-            );
-            break;
-        }
-        case 2:
-        {
-            IF_VERBOSE_ACTION (
-                log_action(_("-------------- local register[%d] = '%s'"),
-                    reg, env.top(0));
-            );
-            break;
-        }
-    }
+    getVM(env).setRegister(reg, env.top(0));
 }
 
 

=== modified file 'libcore/vm/ActionExec.cpp'
--- a/libcore/vm/ActionExec.cpp 2010-07-21 08:35:58 +0000
+++ b/libcore/vm/ActionExec.cpp 2010-07-23 08:10:09 +0000
@@ -36,6 +36,7 @@
 #include "as_environment.h"
 #include "debugger.h"
 #include "SystemClock.h"
+#include "CallStack.h"
 
 #include <sstream>
 #include <string>
@@ -75,7 +76,6 @@
     _func(&func),
     _this_ptr(this_ptr),
     _initialStackSize(0),
-    _initialCallStackDepth(0),
     _originalTarget(0),
     _origExecSWFVersion(0),
     _tryList(),
@@ -111,7 +111,7 @@
         // We assume that the swf_function () operator already initialized
         // its environment so that its activation object is now in the
         // top element of the CallFrame stack
-        CallFrame& topFrame = newEnv.topCallFrame();
+        CallFrame& topFrame = getVM(newEnv).currentCall();
         assert(&topFrame.function() == &func);
         _scopeStack.push_back(&topFrame.locals());
     }
@@ -125,7 +125,6 @@
     _withStackLimit(7),
     _func(0),
     _initialStackSize(0),
-    _initialCallStackDepth(0),
     _originalTarget(0),
     _origExecSWFVersion(0),
     _tryList(),
@@ -164,18 +163,13 @@
 
     _initialStackSize = env.stack_size();
 
-    _initialCallStackDepth = env.callStackDepth();
-
 #if DEBUG_STACK
     IF_VERBOSE_ACTION (
             log_action(_("at ActionExec operator() start, pc=%d"
                    ", stop_pc=%d, code.size=%d, func=%d, codeVersion=%d"),
                 pc, stop_pc, code.size(), _func ? _func : 0, codeVersion);
         std::stringstream ss;
-        env.dump_stack(ss, STACK_DUMP_LIMIT);
-        env.dump_global_registers(ss);
-        env.dump_local_registers(ss);
-        env.dump_local_variables(ss);
+        getVM(env).dumpState(ss, STACK_DUMP_LIMIT);
         log_action("%s", ss.str());
     );
 #endif
@@ -318,10 +312,7 @@
                 log_action(_("After execution: PC %d, next PC %d, "
                         "stack follows"), pc, next_pc);
                 std::stringstream ss;
-                env.dump_stack(ss, STACK_DUMP_LIMIT);
-                env.dump_global_registers(ss);
-                env.dump_local_registers(ss);
-                env.dump_local_variables(ss);
+                getVM(env).dumpState(ss, STACK_DUMP_LIMIT);
                 log_action("%s", ss.str());
             );
 #endif
@@ -416,7 +407,7 @@
                     as_value ex = env.pop();
                     ex.unflag_exception();
                     
-                    env.setRegister(t._registerIndex, ex);
+                    getVM(env).setRegister(t._registerIndex, ex);
                 }
             }
             else
@@ -702,9 +693,10 @@
 void
 ActionExec::setLocalVariable(const std::string& name, const as_value& val)
 {
-    if ( isFunction() ) {
+    if (isFunction()) {
+        string_table& st = getStringTable(env);
         // TODO: set local in the function object?
-        env.set_local(name, val);
+        setLocal(getVM(env).currentCall(), st.find(name), val);
     } else {
         // TODO: set target member  ?
         //       what about 'with' stack ?

=== modified file 'libcore/vm/ActionExec.h'
--- a/libcore/vm/ActionExec.h   2010-07-21 08:35:58 +0000
+++ b/libcore/vm/ActionExec.h   2010-07-22 12:02:11 +0000
@@ -206,9 +206,6 @@
        /// Stack size at start of execution
        size_t _initialStackSize;
 
-       /// Call stack depth at start of execution
-       size_t _initialCallStackDepth;
-
        DisplayObject* _originalTarget;
 
        int _origExecSWFVersion;

=== modified file 'libcore/vm/CallStack.cpp'
--- a/libcore/vm/CallStack.cpp  2010-01-11 06:41:38 +0000
+++ b/libcore/vm/CallStack.cpp  2010-07-23 09:15:22 +0000
@@ -17,18 +17,22 @@
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 #include "CallStack.h"
+
+#include <ostream>
+
 #include "as_object.h"
-#include "as_function.h" 
+#include "UserFunction.h" 
 #include "Global_as.h" 
-
-#include <ostream>
+#include "Property.h"
+#include "log.h"
 
 namespace gnash {
 
-CallFrame::CallFrame(as_function* f)
+CallFrame::CallFrame(UserFunction* f)
     :
     _locals(new as_object(getGlobal(*f))),
-    _func(f)
+    _func(f),
+    _registers(_func->registers())
 {
     assert(_func);
 }
@@ -40,14 +44,53 @@
 void
 CallFrame::markReachableResources() const
 {
-    // Func is always valid.
     assert(_func);
     _func->setReachable();
 
     std::for_each(_registers.begin(), _registers.end(),
             std::mem_fun_ref(&as_value::setReachable));
 
-    if (_locals) _locals->setReachable();
+    assert(_locals);
+    _locals->setReachable();
+}
+
+void
+CallFrame::setLocalRegister(size_t i, const as_value& val)
+{
+    if (i >= _registers.size()) return;
+
+    _registers[i] = val;
+
+    IF_VERBOSE_ACTION(
+        log_action(_("-------------- local register[%d] = '%s'"),
+            i, val);
+    );
+
+}
+
+void
+declareLocal(CallFrame& c, string_table::key name)
+{
+    as_object& locals = c.locals();
+    if (!locals.hasOwnProperty(name)) {
+        locals.set_member(name, as_value());
+    }
+}
+
+void
+setLocal(CallFrame& c, string_table::key name, const as_value& val)
+{
+    as_object& locals = c.locals();
+
+    // This way avoids searching the prototype chain, though it seems
+    // unlikely that it is an optimization.
+    Property* prop = locals.getOwnProperty(name);
+    if (prop) {
+        prop->setValue(locals, val);
+        return;
+    }
+
+    locals.set_member(name, val);
 }
 
 std::ostream&

=== modified file 'libcore/vm/CallStack.h'
--- a/libcore/vm/CallStack.h    2010-01-11 06:41:38 +0000
+++ b/libcore/vm/CallStack.h    2010-07-23 09:23:10 +0000
@@ -22,55 +22,85 @@
 #include <vector>
 
 #include "as_value.h"
+#include "string_table.h"
 
 // Forward declarations
 namespace gnash {
     class as_object;
-    class as_function;
+    class UserFunction;
 }
 
 namespace gnash {
 
-/// An element of a CallStack
-class CallFrame 
+/// A CallFrame is an element of a CallStack.
+//
+/// A CallFrame exists for the duration of a UserFunction call. NativeFunctions
+/// have no call frame.
+//
+/// The CallFrame provides space for local registers and local variables.
+/// These values are discarded when the CallFrame is destroyed at the end of
+/// a function call. It provides a scope for the function's execution.
+class CallFrame
 {
 public:
 
     typedef std::vector<as_value> Registers;
 
-    CallFrame(as_function* func);
-
+    /// Construct a CallFrame for a specific UserFunction
+    //
+    /// @param func     The UserFunction to create the CallFrame for. This
+    ///                 must provide information about the amount of registers
+    ///                 to allocate.
+    CallFrame(UserFunction* func);
+    
+    /// Copy constructor for containers
     CallFrame(const CallFrame& other)
         :
         _locals(other._locals),
-        _registers(other._registers),
-        _func(other._func)
+        _func(other._func),
+        _registers(other._registers)
     {}
 
+    /// Assignment operator for containers.
+    CallFrame& operator=(const CallFrame& other) {
+        _locals = other._locals;
+        _func = other._func;
+        _registers = other._registers;
+        return *this;
+    }
+
+    /// Access the local variables for this function call.
     as_object& locals() {
         return *_locals;
     }
 
-    as_function& function() {
+    /// Get the function for which this CallFrame provides a scope.
+    UserFunction& function() {
         return *_func;
     }
 
-    bool getRegister(size_t i, as_value& val) const {
-        if (i >= _registers.size()) return false;
-        val = _registers[i];
-        return true;
-    }
-
-    bool setRegister(size_t i, const as_value& val) {
-        if (i >= _registers.size()) return false;
-        _registers[i] = val;
-        return true;
-    }
-
-    void resizeRegisters(size_t i) {
-        _registers.resize(i);
-    }
-
+    /// Get a specific register in this CallFrame
+    //
+    /// @param i    The index of the register to return.
+    /// @return     A pointer to the value in the register or 0 if no such
+    ///             register exists.
+    const as_value* getLocalRegister(size_t i) const {
+        if (i >= _registers.size()) return 0;
+        return &_registers[i];
+    }
+
+    /// Set a specific register in this CallFrame
+    //
+    /// If the register doesn't exist, nothing happens.
+    //
+    /// @param i    The index of the register to set.
+    /// @param val  The value to set the register to.
+    void setLocalRegister(size_t i, const as_value& val);
+
+    /// Set the number of registers for this CallFrame.
+    //
+    /// The number of registers may only be set once! This is to ensure
+    /// that pointers to the register values are always valid.
     bool hasRegisters() const {
         return !_registers.empty();
     }
@@ -86,16 +116,33 @@
 
     friend std::ostream& operator<<(std::ostream&, const CallFrame&);
 
-    /// function use this 
+    /// Local variables.
     as_object* _locals;
 
-    /// function2 also use this
+    UserFunction* _func;
+    
+    /// Local registers.
     Registers _registers;
 
-    as_function* _func;
-
 };
 
+/// Declare a local variable in this CallFrame
+//
+/// The variable is declared and set to undefined if it doesn't exist already.
+//
+/// @param c    The CallFrame to set the variable in.
+/// @param name The name of the variable to declare.
+void declareLocal(CallFrame& c, string_table::key name);
+
+/// Set a local variable in this CallFrame
+//
+/// If the variable does not already exist, it is created.
+//
+/// @param c    The CallFrame to set the variable in.
+/// @param name The name of the variable to set.
+/// @param val  The value to set the variable to.
+void setLocal(CallFrame& c, string_table::key name, const as_value& val);
+
 typedef std::vector<CallFrame> CallStack;
 
 std::ostream& operator<<(std::ostream& o, const CallFrame& fr);

=== modified file 'libcore/vm/VM.cpp'
--- a/libcore/vm/VM.cpp 2010-07-09 06:58:02 +0000
+++ b/libcore/vm/VM.cpp 2010-07-23 09:15:22 +0000
@@ -23,10 +23,18 @@
 #endif
 
 #include "VM.h"
+
+#include <iostream>
+#include <memory>
+#include <boost/random.hpp> // for random generator
+#include <cstdlib> // std::getenv
+#ifdef HAVE_SYS_UTSNAME_H
+# include <sys/utsname.h> // For system information
+#endif
+
 #include "SharedObject_as.h" // for SharedObjectLibrary
 #include "smart_ptr.h" // GNASH_USE_GC
 #include "NativeFunction.h"
-#include "builtin_function.h"
 #include "movie_definition.h"
 #include "Movie.h"
 #include "movie_root.h"
@@ -36,13 +44,6 @@
 #include "namedStrings.h"
 #include "VirtualClock.h" // for getTime()
 
-#ifdef HAVE_SYS_UTSNAME_H
-# include <sys/utsname.h> // For system information
-#endif
-
-#include <memory>
-#include <boost/random.hpp> // for random generator
-#include <cstdlib> // std::getenv
 
 namespace {
 gnash::RcInitFile& rcfile = gnash::RcInitFile::getDefaultInstance();
@@ -93,8 +94,7 @@
        _swfversion(version),
        _clock(clock),
        _stack(),
-    _shLib(new SharedObjectLibrary(*this)),
-    _avmVersion(AVM1)
+    _shLib(new SharedObjectLibrary(*this))
 {
        _clock.restart();
 }
@@ -225,6 +225,8 @@
 void
 VM::markReachableResources() const
 {
+    std::for_each(_globalRegisters.begin(), _globalRegisters.end(), 
+            std::mem_fun_ref(&as_value::setReachable));
 
        _rootMovie.markReachableResources();
 
@@ -258,6 +260,83 @@
 
 }
 
+const as_value*
+VM::getRegister(size_t index)
+{
+    // If there is a call frame and it has registers, the value must be
+    // sought there.
+    if (!_callStack.empty()) {
+        const CallFrame& fr = currentCall();
+        if (fr.hasRegisters()) return fr.getLocalRegister(index);
+    }
+
+    // Otherwise it can be in the global registers.
+    if (index < _globalRegisters.size()) return &_globalRegisters[index];
+    return 0;
+}
+
+void
+VM::setRegister(size_t index, const as_value& val)
+{
+    // If there is a call frame and it has registers, the value must be
+    // set there.
+    if (!_callStack.empty()) {
+        CallFrame& fr = currentCall();
+        if (fr.hasRegisters()) {
+            currentCall().setLocalRegister(index, val);
+            return;
+        }
+    }
+
+    // Do nothing if the index is out of bounds.
+    if (index < _globalRegisters.size()) _globalRegisters[index] = val;
+
+    IF_VERBOSE_ACTION(
+        log_action(_("-------------- global register[%d] = '%s'"),
+            index, val);
+    );
+
+}
+
+CallFrame&
+VM::currentCall()
+{
+    assert(!_callStack.empty());
+    return _callStack.back();
+}
+
+CallFrame&
+VM::pushCallFrame(UserFunction& func)
+{
+
+    // The stack size can be changed by the ScriptLimits
+    // tag. There is *no* difference between SWF versions.
+    // TODO: override from gnashrc.
+    
+    // A stack size of 0 is apparently legitimate.
+    const boost::uint16_t recursionLimit = getRoot().getRecursionLimit();
+
+    // Don't proceed if local call frames would reach the recursion limit.
+    if (_callStack.size() + 1 >= recursionLimit) {
+
+        std::ostringstream ss;
+        ss << boost::format(_("Recursion limit reached (%u)")) % 
recursionLimit;
+
+        // throw something
+        throw ActionLimitException(ss.str()); 
+    }
+
+    _callStack.push_back(CallFrame(&func));
+    return _callStack.back();
+}
+
+void 
+VM::popCallFrame()
+{
+    assert(!_callStack.empty());
+    _callStack.pop_back();
+}
+
 void
 VmGcRoot::markReachableResources() const
 {
@@ -287,6 +366,53 @@
     return f;
 }
 
+void
+VM::dumpState(std::ostream& out, size_t limit)
+{
+
+    // Dump stack:
+    size_t si = 0;
+    const size_t n = _stack.size();
+
+    if (limit && n > limit) {
+        si = n - limit;
+        out << "Stack (last " << limit << " of " << n << " items): ";
+    }
+    else {
+        out << "Stack: ";
+    }
+
+    for (size_t i = si; i < n; ++i) {
+        if (i != si) out << " | ";
+        out << '"' << _stack.value(i) << '"';
+    }
+    out << "\n";
+
+    out << "Global registers: ";
+    for (GlobalRegisters::const_iterator it = _globalRegisters.begin(),
+            e = _globalRegisters.end(); it != e; ++it) {
+        const as_value& v = *it;
+        if (v.is_undefined()) continue;
+        if (it != _globalRegisters.begin()) out <<  ", ";
+
+        out << (it - _globalRegisters.begin()) << ":" << v;
+    }
+    out << "\n";
+
+    // Now local registers and variables from the call stack.
+    if (_callStack.empty()) return;
+
+    out << "Local registers: ";
+    for (CallStack::const_iterator it = _callStack.begin(),
+            e = _callStack.end(); it != e; ++it) {
+        if (it != _callStack.begin()) out << " | ";
+        out << *it;
+    }
+    out << "\n";
+
+}
+
+
 ///////////////////////////////////////////////////////////////////////
 //
 // Value ops
@@ -384,7 +510,6 @@
     return as_value(num1 < num2);
 }
 
-
 } // end of namespace gnash
 
 

=== modified file 'libcore/vm/VM.h'
--- a/libcore/vm/VM.h   2010-07-09 05:18:06 +0000
+++ b/libcore/vm/VM.h   2010-07-22 16:14:04 +0000
@@ -24,12 +24,6 @@
 #include "gnashconfig.h"
 #endif
 
-#include "GC.h"
-#include "string_table.h"
-#include "SafeStack.h"
-#include "CallStack.h"
-#include "smart_ptr.h"
-
 #include <map>
 #include <vector>
 #include <memory> 
@@ -38,6 +32,14 @@
 #include <boost/random.hpp>
 #include <boost/noncopyable.hpp>
 #include <boost/intrusive_ptr.hpp>
+#include <boost/array.hpp>
+
+#include "GC.h"
+#include "string_table.h"
+#include "SafeStack.h"
+#include "CallStack.h"
+#include "smart_ptr.h"
+#include "as_value.h"
 
 // Forward declarations
 namespace gnash {
@@ -50,6 +52,7 @@
        class as_value;
        class as_object;
        class VirtualClock;
+    class UserFunction;
 }
 
 namespace gnash {
@@ -65,26 +68,22 @@
     VM& _vm;
 };
 
-/// The virtual machine
-//
-/// This is the machine that executes all actions in the 
-/// main movie, including the actions in movies loaded by
-/// it.
-///
-/// Note that the target SWF version of the "main" movie
-/// (the first movie loaded, the 'root' movie) drives
-/// the operation, as depending on that version the Virtual
-/// Machine acts differently, for backward compatibility.
-/// 
-/// The VM is initialized once for each "stage" (main movie).
-/// Gnash currently only supports a *single* VM as it uses
-/// gloabls a lot. Definition of this class is aimed at
-/// grouping the globals into a specific VM instance that
-/// we might pass around in the future to allow multiple
-/// movies runs.
-/// For the moment, it will be a singleton, providing one-time
-/// initialization.
-///
+/// The AVM1 virtual machine
+//
+/// The VM class has no code for execution, but rather stores the resources
+/// needed for execution:
+//
+/// 1. The stack
+/// 2. Global registers
+/// 3. The call stack.
+//
+/// Actual execution is done by ActionExec.
+//
+/// This header also contains a few utility functions for ActionScript
+/// operations.
+//
+/// Currently the VM is a singleton, but this usage is deprecated. In future
+/// is should be fully re-entrant.
 class DSOEXPORT VM : boost::noncopyable
 {
 
@@ -92,12 +91,6 @@
 
        typedef as_value (*as_c_function_ptr)(const fn_call& fn);
 
-    enum AVMVersion
-    {
-        AVM1,
-        AVM2
-    };
-       
     /// \brief
        /// Initialize the virtual machine singleton with the given
        /// movie definition and return a reference to it.
@@ -121,10 +114,6 @@
                return _stack;
        }
 
-       CallStack& getCallStack() {
-               return _callStack;
-       }
-
     /// Get the VM clock
     //
     /// NOTE: this clock should drive all internal operations
@@ -168,19 +157,6 @@
        /// Set SWF version of the currently executing code
        void setSWFVersion(int v);
 
-    /// Get the version of the currently executing VM
-    //
-    /// Note: this value changes according to the current execution context;
-    /// do not use it to determine what VM version a particular SWF uses.
-    AVMVersion getAVMVersion() const {
-        return _avmVersion;
-    }
-
-    /// Set the version of the currently executing VM.
-    void setAVMVersion(AVMVersion v) {
-        _avmVersion = v;
-    }
-
        /// Get the number of milliseconds since VM was started
        unsigned long int getTime() const;
 
@@ -261,6 +237,71 @@
        /// Return a native function or null
        NativeFunction* getNative(unsigned int x, unsigned int y) const;
 
+    /// Get value of a register (local or global).
+    //
+    /// When not in a function context the selected register will be
+    /// global or not at all (if index is not in the valid range
+    /// of global registers).
+    ///
+    /// When in a function context defining no registers, 
+    /// we'll behave the same as for a non-function context.
+    ///
+    /// When in a function context defining non-zero number
+    /// of local registers, the register set will be either local
+    /// or not at all (if index is not in the valid range of local
+    /// registers).
+    //
+    /// @param index    The index of the register to retrieve.
+    /// @return         A pointer to the as_value at the specified position, or
+    ///                 0 if the index is invalid
+    const as_value* getRegister(size_t index);
+
+    /// Set value of a register (local or global).
+    //
+    /// When not in a function context the set register will be
+    /// global or not at all (if index is not in the valid range
+    /// of global registers).
+    ///
+    /// When in a function context defining no registers, 
+    /// we'll behave the same as for a non-function context.
+    ///
+    /// When in a function context defining non-zero number
+    /// of local registers, the register set will be either local
+    /// or not at all (if index is not in the valid range of local
+    /// registers).
+    ///
+    /// @param index    The index of the register to set. If the index
+    ///                 is invalid, this is a no-op.
+    /// @param val      The value to set the register to.
+    void setRegister(size_t index, const as_value& val);
+
+    /// Add a function call to the call frame.
+    //
+    /// This should be called for all user-defined functions before the
+    /// function is executed
+    //
+    /// @return     The pushed CallFrame. This is identical to currentCall().
+    CallFrame& pushCallFrame(UserFunction& f);
+
+    /// Remove a function call from the call frame.
+    //
+    /// This should be called on return from the function.
+    void popCallFrame();
+
+    /// Return the CallFrame of the currently executing function.
+    //
+    /// Callers must ensure that there is a current function before calling
+    /// this!
+    CallFrame& currentCall();
+
+    /// Whether a function call is in progress.
+    bool calling() const {
+        return !_callStack.empty();
+    }
+
+    /// Print stack, call stack, and registers to the specified ostream
+    void dumpState(std::ostream& o, size_t limit = 0);
+
 #ifdef GNASH_USE_GC
        void addStatic(GcResource* res)
        {
@@ -327,19 +368,42 @@
 
        SafeStack<as_value>     _stack;
 
+    typedef boost::array<as_value, 4> GlobalRegisters;
+    GlobalRegisters _globalRegisters;
+
        CallStack _callStack;
 
        /// Library of SharedObjects. Owned by the VM.
     std::auto_ptr<SharedObjectLibrary> _shLib;
 
-    /// The currently executing machine
-    //
-    /// This is a hack like switching the SWF version, but without this
-    /// there is no way for AS functions to know which version of the
-    /// virtual machine called them. This is necessary e.g. for initializing
-    /// the correct object prototypes etc.
-    AVMVersion _avmVersion;
-
+};
+
+/// A class to wrap frame access.  Stack allocating a frame guard
+/// will ensure that all CallFrame pushes have a corresponding
+/// CallFrame pop, even in the presence of extraordinary returns.
+class FrameGuard
+{
+public:
+
+    FrameGuard(VM& vm, UserFunction& func)
+        :
+        _vm(vm),
+        _callFrame(_vm.pushCallFrame(func))
+    {
+    }
+
+    /// Get the CallFrame we've just pushed.
+    CallFrame& callFrame() {
+        return _callFrame;
+    }
+
+    ~FrameGuard() {
+        _vm.popCallFrame();
+    }
+
+private:
+    VM& _vm;
+    CallFrame& _callFrame;
 };
 
 /////////////////////////////////////////////////////////////////////////////
@@ -381,13 +445,6 @@
 /// @param vm       The VM executing the operation.
 as_value newLessThan(const as_value& op1, const as_value& op2, VM& vm);
 
-/// Return true if the VM is executing AS3 (ABC bytecode).
-inline bool
-isAS3(VM& vm)
-{
-    return vm.getAVMVersion() == VM::AVM2;
-}
-
 } // namespace gnash
 
 #endif // GNASH_VM_H

=== modified file 'libcore/vm/fn_call.h'
--- a/libcore/vm/fn_call.h      2010-06-22 06:03:51 +0000
+++ b/libcore/vm/fn_call.h      2010-07-22 12:52:31 +0000
@@ -319,16 +319,6 @@
     return ret;
 }
 
-/// Check whether the currently executing code is AS3 (ABC)
-//
-/// This is a non-member, non-friend function for better encapsulation.
-/// TODO: drop these when there is a better design!
-inline bool
-isAS3(const fn_call& fn)
-{
-    return fn.getVM().getAVMVersion() == VM::AVM2;
-}
-
 inline string_table&
 getStringTable(const fn_call& fn)
 {


reply via email to

[Prev in Thread] Current Thread [Next in Thread]