[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] /srv/bzr/gnash/trunk r9722: Improve SharedObject test. Co
From: |
Sandro Santilli |
Subject: |
[Gnash-commit] /srv/bzr/gnash/trunk r9722: Improve SharedObject test. Commit patches contributed by Jason Woofenden |
Date: |
Thu, 11 Sep 2008 14:01:40 +0200 |
User-agent: |
Bazaar (1.5) |
------------------------------------------------------------
revno: 9722
committer: Sandro Santilli <address@hidden>
branch nick: trunk
timestamp: Thu 2008-09-11 14:01:40 +0200
message:
Improve SharedObject test. Commit patches contributed by Jason Woofenden
implementing SharedObject.cpp using the buffer-based encoder/decoder.
Jason code make all of the self-contained test succeed except comparison
of input and output (gnash fails in producing an optimization version
of the .sol file for a strict array).
NOTE: Jason code doesn't replace original code; instead it's DISABLED
BY DEFAULT and can be enabled by defining a macro in SharedObject.cpp
for further testing (#define BUFFERED_AMF_SOL).
modified:
libcore/as_value.cpp
libcore/asobj/SharedObject.cpp
testsuite/misc-ming.all/SharedObjectTest.as
testsuite/misc-ming.all/SharedObjectTest.sol/sol1.sol
testsuite/misc-ming.all/SharedObjectTestRunner.sh
------------------------------------------------------------
revno: 9721.1.1
author: Jason Woofenden <address@hidden>
committer: Sandro Santilli <address@hidden>
branch nick: mybranch
timestamp: Thu 2008-09-11 11:51:39 +0200
message:
Add support for serializing BOOLEAN in ::writeAMF0
modified:
libcore/as_value.cpp
------------------------------------------------------------
revno: 9721.1.2
author: Jason Woofenden <address@hidden>
committer: Sandro Santilli <address@hidden>
branch nick: mybranch
timestamp: Thu 2008-09-11 12:17:55 +0200
message:
FIx decoding of ECMA_ARRAY
modified:
libcore/as_value.cpp
------------------------------------------------------------
revno: 9721.1.3
committer: Sandro Santilli <address@hidden>
branch nick: mybranch
timestamp: Thu 2008-09-11 12:24:31 +0200
message:
minor debugging output fix
modified:
libcore/as_value.cpp
------------------------------------------------------------
revno: 9721.1.4
author: Jason Woofenden <address@hidden>
committer: Sandro Santilli <address@hidden>
branch nick: mybranch
timestamp: Thu 2008-09-11 12:24:42 +0200
message:
Add compile-time option to have SharedObject use buffer-based amf
serialization-deserialization. Defaults to NOT use it (as per Rob's
request). #define BUFFERED_AMF_SOL to see how results of
SharedObjectTestRunner changes.
modified:
libcore/asobj/SharedObject.cpp
------------------------------------------------------------
revno: 9721.1.5
committer: Sandro Santilli <address@hidden>
branch nick: mybranch
timestamp: Thu 2008-09-11 12:31:15 +0200
message:
Have SharedObject (non-buffered AMF case) use Rob's Element->as_value
methods, fixing more tests in SharedObjectTestRunner
modified:
libcore/asobj/SharedObject.cpp
testsuite/misc-ming.all/SharedObjectTest.as
------------------------------------------------------------
revno: 9721.1.6
committer: Sandro Santilli <address@hidden>
branch nick: mybranch
timestamp: Thu 2008-09-11 12:40:19 +0200
message:
Add more tests showing difference between a *strict* and a *nonstrict*
array when encoding AMF
modified:
testsuite/misc-ming.all/SharedObjectTest.as
testsuite/misc-ming.all/SharedObjectTest.sol/sol1.sol
------------------------------------------------------------
revno: 9721.1.7
committer: Sandro Santilli <address@hidden>
branch nick: mybranch
timestamp: Thu 2008-09-11 12:54:53 +0200
message:
Fix new tests, prepare to add third step to the tester.
BTW: gnash aborts here
modified:
testsuite/misc-ming.all/SharedObjectTest.as
testsuite/misc-ming.all/SharedObjectTest.sol/sol1.sol
testsuite/misc-ming.all/SharedObjectTestRunner.sh
------------------------------------------------------------
revno: 9721.1.8
committer: Sandro Santilli <address@hidden>
branch nick: mybranch
timestamp: Thu 2008-09-11 13:18:29 +0200
message:
::readAMF0 : don't assume ECMA_ARRAY only have indexed properties, do
some more boundary checking.
modified:
libcore/as_value.cpp
------------------------------------------------------------
revno: 9721.1.9
committer: Sandro Santilli <address@hidden>
branch nick: mybranch
timestamp: Thu 2008-09-11 14:01:00 +0200
message:
revert to use rob's original code when not using buffering, as as_value
from Element crashes gnash
modified:
libcore/asobj/SharedObject.cpp
------------------------------------------------------------
revno: 9721.1.10
committer: Sandro Santilli <address@hidden>
branch nick: mybranch
timestamp: Thu 2008-09-11 14:01:02 +0200
message:
Revert expected gnash tests results to be based on original SharedObject
code.
modified:
testsuite/misc-ming.all/SharedObjectTest.as
=== modified file 'libcore/as_value.cpp'
--- a/libcore/as_value.cpp 2008-09-09 22:50:20 +0000
+++ b/libcore/as_value.cpp 2008-09-11 11:18:29 +0000
@@ -186,7 +186,7 @@
if ( key == NSV::PROP_uuPROTOuu ||
key == NSV::PROP_CONSTRUCTOR )
{
-#ifdef GNASH_DEBUG_AMF_DESERIALIZE
+#ifdef GNASH_DEBUG_AMF_SERIALIZE
log_debug(" skip serialization of specially-named property %s",
_st.value(key));
#endif
return;
@@ -194,7 +194,7 @@
// write property name
const string& name = _st.value(key);
-#ifdef GNASH_DEBUG_AMF_DESERIALIZE
+#ifdef GNASH_DEBUG_AMF_SERIALIZE
log_debug(" serializing property %s", name);
#endif
boost::uint16_t namelen = name.size();
@@ -2116,7 +2116,7 @@
li = readNetworkLong(b); b += 4;
#ifdef GNASH_DEBUG_AMF_DESERIALIZE
- log_debug("amf0 starting read of array with %i
elements", li);
+ log_debug("amf0 starting read of STRICT_ARRAY
with %i elements", li);
#endif
as_value arrayElement;
for(int i = 0; i < li; ++i)
@@ -2133,22 +2133,43 @@
}
case amf::Element::ECMA_ARRAY_AMF0:
{
- boost::intrusive_ptr<as_object> obj(new
as_object(getObjectInterface()));
- objRefs.push_back(obj.get());
+ as_array_object* obj = new as_array_object();
// GC-managed...
+ objRefs.push_back(obj);
li = readNetworkLong(b); b += 4;
+
+ // TODO: do boundary checking (if b >= end...)
+
#ifdef GNASH_DEBUG_AMF_DESERIALIZE
- log_debug("amf0 starting read of object with %i
elements", li);
+ log_debug("amf0 starting read of ECMA_ARRAY
with %i elements", li);
#endif
as_value objectElement;
- VM& vm = VM::get(); // TODO: get VM from outside
string_table& st = vm.getStringTable();
- for(int i = 0; i < li; ++i)
+ for (;;)
{
- boost::uint16_t strlen =
readNetworkShort(b); b+=2;
+ if ( b+2 >= end )
+ {
+ log_error("MALFORMED SOL: premature end of ECMA_ARRAY
block");
+ break;
+ }
+ boost::uint16_t strlen =
readNetworkShort(b); b+=2;
+
+ // end of ECMA_ARRAY is signalled by an empty string
+ // followed by an OBJECT_END_AMF0 (0x09) byte
+ if ( ! strlen )
+ {
+ // expect an object terminator here
+ if ( *b++ != amf::Element::OBJECT_END_AMF0 )
+ {
+ log_error("MALFORMED SOL: empty member name not
followed by OBJECT_END_AMF0 byte");
+ }
+ break;
+ }
+
std::string name((char*)b, strlen);
+
#ifdef GNASH_DEBUG_AMF_DESERIALIZE
- log_debug("amf0 Object prop name is
%s", name);
+ log_debug("amf0 ECMA_ARRAY prop name is
%s", name);
#endif
b += strlen;
if ( ! amf0_read_value(b, end,
objectElement, -1, objRefs, vm) )
@@ -2158,6 +2179,14 @@
obj->set_member(st.find(name),
objectElement);
}
+ // consisteny checking
+ if ( obj->size() != li ) {
+ log_error("MALFORMED SOL: ECMA_ARRAY advertised %d
elements but had just %d", obj->size());
+ }
+
+ // ends with a null string and an object
terminator (0x09)
+ //b += 3;
+
ret.set_as_object(obj);
return true;
}
@@ -2202,17 +2231,26 @@
}
case amf::Element::UNDEFINED_AMF0:
{
+#ifdef GNASH_DEBUG_AMF_DESERIALIZE
+ log_debug("readAMF0: undefined value");
+#endif
ret.set_undefined();
return true;
}
case amf::Element::NULL_AMF0:
{
+#ifdef GNASH_DEBUG_AMF_DESERIALIZE
+ log_debug("readAMF0: null value");
+#endif
ret.set_null();
return true;
}
case amf::Element::REFERENCE_AMF0:
{
si = readNetworkShort(b); b += 2;
+#ifdef GNASH_DEBUG_AMF_DESERIALIZE
+ log_debug("readAMF0: reference #%d", si);
+#endif
if ( si < 1 || si > objRefs.size() )
{
log_error("readAMF0: invalid reference to object %d (%d
known objects)", si, objRefs.size());
@@ -2362,6 +2400,20 @@
buf.appendByte(amf::Element::UNDEFINED_AMF0);
return true;
}
+
+ case BOOLEAN:
+ {
+ bool tf = getBool();
+#ifdef GNASH_DEBUG_AMF_SERIALIZE
+ log_debug(_("writeAMF0: serializing boolean '%s'"), tf);
+#endif
+
+ buf.appendByte(amf::Element::BOOLEAN_AMF0);
+ if(tf) buf.appendByte(1);
+ else buf.appendByte(0);
+
+ return true;
+ }
}
}
=== modified file 'libcore/asobj/SharedObject.cpp'
--- a/libcore/asobj/SharedObject.cpp 2008-09-10 13:53:20 +0000
+++ b/libcore/asobj/SharedObject.cpp 2008-09-11 12:01:00 +0000
@@ -25,8 +25,11 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <boost/tokenizer.hpp>
+#include <boost/scoped_array.hpp>
#include <cerrno>
+#include "SimpleBuffer.h"
+#include "as_value.h"
#include "amf.h"
#include "element.h"
#include "sol.h"
@@ -45,6 +48,16 @@
#include "URL.h"
#include "rc.h" // for use of rcfile
+// Define this to use the buffer-based AMF0 decoder/encoder
+// rather then libamf. Fixes misc-ming.all/SharedObjectTestRunner
+// both behavioural and for memory errors.
+// The only failing case in that test is comparison of input
+// and output .sol file. This is because ::writeAMF0 encodes
+// arrays as ECMA_ARRAY rather then STRIC_ARRAY. Should be
+// checked if this is a common need or only SOL-specific.
+//
+//#define BUFFERED_AMF_SOL
+
namespace {
//gnash::LogFile& dbglogfile = gnash::LogFile::getDefaultInstance();
gnash::RcInitFile& rcfile = gnash::RcInitFile::getDefaultInstance();
@@ -118,6 +131,63 @@
}
};
+/// Class used to serialize properties of an object to a buffer in SOL format
+class SOLPropsBufSerializer {
+ SimpleBuffer& _buf;
+ VM& _vm;
+ string_table& _st;
+ std::map<as_object*, size_t>& _offsetTable;
+ mutable bool _error;
+public:
+ SOLPropsBufSerializer(SimpleBuffer& buf, VM& vm, std::map<as_object*,
size_t>& offsetTable)
+ :
+ _buf(buf),
+ _vm(vm),
+ _st(vm.getStringTable()),
+ _offsetTable(offsetTable),
+ _error(false)
+ {};
+
+ bool success() const { return !_error; }
+
+ void operator() (string_table::key key, const as_value& val) const
+ {
+ if ( _error ) return;
+
+ // 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;
+ }
+
+ // write property name
+ const std::string& name = _st.value(key);
+#ifdef GNASH_DEBUG_AMF_SERIALIZE
+ log_debug(" serializing property %s", name);
+#endif
+ boost::uint16_t namelen = name.size();
+ _buf.appendNetworkShort(namelen);
+ _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;
+ }
+
+ _buf.appendByte(0); // SOL-specific
+ }
+};
+
} // anonimous namespace
static void
@@ -362,18 +432,107 @@
}
bool
-SharedObject::readSOL(const std::string& newspec)
+SharedObject::readSOL(const std::string& filespec)
{
+#ifdef BUFFERED_AMF_SOL
+ struct stat st;
+
+ if (stat(filespec.c_str(), &st) != 0)
+ {
+ return false;
+ }
+
+ 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;
+ }
+
+ 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);
+ ifs.read(reinterpret_cast<char *>(buf), st.st_size);
+
+ // TODO check initial bytes, and print warnings if they are fishy
+
+ buf += 16; // skip const-length headers
+
+ // skip past name TODO add sanity check
+ buf += ntohs(*(reinterpret_cast<boost::uint16_t*>(buf)));
+ buf += 2;
+
+ buf += 4; // skip past padding
+
+ if( buf >= end )
+ {
+ log_error("SharedObject::readSOL: file ends before data segment");
+ return false;
+ }
+
+ 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 )
+ {
+ 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)));
+ buf += 2;
+ if( buf + len >= end )
+ {
+ log_error("SharedObject::readSOL: premature end of input");
+ break;
+ }
+ if ( ! len ) {
+ log_error("SharedObject::readSOL: empty property name");
+ break;
+ }
+ std::string prop_name(reinterpret_cast<char*>(buf), len);
+ buf += len;
+
+ // 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);
+ return false;
+ }
+
+ 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);
+
+ 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;
+ }
+ catch (std::exception& e)
+ {
+ log_error("SharedObject::readSOL: Reading SharedObject %s: %s",
filespec, e.what());
+ return false;
+ }
+
+
+#else
SOL sol;
- log_security("Opening SharedObject file: %s", newspec);
- if (sol.readFile(newspec) == false) {
- log_security("empty or non-existing SOL file \"%s\", will be created
on flush/exit", newspec);
+ 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);
return false;
}
std::vector<Element *>::const_iterator it, e;
std::vector<Element *> els = sol.getElements();
- log_debug("Read %d AMF objects from %s", els.size(), newspec);
+ log_debug("Read %d AMF objects from %s", els.size(), filespec);
string_table& st = _vm.getStringTable();
string_table::key dataKey = st.find("data");
@@ -383,6 +542,14 @@
for (it = els.begin(), e = els.end(); it != e; it++) {
Element *el = *it;
+#if 0 // this would be using as_value::as_value(const Element&)
+
+ std::string name(el->getName());
+ as_value val(*el);
+ ptr->set_member(st.find(name), val);
+
+#else // this is original code
+
switch (el->getType())
{
case Element::NUMBER_AMF0:
@@ -423,9 +590,13 @@
return false;
break;
}
+
+#endif
+
}
return true;
+#endif
}
@@ -434,6 +605,7 @@
// flush(); // needs more care, if destroyed after VM we get killed
}
+
bool
SharedObject::flush() const
{
@@ -473,6 +645,51 @@
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);
@@ -483,6 +700,7 @@
log_error("writing SharedObject file to %s", filespec);
return false;
}
+#endif
log_security("SharedObject '%s' written to filesystem.", filespec);
return true;
=== modified file 'testsuite/misc-ming.all/SharedObjectTest.as'
--- a/testsuite/misc-ming.all/SharedObjectTest.as 2008-09-10 23:20:53
+0000
+++ b/testsuite/misc-ming.all/SharedObjectTest.as 2008-09-11 12:01:02
+0000
@@ -24,17 +24,41 @@
check_equals(so1.data.tbool, true);
check_equals(typeof(so1.data.fbool), 'boolean');
check_equals(so1.data.fbool, false);
+
+// Test reading STRICT_ARRAY, NULL and UNDEFINED
xcheck_equals(typeof(so1.data.ary), 'object');
-xcheck_equals(so1.data.ary.toString(), '1,2,3');
+xcheck_equals(so1.data.ary.toString(), '1,true,string,null,');
+xcheck_equals(typeof(so1.data.ary[0]), 'number');
+xcheck_equals(typeof(so1.data.ary[1]), 'boolean');
+xcheck_equals(typeof(so1.data.ary[2]), 'string');
+xcheck_equals(typeof(so1.data.ary[3]), 'null');
+check_equals(typeof(so1.data.ary[4]), 'undefined');
+xcheck_equals(so1.data.ary.length, 5);
+
+// Test reading ECMA_ARRAY
+xcheck_equals(typeof(so1.data.aryns), 'object', 'aryns was not read from
.sol');
+xcheck_equals(so1.data.aryns.toString(), '4,5,6');
+xcheck_equals(so1.data.aryns.length, 3);
+xcheck_equals(so1.data.aryns.custom, 7);
+
+// Test reading OBJECT
xcheck_equals(typeof(so1.data.obj), 'object');
xcheck_equals(typeof(so1.data.obj.a), 'number');
+
+// Test reading NUMBER
xcheck_equals(so1.data.obj.a, 10);
+
+// Test reading STRING
xcheck_equals(typeof(so1.data.obj.b), 'string');
xcheck_equals(so1.data.obj.b, '20');
+
+// Test reading BOOLEAN
xcheck_equals(typeof(so1.data.obj.c), 'boolean');
xcheck_equals(so1.data.obj.c, true);
+
+// Test reading REFERENCE
xcheck_equals(typeof(so1.data.ref), 'object');
-check_equals(so1.data.ref, so1.data.obj);
+check_equals(so1.data.ref, so1.data.obj);
// force writing the sol or the adobe player won't save it
// again. This will also serve as a kind of reference for
@@ -43,7 +67,8 @@
so1.data.str = 'a string';
so1.data.tbool = true;
so1.data.fbool = false;
-so1.data.ary = [1,2,3];
+so1.data.ary = [1,true,'string',null, undefined]; // strict array
(STRICT_ARRAY)
+so1.data.aryns = [4,5,6]; so1.data.aryns.custom = 7; // non-strict array
(ECMA_ARRAY)
so1.data.obj = {a:10,b:'20',c:true};
so1.data.ref = so1.data.obj;
@@ -53,8 +78,8 @@
{
loadMovie('fscommand:quit', '');
};
-note(" Will quit in 1 seconds");
-setInterval(quit, 1000);
+note(" Will quit in 5 seconds");
+setInterval(quit, 5000);
stop();
totals();
=== modified file 'testsuite/misc-ming.all/SharedObjectTest.sol/sol1.sol'
Binary files a/testsuite/misc-ming.all/SharedObjectTest.sol/sol1.sol
2008-09-10 09:06:37 +0000 and
b/testsuite/misc-ming.all/SharedObjectTest.sol/sol1.sol 2008-09-11 10:54:53
+0000 differ
=== modified file 'testsuite/misc-ming.all/SharedObjectTestRunner.sh'
--- a/testsuite/misc-ming.all/SharedObjectTestRunner.sh 2008-09-10 23:20:53
+0000
+++ b/testsuite/misc-ming.all/SharedObjectTestRunner.sh 2008-09-11 10:54:53
+0000
@@ -61,8 +61,24 @@
sleep 1
export GNASHRC=${TOP_BUILDDIR}/testsuite/gnashrc
+
+#####################################################
+##
+## FIRST STEP: test reading well-known .sol file
+##
+#####################################################
+
${PLAYER} ${SWFTEST}
+#####################################################
+##
+## SECOND STEP: test writing
+##
+## test that written sol files
+## are bytewise equal to input ones
+##
+#####################################################
+
cd ${SOLDIR}; find . -cnewer ${SOLDIR}/copytime | sed 'address@hidden/@@' >
${SOLDIR}/newfiles; cd -
for f in ${INPUTSOLDIR}/*.sol; do
@@ -77,3 +93,12 @@
fi
echo "XPASSED: SharedObject ${solname} matches input"
done
+
+#####################################################
+##
+## THIRD STEP: test re-reading just-written sol files
+##
+#####################################################
+
+# ( temporarly disabled )
+#${PLAYER} ${SWFTEST}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] /srv/bzr/gnash/trunk r9722: Improve SharedObject test. Commit patches contributed by Jason Woofenden,
Sandro Santilli <=