[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] /srv/bzr/gnash/trunk r11564: Fake our fake array so it ap
From: |
Benjamin Wolsey |
Subject: |
[Gnash-commit] /srv/bzr/gnash/trunk r11564: Fake our fake array so it appears less fake. |
Date: |
Thu, 15 Oct 2009 16:04:17 +0200 |
User-agent: |
Bazaar (1.16.1) |
------------------------------------------------------------
revno: 11564 [merge]
committer: Benjamin Wolsey <address@hidden>
branch nick: trunk
timestamp: Thu 2009-10-15 16:04:17 +0200
message:
Fake our fake array so it appears less fake.
modified:
libcore/TextField.cpp
libcore/as_function.cpp
libcore/as_object.cpp
libcore/asobj/Array_as.cpp
libcore/asobj/Array_as.h
libcore/asobj/AsBroadcaster.cpp
libcore/asobj/Global_as.h
libcore/asobj/Globals.cpp
libcore/asobj/MovieClipLoader.cpp
libcore/asobj/Selection_as.cpp
libcore/asobj/TextFormat_as.cpp
libcore/swf_function.cpp
libcore/swf_function.h
libcore/vm/ASHandlers.cpp
testsuite/actionscript.all/Function.as
testsuite/actionscript.all/array.as
testsuite/swfdec/PASSING
=== modified file 'libcore/TextField.cpp'
--- a/libcore/TextField.cpp 2009-10-14 14:50:52 +0000
+++ b/libcore/TextField.cpp 2009-10-14 18:15:19 +0000
@@ -48,7 +48,6 @@
#include "fontlib.h"
#include "Object.h" // for getObjectInterface
#include "namedStrings.h"
-#include "Array_as.h" // for _listeners construction
#include "AsBroadcaster.h" // for initializing self as a broadcaster
#include "StringPredicates.h"
#include "TextFormat_as.h"
=== modified file 'libcore/as_function.cpp'
--- a/libcore/as_function.cpp 2009-10-14 08:47:08 +0000
+++ b/libcore/as_function.cpp 2009-10-14 18:45:06 +0000
@@ -271,6 +271,17 @@
}
+class PushFunctionArgs
+{
+public:
+ PushFunctionArgs(fn_call& fn) : _fn(fn) {}
+ void operator()(const as_value& val) {
+ _fn.pushArg(val);
+ }
+private:
+ fn_call& _fn;
+};
+
as_value
function_apply(const fn_call& fn)
{
@@ -317,37 +328,11 @@
boost::intrusive_ptr<as_object> arg1 =
fn.arg(1).to_object(*getGlobal(fn));
- if (!arg1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("Second arg of
Function.apply"
- " is %s (expected array)"
- " - considering as call with no
args"),
- fn.arg(1));
- );
- goto call_it;
- }
-
- boost::intrusive_ptr<Array_as> arg_array =
-
boost::dynamic_pointer_cast<Array_as>(arg1);
-
- if ( ! arg_array )
- {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("Second arg of
Function.apply"
- " is of type %s, with value %s"
- " (expected array)"
- " - considering as call with no
args"),
- fn.arg(1).typeOf(),
fn.arg(1).to_string());
- );
- goto call_it;
- }
-
- const size_t nelems = arg_array->size();
-
- for (size_t i = 0; i < nelems; ++i) {
- new_fn_call.pushArg(arg_array->at(i));
- }
-
+ if (arg1) {
+ PushFunctionArgs pa(new_fn_call);
+ foreachArray(*arg1, pa);
+ }
+ else goto call_it;
}
}
=== modified file 'libcore/as_object.cpp'
--- a/libcore/as_object.cpp 2009-10-14 08:47:08 +0000
+++ b/libcore/as_object.cpp 2009-10-15 05:59:43 +0000
@@ -1024,101 +1024,45 @@
_members.dump(*this, to);
}
-class FlagsSetterVisitor {
- string_table& _st;
- PropertyList& _pl;
- int _setTrue;
- int _setFalse;
-public:
- FlagsSetterVisitor(string_table& st, PropertyList& pl, int setTrue, int
setFalse)
- :
- _st(st),
- _pl(pl),
- _setTrue(setTrue),
- _setFalse(setFalse)
- {}
-
- void visit(as_value& v)
- {
- string_table::key key = _st.find(v.to_string());
- _pl.setFlags(key, _setTrue, _setFalse);
- }
-};
-
void
as_object::setPropFlags(const as_value& props_val, int set_false, int set_true)
{
- if (props_val.is_string())
- {
- std::string propstr = PROPNAME(props_val.to_string());
-
- for(;;)
- {
- std::string prop;
- size_t next_comma=propstr.find(",");
- if ( next_comma == std::string::npos )
- {
- prop=propstr;
- }
- else
- {
- prop=propstr.substr(0,next_comma);
- propstr=propstr.substr(next_comma+1);
- }
-
- // set_member_flags will take care of case conversion
- if (!set_member_flags(getStringTable(*this).find(prop),
set_true, set_false) )
- {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("Can't set propflags on object "
- "property %s "
- "(either not found or protected)"),
prop);
- );
- }
-
- if ( next_comma == std::string::npos )
- {
- break;
- }
- }
- return;
- }
-
- if (props_val.is_null())
- {
+
+ if (props_val.is_null()) {
// Take all the members of the object
_members.setFlagsAll(set_true, set_false);
-
- // Are we sure we need to descend to __proto__ ?
- // should we recurse then ?
-#if 0
- if (m_prototype)
- {
- m_prototype->_members.setFlagsAll(set_true, set_false);
- }
-#endif
- return;
- }
-
- boost::intrusive_ptr<as_object> props =
props_val.to_object(*getGlobal(*this));
- Array_as* ary = dynamic_cast<Array_as*>(props.get());
- if ( ! ary )
- {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("Invalid call to AsSetPropFlags: "
- "invalid second argument %s "
- "(expected string, null or an array)"),
- props_val);
- );
- return;
- }
-
- // The passed argument has to be considered an array
- //std::pair<size_t, size_t> result =
- FlagsSetterVisitor visitor(getStringTable(*this), _members, set_true,
- set_false);
- ary->visitAll(visitor);
- //_members.setFlagsAll(props->_members, set_true, set_false);
+ return;
+ }
+
+ std::string propstr = props_val.to_string();
+
+ for (;;) {
+
+ std::string prop;
+ size_t next_comma=propstr.find(",");
+ if (next_comma == std::string::npos) {
+ prop = propstr;
+ }
+ else {
+ prop = propstr.substr(0,next_comma);
+ propstr = propstr.substr(next_comma+1);
+ }
+
+ // set_member_flags will take care of case conversion
+ if (!set_member_flags(getStringTable(*this).find(prop), set_true,
set_false) )
+ {
+ IF_VERBOSE_ASCODING_ERRORS(
+ log_aserror(_("Can't set propflags on object "
+ "property %s "
+ "(either not found or protected)"), prop);
+ );
+ }
+
+ if (next_comma == std::string::npos) {
+ break;
+ }
+ }
+ return;
}
=== modified file 'libcore/asobj/Array_as.cpp'
--- a/libcore/asobj/Array_as.cpp 2009-10-14 15:45:27 +0000
+++ b/libcore/asobj/Array_as.cpp 2009-10-15 13:25:00 +0000
@@ -25,62 +25,281 @@
#include "as_value.h"
#include "Array_as.h"
#include "log.h"
-#include "builtin_function.h" // for Array class
+#include "builtin_function.h"
#include "NativeFunction.h"
-#include "as_function.h" // for sort user-defined comparator
+#include "as_function.h"
#include "fn_call.h"
#include "Global_as.h"
#include "GnashException.h"
-#include "action.h" // for call_method
-#include "VM.h" // for PROPNAME, registerNative
-#include "Object.h" // for getObjectInterface()
+#include "action.h"
+#include "VM.h"
+#include "Object.h"
#include "GnashNumeric.h"
#include <string>
#include <algorithm>
#include <cmath>
#include <boost/algorithm/string/case_conv.hpp>
-
-//#define GNASH_DEBUG
+#include <boost/lexical_cast.hpp>
namespace gnash {
-typedef boost::function2<bool, const as_value&, const as_value&> as_cmp_fn;
-
-inline string_table::key
-arrayKey(string_table& st, size_t i)
-{
- std::ostringstream os;
- os << i;
- return st.find(os.str());
-}
-
-namespace {
-
-string_table::key getKey(const fn_call& fn, size_t i) {
- string_table& st = getStringTable(fn);
- return arrayKey(st, i);
-}
-
-}
-
-static as_object* getArrayInterface();
-static void attachArrayProperties(as_object& proto);
-static void attachArrayInterface(as_object& proto);
-static void attachArrayStatics(as_object& proto);
-
-inline static bool int_lt_or_eq (int a)
-{
- return a <= 0;
-}
-
-inline static bool int_gt (int a)
-{
- return a > 0;
-}
+// Forward declarations
+namespace {
+
+ /// Sort flags
+ enum SortFlags {
+ /// Case-insensitive (z precedes A)
+ SORT_CASE_INSENSITIVE = (1<<0), // 1
+ /// Descending order (b precedes a)
+ SORT_DESCENDING = (1<<1), // 2
+ /// If two or more elements in the array
+ /// have identical sort fields, return 0
+ /// and don't modify the array.
+ /// Otherwise proceed to sort the array.
+ SORT_UNIQUE = (1<<2), // 4
+ /// Don't modify the array, rather return
+ /// a new array containing indexes into it
+ /// in sorted order.
+ SORT_RETURN_INDEX = (1<<3), // 8
+ /// Numerical sort (9 precedes 10)
+ SORT_NUMERIC = (1<<4) // 16
+ };
+
+ class indexed_as_value;
+
+ typedef boost::function2<bool, const as_value&, const as_value&> as_cmp_fn;
+
+ as_object* getArrayInterface();
+ void attachArrayInterface(as_object& proto);
+ void attachArrayStatics(as_object& proto);
+
+ as_value join(as_object* array, const std::string& separator);
+
+ as_value array_new(const fn_call& fn);
+ as_value array_slice(const fn_call& fn);
+ as_value array_concat(const fn_call& fn);
+ as_value array_toString(const fn_call& fn);
+ as_value array_join(const fn_call& fn);
+ as_value array_reverse(const fn_call& fn);
+ as_value array_shift(const fn_call& fn);
+ as_value array_pop(const fn_call& fn);
+ as_value array_unshift(const fn_call& fn);
+ as_value array_push(const fn_call& fn);
+ as_value array_sortOn(const fn_call& fn);
+ as_value array_sort(const fn_call& fn);
+ as_value array_splice(const fn_call& fn);
+
+ string_table::key getKey(const fn_call& fn, size_t i);
+
+ /// Implementation of foreachArray that takes a start and end range.
+ template<typename T> void foreachArray(as_object& array, int start,
+ int end, T& pred);
+
+ inline bool int_lt_or_eq (int a) {
+ return a <= 0;
+ }
+
+ inline bool int_gt (int a) {
+ return a > 0;
+ }
+
+ void getIndexedElements(as_object& array, std::vector<indexed_as_value>&
v);
+
+ void pushIndices(as_object& o, const std::vector<indexed_as_value>& index);
+
+}
+
+/// Function objects for foreachArray()
+namespace {
+
+struct indexed_as_value : public as_value
+{
+ int vec_index;
+
+ indexed_as_value(const as_value& val, int index)
+ : as_value(val)
+ {
+ vec_index = index;
+ }
+};
+
+class PushToArray
+{
+public:
+ PushToArray(as_object& obj) : _obj(obj) {}
+ void operator()(const as_value& val) {
+ _obj.callMethod(NSV::PROP_PUSH, val);
+ }
+private:
+ as_object& _obj;
+};
+
+template<typename T>
+class PushToContainer
+{
+public:
+ PushToContainer(T& v) : _v(v) {}
+ void operator()(const as_value& val) {
+ _v.push_back(val);
+ }
+private:
+ T& _v;
+};
+
+
+class PushToIndexedVector
+{
+public:
+ PushToIndexedVector(std::vector<indexed_as_value>& v) : _v(v), _i(0) {}
+ void operator()(const as_value& val) {
+ _v.push_back(indexed_as_value(val, _i));
+ ++_i;
+ }
+private:
+ std::vector<indexed_as_value>& _v;
+ size_t _i;
+};
+
+
+/// \brief
+/// Attempt to sort the array using given values comparator, avc.
+/// If two or more elements in the array are equal, as determined
+/// by the equality comparator ave, then the array is not sorted
+/// and 0 is returned. Otherwise the array is sorted and returned.
+///
+/// @param avc
+/// boolean functor or function comparing two as_value& objects
+/// used to determine sort-order
+///
+/// @param ave
+/// boolean functor or function comparing two as_value& objects
+/// used to determine equality
+///
+template <class AVCMP, class AVEQ>
+bool sort(as_object& o, AVCMP avc, AVEQ ave)
+{
+ // IMPORTANT NOTE
+ //
+ // As for ISO/IEC 14882:2003 - 23.2.2.4.29
+ // the sort algorithm relies on the assumption
+ // that the comparator function implements
+ // a Strict Weak Ordering operator:
+ // http://www.sgi.com/tech/stl/StrictWeakOrdering.html
+ //
+ // Invalid comparator can lead to undefined behaviour,
+ // including invalid memory access and infinite loops.
+ //
+ // Pragmatically, it seems that std::list::sort is
+ // more robust in this reguard, so we'll sort a list
+ // instead of the queue. We want to sort a copy anyway
+ // to avoid the comparator changing the original container.
+
+ typedef std::list<as_value> SortContainer;
+
+ SortContainer v;
+ PushToContainer<SortContainer> pv(v);
+ foreachArray(o, pv);
+
+ const size_t size = v.size();
+
+ v.sort(avc);
+
+ if (std::adjacent_find(v.begin(), v.end(), ave) != v.end()) return false;
+
+ string_table& st = getStringTable(o);
+
+ SortContainer::const_iterator it = v.begin();
+
+ for (size_t i = 0; i < size; ++i) {
+ if (i >= v.size()) {
+ break;
+ }
+ o.set_member(arrayKey(st, i), *it);
+ ++it;
+ }
+ return true;
+}
+
+
+template <class AVCMP>
+void sort(as_object& o, AVCMP avc)
+{
+
+ typedef std::list<as_value> SortContainer;
+
+ SortContainer v;
+ PushToContainer<SortContainer> pv(v);
+ foreachArray(o, pv);
+
+ const size_t size = v.size();
+
+ v.sort(avc);
+
+ string_table& st = getStringTable(o);
+
+ SortContainer::const_iterator it = v.begin();
+
+ for (size_t i = 0; i < size; ++i) {
+ if (it == v.end()) {
+ break;
+ }
+ o.set_member(arrayKey(st, i), *it);
+ ++it;
+ }
+}
+
+/// \brief
+/// Return a new array containing sorted index of this array.
+/// If two or more elements in the array are equal, as determined
+/// by the equality comparator ave, then 0 is returned instead.
+///
+/// @param avc
+/// boolean functor or function comparing two as_value& objects
+/// used to determine sort-order
+///
+/// @param ave
+/// boolean functor or function comparing two as_value& objects
+/// used to determine equality
+///
+template <class AVCMP, class AVEQ>
+as_value sortIndexed(as_object& array, AVCMP avc, AVEQ ave)
+{
+ std::vector<indexed_as_value> v;
+
+ getIndexedElements(array, v);
+
+ std::sort(v.begin(), v.end(), avc);
+
+ if (std::adjacent_find(v.begin(), v.end(), ave) != v.end()) {
+ return as_value(0.0);
+ }
+
+ as_object* o = getGlobal(array)->createArray();
+ pushIndices(*o, v);
+ return o;
+}
+
+
+/// \brief
+/// Return a new array containing sorted index of this array
+///
+/// @param avc
+/// boolean functor or function comparing two as_value& objects
+///
+template <class AVCMP>
+as_object* sortIndexed(as_object& array, AVCMP avc)
+{
+ std::vector<indexed_as_value> v;
+ getIndexedElements(array, v);
+ std::sort(v.begin(), v.end(), avc);
+ as_object* o = getGlobal(array)->createArray();
+ pushIndices(*o, v);
+ return o;
+}
+
// simple as_value strict-weak-ordering comparison functors:
-
// string comparison, ascending (default sort method)
struct as_value_lt
{
@@ -272,15 +491,15 @@
// Return basic as_value comparison functor for corresponding sort flag
// Note:
-// fUniqueSort and fReturnIndexedArray must first be stripped from the flag
+// SORT_UNIQUE and SORT_RETURN_INDEX must first be stripped from the flag
as_cmp_fn
get_basic_cmp(boost::uint8_t flags, int version)
{
as_cmp_fn f;
- // fUniqueSort and fReturnIndexedArray must be stripped by caller
- assert(flags^Array_as::fUniqueSort);
- assert(flags^Array_as::fReturnIndexedArray);
+ // SORT_UNIQUE and SORT_RETURN_INDEX must be stripped by caller
+ assert(flags^SORT_UNIQUE);
+ assert(flags^SORT_RETURN_INDEX);
switch ( flags )
{
@@ -288,35 +507,35 @@
f = as_value_lt(version);
return f;
- case Array_as::fDescending:
+ case SORT_DESCENDING:
f = as_value_gt(version);
return f;
- case Array_as::fCaseInsensitive:
+ case SORT_CASE_INSENSITIVE:
f = as_value_nocase_lt(version);
return f;
- case Array_as::fCaseInsensitive |
- Array_as::fDescending:
+ case SORT_CASE_INSENSITIVE |
+ SORT_DESCENDING:
f = as_value_nocase_gt(version);
return f;
- case Array_as::fNumeric:
+ case SORT_NUMERIC:
f = as_value_num_lt(version);
return f;
- case Array_as::fNumeric | Array_as::fDescending:
+ case SORT_NUMERIC | SORT_DESCENDING:
f = as_value_num_gt(version);
return f;
- case Array_as::fCaseInsensitive |
- Array_as::fNumeric:
+ case SORT_CASE_INSENSITIVE |
+ SORT_NUMERIC:
f = as_value_num_nocase_lt(version);
return f;
- case Array_as::fCaseInsensitive |
- Array_as::fNumeric |
- Array_as::fDescending:
+ case SORT_CASE_INSENSITIVE |
+ SORT_NUMERIC |
+ SORT_DESCENDING:
f = as_value_num_nocase_gt(version);
return f;
@@ -329,12 +548,12 @@
// Return basic as_value equality functor for corresponding sort flag
// Note:
-// fUniqueSort and fReturnIndexedArray must first be stripped from the flag
+// SORT_UNIQUE and SORT_RETURN_INDEX must first be stripped from the flag
as_cmp_fn
get_basic_eq(boost::uint8_t flags, int version)
{
as_cmp_fn f;
- flags &= ~(Array_as::fDescending);
+ flags &= ~(SORT_DESCENDING);
switch ( flags )
{
@@ -342,16 +561,16 @@
f = as_value_eq(version);
return f;
- case Array_as::fCaseInsensitive:
+ case SORT_CASE_INSENSITIVE:
f = as_value_nocase_eq(version);
return f;
- case Array_as::fNumeric:
+ case SORT_NUMERIC:
f = as_value_num_eq(version);
return f;
- case Array_as::fCaseInsensitive |
- Array_as::fNumeric:
+ case SORT_CASE_INSENSITIVE |
+ SORT_NUMERIC:
f = as_value_num_nocase_eq(version);
return f;
@@ -510,15 +729,15 @@
const as_object& _obj;
};
-// Convenience function to strip fUniqueSort and fReturnIndexedArray from sort
+// Convenience function to strip SORT_UNIQUE and SORT_RETURN_INDEX from sort
// flag. Presence of flags recorded in douniq and doindex.
-static inline boost::uint8_t
+inline boost::uint8_t
flag_preprocess(boost::uint8_t flgs, bool* douniq, bool* doindex)
{
- *douniq = (flgs & Array_as::fUniqueSort);
- *doindex = (flgs & Array_as::fReturnIndexedArray);
- flgs &= ~(Array_as::fReturnIndexedArray);
- flgs &= ~(Array_as::fUniqueSort);
+ *douniq = (flgs & SORT_UNIQUE);
+ *doindex = (flgs & SORT_RETURN_INDEX);
+ flgs &= ~(SORT_RETURN_INDEX);
+ flgs &= ~(SORT_UNIQUE);
return flgs;
}
@@ -531,7 +750,7 @@
Array_as::const_iterator it = itBegin;
std::deque<boost::uint8_t> flgs;
- // extract fUniqueSort and fReturnIndexedArray from first flag
+ // extract SORT_UNIQUE and SORT_RETURN_INDEX from first flag
if (it != itEnd)
{
boost::uint8_t flag = static_cast<boost::uint8_t>((*it++).to_number());
@@ -542,52 +761,28 @@
while (it != itEnd)
{
boost::uint8_t flag = static_cast<boost::uint8_t>((*it++).to_number());
- flag &= ~(Array_as::fReturnIndexedArray);
- flag &= ~(Array_as::fUniqueSort);
+ flag &= ~(SORT_RETURN_INDEX);
+ flag &= ~(SORT_UNIQUE);
flgs.push_back(flag);
}
return flgs;
}
+}
+
Array_as::Array_as()
:
as_object(getArrayInterface()), // pass Array inheritance
elements(0)
{
- //IF_VERBOSE_ACTION (
- //log_action("%s: %p", __FUNCTION__, (void*)this);
- //)
- attachArrayProperties(*this);
+ init_member(NSV::PROP_LENGTH, 0.0);
}
-Array_as::Array_as(const Array_as& other)
- :
- as_object(other),
- elements(other.elements)
-{
- //IF_VERBOSE_ACTION (
- //log_action("%s: %p", __FUNCTION__, (void*)this);
- //)
-}
Array_as::~Array_as()
{
}
-std::deque<indexed_as_value>
-Array_as::get_indexed_elements()
-{
- std::deque<indexed_as_value> indexed_elements;
- int i = 0;
-
- for (Array_as::const_iterator it = elements.begin(), e = elements.end();
- it != e; ++it)
- {
- indexed_elements.push_back(indexed_as_value(*it, i++));
- }
- return indexed_elements;
-}
-
Array_as::const_iterator
Array_as::begin()
{
@@ -623,54 +818,6 @@
return int(value);
}
-void
-Array_as::reverse()
-{
- const ArrayContainer::size_type s = elements.size();
- if ( s < 2 ) return; // nothing to do (CHECKME: might be a single hole!)
-
- // We create another container, as we want to fill the gaps
- // There could likely be an in-place version for this, but
- // filling the gaps would need more care
- ArrayContainer newelements(s);
-
- for (size_t i = 0, n = s - 1; i < s; ++i, --n)
- {
- newelements[i] = elements[n];
- }
-
- elements = newelements;
-}
-
-std::string
-Array_as::join(const std::string& separator) const
-{
- // TODO - confirm this is the right format!
- // Reportedly, flash version 7 on linux, and Flash 8 on IE look like
- // "(1,2,3)" and "1,2,3" respectively - which should we mimic?
- // Using no parentheses until confirmed for sure
- //
- // We should change output based on SWF version --strk 2006-04-28
-
- std::string temp;
-
- const ArrayContainer::size_type s = elements.size();
-
- if ( s )
- {
- int swfversion = getSWFVersion(*this);
-
- for (size_t i = 0; i < s; ++i)
- {
- if ( i ) temp += separator;
- temp += elements[i].to_string_versioned(swfversion);
- }
- }
-
- return temp;
-
-}
-
unsigned int
Array_as::size() const
{
@@ -684,32 +831,6 @@
else return elements[index];
}
-boost::intrusive_ptr<Array_as>
-Array_as::slice(unsigned int start, unsigned int one_past_end)
-{
- assert(one_past_end >= start);
- assert(one_past_end <= size());
- assert(start <= size());
-
- boost::intrusive_ptr<Array_as> newarray(new Array_as);
-
-#ifdef GNASH_DEBUG
- log_debug(_("Array.slice(%u, %u) called"), start, one_past_end);
-#endif
-
- size_t newsize = one_past_end - start;
- newarray->elements.resize(newsize);
-
- // maybe there's a standard algorithm for this ?
- for (unsigned int i=start; i<one_past_end; ++i)
- {
- newarray->elements[i-start] = elements[i];
- }
-
- return newarray;
-
-}
-
/* virtual public, overriding as_object::get_member */
bool
Array_as::get_member(string_table::key name, as_value *val,
@@ -787,126 +908,297 @@
// if we were sent a valid array index and not a normal member
if (index >= 0)
{
- if ( size_t(index) >= elements.size() )
+ if (size_t(index) >= elements.size())
{
// if we're setting index (x), the vector
// must be size (x+1)
elements.resize(index+1);
}
+ as_object::set_member(NSV::PROP_LENGTH, elements.size());
// set the appropriate index and return
elements[index] = val;
return true;
}
+ if (name == NSV::PROP_LENGTH) {
+ elements.resize(std::max(val.to_int(), 0));
+ // Don't return before as_object has set the value!
+ }
return as_object::set_member(name,val, nsname, ifFound);
}
-Array_as*
-Array_as::get_indices(std::deque<indexed_as_value> elems)
-{
- Array_as* intIndexes = new Array_as();
-
- for (std::deque<indexed_as_value>::const_iterator it = elems.begin();
- it != elems.end(); ++it)
- {
- intIndexes->callMethod(NSV::PROP_PUSH, it->vec_index);
- }
- return intIndexes;
-}
-
-static as_value
+size_t
+arrayLength(as_object& array)
+{
+ as_value length;
+ if (!array.get_member(NSV::PROP_LENGTH, &length)) return 0;
+
+ const int size = length.to_int();
+ if (size < 0) return 0;
+ return size;
+}
+
+void
+registerArrayNative(as_object& global)
+{
+ VM& vm = getVM(global);
+ vm.registerNative(array_new, 252, 0);
+ vm.registerNative(array_push, 252, 1);
+ vm.registerNative(array_pop, 252, 2);
+ vm.registerNative(array_concat, 252, 3);
+ vm.registerNative(array_shift, 252, 4);
+ vm.registerNative(array_unshift, 252, 5);
+ vm.registerNative(array_slice, 252, 6);
+ vm.registerNative(array_join, 252, 7);
+ vm.registerNative(array_splice, 252, 8);
+ vm.registerNative(array_toString, 252, 9);
+ vm.registerNative(array_sort, 252, 10);
+ vm.registerNative(array_reverse, 252, 11);
+ vm.registerNative(array_sortOn, 252, 12);
+}
+
+void
+array_class_init(as_object& where, const ObjectURI& uri)
+{
+ static as_object* cl = 0;
+
+ if (cl == NULL) {
+
+ // This is going to be the global Array "class"/"function"
+ VM& vm = getVM(where);
+
+ as_object* proto = getArrayInterface();
+ cl = vm.getNative(252, 0);
+ cl->init_member(NSV::PROP_PROTOTYPE, proto);
+ proto->init_member(NSV::PROP_CONSTRUCTOR, cl);
+
+ // Attach static members
+ attachArrayStatics(*cl);
+ }
+
+ const int flags = PropFlags::dontEnum;
+ where.init_member(getName(uri), cl, flags, getNamespace(uri));
+}
+
+void
+Array_as::enumerateNonProperties(as_environment& env) const
+{
+ std::stringstream ss;
+ for (const_iterator it = elements.begin(),
+ itEnd = elements.end(); it != itEnd; ++it)
+ {
+ int idx = it.index();
+ // enumerated values need to be strings, not numbers
+ ss.str(""); ss << idx;
+ env.push(as_value(ss.str()));
+ }
+}
+
+#ifdef GNASH_USE_GC
+void
+Array_as::markReachableResources() const
+{
+ for (const_iterator i=elements.begin(), e=elements.end(); i!=e; ++i)
+ {
+ (*i).setReachable();
+ }
+ markAsObjectReachable();
+}
+#endif // GNASH_USE_GC
+
+void
+Array_as::visitPropertyValues(AbstractPropertyVisitor& visitor) const
+{
+ std::stringstream ss;
+ string_table& st = getStringTable(*this);
+ for (const_iterator i=elements.begin(), ie=elements.end(); i!=ie; ++i)
+ {
+ int idx = i.index();
+ ss.str(""); ss << idx;
+ string_table::key k = st.find(ss.str());
+ visitor.accept(k, *i);
+ }
+
+ // visit proper properties
+ as_object::visitPropertyValues(visitor);
+}
+
+void
+Array_as::visitNonHiddenPropertyValues(AbstractPropertyVisitor& visitor) const
+{
+ std::stringstream ss;
+ string_table& st = getStringTable(*this);
+ for (const_iterator i=elements.begin(), ie=elements.end(); i!=ie; ++i)
+ {
+ // TODO: skip hidden ones
+ int idx = i.index();
+ ss.str(""); ss << idx;
+ string_table::key k = st.find(ss.str());
+ visitor.accept(k, *i);
+ }
+
+ // visit proper properties
+ as_object::visitNonHiddenPropertyValues(visitor);
+}
+
+bool
+Array_as::isStrict() const
+{
+ if ( hasNonHiddenProperties() ) return false;
+ return true;
+}
+
+// Used by foreachArray, declared in Array_as.h
+string_table::key
+arrayKey(string_table& st, size_t i)
+{
+ return st.find(boost::lexical_cast<std::string>(i));
+}
+
+namespace {
+
+void
+attachArrayStatics(as_object& proto)
+{
+ int flags = 0; // these are not protected
+ proto.init_member("CASEINSENSITIVE", SORT_CASE_INSENSITIVE, flags);
+ proto.init_member("DESCENDING", SORT_DESCENDING, flags);
+ proto.init_member("UNIQUESORT", SORT_UNIQUE, flags);
+ proto.init_member("RETURNINDEXEDARRAY", SORT_RETURN_INDEX, flags);
+ proto.init_member("NUMERIC", SORT_NUMERIC, flags);
+}
+
+void
+attachArrayInterface(as_object& proto)
+{
+ VM& vm = getVM(proto);
+
+ proto.init_member("push", vm.getNative(252, 1));
+ proto.init_member("pop", vm.getNative(252, 2));
+ proto.init_member("concat", vm.getNative(252, 3));
+ proto.init_member("shift", vm.getNative(252, 4));
+ proto.init_member("unshift", vm.getNative(252, 5));
+ proto.init_member("slice", vm.getNative(252, 6));
+ proto.init_member("join", vm.getNative(252, 7));
+ proto.init_member("splice", vm.getNative(252, 8));
+ proto.init_member("toString", vm.getNative(252, 9));
+ proto.init_member("sort", vm.getNative(252, 10));
+ proto.init_member("reverse", vm.getNative(252, 11));
+ proto.init_member("sortOn", vm.getNative(252, 12));
+}
+
+as_object*
+getArrayInterface()
+{
+ static boost::intrusive_ptr<as_object> proto = NULL;
+ if ( proto == NULL )
+ {
+ proto = new as_object(getObjectInterface());
+ getVM(*proto).addStatic(proto.get());
+
+ attachArrayInterface(*proto);
+ }
+ return proto.get();
+}
+
+as_value
array_splice(const fn_call& fn)
{
- boost::intrusive_ptr<Array_as> array = ensureType<Array_as>(fn.this_ptr);
-
-#ifdef GNASH_DEBUG
- std::stringstream ss;
- fn.dump_args(ss);
- log_debug(_("Array(%s).splice(%s) called"), array->toString(), ss.str());
-#endif
-
- if (fn.nargs < 1)
- {
+ as_object* array = ensureType<as_object>(fn.this_ptr);
+
+ if (fn.nargs < 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("Array.splice() needs at least 1 argument, call
ignored"));
+ log_aserror(_("Array.splice() needs at least 1 argument, "
+ "call ignored"));
);
return as_value();
}
-
- unsigned origlen = array->size();
+
+ const size_t size = arrayLength(*array);
//----------------
// Get start offset
//----------------
- unsigned startoffset;
+
int start = fn.arg(0).to_int();
- if ( start < 0 ) start = array->size()+start; // start is negative, so +
means -abs()
- startoffset = clamp<int>(start, 0, origlen);
-#ifdef GNASH_DEBUG
- if ( startoffset != start )
- log_debug(_("Array.splice: start:%d became %u"), start, startoffset);
-#endif
+ if (start < 0) start = size + start;
+ start = clamp<int>(start, 0, size);
- //----------------
- // Get length
- //----------------
- unsigned len = origlen - start;
- if (fn.nargs > 1)
- {
- int lenval = fn.arg(1).to_int();
- if ( lenval < 0 )
- {
+ // Get length to delete
+ size_t remove = size - start;
+
+ if (fn.nargs > 1) {
+ int remval = fn.arg(1).to_int();
+ if (remval < 0) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("Array.splice(%d,%d): negative length given, call
ignored"),
- start, lenval);
+ log_aserror(_("Array.splice(%d,%d): negative length "
+ "given, call ignored"), start, remval);
);
return as_value();
}
- len = clamp<int>(lenval, 0, origlen-startoffset);
- }
-
- //----------------
- // Get replacement
- //----------------
- std::vector<as_value> replace;
- for (unsigned i=2; i<fn.nargs; ++i)
- {
- replace.push_back(fn.arg(i));
- }
-
- Array_as* ret = new Array_as();
- array->splice(startoffset, len, &replace, ret);
+ remove = clamp<int>(remval, 0, size - start);
+ }
+
+ Global_as* gl = getGlobal(fn);
+ as_object* ret = gl->createArray();
+
+ // Copy the original array values for reinsertion. It's not possible
+ // to do a simple copy in-place without overwriting values that still
+ // need to be shifted. The algorithm could certainly be improved though.
+ std::vector<as_value> v;
+ PushToContainer<std::vector<as_value> > pv(v);
+ foreachArray(*array, pv);
+
+ const size_t newelements = fn.nargs > 2 ? fn.nargs - 2 : 0;
+
+ // Push removed elements to the new array.
+ for (size_t i = 0; i < remove; ++i) {
+ const size_t key = getKey(fn, start + i);
+ ret->callMethod(NSV::PROP_PUSH, array->getMember(key));
+ }
+
+ // Shift elements in 'this' array by simple assignment, not delete
+ // and readd.
+ for (size_t i = 0; i < static_cast<size_t>(size - remove); ++i) {
+ const bool started = (i >= static_cast<size_t>(start));
+ const size_t index = started ? i + remove : i;
+ const size_t target = started ? i + newelements : i;
+ array->set_member(getKey(fn, target), v[index]);
+ }
+
+ // Insert the replacement elements in the gap we left.
+ for (size_t i = 0; i < newelements; ++i) {
+ array->set_member(getKey(fn, start + i), fn.arg(i + 2));
+ }
+
+ // This one is correct!
+ array->set_member(NSV::PROP_LENGTH, size + newelements - remove);
return as_value(ret);
}
-static as_value
+as_value
array_sort(const fn_call& fn)
{
- boost::intrusive_ptr<Array_as> array =
- ensureType<Array_as>(fn.this_ptr);
+ as_object* array = ensureType<as_object>(fn.this_ptr);
const int version = getSWFVersion(*array);
- if (!fn.nargs)
- {
- array->sort(as_value_lt(version));
- return as_value(array.get());
+ if (!fn.nargs) {
+ sort(*array, as_value_lt(version));
+ return as_value(array);
}
if (fn.arg(0).is_undefined()) return as_value();
boost::uint8_t flags = 0;
- if ( fn.nargs == 1 && fn.arg(0).is_number() )
- {
- flags=static_cast<boost::uint8_t>(fn.arg(0).to_number());
+ if (fn.nargs == 1 && fn.arg(0).is_number()) {
+ flags = static_cast<boost::uint8_t>(fn.arg(0).to_number());
}
- else if (fn.arg(0).is_function())
- {
-
+ else if (fn.arg(0).is_function()) {
// Get comparison function
as_function* as_func = fn.arg(0).to_as_function();
@@ -918,7 +1210,7 @@
flags=static_cast<boost::uint8_t>(fn.arg(1).to_number());
}
- if (flags & Array_as::fDescending) icmp = &int_lt_or_eq;
+ if (flags & SORT_DESCENDING) icmp = &int_lt_or_eq;
else icmp = &int_gt;
const as_environment& env = fn.env();
@@ -926,44 +1218,41 @@
as_value_custom avc =
as_value_custom(*as_func, icmp, fn.this_ptr, env);
- if ((flags & Array_as::fReturnIndexedArray))
- {
- return as_value(array->sort_indexed(avc));
+ if ((flags & SORT_RETURN_INDEX)) {
+ return sortIndexed(*array, avc);
}
- array->sort(avc);
- return as_value(array.get());
+ sort(*array, avc);
+ return as_value(array);
// note: custom AS function sorting apparently ignores the
// UniqueSort flag which is why it is also ignored here
}
else
{
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("Sort called with invalid arguments."));
+ log_aserror(_("Sort called with invalid arguments."));
)
- return as_value(array.get());
+ return as_value(array);
}
+
bool do_unique, do_index;
flags = flag_preprocess(flags, &do_unique, &do_index);
as_cmp_fn comp = get_basic_cmp(flags, version);
- if (do_unique)
- {
- as_cmp_fn eq =
- get_basic_eq(flags, version);
- if (do_index) return array->sort_indexed(comp, eq);
- return array->sort(comp, eq);
+ if (do_unique) {
+ as_cmp_fn eq = get_basic_eq(flags, version);
+ if (do_index) return sortIndexed(*array, comp, eq);
+ return sort(*array, comp, eq) ? as_value(array) : as_value(0.0);
}
- if (do_index) return as_value(array->sort_indexed(comp));
- array->sort(comp);
- return as_value(array.get());
+ if (do_index) return sortIndexed(*array, comp);
+ sort(*array, comp);
+ return as_value(array);
}
-static as_value
+as_value
array_sortOn(const fn_call& fn)
{
- boost::intrusive_ptr<Array_as> array =
- ensureType<Array_as>(fn.this_ptr);
+ as_object* array = ensureType<as_object>(fn.this_ptr);
bool do_unique = false, do_index = false;
boost::uint8_t flags = 0;
@@ -972,29 +1261,33 @@
string_table& st = getStringTable(fn);
// cases: sortOn("prop) and sortOn("prop", Array.FLAG)
- if ( fn.nargs > 0 && fn.arg(0).is_string() )
+ if (fn.nargs > 0 && fn.arg(0).is_string())
{
- string_table::key propField =
st.find(fn.arg(0).to_string_versioned(version));
+ string_table::key propField =
+ st.find(fn.arg(0).to_string_versioned(version));
- if ( fn.nargs > 1 && fn.arg(1).is_number() )
- {
+ if (fn.nargs > 1 && fn.arg(1).is_number()) {
flags = static_cast<boost::uint8_t>(fn.arg(1).to_number());
flags = flag_preprocess(flags, &do_unique, &do_index);
}
+
as_value_prop avc(propField, get_basic_cmp(flags, version),
*getGlobal(fn));
- if (do_unique)
- {
+
+ if (do_unique) {
as_value_prop ave(propField, get_basic_eq(flags, version),
*getGlobal(fn));
if (do_index)
- return array->sort_indexed(avc, ave);
- return array->sort(avc, ave);
- }
- if (do_index)
- return as_value(array->sort_indexed(avc));
- array->sort(avc);
- return as_value(array.get());
+ return sortIndexed(*array, avc, ave);
+ return sort(*array, avc, ave) ? as_value(array) : as_value(0.0);
+ }
+
+ if (do_index) {
+ return sortIndexed(*array, avc);
+ }
+
+ sort(*array, avc);
+ return as_value(array);
}
// case: sortOn(["prop1", "prop2"] ...)
@@ -1010,7 +1303,7 @@
for (Array_as::const_iterator it = props->begin();
it != props->end(); ++it)
{
- string_table::key s =
st.find(PROPNAME((*it).to_string_versioned(version)));
+ string_table::key s = st.find((*it).to_string_versioned(version));
prp.push_back(s);
}
@@ -1077,12 +1370,12 @@
if (do_unique)
{
as_value_multiprop_eq ave(prp, eq, *getGlobal(fn));
- if (do_index) return array->sort_indexed(avc, ave);
- return array->sort(avc, ave);
+ if (do_index) return sortIndexed(*array, avc, ave);
+ return sort(*array, avc, ave) ? as_value(array) : as_value(0.0);
}
- if (do_index) return as_value(array->sort_indexed(avc));
- array->sort(avc);
- return as_value(array.get());
+ if (do_index) return sortIndexed(*array, avc);
+ sort(*array, avc);
+ return as_value(array);
}
IF_VERBOSE_ASCODING_ERRORS(
@@ -1091,7 +1384,7 @@
if (fn.nargs == 0 )
return as_value();
- return as_value(array.get());
+ return as_value(array);
}
// Callback to push values to the back of an array
@@ -1104,11 +1397,7 @@
const size_t shift = fn.nargs;
- as_value length;
- if (!array->get_member(NSV::PROP_LENGTH, &length)) return as_value();
-
- const int size = length.to_int();
- if (size < 0) return as_value();
+ const size_t size = arrayLength(*array);
for (size_t i = 0; i < fn.nargs; ++i) {
array->set_member(getKey(fn, size + i), fn.arg(i));
@@ -1121,7 +1410,7 @@
}
// Callback to push values to the front of an array
-static as_value
+as_value
array_unshift(const fn_call& fn)
{
@@ -1131,11 +1420,7 @@
const size_t shift = fn.nargs;
- as_value length;
- if (!array->get_member(NSV::PROP_LENGTH, &length)) return as_value();
-
- const int size = length.to_int();
- if (size < 0) return as_value();
+ const size_t size = arrayLength(*array);
string_table& st = getStringTable(fn);
as_value ret = array->getMember(st.find("0"));
@@ -1159,16 +1444,13 @@
}
// Callback to pop a value from the back of an array
-static as_value
+as_value
array_pop(const fn_call& fn)
{
as_object* array = ensureType<as_object>(fn.this_ptr);
- as_value length;
- if (!array->get_member(NSV::PROP_LENGTH, &length)) return as_value();
-
- const int size = length.to_int();
+ const size_t size = arrayLength(*array);
if (size < 1) return as_value();
const string_table::key ind = getKey(fn, size - 1);
@@ -1182,16 +1464,12 @@
}
// Callback to pop a value from the front of an array
-static as_value
+as_value
array_shift(const fn_call& fn)
{
as_object* array = ensureType<as_object>(fn.this_ptr);
- as_value length;
- if (!array->get_member(NSV::PROP_LENGTH, &length)) return as_value();
-
- const int size = length.to_int();
-
+ const size_t size = arrayLength(*array);
// An array with no elements has nothing to return.
if (size < 1) return as_value();
@@ -1211,47 +1489,28 @@
}
// Callback to reverse the position of the elements in an array
-static as_value
+as_value
array_reverse(const fn_call& fn)
{
- boost::intrusive_ptr<Array_as> array = ensureType<Array_as>(fn.this_ptr);
-
- array->reverse();
-
- as_value rv(array.get());
-
- IF_VERBOSE_ACTION (
- log_action(_("called array reverse, result:%s, new array size:%d"),
- rv, array->size());
- );
- return rv;
-}
-
-as_value
-join(as_object* array, const std::string& separator)
-{
- as_value length;
- if (!array->get_member(NSV::PROP_LENGTH, &length)) return as_value("");
-
- const double size = length.to_int();
- if (size < 0) return as_value("");
-
- std::string s;
-
- string_table& st = getStringTable(*array);
- const int version = getSWFVersion(*array);
-
- for (size_t i = 0; i < size; ++i) {
- std::ostringstream os;
- os << i;
- if (i) s += separator;
- as_value el;
- array->get_member(st.find(os.str()), &el);
- s += el.to_string_versioned(version);
+ as_object* array = ensureType<as_object>(fn.this_ptr);
+
+ const size_t size = arrayLength(*array);
+ // An array with 0 or 1 elements has nothing to reverse.
+ if (size < 2) return as_value();
+
+ for (size_t i = 0; i < static_cast<size_t>(size) / 2; ++i) {
+ const string_table::key bottomkey = getKey(fn, i);
+ const string_table::key topkey = getKey(fn, size - i - 1);
+ const as_value top = array->getMember(topkey);
+ const as_value bottom = array->getMember(bottomkey);
+ array->delProperty(topkey);
+ array->delProperty(bottomkey);
+ array->set_member(bottomkey, top);
+ array->set_member(topkey, bottom);
}
- return as_value(s);
+
+ return array;
}
-
as_value
array_join(const fn_call& fn)
{
@@ -1272,17 +1531,6 @@
return join(array, ",");
}
-class PushToArray
-{
-public:
- PushToArray(as_object& obj) : _obj(obj) {}
- void operator()(const as_value& val) {
- _obj.callMethod(NSV::PROP_PUSH, val);
- }
-private:
- as_object& _obj;
-};
-
/// concatenates the elements specified in the parameters with
/// the elements in my_array, and creates a new array. If the
/// value parameters specify an array, the elements of that
@@ -1291,13 +1539,13 @@
as_value
array_concat(const fn_call& fn)
{
- boost::intrusive_ptr<Array_as> array = ensureType<Array_as>(fn.this_ptr);
+ as_object* array = ensureType<as_object>(fn.this_ptr);
- // use copy ctor
- Array_as* newarray = new Array_as();
+ Global_as* gl = getGlobal(fn);
+ as_object* newarray = gl->createArray();
PushToArray push(*newarray);
- if (!foreachArray(*array, push)) return as_value();
+ foreachArray(*array, push);
for (size_t i = 0; i < fn.nargs; ++i) {
@@ -1326,17 +1574,12 @@
// Callback to slice part of an array to a new array
// without changing the original
-static as_value
+as_value
array_slice(const fn_call& fn)
{
- boost::intrusive_ptr<Array_as> array = ensureType<Array_as>(fn.this_ptr);
-
- // start and end index of the part we're slicing
- int startindex, endindex;
- unsigned int arraysize = array->size();
-
- if (fn.nargs > 2)
- {
+ as_object* array = ensureType<as_object>(fn.this_ptr);
+
+ if (fn.nargs > 2) {
IF_VERBOSE_ASCODING_ERRORS(
log_aserror(_("More than 2 arguments to Array.slice, "
"and I don't know what to do with them. "
@@ -1344,314 +1587,125 @@
);
}
- // They passed no arguments: simply duplicate the array
- // and return the new one
- if (fn.nargs < 1)
- {
- Array_as* newarray = new Array_as(*array);
- return as_value(newarray);
- }
-
-
- startindex = fn.arg(0).to_int();
-
- // if the index is negative, it means "places from the end"
- // where -1 is the last element
- if (startindex < 0) startindex = startindex + arraysize;
+ int startindex = fn.nargs ? fn.arg(0).to_int() : 0;
// if we sent at least two arguments, setup endindex
- if (fn.nargs >= 2)
- {
- endindex = fn.arg(1).to_int();
-
- // if the index is negative, it means
- // "places from the end" where -1 is the last element
- if (endindex < 0) endindex = endindex + arraysize;
- }
- else
- {
- // They didn't specify where to end,
- // so choose the end of the array
- endindex = arraysize;
- }
-
- if ( startindex < 0 ) startindex = 0;
- else if ( static_cast<size_t>(startindex) > arraysize ) startindex =
arraysize;
-
- if ( endindex < startindex ) endindex = startindex;
- else if ( static_cast<size_t>(endindex) > arraysize ) endindex =
arraysize;
-
- boost::intrusive_ptr<Array_as> newarray(array->slice(
- startindex, endindex));
-
- return as_value(newarray.get());
-}
-
-static as_value
-array_length(const fn_call& fn)
-{
- boost::intrusive_ptr<Array_as> array = ensureType<Array_as>(fn.this_ptr);
-
- if ( fn.nargs ) // setter
- {
- int length = fn.arg(0).to_int();
- if ( length < 0 ) // TODO: set a max limit too ?
- {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Attempt to set Array.length to a negative value %d",
length);
- )
- length = 0;
- }
-
- array->resize(length);
- return as_value();
- }
- else // getter
- {
- return as_value(array->size());
- }
+ int endindex = fn.nargs > 1 ? fn.arg(1).to_int() :
+ std::numeric_limits<int>::max();
+
+ Global_as* gl = getGlobal(fn);
+ as_object* newarray = gl->createArray();
+
+ PushToArray push(*newarray);
+
+ foreachArray(*array, startindex, endindex, push);
+
+ return as_value(newarray);
}
as_value
array_new(const fn_call& fn)
{
IF_VERBOSE_ACTION (
- log_action(_("array_new called, nargs = %d"), fn.nargs);
+ log_action(_("array_new called, nargs = %d"), fn.nargs);
);
- boost::intrusive_ptr<Array_as> ao = new Array_as;
+ // TODO: use the this_ptr
+ as_object* ao = new Array_as;
- if (fn.nargs == 0)
- {
- // Empty array.
+ if (fn.nargs == 0) {
+ return ao;
}
- else if (fn.nargs == 1 && fn.arg(0).is_number() )
- {
- // TODO: limit max size !!
+
+ if (fn.nargs == 1 && fn.arg(0).is_number()) {
int newSize = fn.arg(0).to_int();
- if ( newSize < 0 ) newSize = 0;
- else ao->resize(newSize);
- }
- else
- {
- // Use the arguments as initializers.
- as_value index_number;
- for (unsigned int i = 0; i < fn.nargs; i++)
- {
- ao->callMethod(NSV::PROP_PUSH, fn.arg(i));
+ if (newSize < 0) newSize = 0;
+ else {
+ ao->set_member(NSV::PROP_LENGTH, newSize);
}
- }
-
- IF_VERBOSE_ACTION (
- log_action(_("array_new setting object %p in result"), (void*)ao.get());
- );
-
- return as_value(ao.get());
- //return as_value(ao);
-}
-
-static void
-attachArrayProperties(as_object& proto)
-{
- proto.init_property(NSV::PROP_LENGTH, &array_length, &array_length);
-}
-
-static void
-attachArrayStatics(as_object& proto)
-{
- int flags = 0; // these are not protected
- proto.init_member("CASEINSENSITIVE", Array_as::fCaseInsensitive, flags);
- proto.init_member("DESCENDING", Array_as::fDescending, flags);
- proto.init_member("UNIQUESORT", Array_as::fUniqueSort, flags);
- proto.init_member("RETURNINDEXEDARRAY", Array_as::fReturnIndexedArray,
flags);
- proto.init_member("NUMERIC", Array_as::fNumeric, flags);
-}
-
-static void
-attachArrayInterface(as_object& proto)
-{
- VM& vm = getVM(proto);
-
- proto.init_member("push", vm.getNative(252, 1));
- proto.init_member("pop", vm.getNative(252, 2));
- proto.init_member("concat", vm.getNative(252, 3));
- proto.init_member("shift", vm.getNative(252, 4));
- proto.init_member("unshift", vm.getNative(252, 5));
- proto.init_member("slice", vm.getNative(252, 6));
- proto.init_member("join", vm.getNative(252, 7));
- proto.init_member("splice", vm.getNative(252, 8));
- proto.init_member("toString", vm.getNative(252, 9));
- proto.init_member("sort", vm.getNative(252, 10));
- proto.init_member("reverse", vm.getNative(252, 11));
- proto.init_member("sortOn", vm.getNative(252, 12));
-}
-
-static as_object*
-getArrayInterface()
-{
- static boost::intrusive_ptr<as_object> proto = NULL;
- if ( proto == NULL )
- {
- proto = new as_object(getObjectInterface());
- getVM(*proto).addStatic(proto.get());
-
- attachArrayInterface(*proto);
- }
- return proto.get();
-}
-
-void
-registerArrayNative(as_object& global)
-{
- VM& vm = getVM(global);
- vm.registerNative(array_new, 252, 0);
- vm.registerNative(array_push, 252, 1);
- vm.registerNative(array_pop, 252, 2);
- vm.registerNative(array_concat, 252, 3);
- vm.registerNative(array_shift, 252, 4);
- vm.registerNative(array_unshift, 252, 5);
- vm.registerNative(array_slice, 252, 6);
- vm.registerNative(array_join, 252, 7);
- vm.registerNative(array_splice, 252, 8);
- vm.registerNative(array_toString, 252, 9);
- vm.registerNative(array_sort, 252, 10);
- vm.registerNative(array_reverse, 252, 11);
- vm.registerNative(array_sortOn, 252, 12);
-}
-
-// this registers the "Array" member on a "Global"
-// object. "Array" is a constructor, thus an object
-// with .prototype full of exported functions +
-// 'constructor'
-//
-void
-array_class_init(as_object& glob, const ObjectURI& uri)
-{
- // This is going to be the global Array "class"/"function"
- static as_object* ar = 0;
-
- if ( ar == NULL )
- {
- Global_as* gl = getGlobal(glob);
- as_object* proto = getArrayInterface();
- ar = gl->createClass(&array_new, proto);
-
- // Attach static members
- attachArrayStatics(*ar);
- }
-
- int flags = PropFlags::dontEnum; // |PropFlags::onlySWF5Up;
- glob.init_member(getName(uri), ar, flags, getNamespace(uri));
-}
-
-void
-Array_as::enumerateNonProperties(as_environment& env) const
-{
- std::stringstream ss;
- for (const_iterator it = elements.begin(),
- itEnd = elements.end(); it != itEnd; ++it)
- {
- int idx = it.index();
- // enumerated values need to be strings, not numbers
- ss.str(""); ss << idx;
- env.push(as_value(ss.str()));
- }
-}
-
-void
-Array_as::splice(unsigned int start, unsigned int count, const
std::vector<as_value>* replace, Array_as* receive)
-{
- size_t sz = elements.size();
-
- assert ( start <= sz );
- assert ( start+count <= sz );
-
- size_t newsize = sz-count;
- if ( replace ) newsize += replace->size();
- ArrayContainer newelements(newsize);
-
- size_t ni=0;
-
- // add initial portion
- for (size_t i=0; i<start; ++i )
- {
- newelements[ni++] = elements[i];
- }
-
- // add replacement, if any
- if ( replace )
- {
- for (size_t i=0, e=replace->size(); i<e; ++i)
- newelements[ni++] = replace->at(i);
- }
-
- // add final portion
- for (size_t i=start+count; i<sz; ++i )
- newelements[ni++] = elements[i];
-
- // push trimmed data to the copy array, if any
- if ( receive )
- {
- for (size_t i=start; i<start+count; ++i )
- receive->callMethod(NSV::PROP_PUSH, elements[i]);
- }
-
- elements = newelements;
-}
-
-#ifdef GNASH_USE_GC
-void
-Array_as::markReachableResources() const
-{
- for (const_iterator i=elements.begin(), e=elements.end(); i!=e; ++i)
- {
- (*i).setReachable();
- }
- markAsObjectReachable();
-}
-#endif // GNASH_USE_GC
-
-void
-Array_as::visitPropertyValues(AbstractPropertyVisitor& visitor) const
-{
- std::stringstream ss;
- string_table& st = getStringTable(*this);
- for (const_iterator i=elements.begin(), ie=elements.end(); i!=ie; ++i)
- {
- int idx = i.index();
- ss.str(""); ss << idx;
- string_table::key k = st.find(ss.str());
- visitor.accept(k, *i);
- }
-
- // visit proper properties
- as_object::visitPropertyValues(visitor);
-}
-
-void
-Array_as::visitNonHiddenPropertyValues(AbstractPropertyVisitor& visitor) const
-{
- std::stringstream ss;
- string_table& st = getStringTable(*this);
- for (const_iterator i=elements.begin(), ie=elements.end(); i!=ie; ++i)
- {
- // TODO: skip hidden ones
- int idx = i.index();
- ss.str(""); ss << idx;
- string_table::key k = st.find(ss.str());
- visitor.accept(k, *i);
- }
-
- // visit proper properties
- as_object::visitNonHiddenPropertyValues(visitor);
-}
-
-bool
-Array_as::isStrict() const
-{
- if ( hasNonHiddenProperties() ) return false;
- return true;
-}
+ return ao;
+ }
+
+ // Use the arguments as initializers.
+ for (size_t i = 0; i < fn.nargs; i++) {
+ ao->callMethod(NSV::PROP_PUSH, fn.arg(i));
+ }
+
+ return as_value(ao);
+}
+
+as_value
+join(as_object* array, const std::string& separator)
+{
+ const size_t size = arrayLength(*array);
+ as_value length;
+ if (size < 1) return as_value("");
+
+ std::string s;
+
+ string_table& st = getStringTable(*array);
+ const int version = getSWFVersion(*array);
+
+ for (size_t i = 0; i < size; ++i) {
+ std::ostringstream os;
+ os << i;
+ if (i) s += separator;
+ as_value el;
+ array->get_member(st.find(os.str()), &el);
+ s += el.to_string_versioned(version);
+ }
+ return as_value(s);
+}
+
+string_table::key
+getKey(const fn_call& fn, size_t i)
+{
+ string_table& st = getStringTable(fn);
+ return arrayKey(st, i);
+}
+
+template<typename T>
+void foreachArray(as_object& array, int start, int end, T& pred)
+{
+ const int size = arrayLength(array);
+ if (!size) return;
+
+ if (start < 0) start = size + start;
+ if (start >= size) return;
+ start = std::max(start, 0);
+
+ if (end < 0) end = size + end;
+ end = std::max(start, end);
+ end = std::min<size_t>(end, size);
+
+ assert(start >= 0);
+ assert(end >= start);
+ assert(size >= end);
+
+ string_table& st = getStringTable(array);
+
+ for (size_t i = start; i < static_cast<size_t>(end); ++i) {
+ pred(array.getMember(arrayKey(st, i)));
+ }
+}
+
+void
+pushIndices(as_object& o, const std::vector<indexed_as_value>& elems)
+{
+ for (std::vector<indexed_as_value>::const_iterator it = elems.begin();
+ it != elems.end(); ++it) {
+ o.callMethod(NSV::PROP_PUSH, it->vec_index);
+ }
+}
+
+void
+getIndexedElements(as_object& array, std::vector<indexed_as_value>& v)
+{
+ PushToIndexedVector pv(v);
+ foreachArray(array, pv);
+}
+
+} // anonymous namespace
} // end of gnash namespace
=== modified file 'libcore/asobj/Array_as.h'
--- a/libcore/asobj/Array_as.h 2009-10-14 16:12:54 +0000
+++ b/libcore/asobj/Array_as.h 2009-10-15 13:25:00 +0000
@@ -21,6 +21,7 @@
#include "as_object.h" // for inheritance
#include "smart_ptr.h" // GNASH_USE_GC
#include "namedStrings.h"
+#include "Global_as.h"
#include <deque>
#include <vector>
@@ -37,23 +38,8 @@
namespace gnash {
-struct indexed_as_value : public as_value
-{
- int vec_index;
-
- indexed_as_value(const as_value& val, int index)
- : as_value(val)
- {
- vec_index = index;
- }
-};
-
-template <class T>
-struct ContainerFiller {
- T& cont;
- ContainerFiller(T& c): cont(c) {}
- void visit(as_value& v) { cont.push_back(v); }
-};
+size_t arrayLength(as_object& array);
+string_table::key arrayKey(string_table& st, size_t i);
/// The Array ActionScript object
class Array_as : public as_object
@@ -68,57 +54,15 @@
typedef ArrayContainer::const_iterator const_iterator;
typedef ArrayContainer::iterator iterator;
- typedef std::list<as_value> ValueList;
-
- /// Visit all elements
- //
- /// The visitor class will have to expose a visit(as_value&) method
- ///
- template<class V> void visitAll(V& v)
- {
- // NOTE: we copy the elements as the visitor might call
arbitrary code
- // possibly modifying the container itself.
- ArrayContainer copy = elements;
-
- // iterating this way will skip holes
- for (Array_as::iterator i=copy.begin(), ie=copy.end(); i!=ie;
++i)
- v.visit(*i);
- }
-
// see dox in as_object.h
virtual void visitPropertyValues(AbstractPropertyVisitor& visitor)
const;
// see dox in as_object.h
virtual void visitNonHiddenPropertyValues(AbstractPropertyVisitor&
visitor) const;
- /// Sort flags
- enum SortFlags {
-
- /// Case-insensitive (z precedes A)
- fCaseInsensitive = (1<<0), // 1
-
- /// Descending order (b precedes a)
- fDescending = (1<<1), // 2
-
- /// If two or more elements in the array
- /// have identical sort fields, return 0
- /// and don't modify the array.
- /// Otherwise proceed to sort the array.
- fUniqueSort = (1<<2), // 4
-
- /// Don't modify the array, rather return
- /// a new array containing indexes into it
- /// in sorted order.
- fReturnIndexedArray = (1<<3), // 8
-
- /// Numerical sort (9 preceeds 10)
- fNumeric = (1<<4) // 16
- };
Array_as();
- Array_as(const Array_as& other);
-
~Array_as();
/// Return true if this is a strict array
@@ -130,203 +74,16 @@
///
bool isStrict() const;
- std::deque<indexed_as_value> get_indexed_elements();
-
Array_as::const_iterator begin();
Array_as::const_iterator end();
as_value at(unsigned int index) const;
- Array_as* get_indices(std::deque<indexed_as_value> origElems);
-
- void reverse();
-
- /// @param separator
- /// String to use as separator between elements
- std::string join(const std::string& separator) const;
-
unsigned int size() const;
void resize(unsigned int);
- /// \brief
- /// Return a newly created array containing elements
- /// from 'start' up to but not including 'end'.
- //
- ///
- /// NOTE: assertions are:
- ///
- /// assert(one_past_end >= start);
- /// assert(one_past_end <= size());
- /// assert(start <= size());
- ///
- /// @param start
- /// index to first element to include in result
- /// 0-based index.
- ///
- /// @param one_past_end
- /// index to one-past element to include in result
- /// 0-based index.
- ///
- boost::intrusive_ptr<Array_as> slice(
- unsigned int start, unsigned int one_past_end);
-
- /// \brief
- /// Replace count elements from start with given values, optionally
- /// returning the erased ones.
- //
- /// @param start
- /// First element to remove. Will abort if invalid.
- ///
- /// @param count
- /// Number of elements to remove. Will abort if > then available.
- ///
- /// @param replace
- /// If not null, use as a replacement for the cutted values
- ///
- /// @param copy
- /// If not null, an array to push cutted values to.
- ///
- void splice(unsigned int start, unsigned int count,
- const std::vector<as_value>* replace=NULL,
- Array_as* copy=NULL);
-
- /// \brief
- /// Sort the array, using given values comparator
- ///
- /// @param avc
- /// boolean functor or function comparing two as_value& objects
- ///
- template <class AVCMP>
- void sort(AVCMP avc)
- {
- // IMPORTANT NOTE
- //
- // As for ISO/IEC 14882:2003 - 23.2.2.4.29
- // the sort algorithm relies on the assumption
- // that the comparator function implements
- // a Strict Weak Ordering operator:
- // http://www.sgi.com/tech/stl/StrictWeakOrdering.html
- //
- // Invalid comparator can lead to undefined behaviour,
- // including invalid memory access and infinite loops.
- //
- // Pragmatically, it seems that std::list::sort is
- // more robust in this reguard, so we'll sort a list
- // instead of the queue. We want to sort a copy anyway
- // to avoid the comparator changing the original container.
- //
- ValueList nelem;
- ContainerFiller<ValueList> filler(nelem);
- visitAll(filler);
-
- size_t oldSize = elements.size(); // custom comparator might
change input size
- nelem.sort(avc);
- elements.resize(oldSize, false);
- size_t idx=0;
- for (ValueList::iterator i=nelem.begin(), e=nelem.end(); i!=e;
++i)
- {
- elements[idx++] = *i;
- }
- }
-
- /// \brief
- /// Attempt to sort the array using given values comparator, avc.
- /// If two or more elements in the array are equal, as determined
- /// by the equality comparator ave, then the array is not sorted
- /// and 0 is returned. Otherwise the array is sorted and returned.
- ///
- /// @param avc
- /// boolean functor or function comparing two as_value& objects
- /// used to determine sort-order
- ///
- /// @param ave
- /// boolean functor or function comparing two as_value& objects
- /// used to determine equality
- ///
- template <class AVCMP, class AVEQ>
- as_value sort(AVCMP avc, AVEQ ave)
- {
- // IMPORTANT NOTE
- //
- // As for ISO/IEC 14882:2003 - 23.2.2.4.29
- // the sort algorithm relies on the assumption
- // that the comparator function implements
- // a Strict Weak Ordering operator:
- // http://www.sgi.com/tech/stl/StrictWeakOrdering.html
- //
- // Invalid comparator can lead to undefined behaviour,
- // including invalid memory access and infinite loops.
- //
- // Pragmatically, it seems that std::list::sort is
- // more robust in this reguard, so we'll sort a list
- // instead of the queue. We want to sort a copy anyway
- // to avoid the comparator changing the original container.
- //
-
- typedef std::list<as_value> ValueList;
- ValueList nelem;
- ContainerFiller<ValueList> filler(nelem);
- visitAll(filler);
-
- size_t oldSize = elements.size(); // custom comparator might
change input size
-
- nelem.sort(avc);
-
- if (std::adjacent_find(nelem.begin(), nelem.end(), ave) !=
nelem.end() )
- return as_value(0.0);
-
- elements.resize(oldSize, false);
- size_t idx=0;
- for (ValueList::iterator i=nelem.begin(), e=nelem.end(); i!=e;
++i)
- {
- elements[idx++] = *i;
- }
-
- return as_value(this);
- }
-
- /// \brief
- /// Return a new array containing sorted index of this array
- ///
- /// @param avc
- /// boolean functor or function comparing two as_value& objects
- ///
- template <class AVCMP>
- Array_as* sort_indexed(AVCMP avc)
- {
- std::deque<indexed_as_value> ielem = get_indexed_elements();
- std::sort(ielem.begin(), ielem.end(), avc);
- return get_indices(ielem);
- }
-
- /// \brief
- /// Return a new array containing sorted index of this array.
- /// If two or more elements in the array are equal, as determined
- /// by the equality comparator ave, then 0 is returned instead.
- ///
- /// @param avc
- /// boolean functor or function comparing two as_value& objects
- /// used to determine sort-order
- ///
- /// @param ave
- /// boolean functor or function comparing two as_value& objects
- /// used to determine equality
- ///
- template <class AVCMP, class AVEQ>
- as_value sort_indexed(AVCMP avc, AVEQ ave)
- {
- std::deque<indexed_as_value> ielem = get_indexed_elements();
-
- std::sort(ielem.begin(), ielem.end(), avc);
-
- if (std::adjacent_find(ielem.begin(), ielem.end(), ave) !=
ielem.end() )
- return as_value(0.0);
-
- return get_indices(ielem);
- }
-
/// Why is this overridden?
virtual bool get_member(string_table::key name, as_value* val,
string_table::key nsname = 0);
@@ -370,23 +127,17 @@
};
-string_table::key arrayKey(string_table& st, size_t i);
-
template<typename T>
-bool foreachArray(as_object& array, T& pred)
+void foreachArray(as_object& array, T& pred)
{
- as_value length;
- if (!array.get_member(NSV::PROP_LENGTH, &length)) return false;
-
- const int size = length.to_int();
- if (size < 0) return false;
+ size_t size = arrayLength(array);
+ if (!size) return;
string_table& st = getStringTable(array);
for (size_t i = 0; i < static_cast<size_t>(size); ++i) {
pred(array.getMember(arrayKey(st, i)));
}
- return true;
}
/// Initialize the global.Array object
=== modified file 'libcore/asobj/AsBroadcaster.cpp'
--- a/libcore/asobj/AsBroadcaster.cpp 2009-10-14 14:31:24 +0000
+++ b/libcore/asobj/AsBroadcaster.cpp 2009-10-15 09:01:35 +0000
@@ -84,7 +84,7 @@
}
/// Call a method on the given value
- void visit(as_value& v)
+ void operator()(const as_value& v)
{
boost::intrusive_ptr<as_object> o = v.to_object(*getGlobal(_fn));
if ( ! o ) return;
@@ -126,6 +126,8 @@
// always attached to the initialized object.
as_value al, rl;
+ const int flags = as_object::DefaultFlags;
+
if (asb) {
al = asb->getMember(NSV::PROP_ADD_LISTENER);
rl = asb->getMember(NSV::PROP_REMOVE_LISTENER);
@@ -139,7 +141,15 @@
const as_value& asn = gl->callMethod(NSV::PROP_AS_NATIVE, 101, 12);
o.set_member(NSV::PROP_BROADCAST_MESSAGE, asn);
+ // This corresponds to "_listeners = [];", which is different from
+ // _listeners = new Array();
o.set_member(NSV::PROP_uLISTENERS, gl->createArray());
+
+ // This function should call ASSetPropFlags on these four properties.
+ o.set_member_flags(NSV::PROP_BROADCAST_MESSAGE, flags);
+ o.set_member_flags(NSV::PROP_ADD_LISTENER, flags);
+ o.set_member_flags(NSV::PROP_REMOVE_LISTENER, flags);
+ o.set_member_flags(NSV::PROP_uLISTENERS, flags);
}
@@ -303,67 +313,34 @@
return as_value(false); // TODO: check this
}
- boost::intrusive_ptr<as_object> listenersObj =
+ boost::intrusive_ptr<as_object> listeners =
listenersValue.to_object(*getGlobal(fn));
- assert(listenersObj);
-
- as_value listenerToRemove; assert(listenerToRemove.is_undefined());
- if ( fn.nargs ) listenerToRemove = fn.arg(0);
-
- boost::intrusive_ptr<Array_as> listeners =
- boost::dynamic_pointer_cast<Array_as>(listenersObj);
- if ( ! listeners )
- {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("%p.addListener(%s): this object's _listener isn't an "
- "array: %s"), (void*)fn.this_ptr, fn.dump_args(),
- listenersValue);
- );
-
- // TODO: implement brute force scan of pseudo-array
- unsigned int length =
- listenersObj->getMember(NSV::PROP_LENGTH).to_int();
-
- string_table& st = getStringTable(fn);
-
- for (unsigned int i=0; i<length; ++i)
- {
- as_value iVal(i);
- std::string n = iVal.to_string();
- as_value v = listenersObj->getMember(st.find(n));
- if ( v.equals(listenerToRemove) )
- {
- listenersObj->callMethod(NSV::PROP_SPLICE, iVal, as_value(1));
- return as_value(true);
- }
- }
-
- return as_value(false); // TODO: check this
- }
- else
- {
- // Remove the first listener matching the new value
- // See http://www.senocular.com/flash/tutorials/
- // listenersasbroadcaster/?page=2
-
- // This is an ActionScript-like implementation, which is why it looks
- // like poor C++.
- int length = listenersObj->getMember(NSV::PROP_LENGTH).to_int();
- int i = 0;
- string_table& st = getStringTable(fn);
- while (i < length) {
- std::ostringstream s;
- s << i;
- as_value el =
- listenersObj->getMember(st.find(s.str()));
- if (el.equals(listenerToRemove)) {
- listeners->callMethod(NSV::PROP_SPLICE, s.str(), 1);
- return as_value(true);
- }
- ++i;
- }
- return as_value(false);
- }
+ assert(listeners);
+
+ as_value listenerToRemove;
+ if (fn.nargs) listenerToRemove = fn.arg(0);
+
+ // Remove the first listener matching the new value
+ // See http://www.senocular.com/flash/tutorials/
+ // listenersasbroadcaster/?page=2
+
+ // This is an ActionScript-like implementation, which is why it looks
+ // like poor C++.
+ int length = listeners->getMember(NSV::PROP_LENGTH).to_int();
+ int i = 0;
+ string_table& st = getStringTable(fn);
+ while (i < length) {
+ std::ostringstream s;
+ s << i;
+ as_value el =
+ listeners->getMember(st.find(s.str()));
+ if (el.equals(listenerToRemove)) {
+ listeners->callMethod(NSV::PROP_SPLICE, s.str(), 1);
+ return as_value(true);
+ }
+ ++i;
+ }
+ return as_value(false);
}
@@ -400,21 +377,9 @@
return as_value(); // TODO: check this
}
- boost::intrusive_ptr<Array_as> listeners =
- dynamic_cast<Array_as*>(listenersValue.to_object(*getGlobal(fn)));
-
- if ( ! listeners )
- {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror(_("%p.addListener(%s): this object's _listener "
- "isn't an array: %s"), (void*)fn.this_ptr,
- fn.dump_args(), listenersValue);
- );
- return as_value(); // TODO: check this
- }
-
- if (!fn.nargs)
- {
+ as_object* listeners = listenersValue.to_object(*getGlobal(fn));
+
+ if (!fn.nargs) {
IF_VERBOSE_ASCODING_ERRORS(
log_aserror("%p.broadcastMessage() needs an argument",
(void*)fn.this_ptr);
@@ -423,11 +388,11 @@
}
BroadcasterVisitor visitor(fn);
- listeners->visitAll(visitor);
-
- unsigned int dispatched = visitor.eventsDispatched();
-
- if ( dispatched ) return as_value(true);
+ foreachArray(*listeners, visitor);
+
+ const size_t dispatched = visitor.eventsDispatched();
+
+ if (dispatched) return as_value(true);
return as_value();
=== modified file 'libcore/asobj/Global_as.h'
--- a/libcore/asobj/Global_as.h 2009-10-14 14:20:47 +0000
+++ b/libcore/asobj/Global_as.h 2009-10-15 09:01:14 +0000
@@ -85,9 +85,7 @@
/// Create an Array object
//
- /// This calls the Array constructor. If that has been changed, this
- /// function may not produce an Array object. This is generally
- /// expected behaviour.
+ /// This creates an Array object without calling the Array constructor.
virtual as_object* createArray() = 0;
/// Create an Object
=== modified file 'libcore/asobj/Globals.cpp'
--- a/libcore/asobj/Globals.cpp 2009-10-14 14:20:47 +0000
+++ b/libcore/asobj/Globals.cpp 2009-10-15 08:59:26 +0000
@@ -247,7 +247,9 @@
as_object*
AVM1Global::createArray()
{
- return new Array_as();
+ as_object* array = new Array_as;
+ array->init_member(NSV::PROP_CONSTRUCTOR, getMember(NSV::CLASS_ARRAY));
+ return array;
}
as_object*
@@ -307,7 +309,9 @@
as_object*
AVM2Global::createArray()
{
- return new Array_as();
+ as_object* array = new Array_as;
+ array->init_member(NSV::PROP_CONSTRUCTOR, getMember(NSV::CLASS_ARRAY));
+ return array;
}
void
=== modified file 'libcore/asobj/MovieClipLoader.cpp'
--- a/libcore/asobj/MovieClipLoader.cpp 2009-10-14 14:41:39 +0000
+++ b/libcore/asobj/MovieClipLoader.cpp 2009-10-14 18:37:43 +0000
@@ -39,7 +39,6 @@
#include "Object.h" // for getObjectInterface
#include "AsBroadcaster.h" // for initializing self as a broadcaster
#include "namedStrings.h"
-#include "Array_as.h" // for _listeners construction
#include "ExecutableCode.h"
#include <string>
=== modified file 'libcore/asobj/Selection_as.cpp'
--- a/libcore/asobj/Selection_as.cpp 2009-10-14 08:47:08 +0000
+++ b/libcore/asobj/Selection_as.cpp 2009-10-15 09:16:40 +0000
@@ -56,11 +56,17 @@
selection_class_init(as_object& where, const ObjectURI& uri)
{
// Selection is NOT a class, but a simple object, see Selection.as
- as_object* proto = registerBuiltinObject(where, attachSelectionInterface,
+ as_object* o = registerBuiltinObject(where, attachSelectionInterface,
uri);
/// Handles addListener, removeListener, and _listeners.
- AsBroadcaster::initialize(*proto);
+ AsBroadcaster::initialize(*o);
+
+ // All properties are protected using ASSetPropFlags.
+ string_table& st = getStringTable(where);
+ Global_as* gl = getGlobal(where);
+ as_object* null = 0;
+ gl->callMethod(st.find("ASSetPropFlags"), o, null, 7);
}
void
=== modified file 'libcore/asobj/TextFormat_as.cpp'
--- a/libcore/asobj/TextFormat_as.cpp 2009-10-14 08:47:08 +0000
+++ b/libcore/asobj/TextFormat_as.cpp 2009-10-15 07:30:20 +0000
@@ -280,41 +280,40 @@
return ret;
}
+class PushToVector
+{
+public:
+ PushToVector(std::vector<int>& v) : _v(v) {}
+ void operator()(const as_value& val) {
+ _v.push_back(val.to_number());
+ }
+private:
+ std::vector<int>& _v;
+};
+
as_value
textformat_tabStops(const fn_call& fn)
{
TextFormat_as* relay = ensureNativeType<TextFormat_as>(fn.this_ptr);
- as_value ret;
-
- if (!fn.nargs)
- {
- ret.set_null();
- return ret;
+ if (!fn.nargs) {
+ LOG_ONCE( log_unimpl("Getter for textformat_tabStops") );
+ as_value null;
+ null.set_null();
+ return null;
}
as_object* arg = fn.arg(0).to_object(*getGlobal(fn));
- Array_as* tStops = dynamic_cast<Array_as*>(arg);
-
- if (!tStops) return as_value();
-
- std::vector<int> tabStops(tStops->size());
-
- for (size_t i = 0; i !=tStops->size(); ++i)
- {
- tabStops[i]=tStops->at(i).to_number();
- }
-
- if ( fn.nargs == 0) // getter
- {
- LOG_ONCE( log_unimpl("Getter for textformat_tabStops") );
- }
- else // setter
- {
- relay->tabStopsSet(tabStops);
- }
+ if (!arg) return as_value();
+
+ std::vector<int> tabStops;
+
+ PushToVector pv(tabStops);
+ foreachArray(*arg, pv);
+
+ relay->tabStopsSet(tabStops);
- return ret;
+ return as_value();
}
as_value
=== modified file 'libcore/swf_function.cpp'
--- a/libcore/swf_function.cpp 2009-10-14 14:41:58 +0000
+++ b/libcore/swf_function.cpp 2009-10-15 09:00:54 +0000
@@ -17,7 +17,6 @@
#include "log.h"
#include "swf_function.h"
-#include "Array_as.h"
#include "fn_call.h"
#include "MovieClip.h"
#include "action_buffer.h"
@@ -364,22 +363,15 @@
getArguments(swf_function& callee, const fn_call& fn,
as_object* caller)
{
-#ifndef GNASH_USE_GC
- // We'll be storing the callee as_object into an as_value
- // so you must make sure you have a reference on it before
- // callign this function.
- assert(callee.get_ref_count() > 0);
-#endif // ndef GNASH_USE_GC
- // Super class prototype is : obj.__proto__.constructor.prototype
as_object* arguments = getGlobal(fn)->createArray();
- for (unsigned int i=0; i<fn.nargs; ++i) {
+ for (size_t i = 0; i < fn.nargs; ++i) {
arguments->callMethod(NSV::PROP_PUSH, fn.arg(i));
}
arguments->init_member(NSV::PROP_CALLEE, &callee);
- arguments->init_member(NSV::PROP_CALLER, as_value(caller));
+ arguments->init_member(NSV::PROP_CALLER, caller);
return arguments;
=== modified file 'libcore/swf_function.h'
--- a/libcore/swf_function.h 2009-10-14 14:41:58 +0000
+++ b/libcore/swf_function.h 2009-10-14 18:14:45 +0000
@@ -34,7 +34,6 @@
namespace gnash {
class action_buffer;
class as_environmnet;
- class Array_as;
}
namespace gnash {
=== modified file 'libcore/vm/ASHandlers.cpp'
--- a/libcore/vm/ASHandlers.cpp 2009-10-14 15:44:50 +0000
+++ b/libcore/vm/ASHandlers.cpp 2009-10-15 09:00:29 +0000
@@ -2807,11 +2807,12 @@
const int array_size = env.pop().to_int();
assert(array_size >= 0); // TODO: trigger this !!
-
- // Call the array constructor, to create an empty array.
- as_value result = getGlobal(env)->createArray();
-
- boost::intrusive_ptr<as_object> ao =
convertToObject(*getGlobal(thread.env), result);
+
+ Global_as* gl = getGlobal(env);
+
+ as_value result = gl->createArray();
+
+ as_object* ao = convertToObject(*getGlobal(thread.env), result);
assert(ao);
// Fill the elements with the initial values from the stack.
=== modified file 'testsuite/actionscript.all/Function.as'
--- a/testsuite/actionscript.all/Function.as 2009-02-25 22:33:03 +0000
+++ b/testsuite/actionscript.all/Function.as 2009-10-15 09:45:00 +0000
@@ -441,12 +441,12 @@
propRecorder.push(props.toString());
}
propRecorder.sort();
- xcheck_equals(propRecorder.length, 5);
+ check_equals(propRecorder.length, 5);
check_equals(propRecorder[0], '__proto__');
check_equals(propRecorder[1], 'callee');
check_equals(propRecorder[2], 'caller');
- xcheck_equals(propRecorder[3], 'constructor');
- xcheck_equals(propRecorder[4], 'length');
+ check_equals(propRecorder[3], 'constructor');
+ check_equals(propRecorder[4], 'length');
}
f();
=== modified file 'testsuite/actionscript.all/array.as'
--- a/testsuite/actionscript.all/array.as 2009-10-14 11:56:50 +0000
+++ b/testsuite/actionscript.all/array.as 2009-10-15 12:28:05 +0000
@@ -211,8 +211,8 @@
gaparray.sort();
check_equals(gaparray.length, 17);
#if OUTPUT_VERSION < 7
- xcheck_equals(gaparray[0], undefined); // this is 16 with gnash
- xcheck_equals(gaparray[1], undefined); // this is 4 with gnash
+ check_equals(gaparray[0], undefined); // this is 16 with gnash
+ check_equals(gaparray[1], undefined); // this is 4 with gnash
#else
check_equals(gaparray[0], '16');
check_equals(gaparray[1], '4');
@@ -231,8 +231,8 @@
check_equals(gaparray[13], undefined);
check_equals(gaparray[14], undefined);
#if OUTPUT_VERSION < 7
- xcheck_equals(gaparray[15], '16'); // this is at [0] with gnash
- xcheck_equals(gaparray[16], '4'); // this is at [1] with gnash
+ check_equals(gaparray[15], '16'); // this is at [0] with gnash
+ check_equals(gaparray[16], '4'); // this is at [1] with gnash
#else
check_equals(gaparray[15], undefined);
check_equals(gaparray[16], undefined);
@@ -240,16 +240,16 @@
#if OUTPUT_VERSION > 5
#if OUTPUT_VERSION < 7
- xcheck(gaparray.hasOwnProperty('15'));
- xcheck(gaparray.hasOwnProperty('16'));
- xcheck(gaparray.hasOwnProperty('4')); // a-ha!
+ check(gaparray.hasOwnProperty('15'));
+ check(gaparray.hasOwnProperty('16'));
+ check(gaparray.hasOwnProperty('4')); // a-ha!
xcheck(!gaparray.hasOwnProperty('0'));
#else
- xcheck(gaparray.hasOwnProperty('16'));
- xcheck(gaparray.hasOwnProperty('4'));
+ check(gaparray.hasOwnProperty('16'));
+ check(gaparray.hasOwnProperty('4'));
check(gaparray.hasOwnProperty('1'));
check(gaparray.hasOwnProperty('0'));
- xcheck(gaparray.hasOwnProperty('2'));
+ check(gaparray.hasOwnProperty('2'));
#endif
#endif
@@ -739,7 +739,7 @@
check_equals(c[0], 'zero');
c.length = -1;
// it seems Gnash needs to store the 'length' property as a normal property
-xcheck_equals(c.length, -1);
+check_equals(c.length, -1);
check_equals(c[0], undefined);
//-------------------------------
@@ -1519,6 +1519,96 @@
xcheck_equals(o.length, 6);
+o = fakeArray();
+o.reverse = Array.prototype.reverse;
+
+// Order of property creation.
+check_equals(traceProps(o), "reverse,length,7,6,5,4,3,2,1,");
+
+o.reverse();
+
+// Length is unchanged, Properties are swapped from the outside
+check_equals(traceProps(o), "3,2,4,1,5,0,reverse,length,7,6,");
+check_equals(o.length, 6);
+
+// Check with an uneven length to see what happens to the middle property.
+o = fakeArray();
+o.reverse = Array.prototype.reverse;
+o.length = 5;
+o.reverse();
+// The middle property is left alone...
+check_equals(traceProps(o), "3,1,4,0,reverse,length,7,6,5,2,");
+check_equals(o.length, 5);
+
+// Array.splice
+
+// This is different from other functions in that it doesn't delete and
+// readd properties so much.
+
+o = fakeArray();
+o.splice = Array.prototype.splice;
+
+// Order of property creation.
+check_equals(traceProps(o), "splice,length,7,6,5,4,3,2,1,");
+
+// Note: this function *does* set length!
+check_equals(o.length, 6);
+o.splice(2, 3, "new1", "new2");
+
+check_equals(traceProps(o), "0,splice,length,7,6,5,4,3,2,1,");
+check_equals(o.length, 5);
+
+// The spliced elements are there.
+check_equals(o[2], "new1");
+check_equals(o[3], "new2");
+
+// The new 0 element is undefined.
+check_equals(o[0], undefined);
+
+// The last element is shifted down (because 3 elements were deleted).
+check_equals(o[4], "five");
+
+// Same again with different arguments
+
+o = fakeArray();
+o.splice = Array.prototype.splice;
+
+// Order of property creation.
+check_equals(traceProps(o), "splice,length,7,6,5,4,3,2,1,");
+
+// Note: this function *does* set length!
+check_equals(o.length, 6);
+o.splice(2, 1, "new1", "new2", "new3", "new4");
+
+check_equals(traceProps(o), "8,0,splice,length,7,6,5,4,3,2,1,");
+check_equals(o.length, 9);
+
+// The spliced elements are there.
+check_equals(o[2], "new1");
+check_equals(o[3], "new2");
+check_equals(o[4], "new3");
+check_equals(o[5], "new4");
+
+// The new 0 element is undefined.
+check_equals(o[0], undefined);
+
+// Elements were shifted up.
+check_equals(o[6], "three");
+
+// Sort
+
+o = fakeArray();
+o.sort = Array.prototype.sort;
+
+// Order of property creation.
+check_equals(traceProps(o), "sort,length,7,6,5,4,3,2,1,");
+
+check_equals(o.length, 6);
+o.sort();
+xcheck_equals(traceProps(o), "5,4,2,1,0,sort,length,7,6,3,");
+o.sort();
+xcheck_equals(traceProps(o), "5,4,2,1,0,sort,length,7,6,3,");
+
// TODO: test ASnative-returned functions:
//
// ASnative(252, 1) - [Array.prototype] push
@@ -1537,11 +1627,11 @@
#if OUTPUT_VERSION < 6
- check_totals(511);
+ check_totals(538);
#else
# if OUTPUT_VERSION < 7
- check_totals(572);
+ check_totals(599);
# else
- check_totals(582);
+ check_totals(609);
# endif
#endif
=== modified file 'testsuite/swfdec/PASSING'
--- a/testsuite/swfdec/PASSING 2009-10-14 14:52:58 +0000
+++ b/testsuite/swfdec/PASSING 2009-10-15 12:29:22 +0000
@@ -37,6 +37,14 @@
and-or-5.swf:a734facbc191156759ad278e25c3f308
and-or-6.swf:a3a02811b62c07c4d2dd6d6d547f1d2f
and-or-7.swf:b4c3eb73aa8e0403e8d141516b25c9c6
+arguments-5.swf:2712ea54d1025d73ad6e795df93f903f
+arguments-6.swf:d4748be43bba30ec009054c66088b2d1
+arguments-7.swf:bd2a91045bf857425fc2a3fc76b01432
+arguments-8.swf:f5a0a73cb5edf43fb33addb29477dbc1
+arguments-properties-5.swf:b6cc55d99c634b93ba165ef3b260773e
+arguments-properties-6.swf:50791d04c6e61b979acd669d07cbf020
+arguments-properties-7.swf:702ef669c7c91b474a1148a4ca9e988d
+arguments-properties-8.swf:69c543519ab2cb034179121b7f334a3a
array-init.swf:dc3e0f1daa53a2574efdb75f98579f10
array-movieclip-5.swf:99c50528aa81f1ef5184d6ae35b14419
array-movieclip-6.swf:dcef7fcaf030813f6e53823231ce16e1
@@ -47,6 +55,10 @@
array-new-7.swf:77c4510eed0fd37d8654f761800edcce
array-new-8.swf:8b26fcd892b0cd7a1cd2832d3844d00d
array-new-override-5.swf:5016b51c60e30ee262b3cb022ba4c58f
+array-properties-5.swf:e4b9235bdad5543e29893d8166c16a9a
+array-properties-6.swf:822c1697e2f36bdfc87574a100583c78
+array-properties-7.swf:bd3778e753752345ae8c2d72b3ccaf3a
+array-properties-8.swf:0c1b9f40c4f736592b7673dbdd7ed7da
array-sort-custom-call-5.swf:3bfe136dfda4403b1984aae59fde2144
array-sort-custom-call-6.swf:d2517fbff896c1586947664ea20befa7
array-sort-custom-call-7.swf:6293fd0f9a5cc6498ebcbe3d90aeca69
@@ -73,6 +85,9 @@
asbroadcaster-override-7.swf:5834d9f0de106265f5ff479d789d2f35
asbroadcaster-override-8.swf:017c5505a342570726c90b81297626dc
asbroadcaster-properties-5.swf:577ad0de1cd33a97f6e2f146c61acc5f
+asbroadcaster-properties-6.swf:84104e706af43d2475ca3205bf29a0d7
+asbroadcaster-properties-7.swf:903c541124f61a03b0ca9dac94d58ad5
+asbroadcaster-properties-8.swf:f2505bf99e20b7ebc3c1f6299afb62bc
asnative-create-5.swf:d10ce975ae57f9a6e29c8fc97eb01489
asnative-create-6.swf:79aa26f566218edbdecdb46e88be7a1a
asnative-create-7.swf:b3f18c999ed081ac39132ec6a9df2cb0
@@ -233,6 +248,9 @@
constructor-prototype.swf:22505f0f8dd8440972a298110d697e3b
constructor-relay-5.swf:2d1a814c37f55485d66624cb97c58e03
construct-properties-5.swf:2683d3e400600e0288470ca5b6fbe94c
+construct-properties-6.swf:d8c0a2d334d05f749723a6838ed74846
+construct-properties-7.swf:de42399270c7f000cb4c1ce45a0cc960
+construct-properties-8.swf:854f7cddf97ab71b44b5f3bba3b193ef
context-menu-5.swf:867624e2cb2c3d47dc07270d756152db
context-menu-6.swf:9124c06f1cc4725684717eb3641837bd
context-menu-7.swf:fc22bea201998188714399c2de1ac1f5
@@ -517,6 +535,9 @@
function1.swf:56879c1d57617765e2ecf8c0a49047d8
function2.swf:021842f44ba2c3e5c7c13786c7cc88ea
function-apply-5.swf:4a8ae3ed3fe3e636c0aede2188dd9b92
+function-apply-6.swf:073f44454b9bb49166669972f4c42b07
+function-apply-7.swf:d66d3cd59b25291608b0d8de6d12d3f3
+function-apply-8.swf:1059cbf4b69d722259b0c40bad6d2b75
function-apply-crash-5.swf:cdaa243e5975d08e61781ad5632423f2
function-apply-crash-6.swf:d37c7e90d823363dcc4052167fcbb754
function-apply-crash-7.swf:28334f00875b4d137083a98c13fbab87
@@ -963,6 +984,11 @@
propflags-get-9.swf:56b0cf506962343f670d24c4ed64dedf
propflags-get-prototype-5.swf:7fa60f65a1c0eb1a2f52c4e8e982a91e
propflags-get-prototype-9.swf:713e3808411e4c89458fae8c082e208f
+propflags-set-5.swf:02158398265d21cd9affa7dfa79962fe
+propflags-set-6.swf:c56c07b9b4f13e7134317ad34c148087
+propflags-set-7.swf:6978b6af0019c9d63094ec7cb3b300f3
+propflags-set-8.swf:02e155d9e727aa14abed95ef53e4eee3
+propflags-set-9.swf:69ed1781166bfb3688e0bb16052a34eb
propflags-set-native-5.swf:a0c0652f4c7a1626e064ae0dd6a867a8
propflags-set-native-8.swf:b11c0be85a33c08d588897a99ed35f7f
propflags-set-proto-5.swf:d8cfcbffc8213a9eb60301a877f750e1
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] /srv/bzr/gnash/trunk r11564: Fake our fake array so it appears less fake.,
Benjamin Wolsey <=