gnash-commit
[Top][All Lists]
Advanced

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

[Gnash-commit] /srv/bzr/gnash/trunk r10401: Various fixes to SharedObjec


From: Benjamin Wolsey
Subject: [Gnash-commit] /srv/bzr/gnash/trunk r10401: Various fixes to SharedObject AS compatibility.
Date: Tue, 09 Dec 2008 17:15:01 +0100
User-agent: Bazaar (1.5)

------------------------------------------------------------
revno: 10401
committer: Benjamin Wolsey <address@hidden>
branch nick: trunk
timestamp: Tue 2008-12-09 17:15:01 +0100
message:
  Various fixes to SharedObject AS compatibility.
modified:
  libamf/sol.cpp
  libamf/sol.h
  libcore/as_value.cpp
  libcore/asobj/ClassHierarchy.cpp
  libcore/asobj/ClassHierarchy.h
  libcore/asobj/Global.cpp
  libcore/asobj/SharedObject.cpp
  libcore/asobj/SharedObject.h
  libcore/namedStrings.cpp
  libcore/namedStrings.h
  testsuite/actionscript.all/ASnative.as
  testsuite/actionscript.all/SharedObject.as
    ------------------------------------------------------------
    revno: 10400.1.1
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Tue 2008-12-09 14:32:22 +0100
    message:
      Stub unimplemented undocumented methods of SharedObject. Tidy up. Correct
      prop flags. Don't initialize data member until a SharedObject instance
      exists, and even then not if the SOL file existed but was invalid.
    modified:
      libcore/asobj/ClassHierarchy.cpp
      libcore/asobj/ClassHierarchy.h
      libcore/asobj/Global.cpp
      libcore/asobj/SharedObject.cpp
      libcore/asobj/SharedObject.h
      testsuite/actionscript.all/SharedObject.as
    ------------------------------------------------------------
    revno: 10400.1.2
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Tue 2008-12-09 14:58:38 +0100
    message:
      Comments and tests on ASnative SharedObject.
    modified:
      libcore/asobj/SharedObject.cpp
      testsuite/actionscript.all/ASnative.as
    ------------------------------------------------------------
    revno: 10400.1.3
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Tue 2008-12-09 15:29:19 +0100
    message:
      Return undefined from SharedObject.flush() when there is no data member.
      Alter SharedObject::flush() to take the data object as an argument to
      facilitate this.
      
      Add some tests.
      
      Add PROP_DATA to namedStrings.
    modified:
      libcore/asobj/SharedObject.cpp
      libcore/namedStrings.cpp
      libcore/namedStrings.h
      testsuite/actionscript.all/SharedObject.as
    ------------------------------------------------------------
    revno: 10400.1.4
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Tue 2008-12-09 15:51:39 +0100
    message:
      Invalid SOL files act the same as non-existent ones. Only files with
      a header and no data end up with no data member.
    modified:
      libcore/asobj/SharedObject.cpp
    ------------------------------------------------------------
    revno: 10400.1.5
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Tue 2008-12-09 16:11:10 +0100
    message:
      Use a scoped_array for the buffer. Make the readSOL more readable.
    modified:
      libamf/sol.cpp
    ------------------------------------------------------------
    revno: 10400.1.6
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Tue 2008-12-09 16:57:34 +0100
    message:
      Various cleanups.
    modified:
      libamf/sol.cpp
      libamf/sol.h
      libcore/as_value.cpp
      libcore/asobj/SharedObject.cpp
    ------------------------------------------------------------
    revno: 10400.1.7
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Tue 2008-12-09 16:58:56 +0100
    message:
      Comment.
    modified:
      libcore/asobj/SharedObject.cpp
=== modified file 'libamf/sol.cpp'
--- a/libamf/sol.cpp    2008-11-21 13:05:13 +0000
+++ b/libamf/sol.cpp    2008-12-09 15:57:34 +0000
@@ -282,7 +282,7 @@
     _filesize = size;
     
     boost::scoped_array<char> body ( new char[size + 20] );
-    memset(body.get(), 0, size);
+    std::fill_n(body.get(), size, 0);
     ptr = body.get();
     char* endPtr = ptr+size+20; // that's the amount we allocated..
 
@@ -348,14 +348,15 @@
     
     if ( ofs.write(head.get(), _header.size()).fail() )
     {
-        log_error("Error writing %d bytes of header to output file %s", 
_header.size(), filespec.c_str());
+        log_error("Error writing %d bytes of header to output file %s", 
+                _header.size(), filespec);
         return false;
     }
 
-//    ofs.write(body, (ptr - body));
     if ( ofs.write(body.get(), _filesize).fail() )
     {
-        log_error("Error writing %d bytes of body to output file %s", 
_filesize, filespec.c_str());
+        log_error("Error writing %d bytes of body to output file %s",
+                _filesize, filespec);
         return false;
     }
     ofs.close();
@@ -374,19 +375,23 @@
 //    GNASH_REPORT_FUNCTION;
     struct stat st;
     boost::uint16_t size;
-    Network::byte_t *buf = 0;
-    Network::byte_t *ptr = 0;
     size_t bodysize;
 
     // Make sure it's an SOL file
-    if (stat(filespec.c_str(), &st) == 0) {
+    if (stat(filespec.c_str(), &st) != 0)  return false;
 
        try {
+
+        Network::byte_t *ptr = 0;
+
            ifstream ifs(filespec.c_str(), ios::binary);
-           _filesize = st.st_size;
-           buf = new Network::byte_t[_filesize + sizeof(int)];
-           ptr = buf;
-           Network::byte_t* tooFar = buf+_filesize;
+
+        _filesize = st.st_size;
+        boost::scoped_array<Network::byte_t> buf(
+                new Network::byte_t[_filesize + sizeof(int)]);
+
+           ptr = buf.get();
+           Network::byte_t* tooFar = buf.get() + _filesize;
            
            bodysize = st.st_size - 6;
            _filespec = filespec;
@@ -409,22 +414,25 @@
            
            // consistency check
            if ((buf[0] == 0) && (buf[1] == 0xbf)) {
-               if (bodysize == length) {
-                   log_debug("%s is an SOL file", filespec.c_str());
-               } else {
-                   log_error("%s looks like an SOL file, but the length is 
wrong. Should be %d, got %d",
-                             filespec.c_str(), (_filesize - 6), length);
-               }
-               
-        } else {
-               log_error("%s isn't an SOL file", filespec.c_str());
+            if (bodysize == length) {
+                log_debug("%s is an SOL file", filespec);
+            }
+            else {
+                   log_error("%s looks like an SOL file, but the length is 
wrong. "
+                    "Should be %d, got %d",
+                    filespec, (_filesize - 6), length);
+            }
+        }
+        else {
+            log_error("%s isn't an SOL file", filespec);
            }
            
 #ifndef GNASH_TRUST_AMF
            ENSUREBYTES(ptr, tooFar, 2); 
 #endif
            
-           // 2 bytes for the length of the object name, but it's also null 
terminated
+           // 2 bytes for the length of the object name, but it's also 
+        // null terminated
            size = *(reinterpret_cast<boost::uint16_t *>(ptr));
            size = ntohs(size);
            ptr += 2;
@@ -444,31 +452,24 @@
            AMF amf_obj;
            boost::shared_ptr<amf::Element> el;
            while ( ptr < tooFar) {
-               if (ptr) {
-                   el = amf_obj.extractProperty(ptr, tooFar);
-                   if (el != 0) {
-                       ptr += amf_obj.totalsize() + 1;
-                       _amfobjs.push_back(el);
-                   } else {
-                       break;
-                   }
-//             log_debug("Bodysize is: %d size is: %d for %s", bodysize, size, 
el->getName());
-               } else {
-                   break;
-               }
+            if (ptr) {
+                el = amf_obj.extractProperty(ptr, tooFar);
+                if (el != 0) {
+                    ptr += amf_obj.totalsize() + 1;
+                    _amfobjs.push_back(el);
+                }
+                else break;
+            } else break;
            }
-           delete[] buf;
            
            ifs.close();
            return true;
-       } catch (std::exception& e) {
+       }
+    catch (std::exception& e) {
            log_error("Reading SharedObject %s: %s", filespec, e.what());
            return false;
        }
-    }
     
-//    log_error("Couldn't open file: %s", strerror(errno));
-    return false;
 }
 
 ///  \brief Dump the internal data of this class in a human readable form.

=== modified file 'libamf/sol.h'
--- a/libamf/sol.h      2008-10-26 23:35:38 +0000
+++ b/libamf/sol.h      2008-12-09 15:57:34 +0000
@@ -15,8 +15,8 @@
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
-#ifndef _SOL_H_
-#define _SOL_H_
+#ifndef GNASH_SOL_H
+#define GNASH_SOL_H
 
 #ifdef HAVE_CONFIG_H
 #include "gnashconfig.h"
@@ -64,8 +64,10 @@
     /// \brief Get the number of Elements in this class.
     ///
     /// @return The count of Elements.
-    size_t size() const { return _amfobjs.size(); };
-    
+    size_t size() const { return _amfobjs.size(); }
+ 
+    size_t fileSize() const { return _filesize; }
+
     /// \brief Extract the header from the file.
     ///
     /// @param data a reference to a vector of bytes that contains the

=== modified file 'libcore/as_value.cpp'
--- a/libcore/as_value.cpp      2008-12-05 08:03:35 +0000
+++ b/libcore/as_value.cpp      2008-12-09 15:57:34 +0000
@@ -63,8 +63,6 @@
 // Define this macto to make AMF writing verbose
 //#define GNASH_DEBUG_AMF_SERIALIZE
 
-using namespace std;
-
 namespace {
 
 
@@ -102,81 +100,87 @@
 
 // This class is used to iterate through all the properties of an AS object,
 // so we can change them to children of an AMF0 element.
-class PropsSerializer : public AbstractPropertyVisitor {
+class PropsSerializer : public AbstractPropertyVisitor
+{
+
+public:
+
+    PropsSerializer(amf::Element& el, VM& vm)
+        :
+        _obj(el),
+           _st(vm.getStringTable())
+       {}
+    
+    void accept(string_table::key key, const as_value& val) 
+    {
+
+        // Test conducted with AMFPHP:
+        // '__proto__' and 'constructor' members
+        // of an object don't get back from an 'echo-service'.
+        // Dunno if they are not serialized or just not sent back.
+        // A '__constructor__' member gets back, but only if 
+        // not a function. Actually no function gets back.
+        // 
+        if (key == NSV::PROP_uuPROTOuu || key == NSV::PROP_CONSTRUCTOR)
+        {
+#ifdef GNASH_DEBUG_AMF_SERIALIZE
+            log_debug(" skip serialization of specially-named property %s",
+                    _st.value(key));
+#endif
+            return;
+        }
+
+        amf::AMF amf;
+        boost::shared_ptr<amf::Element> el;
+    
+        const std::string& name = _st.value(key);
+
+        if (val.is_string()) {
+            std::string str;
+            if (!val.is_undefined()) {
+                str = val.to_string();
+            }
+            el.reset(new amf::Element(name, str));
+        }
+
+        else if (val.is_bool()) {
+            bool flag = val.to_bool();
+            el.reset(new amf::Element(name, flag));
+        }
+
+        else if (val.is_number()) { 
+            double dub;
+            if (val.is_undefined()) {
+                dub = 0.0;
+            } else {
+                dub = val.to_number();
+            }
+            el.reset(new amf::Element(name, dub));
+        }
+    
+        if (el) {
+            _obj.addProperty(el);
+        }
+    }
+
+private:
+
     amf::Element& _obj;
     string_table& _st;
-public:
-    PropsSerializer(amf::Element& el, VM& vm)
-        : _obj(el),
-         _st(vm.getStringTable())
-       {};
-    
-    void accept(string_table::key key, const as_value& val) 
-        {
-
-            // Test conducted with AMFPHP:
-            // '__proto__' and 'constructor' members
-            // of an object don't get back from an 'echo-service'.
-            // Dunno if they are not serialized or just not sent back.
-            // A '__constructor__' member gets back, but only if 
-            // not a function. Actually no function gets back.
-            // 
-            if ( key == NSV::PROP_uuPROTOuu || 
-                 key == NSV::PROP_CONSTRUCTOR )
-            {
-#ifdef GNASH_DEBUG_AMF_SERIALIZE
-                log_debug(" skip serialization of specially-named property 
%s", _st.value(key));
-#endif
-                return;
-            }
-
-            //GNASH_REPORT_FUNCTION;
-            amf::AMF amf;
-            boost::shared_ptr<amf::Element> el;
-           
-            const string& name = _st.value(key);
-
-           
-//          cerr << "FIXME: yes!!!!! " << name << ": "<< val << std::endl;
-           
-            if (val.is_string()) {
-                string str;
-                if (!val.is_undefined()) {
-                    str = val.to_string();
-                }
-                el.reset(new amf::Element(name, str));
-            }
-            if (val.is_bool()) {
-                bool flag = val.to_bool();
-                el.reset(new amf::Element(name, flag));
-            }
-            if (val.is_number()) { 
-                double dub;
-                if (val.is_undefined()) {
-                    dub = 0.0;
-                } else {
-                    dub = val.to_number();
-                }
-                el.reset(new amf::Element(name, dub));
-            }
-           
-            if (el) {
-                _obj.addProperty(el);
-            }
-        }
+
 };
 
-} // anonimous namespace
+} // anonymous namespace
 
 /// Class used to serialize properties of an object to a buffer
-class PropsBufSerializer : public AbstractPropertyVisitor {
-    SimpleBuffer& _buf;
-    VM& _vm;
-    string_table& _st;
-    std::map<as_object*, size_t>& _offsetTable;
-    mutable bool _error;
+class PropsBufSerializer : public AbstractPropertyVisitor
+{
+
+    typedef std::map<as_object*, size_t> PropertyOffsets;
+
 public:
-    PropsBufSerializer(SimpleBuffer& buf, VM& vm, std::map<as_object*, 
size_t>& offsetTable)
+    PropsBufSerializer(SimpleBuffer& buf, VM& vm,
+            PropertyOffsets& offsetTable)
         :
         _buf(buf),
         _vm(vm),
@@ -205,17 +209,17 @@
         // A '__constructor__' member gets back, but only if 
         // not a function. Actually no function gets back.
         // 
-        if ( key == NSV::PROP_uuPROTOuu || 
-             key == NSV::PROP_CONSTRUCTOR )
+        if (key == NSV::PROP_uuPROTOuu || key == NSV::PROP_CONSTRUCTOR)
         {
 #ifdef GNASH_DEBUG_AMF_SERIALIZE
-            log_debug(" skip serialization of specially-named property %s", 
_st.value(key));
+            log_debug(" skip serialization of specially-named property %s",
+                    _st.value(key));
 #endif
             return;
         }
 
         // write property name
-        const string& name = _st.value(key);
+        const std::string& name = _st.value(key);
 #ifdef GNASH_DEBUG_AMF_SERIALIZE
         log_debug(" serializing property %s", name);
 #endif
@@ -228,6 +232,15 @@
             _error=true;
         }
     }
+
+private:
+
+    SimpleBuffer& _buf;
+    VM& _vm;
+    string_table& _st;
+    PropertyOffsets& _offsetTable;
+    mutable bool _error;
+
 };
     
 //
@@ -2094,8 +2107,8 @@
 // TODO restore first parameter on parse errors
 //
 static bool
-amf0_read_value(boost::uint8_t *&b, boost::uint8_t *end, as_value& ret, int 
inType,
-    std::vector<as_object*>& objRefs, VM& vm)
+amf0_read_value(boost::uint8_t *&b, boost::uint8_t *end, 
+        as_value& ret, int inType, std::vector<as_object*>& objRefs, VM& vm)
 {
        int amf_type;
 
@@ -2387,7 +2400,9 @@
                 {
                     size_t len = ary->size();
 #ifdef GNASH_DEBUG_AMF_SERIALIZE
-                    log_debug(_("writeAMF0: serializing array of %d elements 
as ECMA_ARRAY (index %d)"), len, idx);
+                    log_debug(_("writeAMF0: serializing array of %d "
+                                "elements as ECMA_ARRAY (index %d)"),
+                                len, idx);
 #endif
                     buf.appendByte(amf::Element::ECMA_ARRAY_AMF0);
                     buf.appendNetworkLong(len);
@@ -2395,7 +2410,8 @@
                 else
                 {
 #ifdef GNASH_DEBUG_AMF_SERIALIZE
-                    log_debug(_("writeAMF0: serializing object (or function) 
with index %d"), idx);
+                    log_debug(_("writeAMF0: serializing object (or function) "
+                                "with index %d"), idx);
 #endif
                     buf.appendByte(amf::Element::OBJECT_AMF0);
                 }
@@ -2415,7 +2431,8 @@
             {
                 size_t idx = it->second;
 #ifdef GNASH_DEBUG_AMF_SERIALIZE
-                log_debug(_("writeAMF0: serializing object (or function) as 
reference to %d"), idx);
+                log_debug(_("writeAMF0: serializing object (or function) "
+                            "as reference to %d"), idx);
 #endif
                 buf.appendByte(amf::Element::REFERENCE_AMF0);
                 buf.appendNetworkShort(idx);

=== modified file 'libcore/asobj/ClassHierarchy.cpp'
--- a/libcore/asobj/ClassHierarchy.cpp  2008-12-08 11:11:44 +0000
+++ b/libcore/asobj/ClassHierarchy.cpp  2008-12-09 13:32:22 +0000
@@ -71,14 +71,27 @@
 
 namespace { // anonymous namespace
 
-static void
+void
 addVisibilityFlag(int& flags, int version)
 {
     // TODO: more visibility for swf10+?
-    if ( version >= 9 ) flags |= as_prop_flags::onlySWF9Up;
-    else if ( version >= 8 ) flags |= as_prop_flags::onlySWF8Up;
-    else if ( version >= 7 ) flags |= as_prop_flags::onlySWF7Up;
-    else if ( version >= 6 ) flags |= as_prop_flags::onlySWF6Up;
+    switch (version)
+    {
+        default:
+            return;
+        case 9:
+            flags |= as_prop_flags::onlySWF9Up;
+            break;
+        case 8:
+            flags |= as_prop_flags::onlySWF8Up;
+            break;
+        case 7:
+            flags |= as_prop_flags::onlySWF7Up;
+            break;
+        case 6:
+            flags |= as_prop_flags::onlySWF6Up;
+            break;
+    }
 }
 
 class declare_extension_function : public as_function
@@ -230,8 +243,7 @@
 bool
 ClassHierarchy::declareClass(extensionClass& c)
 {
-       if (mExtension == NULL)
-               return false; // Extensions can't be loaded.
+       if (mExtension == NULL) return false; 
 
        mGlobalNamespace->stubPrototype(c.name);
        mGlobalNamespace->getClass(c.name)->setDeclared();
@@ -251,8 +263,9 @@
        // For AS2 and below, registering with mGlobal _should_ make it 
equivalent
        // to being in the global namespace, since everything is global there.
        asNamespace *nso = findNamespace(c.namespace_name);
-       if (!nso)
-               nso = addNamespace(c.namespace_name);
+
+       if (!nso) nso = addNamespace(c.namespace_name);
+
        nso->stubPrototype(c.name);
        nso->getClass(c.name)->setDeclared();
        nso->getClass(c.name)->setSystem();
@@ -260,10 +273,9 @@
        boost::intrusive_ptr<as_function> getter =
                new declare_native_function(c, mGlobal, mExtension);
 
-       int flags=as_prop_flags::dontEnum;
+       int flags = as_prop_flags::dontEnum;
        addVisibilityFlag(flags, c.version);
-       return mGlobal->init_destructive_property(c.name,
-               *getter, flags);
+       return mGlobal->init_destructive_property(c.name, *getter, flags);
 }
 
 static const ClassHierarchy::nativeClass knownClasses[] =
@@ -299,7 +311,7 @@
        { video_class_init, NSV::CLASS_VIDEO, NSV::CLASS_OBJECT, 
NSV::NS_FLASH_MEDIA, 6 },
        { camera_class_init, NSV::CLASS_CAMERA, NSV::CLASS_OBJECT, 
NSV::NS_FLASH_UI, 6 },
        { microphone_class_init, NSV::CLASS_MICROPHONE, NSV::CLASS_OBJECT, 
NSV::NS_FLASH_UI, 6 },
-       { sharedobject_class_init, NSV::CLASS_SHARED_OBJECT, NSV::CLASS_OBJECT, 
NSV::NS_FLASH_NET, 6 },
+       { sharedobject_class_init, NSV::CLASS_SHARED_OBJECT, NSV::CLASS_OBJECT, 
NSV::NS_FLASH_NET, 5 },
        { loadvars_class_init, NSV::CLASS_LOAD_VARS, NSV::CLASS_OBJECT, 
NS_GLOBAL, 6 },
        { localconnection_class_init, NSV::CLASS_LOCAL_CONNECTION, 
NSV::CLASS_OBJECT, NS_GLOBAL, 6 }, // FIXME: not global ?
        { customactions_class_init, NSV::CLASS_CUSTOM_ACTIONS, 
NSV::CLASS_OBJECT, NSV::NS_ADOBE_UTILS, 6 },
@@ -322,7 +334,7 @@
 };
 
 void
-ClassHierarchy::massDeclare(int /*version*/) // drop version...
+ClassHierarchy::massDeclare()
 {
        // Natives get declared first. It doesn't make any sense for a native
        // to depend on an extension, but it does make sense the other way
@@ -331,7 +343,6 @@
        for (size_t i = 0; i < size; ++i)
        {
                const nativeClass& c = knownClasses[i];
-               //if (c.version > version) continue;
 
                if ( ! declareClass(c) )
                {

=== modified file 'libcore/asobj/ClassHierarchy.h'
--- a/libcore/asobj/ClassHierarchy.h    2008-06-18 16:19:22 +0000
+++ b/libcore/asobj/ClassHierarchy.h    2008-12-09 13:32:22 +0000
@@ -127,7 +127,7 @@
        /// Declare all of the native and extension classes from the
        /// tables contained in the source file.
        ///
-       void massDeclare(int version);
+       void massDeclare();
 
        /// The global namespace
        ///

=== modified file 'libcore/asobj/Global.cpp'
--- a/libcore/asobj/Global.cpp  2008-12-08 11:11:44 +0000
+++ b/libcore/asobj/Global.cpp  2008-12-09 13:32:22 +0000
@@ -153,9 +153,7 @@
     ch->setExtension(&_et);
 #endif
 
-    const int version = vm.getSWFVersion();
-
-    ch->massDeclare(version);
+    ch->massDeclare();
 
     object_class_init(*this); 
     string_class_init(*this); 
@@ -167,6 +165,7 @@
     // SWF8 visibility:
     flash_package_init(*this); 
 
+    const int version = vm.getSWFVersion();
 
     switch (version)
     {
@@ -671,6 +670,7 @@
     registerMathNative(global);
     registerSystemNative(global);
     registerStageNative(global);
+    registerSharedObjectNative(global);
 
     // LoadableObject has natives shared between LoadVars and XML, so 
     // should be registered first.

=== modified file 'libcore/asobj/SharedObject.cpp'
--- a/libcore/asobj/SharedObject.cpp    2008-11-21 15:15:25 +0000
+++ b/libcore/asobj/SharedObject.cpp    2008-12-09 15:58:56 +0000
@@ -66,25 +66,37 @@
 #define MAXHOSTNAMELEN 64
 #endif
 
-static as_value sharedobject_connect(const fn_call& fn);
-static as_value sharedobject_send(const fn_call& fn);
-static as_value sharedobject_flush(const fn_call& fn);
-static as_value sharedobject_close(const fn_call& fn);
-static as_value sharedobject_getsize(const fn_call& fn);
-static as_value sharedobject_setFps(const fn_call& fn);
-static as_value sharedobject_clear(const fn_call& fn);
-
-static as_value sharedobject_getlocal(const fn_call& fn);
-static as_value sharedobject_ctor(const fn_call& fn);
-
-void sharedobject_iter(SOL &sol, string_table::key key, const as_value 
&reference);
-
+// Forward declarations
+namespace {
+
+    as_value sharedobject_connect(const fn_call& fn);
+    as_value sharedobject_send(const fn_call& fn);
+    as_value sharedobject_flush(const fn_call& fn);
+    as_value sharedobject_close(const fn_call& fn);
+    as_value sharedobject_getsize(const fn_call& fn);
+    as_value sharedobject_setFps(const fn_call& fn);
+    as_value sharedobject_clear(const fn_call& fn);
+    as_value sharedobject_deleteAll(const fn_call& fn);
+    as_value sharedobject_getDiskUsage(const fn_call& fn);
+    as_value sharedobject_getRemote(const fn_call& fn);
+
+    as_value sharedobject_getLocal(const fn_call& fn);
+    as_value sharedobject_ctor(const fn_call& fn);
+    
+    as_object* readSOL(VM& vm, const std::string& filespec);
+
+    as_object* getSharedObjectInterface();
+    void attachSharedObjectStaticInterface(as_object& o);
+    bool createDirForFile(const std::string& filespec);
+}
+
+// Serializer helper
 namespace { 
 
-class PropsSerializer : public AbstractPropertyVisitor {
-    SOL& _sol;
-    string_table& _st;
+class PropsSerializer : public AbstractPropertyVisitor
+{
 public:
+
     PropsSerializer(SOL& sol, VM& vm)
         :
         _sol(sol),
@@ -92,51 +104,56 @@
     {};
 
     void accept(string_table::key key, const as_value& val) 
-        {
-            //GNASH_REPORT_FUNCTION;
-            AMF amf;
-            boost::shared_ptr<amf::Element> el;
-            
-            const std::string& name = _st.string_table::value(key);
-
-            //log_debug("Serializing SharedObject property %s:%s", name, val);
-
-            if (val.is_string()) {
-                std::string str;
-                if (!val.is_undefined()) {
-                    str = val.to_string();
-                }
-                el.reset(new amf::Element(name, str));
-            }
-            if (val.is_bool()) {
-                bool flag = val.to_bool();
-                el.reset(new amf::Element(name, flag));
-            }
-            if (val.is_number()) { 
-                double dub;
-                if (val.is_undefined()) {
-                    dub = 0.0;
-                } else {
-                    dub = val.to_number();
-                }
-                el.reset(new amf::Element(name, dub));
-            }
-
-            if (el) {
-                _sol.addObj(el);
-            }
-        }
+    {
+        AMF amf;
+        boost::shared_ptr<amf::Element> el;
+        
+        const std::string& name = _st.string_table::value(key);
+
+        //log_debug("Serializing SharedObject property %s:%s", name, val);
+
+        if (val.is_string()) {
+            std::string str;
+            if (!val.is_undefined()) {
+                str = val.to_string();
+            }
+            el.reset(new amf::Element(name, str));
+        }
+        if (val.is_bool()) {
+            bool flag = val.to_bool();
+            el.reset(new amf::Element(name, flag));
+        }
+        if (val.is_number()) { 
+            double dub;
+            if (val.is_undefined()) {
+                dub = 0.0;
+            } else {
+                dub = val.to_number();
+            }
+            el.reset(new amf::Element(name, dub));
+        }
+
+        if (el) {
+            _sol.addObj(el);
+        }
+    }
+
+private:
+
+    SOL& _sol;
+    string_table& _st;
 };
 
 /// Class used to serialize properties of an object to a buffer in SOL format
-class SOLPropsBufSerializer : public AbstractPropertyVisitor {
-    SimpleBuffer& _buf;
-    VM& _vm;
-    string_table& _st;
-    std::map<as_object*, size_t>& _offsetTable;
-    bool _error;
+class SOLPropsBufSerializer : public AbstractPropertyVisitor
+{
+
+    typedef std::map<as_object*, size_t> PropertiesOffsetTable;
+
 public:
-    SOLPropsBufSerializer(SimpleBuffer& buf, VM& vm, std::map<as_object*, 
size_t>& offsetTable)
+
+    SOLPropsBufSerializer(SimpleBuffer& buf, VM& vm,
+            PropertiesOffsetTable& offsetTable)
         :
         _buf(buf),
         _vm(vm),
@@ -164,11 +181,11 @@
         // A '__constructor__' member gets back, but only if 
         // not a function. Actually no function gets back.
         // 
-        if ( key == NSV::PROP_uuPROTOuu || 
-             key == NSV::PROP_CONSTRUCTOR )
+        if ( key == NSV::PROP_uuPROTOuu || key == NSV::PROP_CONSTRUCTOR )
         {
 #ifdef GNASH_DEBUG_AMF_SERIALIZE
-            log_debug(" skip serialization of specially-named property %s", 
_st.value(key));
+            log_debug(" skip serialization of specially-named property %s",
+                    _st.value(key));
 #endif
             return;
         }
@@ -183,75 +200,28 @@
         _buf.append(name.c_str(), namelen);
         if ( ! val.writeAMF0(_buf, _offsetTable, _vm) )
         {
-            log_error("Problems serializing an object's member %s=%s", name, 
val);
-            _error=true;
+            log_error("Problems serializing an object's member %s=%s",
+                    name, val);
+            _error = true;
         }
 
         _buf.appendByte(0); // SOL-specific
     }
+
+private:
+
+    SimpleBuffer& _buf;
+
+    VM& _vm;
+
+    string_table& _st;
+
+    PropertiesOffsetTable& _offsetTable;
+
+    bool _error;
 };
 
-} // anonimous namespace
-
-static void
-attachProperties(as_object& o)
-{
-//    GNASH_REPORT_FUNCTION;
-     as_object *proto = new as_object(getObjectInterface());
-     o.init_member("data", proto, 
as_prop_flags::dontDelete|as_prop_flags::readOnly);
-}
-
-static void
-attachSharedObjectInterface(as_object& o)
-{
-//    GNASH_REPORT_FUNCTION;
-
-    VM& vm = o.getVM();
-
-    // ASnative table registration
-       vm.registerNative(sharedobject_connect, 2106, 0);
-       vm.registerNative(sharedobject_send, 2106, 1);
-       vm.registerNative(sharedobject_flush, 2106, 2);
-       vm.registerNative(sharedobject_close, 2106, 3);
-       vm.registerNative(sharedobject_getsize, 2106, 4);
-       vm.registerNative(sharedobject_setFps, 2106, 5);
-       vm.registerNative(sharedobject_clear, 2106, 6);
-
-    const int swfVersion = vm.getSWFVersion();
-
-    // clear, flush and getSize not in SWF<6 , it seems
-    if ( swfVersion < 6 ) return; 
-
-    o.init_member("connect", new builtin_function(sharedobject_connect)); // 
asnative 2106,0
-    o.init_member("send", new builtin_function(sharedobject_send)); // 
asnative 2106,1
-    o.init_member("flush", new builtin_function(sharedobject_flush)); // 
asnative 2106,2
-    o.init_member("close", new builtin_function(sharedobject_close)); // 
asnative 2106,3
-    o.init_member("getSize", new builtin_function(sharedobject_getsize)); // 
asnative 2106,4
-    o.init_member("setFps", new builtin_function(sharedobject_setFps)); // 
asnative 2106,5
-    o.init_member("clear", new builtin_function(sharedobject_clear)); // 
asnative 2106,6
-}
-
-static void
-attachSharedObjectStaticInterface(as_object& o)
-{
-//    GNASH_REPORT_FUNCTION;
-
-    o.init_member("getLocal", new builtin_function(sharedobject_getlocal));
-}
-
-static as_object*
-getSharedObjectInterface()
-{
-//    GNASH_REPORT_FUNCTION;
-
-    static boost::intrusive_ptr<as_object> o;
-    if ( ! o ) {
-        o = new as_object(getObjectInterface());
-        attachSharedObjectInterface(*o);
-    }
-    return o.get();
-}
-
+} // anonymous namespace
 
 class SharedObject: public as_object 
 {
@@ -263,10 +233,9 @@
         :
         as_object(getSharedObjectInterface())
     { 
-               attachProperties(*this);
     }
 
-    bool flush() const;
+    bool flush(as_object& data) const;
 
     const std::string& getFilespec() const {
         return _sol.getFilespec();
@@ -284,17 +253,115 @@
         _sol.setObjectName(s);
     }
 
+    /// This isn't correct, as the default implementation doesn't use SOL
+    /// for reading.
     size_t size() const { 
-        return _sol.size(); // TODO: fix this, is bogus
+        return _sol.fileSize(); 
     }
 
-    bool readSOL(const std::string& filename);
-
 private:
 
     SOL _sol;
 };
 
+
+SharedObject::~SharedObject()
+{
+}
+
+
+bool
+SharedObject::flush(as_object& data) const
+{
+    const std::string& filespec = _sol.getFilespec();
+
+    if ( ! createDirForFile(filespec) )
+    {
+        log_error("Couldn't create dir for flushing SharedObject %s", 
filespec);
+        return false;
+    }
+
+#ifdef USE_SOL_READONLY
+    log_debug(_("SharedObject %s not flushed (compiled as read-only mode)"),
+            filespec);
+    return false;
+#endif
+
+    if (rcfile.getSOLReadOnly() ) {
+        log_security("Attempting to write object %s when it's SOL "
+                "Read Only is set! Refusing...", filespec);
+        return false;
+    }
+    
+#ifdef BUFFERED_AMF_SOL
+
+    gnash::SimpleBuffer buf;
+    // see http://osflash.org/documentation/amf/envelopes/sharedobject
+    buf.append("\x00\xbf\x00\x00\x00\x00TCSO\x00\x04\x00\x00\x00\x00", 16); // 
length field filled in later
+
+    // append object name
+    std::string object_name = getObjectName();
+    boost::uint16_t len = object_name.length();
+    buf.appendNetworkShort(len);
+    buf.append(object_name.c_str(), len);
+
+    // append padding
+    buf.append("\x00\x00\x00\x00", 4);
+
+    // append properties of object
+    VM& vm = getVM();
+
+    std::map<as_object*, size_t> offsetTable;
+    SOLPropsBufSerializer props(buf, vm, offsetTable);
+    data.visitPropertyValues(props);
+    if ( ! props.success() ) 
+    {
+        log_error("Could not serialize object");
+        return false;
+    }
+
+    // fix length field
+    *(reinterpret_cast<uint32_t*>(buf.data() + 2)) = htonl(buf.size() - 6);
+    
+    // TODO write file
+    std::ofstream ofs(filespec.c_str(), std::ios::binary);
+    if (!ofs) {
+        log_error("SharedObject::flush(): Failed opening file '%s' in "
+                "binary mode", filespec.c_str());
+        return false;
+    }
+    
+    if (ofs.write(reinterpret_cast<const char*>(buf.data()), 
buf.size()).fail())
+    {
+        log_error("Error writing %d bytes to output file %s",
+                buf.size(), filespec.c_str());
+        ofs.close();
+        return false;
+    }
+    ofs.close();
+
+#else // amf::SOL-based serialization
+
+    // append properties of object
+    VM& vm = getVM();
+
+    SOL sol;
+    PropsSerializer props(sol, vm);
+    data.visitPropertyValues(props);
+    // We only want to access files in this directory
+    bool ret = sol.writeFile(filespec, getObjectName().c_str());
+    if ( ! ret )
+    {
+        log_error("writing SharedObject file to %s", filespec);
+        return false;
+    }
+#endif
+
+    log_security("SharedObject '%s' written to filesystem.", filespec);
+    return true;
+}
+
+
 SharedObjectLibrary::SharedObjectLibrary(VM& vm)
     :
     _vm(vm),
@@ -310,8 +377,8 @@
     struct stat statbuf;
     if ( -1 == stat(_solSafeDir.c_str(), &statbuf) )
     {
-       log_debug("Invalid SOL safe dir %s: %s. Will try to create on 
flush/exit.", _solSafeDir, std::strerror(errno));
-        //_solSafeDir.clear();
+       log_debug("Invalid SOL safe dir %s: %s. Will try to create on "
+               "flush/exit.", _solSafeDir, std::strerror(errno));
     }
 
     // Which URL we should use here is under research.
@@ -338,7 +405,8 @@
     // Get the domain part, or take as 'localhost' if none
     // (loaded from filesystem)
     URL url(swfURL);
-//  log_debug(_("BASE URL=%s (%s)"), url.str(), url.hostname());
+
+    //  log_debug(_("BASE URL=%s (%s)"), url.str(), url.hostname());
     _baseDomain = url.hostname();
     if ( _baseDomain.empty() ) _baseDomain = "localhost";
 
@@ -352,68 +420,36 @@
 void
 SharedObjectLibrary::markReachableResources() const
 {
-    for (SoLib::const_iterator it=_soLib.begin(), itE=_soLib.end(); it!=itE; 
++it)
+    for (SoLib::const_iterator it = _soLib.begin(), itE = _soLib.end();
+            it != itE; ++it)
     {
         SharedObject* sh = it->second;
         sh->setReachable();
     }
 }
 
-static bool createDirForFile(const std::string& filename)
-{
-    if (filename.find("/", 0) != std::string::npos)
-    {
-        typedef boost::tokenizer<boost::char_separator<char> > Tok;
-        boost::char_separator<char> sep("/");
-        Tok t(filename, sep);
-        Tok::iterator tit;
-        std::string newdir = "/";
-        for(tit=t.begin(); tit!=t.end();++tit){
-            //cout << *tit << "\n";
-            newdir += *tit;
-            if (newdir.find("..", 0) != std::string::npos) {
-               log_error("Invalid SharedObject path (contains '..'): %s", 
filename);
-                return false;
-            }
-            // Don't try to create a directory of the .sol file name!
-            // TODO: don't fail if the movie url has a component ending with 
.sol (eh...)
-            //
-            if (newdir.rfind(".sol") != (newdir.size()-4)) {
-#ifndef _WIN32
-                int ret = mkdir(newdir.c_str(), S_IRUSR|S_IWUSR|S_IXUSR);
-#else
-                int ret = mkdir(newdir.c_str());
-#endif
-                if ((errno != EEXIST) && (ret != 0)) {
-                    log_error(_("Couldn't create SOL files directory %s: %s"),
-                              newdir, std::strerror(errno));
-                    return false;
-                }
-            } // else log_debug("newdir %s ends with .sol", newdir);
-            newdir += "/";
-        }
-    }
-    else log_debug("no slash in filespec %s", filename);
-    return true;
-}
-
 SharedObject*
-SharedObjectLibrary::getLocal(const std::string& objName, const std::string& 
root)
+SharedObjectLibrary::getLocal(const std::string& objName,
+        const std::string& root)
 {
     assert ( ! objName.empty() );
 
-    if ( _solSafeDir.empty() ) return 0; // already warned about it at 
construction time
+    // already warned about it at construction time
+    if ( _solSafeDir.empty() ) return 0; 
 
     // TODO: this check sounds kind of lame, fix it
     if ( rcfile.getSOLLocalDomain() && _baseDomain != "localhost") 
     {
-        log_security("Attempting to open SOL file from non localhost-loaded 
SWF");
+        log_security("Attempting to open SOL file from non "
+                "localhost-loaded SWF");
         return 0;
     }
 
     // The optional second argument drops the domain and the swf file name
     std::string key;
-    if ( root.empty() ) key = "/" + _baseDomain + "/" + _basePath + "/" + 
objName;
+    if ( root.empty() ) {
+        key = "/" + _baseDomain + "/" + _basePath + "/" + objName;
+    }
     else key = root + "/" + objName;
 
     // TODO: normalize key!
@@ -441,37 +477,335 @@
 
     log_debug("SharedObject path: %s", newspec);
         
-    if ( ! obj->readSOL(newspec) )
-    {
-        log_debug("Couldn't read SOL %s, will create on flush/exit.", newspec);
+    boost::intrusive_ptr<as_object> data = readSOL(_vm, newspec);
+
+    if (data) {
+        const int flags = as_prop_flags::dontDelete |
+                          as_prop_flags::readOnly;
+        obj->init_member(NSV::PROP_DATA, data, flags);
     }
 
     return obj;
 }
 
-bool
-SharedObject::readSOL(const std::string& filespec)
-{
+
+// extern (used by Global.cpp)
+void
+sharedobject_class_init(as_object& global)
+{
+    static boost::intrusive_ptr<builtin_function> cl;
+    
+    if (cl == NULL) {
+        cl=new builtin_function(&sharedobject_ctor, 
getSharedObjectInterface());
+        attachSharedObjectStaticInterface(*cl);
+    }
+    
+    // Register _global.SharedObject
+    global.init_member("SharedObject", cl.get());    
+}
+
+void
+registerSharedObjectNative(as_object& o)
+{
+    VM& vm = o.getVM();
+
+    // ASnative table registration
+       vm.registerNative(sharedobject_connect, 2106, 0);
+       vm.registerNative(sharedobject_send, 2106, 1);
+       vm.registerNative(sharedobject_flush, 2106, 2);
+       vm.registerNative(sharedobject_close, 2106, 3);
+       vm.registerNative(sharedobject_getsize, 2106, 4);
+       vm.registerNative(sharedobject_setFps, 2106, 5);
+       vm.registerNative(sharedobject_clear, 2106, 6);
+
+    // FIXME: getRemote and getLocal use both these methods,
+    // but aren't identical with either of them.
+    // TODO: The first method looks in a library and returns either a
+    // SharedObject or null. The second takes a new SharedObject as
+    // its first argument and populates its data member (more or less
+    // like readSOL). This is only important for ASNative compatibility.
+       vm.registerNative(sharedobject_getLocal, 2106, 202);
+       vm.registerNative(sharedobject_getRemote, 2106, 203);
+       vm.registerNative(sharedobject_getLocal, 2106, 204);
+       vm.registerNative(sharedobject_getRemote, 2106, 205);
+
+       vm.registerNative(sharedobject_deleteAll, 2106, 206);
+       vm.registerNative(sharedobject_getDiskUsage, 2106, 207);
+}
+
+
+/// SharedObject AS interface
+namespace {
+
+void
+attachSharedObjectInterface(as_object& o)
+{
+
+    VM& vm = o.getVM();
+
+    const int flags = as_prop_flags::dontEnum |
+                      as_prop_flags::dontDelete |
+                      as_prop_flags::onlySWF6Up;
+
+    o.init_member("connect", vm.getNative(2106, 0), flags);
+    o.init_member("send", vm.getNative(2106, 1), flags);
+    o.init_member("flush", vm.getNative(2106, 2), flags);
+    o.init_member("close", vm.getNative(2106, 3), flags);
+    o.init_member("getSize", vm.getNative(2106, 4), flags);
+    o.init_member("setFps", vm.getNative(2106, 5), flags);
+    o.init_member("clear", vm.getNative(2106, 6), flags);
+
+}
+
+
+void
+attachSharedObjectStaticInterface(as_object& o)
+{
+    VM& vm = o.getVM();
+
+    const int flags = 0;
+
+    o.init_member("getLocal", 
+            new builtin_function(sharedobject_getLocal), flags);
+    o.init_member("getRemote",
+            new builtin_function(sharedobject_getRemote), flags);
+
+    const int hiddenOnly = as_prop_flags::dontEnum;
+
+    o.init_member("deleteAll",  vm.getNative(2106, 206), hiddenOnly);
+    o.init_member("getDiskUsage",  vm.getNative(2106, 207), hiddenOnly);
+}
+
+
+as_object*
+getSharedObjectInterface()
+{
+
+    static boost::intrusive_ptr<as_object> o;
+    if ( ! o ) {
+        o = new as_object(getObjectInterface());
+        attachSharedObjectInterface(*o);
+    }
+    return o.get();
+}
+
+
+as_value
+sharedobject_clear(const fn_call& fn)
+{
+    boost::intrusive_ptr<SharedObject> obj = 
+        ensureType<SharedObject>(fn.this_ptr);
+    UNUSED(obj);
+    
+    LOG_ONCE(log_unimpl (__FUNCTION__));
+
+    return as_value();
+}
+
+as_value
+sharedobject_connect(const fn_call& fn)
+{
+    boost::intrusive_ptr<SharedObject> obj =
+        ensureType<SharedObject>(fn.this_ptr);
+    UNUSED(obj);
+
+    LOG_ONCE(log_unimpl("SharedObject.connect"));
+    return as_value();
+}
+
+as_value
+sharedobject_close(const fn_call& fn)
+{
+    boost::intrusive_ptr<SharedObject> obj =
+        ensureType<SharedObject>(fn.this_ptr);
+    UNUSED(obj);
+
+    LOG_ONCE(log_unimpl("SharedObject.close"));
+    return as_value();
+}
+
+as_value
+sharedobject_setFps(const fn_call& fn)
+{
+    boost::intrusive_ptr<SharedObject> obj =
+        ensureType<SharedObject>(fn.this_ptr);
+    UNUSED(obj);
+
+    LOG_ONCE(log_unimpl("SharedObject.setFps"));
+    return as_value();
+}
+
+as_value
+sharedobject_send(const fn_call& fn)
+{
+    boost::intrusive_ptr<SharedObject> obj =
+        ensureType<SharedObject>(fn.this_ptr);
+    UNUSED(obj);
+
+    LOG_ONCE(log_unimpl("SharedObject.send"));
+    return as_value();
+}
+
+as_value
+sharedobject_flush(const fn_call& fn)
+{
+    
+    boost::intrusive_ptr<SharedObject> obj =
+        ensureType<SharedObject>(fn.this_ptr);
+
+    IF_VERBOSE_ASCODING_ERRORS(
+        if ( fn.nargs )
+        {
+            std::stringstream ss;
+            fn.dump_args(ss);
+            log_aserror(_("Arguments to SharedObject.flush(%s) will be "
+                    "ignored"), ss.str());
+        }
+    );
+
+    /// If there is no data member, returns undefined.
+    as_value dataMember;
+    if (!obj->get_member(NSV::PROP_DATA, &dataMember)) return as_value();
+   
+    as_object* data = dataMember.to_object().get();
+    if (!data) return as_value();
+
+    // If there is an object data member, returns the success of flush().
+    return as_value(obj->flush(*data));
+}
+
+// Set the file name
+as_value
+sharedobject_getLocal(const fn_call& fn)
+{
+
+    VM& vm = fn.env().getVM();
+    int swfVersion = vm.getSWFVersion();
+
+    as_value objNameVal;
+    if (fn.nargs > 0) objNameVal = fn.arg(0);
+    std::string objName = objNameVal.to_string_versioned(swfVersion);
+    if ( objName.empty() )
+    {
+        IF_VERBOSE_ASCODING_ERRORS(
+            std::ostringstream ss;
+            fn.dump_args(ss);
+            log_aserror("SharedObject.getLocal(%s): %s", 
+                _("missing object name"));
+        );
+        as_value ret;
+        ret.set_null();
+        return ret;
+    }
+
+    std::string root;
+    if (fn.nargs > 1)
+    {
+        root = fn.arg(1).to_string_versioned(swfVersion);
+    }
+
+    log_debug("SO name:%s, root:%s", objName, root);
+
+    SharedObject* obj = vm.getSharedObjectLibrary().getLocal(objName, root);
+
+    as_value ret(obj);
+    log_debug("SharedObject.getLocal returning %s", ret);
+    return ret;
+}
+
+/// Undocumented
+as_value
+sharedobject_getRemote(const fn_call& fn)
+{
+    boost::intrusive_ptr<SharedObject> obj =
+        ensureType<SharedObject>(fn.this_ptr);
+
+    UNUSED(obj);
+
+    LOG_ONCE(log_unimpl("SharedObject.getRemote()"));
+    return as_value();
+}
+
+
+/// Undocumented
+//
+/// Takes a URL argument and deletes all SharedObjects under that URL.
+as_value
+sharedobject_deleteAll(const fn_call& fn)
+{
+    boost::intrusive_ptr<SharedObject> obj =
+        ensureType<SharedObject>(fn.this_ptr);
+
+    UNUSED(obj);
+
+    LOG_ONCE(log_unimpl("SharedObject.deleteAll()"));
+    return as_value();
+}
+
+/// Undocumented
+//
+/// Should be quite obvious what it does.
+as_value
+sharedobject_getDiskUsage(const fn_call& fn)
+{
+    boost::intrusive_ptr<SharedObject> obj =
+        ensureType<SharedObject>(fn.this_ptr);
+
+    UNUSED(obj);
+
+    LOG_ONCE(log_unimpl("SharedObject.getDiskUsage()"));
+    return as_value();
+}
+
+as_value
+sharedobject_getsize(const fn_call& fn)
+{
+    boost::intrusive_ptr<SharedObject> obj =
+        ensureType<SharedObject>(fn.this_ptr);
+    return as_value(obj->size());
+}
+
+as_value
+sharedobject_ctor(const fn_call& /* fn */)
+{
+    boost::intrusive_ptr<as_object> obj = new SharedObject;
+    
+    return as_value(obj.get()); // will keep alive
+}
+
+as_object*
+readSOL(VM& vm, const std::string& filespec)
+{
+
 #ifdef BUFFERED_AMF_SOL
+
+    // The 'data' member is initialized only on getLocal() (and probably
+    // getRemote()): i.e. when there is some data, or when it's ready to
+    // be added.
+    as_object* data = new as_object(getObjectInterface());
+
     struct stat st;
 
     if (stat(filespec.c_str(), &st) != 0)
     {
-        return false;
+        // No existing SOL file. A new one will be created.
+        log_debug("No existing SOL %s found. Will create on flush/exit.",
+                filespec);
+        return data;
     }
 
-    if( st.st_size < 28 )
+    if (st.st_size < 28)
     {
-        log_error("SharedObject::readSOL: SOL file %s is too short (only %s 
bytes long) to be valid.", filespec, st.st_size);
-        return false;
+        // A SOL file exists, but it was invalid. Count it as not existing.
+        log_error("SharedObject::readSOL: SOL file %s is too short "
+                "(only %s bytes long) to be valid.", filespec, st.st_size);
+        return data;
     }
 
     boost::scoped_array<boost::uint8_t> sbuf(new boost::uint8_t[st.st_size]);
     boost::uint8_t *buf = sbuf.get();
     boost::uint8_t *end = buf + st.st_size;
 
-    // FIXME clear existing key/value pairs?
-
     try
     {
         std::ifstream ifs(filespec.c_str(), std::ios::binary);
@@ -487,22 +821,24 @@
         
         buf += 4; // skip past padding
 
-        if( buf >= end )
+        if (buf >= end)
         {
+            // In this case there is no data member.
             log_error("SharedObject::readSOL: file ends before data segment");
-            return false;
+            return 0;
         }
 
-        string_table& strtab = _vm.getStringTable();
         std::vector<as_object*> objRefs;
-        boost::intrusive_ptr<as_object> data = 
getMember(strtab.string_table::find("data")).to_object();
 
-        while( buf < end )
+        while (buf < end)
         {
-            log_debug("SharedObject::readSOL: reading property name at byte 
%s", buf - sbuf.get());
+            log_debug("SharedObject::readSOL: reading property name at "
+                    "byte %s", buf - sbuf.get());
             // read property name
-            boost::uint16_t len = 
ntohs(*(reinterpret_cast<boost::uint16_t*>(buf)));
+            boost::uint16_t len = 
+                ntohs(*(reinterpret_cast<boost::uint16_t*>(buf)));
             buf += 2;
+
             if( buf + len >= end )
             {
                 log_error("SharedObject::readSOL: premature end of input");
@@ -517,34 +853,36 @@
 
             // read value
             as_value as;
-            if(as.readAMF0(buf, end, -1 /* read type from buffer */, objRefs, 
_vm) == false) {
-                log_error("SharedObject::readSOL: Parsing SharedObject '%s'", 
filespec);
+            if (!as.readAMF0(buf, end, -1, objRefs, vm)) {
+                log_error("SharedObject::readSOL: Parsing SharedObject '%s'",
+                        filespec);
                 return false;
             }
 
-            log_debug("parsed sol member named '%s' (len %s),  value '%s'", 
prop_name, len, as);
+            log_debug("parsed sol member named '%s' (len %s),  value '%s'",
+                    prop_name, len, as);
 
             // set name/value as a member of this (SharedObject) object
-            data->set_member(strtab.find(prop_name), as);
+            string_table& st = vm.getStringTable();
+            data->set_member(st.find(prop_name), as);
             
             buf += 1; // skip null byte after each property
         }
-        log_debug("setting data member: %s, %s", 
strtab.find(std::string("data")),  as_value(data.get()));
-        set_member(strtab.find(std::string("data")), as_value(data.get()));
-        return true;
+        return data;
     }
     catch (std::exception& e)
     {
-        log_error("SharedObject::readSOL: Reading SharedObject %s: %s", 
filespec, e.what());
-        return false;
+        log_error("SharedObject::readSOL: Reading SharedObject %s: %s", 
+                filespec, e.what());
+        return 0;
     }
 
-
 #else
     SOL sol;
     log_security("Opening SharedObject file: %s", filespec);
     if (sol.readFile(filespec) == false) {
-        log_security("empty or non-existing SOL file \"%s\", will be created 
on flush/exit", filespec);
+        log_security("empty or non-existing SOL file \"%s\", will be "
+                "created on flush/exit", filespec);
         return false;
     }
     
@@ -552,9 +890,7 @@
     std::vector<boost::shared_ptr<amf::Element> > els = sol.getElements();
     log_debug("Read %d AMF objects from %s", els.size(), filespec);
 
-    string_table& st = _vm.getStringTable();
-    string_table::key dataKey =  st.find("data");
-    as_value as = getMember(dataKey);
+    as_value as = getMember(NSV::PROP_DATA);
     boost::intrusive_ptr<as_object> ptr = as.to_object();
     
     for (it = els.begin(), e = els.end(); it != e; it++) {
@@ -573,7 +909,8 @@
             case Element::NUMBER_AMF0:
             {
                 double dub =  *(reinterpret_cast<double*>(el->getData()));
-                ptr->set_member(st.string_table::find(el->getName()), 
as_value(dub));
+                ptr->set_member(st.string_table::find(el->getName()),
+                        as_value(dub));
                 break;
             }
 
@@ -585,12 +922,13 @@
             case Element::STRING_AMF0:
             {
                 if (el->getLength() == 0) {
-                    ptr->set_member(st.string_table::find(el->getName()), 
as_value(""));
+                    ptr->set_member(st.string_table::find(el->getName()), "");
                     break;
                 }
                 
-                std::string str(reinterpret_cast<const char*>(el->getData()), 
el->getLength());
-                ptr->set_member(st.string_table::find(el->getName()), 
as_value(str));
+                std::string str(reinterpret_cast<const char*>(el->getData()),
+                        el->getLength());
+                ptr->set_member(st.string_table::find(el->getName()), str);
                 break;
             }
 
@@ -617,253 +955,45 @@
 #endif
 }
 
-
-SharedObject::~SharedObject()
-{
-    // flush(); // needs more care, if destroyed after VM we get killed
-}
-
-
 bool
-SharedObject::flush() const
+createDirForFile(const std::string& filename)
 {
-    const std::string& filespec = _sol.getFilespec();
-
-    if ( ! createDirForFile(filespec) )
-    {
-        log_error("Couldn't create dir for flushing SharedObject %s", 
filespec);
-        return false;
-    }
-
-#ifdef USE_SOL_READONLY
-    log_debug(_("SharedObject %s not flushed (compiled as read-only mode)"), 
filespec);
-    return false;
-#endif
-
-//    log_debug("Flushing to file %s", filespec);
-
-    VM& vm = getVM();
-
-    if (rcfile.getSOLReadOnly() ) {
-        log_security("Attempting to write object %s when it's SOL Read Only is 
set! Refusing...",
-                     filespec);
-        return false;
-    }
-    
-    // TODO: cache the dataKey in SharedObject prototype on first use ?
-    //       a SharedObject::getDataKey() might do...
-    string_table::key dataKey = vm.getStringTable().find("data");
-    
-    as_value as = const_cast<SharedObject*>(this)->getMember(dataKey);
-    log_debug("data member of this SharedObject is %s", as);
-    boost::intrusive_ptr<as_object> ptr = as.to_object();
-    if ( ! ptr ) {
-        log_aserror("'data' member of SharedObject is not an object (%s)",
-                  as);
-        return true;
-    }
-
-#ifdef BUFFERED_AMF_SOL
-
-    gnash::SimpleBuffer buf;
-    // see http://osflash.org/documentation/amf/envelopes/sharedobject
-    buf.append("\x00\xbf\x00\x00\x00\x00TCSO\x00\x04\x00\x00\x00\x00", 16); // 
length field filled in later
-
-    // append object name
-    std::string object_name = getObjectName();
-    boost::uint16_t len = object_name.length();
-    buf.appendNetworkShort(len);
-    buf.append(object_name.c_str(), len);
-
-    // append padding
-    buf.append("\x00\x00\x00\x00", 4);
-
-    // append properties of object
-    std::map<as_object*, size_t> offsetTable;
-    SOLPropsBufSerializer props(buf, vm, offsetTable);
-    ptr->visitPropertyValues(props);
-    if ( ! props.success() ) 
-    {
-        log_error("Could not serialize object");
-        return false;
-    }
-
-    // fix length field
-    *(reinterpret_cast<uint32_t*>(buf.data() + 2)) = htonl(buf.size() - 6);
-    
-    // TODO write file
-    std::ofstream ofs(filespec.c_str(), std::ios::binary);
-    if(! ofs) {
-        log_error("SharedObject::flush(): Failed opening file '%s' in binary 
mode", filespec.c_str());
-        return false;
-    }
-    
-    if(ofs.write(reinterpret_cast<const char*>(buf.data()), buf.size()).fail() 
)
-    {
-        log_error("Error writing %d bytes to output file %s", buf.size(), 
filespec.c_str());
-        ofs.close();
-        return false;
-    }
-    ofs.close();
-
-#else // amf::SOL-based serialization
-
-    SOL sol;
-    PropsSerializer props(sol, vm);
-    ptr->visitPropertyValues(props);
-    // We only want to access files in this directory
-    bool ret = sol.writeFile(filespec, getObjectName().c_str());
-    if ( ! ret )
-    {
-        log_error("writing SharedObject file to %s", filespec);
-        return false;
-    }
-#endif
-
-    log_security("SharedObject '%s' written to filesystem.", filespec);
+    if (filename.find("/", 0) != std::string::npos)
+    {
+        typedef boost::tokenizer<boost::char_separator<char> > Tok;
+        boost::char_separator<char> sep("/");
+        Tok t(filename, sep);
+        Tok::iterator tit;
+        std::string newdir = "/";
+        for(tit=t.begin(); tit!=t.end();++tit){
+            //cout << *tit << "\n";
+            newdir += *tit;
+            if (newdir.find("..", 0) != std::string::npos) {
+               log_error("Invalid SharedObject path (contains '..'): %s", 
filename);
+                return false;
+            }
+            // Don't try to create a directory of the .sol file name!
+            // TODO: don't fail if the movie url has a component 
+            // ending with .sol (eh...)
+            //
+            if (newdir.rfind(".sol") != (newdir.size()-4)) {
+#ifndef _WIN32
+                int ret = mkdir(newdir.c_str(), S_IRUSR|S_IWUSR|S_IXUSR);
+#else
+                int ret = mkdir(newdir.c_str());
+#endif
+                if ((errno != EEXIST) && (ret != 0)) {
+                    log_error(_("Couldn't create SOL files directory %s: %s"),
+                              newdir, std::strerror(errno));
+                    return false;
+                }
+            } // else log_debug("newdir %s ends with .sol", newdir);
+            newdir += "/";
+        }
+    }
+    else log_debug("no slash in filespec %s", filename);
     return true;
 }
 
-
-as_value
-sharedobject_clear(const fn_call& fn)
-{
-//    GNASH_REPORT_FUNCTION;
-    boost::intrusive_ptr<SharedObject> obj = 
ensureType<SharedObject>(fn.this_ptr);
-    UNUSED(obj);
-    
-    LOG_ONCE(log_unimpl (__FUNCTION__));
-
-    return as_value();
-}
-
-as_value
-sharedobject_connect(const fn_call& fn)
-{
-    boost::intrusive_ptr<SharedObject> obj = 
ensureType<SharedObject>(fn.this_ptr);
-    UNUSED(obj);
-
-    LOG_ONCE(log_unimpl("SharedObject.connect"));
-    return as_value();
-}
-
-as_value
-sharedobject_close(const fn_call& fn)
-{
-    boost::intrusive_ptr<SharedObject> obj = 
ensureType<SharedObject>(fn.this_ptr);
-    UNUSED(obj);
-
-    LOG_ONCE(log_unimpl("SharedObject.close"));
-    return as_value();
-}
-
-as_value
-sharedobject_setFps(const fn_call& fn)
-{
-    boost::intrusive_ptr<SharedObject> obj = 
ensureType<SharedObject>(fn.this_ptr);
-    UNUSED(obj);
-
-    LOG_ONCE(log_unimpl("SharedObject.setFps"));
-    return as_value();
-}
-
-as_value
-sharedobject_send(const fn_call& fn)
-{
-    boost::intrusive_ptr<SharedObject> obj = 
ensureType<SharedObject>(fn.this_ptr);
-    UNUSED(obj);
-
-    LOG_ONCE(log_unimpl("SharedObject.send"));
-    return as_value();
-}
-
-as_value
-sharedobject_flush(const fn_call& fn)
-{
-//    GNASH_REPORT_FUNCTION;
-    
-    boost::intrusive_ptr<SharedObject> obj = 
ensureType<SharedObject>(fn.this_ptr);
-
-    IF_VERBOSE_ASCODING_ERRORS(
-    if ( fn.nargs )
-    {
-        std::stringstream ss;
-        fn.dump_args(ss);
-        log_aserror(_("Arguments to SharedObject.flush(%s) will be ignored"), 
ss.str());
-    }
-    )
-
-    return as_value(obj->flush());
-}
-
-// Set the file name
-as_value
-sharedobject_getlocal(const fn_call& fn)
-{
-//    GNASH_REPORT_FUNCTION;
-
-    VM& vm = fn.env().getVM();
-    int swfVersion = vm.getSWFVersion();
-
-    as_value objNameVal;
-    if (fn.nargs > 0) objNameVal = fn.arg(0);
-    std::string objName = objNameVal.to_string_versioned(swfVersion);
-    if ( objName.empty() )
-    {
-        IF_VERBOSE_ASCODING_ERRORS(
-        std::stringstream ss; fn.dump_args(ss);
-        log_aserror("SharedObject.getLocal(%s): %s", _("missing object name"));
-        );
-        as_value ret; ret.set_null();
-        return ret;
-    }
-
-    std::string root;
-    if (fn.nargs > 1)
-    {
-        root = fn.arg(1).to_string_versioned(swfVersion);
-    }
-
-    log_debug("SO name:%s, root:%s", objName, root);
-
-    SharedObject* obj = vm.getSharedObjectLibrary().getLocal(objName, root);
-    as_value ret(obj);
-    log_debug("SharedObject.getLocal returning %s", ret);
-    return ret;
-}
-
-as_value
-sharedobject_getsize(const fn_call& fn)
-{
-//    GNASH_REPORT_FUNCTION;
-    boost::intrusive_ptr<SharedObject> obj = 
ensureType<SharedObject>(fn.this_ptr);
-    return as_value(obj->size());
-}
-
-as_value
-sharedobject_ctor(const fn_call& /* fn */)
-{
-//    GNASH_REPORT_FUNCTION;
-    boost::intrusive_ptr<as_object> obj = new SharedObject;
-//    static boost::intrusive_ptr<as_object> obj = new 
as_object(getSharedObjectInterface());
-    
-    return as_value(obj.get()); // will keep alive
-}
-
-// extern (used by Global.cpp)
-void sharedobject_class_init(as_object& global)
-{
-//    GNASH_REPORT_FUNCTION;
-    // This is going to be the global SharedObject "class"/"function"
-    static boost::intrusive_ptr<builtin_function> cl;
-    
-    if (cl == NULL) {
-        cl=new builtin_function(&sharedobject_ctor, 
getSharedObjectInterface());
-        attachSharedObjectStaticInterface(*cl);
-    }
-    
-    // Register _global.SharedObject
-    global.init_member("SharedObject", cl.get());    
-}
-
+} // anonymous namespace
 } // end of gnash namespace

=== modified file 'libcore/asobj/SharedObject.h'
--- a/libcore/asobj/SharedObject.h      2008-09-10 13:55:34 +0000
+++ b/libcore/asobj/SharedObject.h      2008-12-09 13:32:22 +0000
@@ -16,10 +16,9 @@
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 //
 
-#ifndef __GNASH_ASOBJ_SHAREDOBJECT_H__
-#define __GNASH_ASOBJ_SHAREDOBJECT_H__
+#ifndef GNASH_ASOBJ_SHAREDOBJECT_H
+#define GNASH_ASOBJ_SHAREDOBJECT_H
 
-#include <memory> // for auto_ptr
 #include <string>
 #include <map>
 
@@ -70,11 +69,9 @@
 /// Initialize the global SharedObject class
 void sharedobject_class_init(as_object& global);
 
-/// Return a SharedObject instance (in case the core lib needs it)
-//std::auto_ptr<as_object> init_sharedobject_instance();
+void registerSharedObjectNative(as_object& o);
   
 } // end of gnash namespace
 
-// __GNASH_ASOBJ_SHAREDOBJECT_H__
 #endif
 

=== modified file 'libcore/namedStrings.cpp'
--- a/libcore/namedStrings.cpp  2008-12-01 21:30:39 +0000
+++ b/libcore/namedStrings.cpp  2008-12-09 14:29:19 +0000
@@ -51,6 +51,7 @@
        string_table::svt( "_currentframe", NSV::PROP_uCURRENTFRAME ),
        string_table::svt( "_customHeaders", NSV::PROP_uCUSTOM_HEADERS ),
        string_table::svt( "d", NSV::PROP_D ),
+       string_table::svt( "data", NSV::PROP_DATA ),
        string_table::svt( "decode", NSV::PROP_DECODE ),
        string_table::svt( "e", NSV::PROP_E ),  
        string_table::svt( "_droptarget", NSV::PROP_uDROPTARGET ),

=== modified file 'libcore/namedStrings.h'
--- a/libcore/namedStrings.h    2008-12-01 21:30:39 +0000
+++ b/libcore/namedStrings.h    2008-12-09 14:29:19 +0000
@@ -115,6 +115,7 @@
                PROP_CONSTRUCTOR,
                PROP_CONTENT_TYPE,
                PROP_D,
+        PROP_DATA,
         PROP_DECODE,
                PROP_E,
                PROP_ENABLED,

=== modified file 'testsuite/actionscript.all/ASnative.as'
--- a/testsuite/actionscript.all/ASnative.as    2008-08-04 12:02:51 +0000
+++ b/testsuite/actionscript.all/ASnative.as    2008-12-09 13:58:38 +0000
@@ -262,8 +262,21 @@
 
 xcheck_equals (countVO, 25);
 
+/// SharedObject undocumented functions.
+
+a = ASnative(2106, 202);
+f = a("level1/level2/settings", "/", undefined); 
+xcheck_equals(typeof(f), "null");
+
+a = ASnative(2106, 204);
+f = new SharedObject;
+check_equals (typeof(f.data), "undefined");
+ret = a(f, "level1/level2/settings", "/", undefined); 
+xcheck_equals(ret, true);
+xcheck_equals (typeof(f.data), "object");
+
 #if OUTPUT_VERSION > 5
-check_totals(79);
+check_totals(83);
 #else
-check_totals(76);
+check_totals(80);
 #endif

=== modified file 'testsuite/actionscript.all/SharedObject.as'
--- a/testsuite/actionscript.all/SharedObject.as        2008-09-13 07:08:00 
+0000
+++ b/testsuite/actionscript.all/SharedObject.as        2008-12-09 14:29:19 
+0000
@@ -26,19 +26,6 @@
 
 var sharedobjectObj = new SharedObject;
 
-#if OUTPUT_VERSION < 6
-
-// test the SharedObject constuctor
-xcheck_equals (typeof(sharedobjectObj), 'object');
-
-// test the SharedObject::getlocal method
-check_equals (typeof(sharedobjectObj.getLocal), 'undefined');
-xcheck_equals (typeof(SharedObject.getLocal), 'function');
-
-check_totals(3);
-
-#else // OUTPUT_VERSION >= 6
-
 // test the SharedObject constuctor
 check_equals (typeof(sharedobjectObj), 'object');
 
@@ -47,36 +34,59 @@
 check_equals (typeof(sharedobjectObj.getLocal), 'undefined');
 check_equals (typeof(SharedObject.getLocal), 'function');
 
-// test the SharedObject.connect method (asnative 2106,0)
+#if OUTPUT_VERSION > 5
+
+check(SharedObject.prototype.hasOwnProperty("connect"));
+check(SharedObject.prototype.hasOwnProperty("send"));
+check(SharedObject.prototype.hasOwnProperty("flush"));
+check(SharedObject.prototype.hasOwnProperty("getSize"));
+check(SharedObject.prototype.hasOwnProperty("setFps"));
+check(SharedObject.prototype.hasOwnProperty("clear"));
+check(SharedObject.prototype.hasOwnProperty("close"));
+check(!SharedObject.prototype.hasOwnProperty("data"));
+
+check(!SharedObject.prototype.hasOwnProperty("getLocal"));
+check(!SharedObject.prototype.hasOwnProperty("getRemote"));
+check(!SharedObject.prototype.hasOwnProperty("getDiskUsage"));
+check(!SharedObject.prototype.hasOwnProperty("deleteAll"));
+check(SharedObject.hasOwnProperty("getLocal"));
+check(SharedObject.hasOwnProperty("getRemote"));
+check(SharedObject.hasOwnProperty("getDiskUsage"));
+check(SharedObject.hasOwnProperty("deleteAll"));
+
 check_equals (typeof(sharedobjectObj.connect), 'function');
-
-// test the SharedObject.send method (asnative 2106,1)
 check_equals (typeof(sharedobjectObj.send), 'function');
-
-// test the SharedObject.flush method (asnative 2106,2)
 check_equals (typeof(sharedobjectObj.flush), 'function');
-
-// test the SharedObject.close method (asnative 2106,3)
 check_equals (typeof(sharedobjectObj.close), 'function');
-
-// test the SharedObject.getsize method (asnative 2106,4)
 check_equals (typeof(sharedobjectObj.getSize), 'function');
-
-// test the SharedObject.setFps method (asnative 2106,5)
 check_equals (typeof(sharedobjectObj.setFps), 'function');
-
-// test the SharedObject.clear method (asnative 2106,6)
 check_equals (typeof(sharedobjectObj.clear), 'function');
+check_equals (typeof(sharedobjectObj.data), 'undefined');
+#else
+check_equals (typeof(sharedobjectObj.connect), 'undefined');
+check_equals (typeof(sharedobjectObj.send), 'undefined');
+check_equals (typeof(sharedobjectObj.flush), 'undefined');
+check_equals (typeof(sharedobjectObj.close), 'undefined');
+check_equals (typeof(sharedobjectObj.getSize), 'undefined');
+check_equals (typeof(sharedobjectObj.setFps), 'undefined');
+check_equals (typeof(sharedobjectObj.clear), 'undefined');
+#endif
 
 // FIXME: Test code that will soon be a formal test case.
 so = SharedObject.getLocal("level1/level2/settings", "/");
 
+check(so instanceof SharedObject);
+
 // Private data
 so.name = "Joe";
 so.age = 20;
 so.pet = "Dog";
 
 // public data that gets written
+#if OUTPUT_VERSION > 5
+check(so.hasOwnProperty("data"));
+#endif
+
 so.data.gain = 50.0;
 so.data.echosuppression = false;
 so.data.defaultmicrophone = "/dev/input/mic";
@@ -110,13 +120,14 @@
 so3 = SharedObject.getLocal("level1/level2/settings3", "/");
 check(so3 != so);
 
+// Doesn't make much sense to test the rest for SWF5
+#if OUTPUT_VERSION > 5
 
 // trace(so.getSize());
 ret = so.flush();
 check_equals(typeof(ret), 'boolean');
 check_equals(ret, true);
 
-
 newso = SharedObject.getLocal("level1/level2/settings", "/");
 check_equals (typeof(newso), 'object');
 trace(newso.getSize());
@@ -138,8 +149,6 @@
     check_equals (typeof(newso.data.defaultalways), 'boolean');
     check_equals (newso.data.defaultalways, so.data.defaultalways);
 
-    // FIXME: why did all these start failing ? Accoring to dump() they
-    // all still exist.
     check_equals (typeof(newso.data.crossdomainAllow), 'boolean');
     check_equals (newso.data.crossdomainAllow, true);
     check_equals (typeof(newso.data.crossdomainAlways), 'boolean');
@@ -156,9 +165,9 @@
 
 so4 = SharedObject.getLocal("Another-one", "/subdir");
 check(so4 != so3);
-xcheck_equals(typeof(so4.data), 'undefined');
+check_equals(typeof(so4.data), 'undefined');
 ret = so4.flush();
-xcheck_equals(typeof(ret), 'undefined');
+check_equals(typeof(ret), 'undefined');
 
 //------------------------------------------
 // Test that if 'data' is a getter-setter,
@@ -183,6 +192,10 @@
 //------------------------------------------
 
 so6 = SharedObject.getLocal("so6");
+check(so6.hasOwnProperty("data"));
+ret = so6.flush();
+check_equals(ret, true);
+
 a = new Array;
 for (var i in so6) a.push(i);
 check_equals(a.toString(), 'data');
@@ -204,7 +217,12 @@
  check_equals(typeof(so7), 'null');
 #endif
 so7.data.a = 1;
-so7.flush();
+ret = so7.flush();
+#if OUTPUT_VERSION < 7
+check_equals(ret, undefined);
+#else
+check_equals(ret, true);
+#endif 
 
 so8 = SharedObject.getLocal('');
 check_equals(typeof(so8), 'null');
@@ -216,6 +234,12 @@
 // END OF TESTS
 //------------------------------------------
 
-check_totals(55);
-
-#endif // OUTPUT_VERSION >= 6
+check_totals(77);
+
+#else
+
+// SWF5 totals
+check_totals(17);
+
+#endif
+


reply via email to

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