[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] /srv/bzr/gnash/trunk r11583: Add more tests for TextField
From: |
Benjamin Wolsey |
Subject: |
[Gnash-commit] /srv/bzr/gnash/trunk r11583: Add more tests for TextFields and their construction. |
Date: |
Thu, 22 Oct 2009 14:46:58 +0200 |
User-agent: |
Bazaar (1.16.1) |
------------------------------------------------------------
revno: 11583 [merge]
committer: Benjamin Wolsey <address@hidden>
branch nick: trunk
timestamp: Thu 2009-10-22 14:46:58 +0200
message:
Add more tests for TextFields and their construction.
Simplify and correct TextField construction, add notes on what's needed to
do it properly.
Minor fix to Array.
modified:
libcore/TextField.cpp
libcore/as_function.cpp
libcore/asobj/Array_as.cpp
testsuite/actionscript.all/Object.as
testsuite/actionscript.all/TextField.as
testsuite/actionscript.all/array.as
testsuite/swfdec/PASSING
=== modified file 'libcore/TextField.cpp'
--- a/libcore/TextField.cpp 2009-10-14 18:15:19 +0000
+++ b/libcore/TextField.cpp 2009-10-22 12:31:08 +0000
@@ -83,9 +83,9 @@
// Forward declarations
namespace {
- as_object* getTextFieldInterface(VM& vm);
void attachPrototypeProperties(as_object& proto);
void attachTextFieldStaticMembers(as_object& o);
+ void attachTextFieldInterface(as_object& o);
as_value textfield_createTextField(const fn_call& fn);
@@ -253,10 +253,12 @@
void
TextField::init()
{
-
- as_object* proto = getTextFieldInterface(getVM(*this));
- attachPrototypeProperties(*proto);
-
+ as_environment env(getVM(*this));
+ as_object* proto = env.find_object("_global.TextField.prototype");
+ if (proto) {
+ attachPrototypeProperties(*proto);
+ }
+
set_prototype(proto);
as_object* ar = getGlobal(*this)->createArray();
@@ -2333,17 +2335,20 @@
textfield_class_init(as_object& where, const ObjectURI& uri)
{
- VM& vm = getVM(where);
Global_as* gl = getGlobal(where);
- as_object* proto = getSWFVersion(where) < 6 ? 0 :
getTextFieldInterface(vm);
+ as_object* proto = gl->createObject();
as_object* cl = gl->createClass(&textfield_ctor, proto);
- // replicate static members to class, to be able to access
- // all methods as static functions
+ attachTextFieldInterface(*proto);
attachTextFieldStaticMembers(*cl);
where.init_member(getName(uri), cl, as_object::DefaultFlags,
getNamespace(uri));
+
+ // ASSetPropFlags is called on the TextField class.
+ string_table& st = getStringTable(where);
+ as_object* null = 0;
+ gl->callMethod(st.find("ASSetPropFlags"), cl, null, 131);
}
void
@@ -2903,7 +2908,10 @@
// Set textfield bounds
SWFRect bounds(0, 0, pixelsToTwips(width), pixelsToTwips(height));
- // Create an instance
+ // Tests in actionscript.all/TextField.as show that this function must:
+ // 1. Call "new _global.TextField()" (which takes care of
+ // assigning properties to the prototype).
+ // 2. Make that object into a TextField and put it on the display list.
DisplayObject* tf = new TextField(ptr.get(), bounds);
// Give name and mark as dynamic
@@ -3661,41 +3669,42 @@
}
-/// This is called for 'new TextField()' only
+/// This is called for 'new TextField()'
+//
+/// Note that MovieClip.createTextField must call this function (or anything
+/// that replaces it).
+//
+/// Tests in actionscript.all/TextField.as show that this constructor:
+/// 1. Adds properties to the prototype.
+/// 2. Removes array typing.
+/// 3. Removes any Relay.
+/// 4. Does not produce a DisplayObject.
+/// 5. Operates on a 'this' pointer that createTextField turns into a
+/// real TextField.
as_value
textfield_ctor(const fn_call& fn)
{
- VM& vm = getVM(fn);
-
- as_object* proto = getTextFieldInterface(vm);
-
- as_object* obj = 0;
-
- if (!isAS3(fn)) {
- // We should attach more properties to the prototype on first
- // instantiation.
- // TODO: this also attaches properties to the SWF5 prototype but makes
- // them invisible with prop flags. Is this correct?
- attachPrototypeProperties(*proto);
-
- obj = new as_object(proto);
- }
- else {
+ if (isAS3(fn)) {
SWFRect nullRect;
- obj = new TextField(0, nullRect);
- }
-
- return as_value(obj);
+ as_object* obj = new TextField(0, nullRect);
+ return as_value(obj);
+ }
+
+ as_object* obj = ensureType<as_object>(fn.this_ptr);
+ as_object* proto = obj->get_prototype();
+
+ if (proto) {
+ attachPrototypeProperties(*proto);
+ }
+
+ return as_value();
}
void
attachTextFieldInterface(as_object& o)
{
- // TextField is an AsBroadcaster
- AsBroadcaster::initialize(o);
-
// SWF6 or higher
const int swf6Flags = as_object::DefaultFlags | PropFlags::onlySWF6Up;
@@ -3712,7 +3721,15 @@
const int swf7Flags = as_object::DefaultFlags | PropFlags::onlySWF7Up;
o.init_member("replaceText",vm.getNative(104, 107), swf7Flags);
+
+ // TextField is an AsBroadcaster
+ AsBroadcaster::initialize(o);
+ // Finally ASSetPropFlags is called on the prototype.
+ string_table& st = getStringTable(o);
+ Global_as* gl = getGlobal(o);
+ as_object* null = 0;
+ gl->callMethod(st.find("ASSetPropFlags"), &o, null, 131);
}
void
@@ -3724,34 +3741,6 @@
o.init_member("getFontList", vm.getNative(104, 201), swf6Flags);
}
-/// This is called when a prototype should be added
-//
-/// @note This is called at different times, depending on the version.
-/// For SWF5 it is called only on first instantiation. For SWF6 it
-/// is called at the registration of _global.TextField.
-as_object*
-getTextFieldInterface(VM& vm)
-{
- static boost::intrusive_ptr<as_object> proto;
-
- if ( proto == NULL )
- {
- if (vm.getSWFVersion() < 6) {
- /// The prototype for SWF5 is a simple as_object without
- /// toString() or valueOf().
- proto = new as_object();
- vm.addStatic(proto.get());
- }
- else {
- proto = new as_object(getObjectInterface());
- vm.addStatic(proto.get());
- attachTextFieldInterface(*proto);
- }
-
- }
- return proto.get();
-}
-
} // anonymous namespace
} // namespace gnash
=== modified file 'libcore/as_function.cpp'
--- a/libcore/as_function.cpp 2009-10-21 12:02:12 +0000
+++ b/libcore/as_function.cpp 2009-10-22 09:26:18 +0000
@@ -132,13 +132,14 @@
int swfversion = getSWFVersion(env);
- as_value proto;
- bool has_proto = get_member(NSV::PROP_PROTOTYPE, &proto);
+ Property* proto = getOwnProperty(NSV::PROP_PROTOTYPE);
// Create an empty object, with a ref to the constructor's prototype.
- // TODO: The prototype should not be converted to an object!
+ // The function's prototype property always becomes the new object's
+ // __proto__ member, regardless of whether it is an object and regardless
+ // of its visibility.
as_object* newobj = new as_object();
- if (has_proto) newobj->set_prototype(proto);
+ if (proto) newobj->set_prototype(proto->getValue(*this));
// Add a __constructor__ member to the new object, but only for SWF6 up
// (to be checked). NOTE that we assume the builtin constructors
=== modified file 'libcore/asobj/Array_as.cpp'
--- a/libcore/asobj/Array_as.cpp 2009-10-21 13:15:42 +0000
+++ b/libcore/asobj/Array_as.cpp 2009-10-22 07:14:33 +0000
@@ -228,7 +228,8 @@
template <class AVCMP>
-void sort(as_object& o, AVCMP avc)
+void
+sort(as_object& o, AVCMP avc)
{
typedef std::list<as_value> SortContainer;
@@ -293,7 +294,8 @@
/// boolean functor or function comparing two as_value& objects
///
template <class AVCMP>
-as_object* sortIndexed(as_object& array, AVCMP avc)
+as_object*
+sortIndexed(as_object& array, AVCMP avc)
{
std::vector<indexed_as_value> v;
getIndexedElements(array, v);
@@ -672,23 +674,27 @@
bool operator() (const as_value& a, const as_value& b)
{
- if ( _cmps.empty() ) return false;
+ if (_cmps.empty()) return false;
std::vector<as_cmp_fn>::iterator cmp = _cmps.begin();
// why do we cast ao/bo to objects here ?
- boost::intrusive_ptr<as_object> ao = a.to_object(*getGlobal(_obj));
- boost::intrusive_ptr<as_object> bo = b.to_object(*getGlobal(_obj));
+ as_object* ao = a.to_object(*getGlobal(_obj));
+ as_object* bo = b.to_object(*getGlobal(_obj));
+
+ // TODO: this may not be correct, but it is better than accessing
+ // null pointers.
+ if (!ao || !bo) return false;
- for (Props::iterator pit = _prps.begin(), pend = _prps.end(); pit !=
pend; ++pit, ++cmp)
- {
+ for (Props::iterator pit = _prps.begin(), pend = _prps.end();
+ pit != pend; ++pit, ++cmp) {
as_value av, bv;
ao->get_member(*pit, &av);
bo->get_member(*pit, &bv);
- if ( (*cmp)(av, bv) ) return true;
- if ( (*cmp)(bv, av) ) return false;
+ if ((*cmp)(av, bv)) return true;
+ if ((*cmp)(bv, av)) return false;
// Note: for loop finishes only if a == b for
// each requested comparison
// (since *cmp(av,bv) == *cmp(bv,av) == false)
@@ -968,8 +974,9 @@
// 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);
+ typedef std::vector<as_value> TempContainer;
+ TempContainer v;
+ PushToContainer<TempContainer> pv(v);
foreachArray(*array, pv);
const size_t newelements = fn.nargs > 2 ? fn.nargs - 2 : 0;
@@ -1078,7 +1085,7 @@
bool do_unique = false, do_index = false;
boost::uint8_t flags = 0;
- int version = getSWFVersion(fn);
+ const int version = getSWFVersion(fn);
string_table& st = getStringTable(fn);
if (fn.nargs == 0) return as_value();
@@ -1113,7 +1120,6 @@
return as_value(array);
}
-#if 1
// case: sortOn(["prop1", "prop2"] ...)
if (fn.arg(0).is_object())
{
@@ -1141,7 +1147,8 @@
as_object* farray = fn.arg(1).to_object(*getGlobal(fn));
- if (arrayLength(*farray) == optnum) {
+ // Only an array will do for this case.
+ if (farray->array() && arrayLength(*farray) == optnum) {
std::vector<boost::uint8_t> flgs;
GetMultiFlags mf(flgs);
@@ -1168,10 +1175,9 @@
}
}
// case: sortOn(["prop1", "prop2"], Array.FLAG)
- else if (fn.arg(1).is_number())
- {
+ else {
boost::uint8_t flags =
- static_cast<boost::uint8_t>(fn.arg(1).to_number());
+ static_cast<boost::uint8_t>(fn.arg(1).to_int());
flags = flag_preprocess(flags, &do_unique, &do_index);
as_cmp_fn c = get_basic_cmp(flags, version);
@@ -1184,8 +1190,7 @@
}
as_value_multiprop avc(prp, cmp, *getGlobal(fn));
- if (do_unique)
- {
+ if (do_unique) {
as_value_multiprop_eq ave(prp, eq, *getGlobal(fn));
if (do_index) return sortIndexed(*array, avc, ave);
return sort(*array, avc, ave) ? as_value(array) : as_value(0.0);
@@ -1195,7 +1200,7 @@
return as_value(array);
}
-#endif
+
IF_VERBOSE_ASCODING_ERRORS(
log_aserror(_("SortOn called with invalid arguments."));
)
=== modified file 'testsuite/actionscript.all/Object.as'
--- a/testsuite/actionscript.all/Object.as 2009-10-08 09:18:08 +0000
+++ b/testsuite/actionscript.all/Object.as 2009-10-22 10:02:18 +0000
@@ -931,6 +931,25 @@
check_equals(result, "udef");
check_equals(resolveCalled, 4);
+/// Check __proto__ and prototype during construction
+
+TestO = function() {};
+TestO.prototype = {};
+TestO.prototype.toString = function() { return "hello there"; };
+
+// Set to invisible in all versions up to 9.
+ASSetPropFlags(TestO, null, 8193);
+check_equals(TestO.prototype, undefined);
+
+o = new TestO();
+
+/// prototype became __proto__
+check_equals(typeof(o.__proto__), "object");
+check_equals(o.toString(), "hello there");
+
+/// prototye is still invisible
+check_equals(TestO.prototype, undefined);
+
/// Check DisplayObject property lookup.
/// Apparently no DisplayObjects are included in the prototype
@@ -987,10 +1006,10 @@
check_equals(typeof(o), "undefined");
#if OUTPUT_VERSION <= 5
-totals(130);
+totals(134);
#endif
#if OUTPUT_VERSION >= 6
-totals(318);
+totals(322);
#endif
=== modified file 'testsuite/actionscript.all/TextField.as'
--- a/testsuite/actionscript.all/TextField.as 2009-10-13 07:31:47 +0000
+++ b/testsuite/actionscript.all/TextField.as 2009-10-22 11:45:14 +0000
@@ -1141,16 +1141,101 @@
_root._xscale = _root._yscale = 100;
+// Check that "new _global.TextField()" is called in createTextField
+
+backup = _global.TextField;
+count = 0;
+args = 0;
+
+storedthis = undefined;
+
+_global.TextField = function() {
+ storedthis = this;
+ args = arguments.length;
+ count++;
+};
+
+TextField.prototype = {};
+TextField.prototype.toString = function() { return "Hoppla!"; };
+
+// The fact that createTextField works even when _global.TextField is
+// replaced shows that the native functions (making into a real TextField)
+// is done in createTextField.
+r = _root.createTextField("tfmo", 2, 2, 10, 10, 6);
+xcheck_equals(count, 1);
+check_equals(args, 0);
+check_equals(_root.tfmo._x, 2);
+
+/// The returned object is still the this pointer that our fake constructor
+/// worked on.
+xcheck_equals(_root.tfmo, storedthis);
+xcheck(_root.tfmo === storedthis);
+
+// Not sure why this isn't the case for version 6 or 7.
+#if OUTPUT_VERSION >= 8
+check_equals(r.toString(), "Hoppla!");
+#else
+check_equals(r.toString(), undefined);
+#endif
+
+_global.TextField = backup;
+
+// So if createTextField calls the TextField ctor, what does that constructor
+// do?
+
+// This only confirms that the TextField constructor a) removes the
+// array typing, b) removes the relay, and c) doesn't produce a
+// DisplayObject. We still don't have a way to check what happens
+// inside createTextField.
+
+CTF = function () {
+
+ // We are called with 'new'.
+ fun = ASnative(2, 0);
+ xcheck_equals(fun(), true);
+
+ backup = this;
+ this.__proto__.__constructor__ = Array;
+ super ();
+ check_equals(this.length, 0);
+
+ // It's not a proper TextField.
+ this.__proto__.__constructor__ = TextField;
+ super ();
+ check_equals(backup, this);
+ check_equals(this.length, 0);
+ check_equals(this._x, undefined);
+ check_equals(this._visible, undefined);
+ check_equals(this._width, undefined);
+
+ // It is no longer an array.
+ this[2] = 3;
+ xcheck_equals(this.length, 0);
+ check_equals(this[2], 3);
+
+ this.__proto__.__constructor__ = Date;
+ this.getTime = Date.prototype.getTime;
+ super();
+ check_equals(typeof(this.getTime()), "number");
+
+ this.__proto__.__constructor__ = TextField;
+ super();
+ xcheck_equals(typeof(this.getTime()), "undefined");
+
+};
+
+o = new CTF();
+
//------------------------------------------------------------
// END OF TESTS
//------------------------------------------------------------
#if OUTPUT_VERSION == 6
- check_totals(502);
+ check_totals(519);
#elif OUTPUT_VERSION == 7
- check_totals(508);
+ check_totals(525);
#elif OUTPUT_VERSION == 8
- check_totals(509);
+ check_totals(526);
#endif
#endif
=== modified file 'testsuite/actionscript.all/array.as'
--- a/testsuite/actionscript.all/array.as 2009-10-21 12:43:52 +0000
+++ b/testsuite/actionscript.all/array.as 2009-10-22 10:50:48 +0000
@@ -1619,6 +1619,8 @@
#if OUTPUT_VERSION > 5
+Empty = function() {};
+
/// This checks that Array and relay objects are not compatible (unlike
/// DisplayObjects).
CA = function () {
@@ -1637,6 +1639,24 @@
check_equals(this.length, 3);
this[6] = 3;
check_equals(this.length, 3);
+
+
+ // Make into an array again.
+ this.__proto__.__constructor__ = Array;
+ super ();
+ check_equals(backup, this);
+ check_equals(this.length, 0);
+ this[2] = 3;
+ check_equals(this.length, 3);
+
+ // Array typing is not removed in all constructors.
+ this.__proto__.__constructor__ = Empty;
+ super();
+ check_equals(backup, this);
+ check_equals(this.length, 3);
+ this[5] = 77;
+ check_equals(this.length, 6);
+
};
o = new CA();
@@ -1705,8 +1725,8 @@
check_totals(538);
#else
# if OUTPUT_VERSION < 7
- check_totals(616);
+ check_totals(622);
# else
- check_totals(626);
+ check_totals(632);
# endif
#endif
=== modified file 'testsuite/swfdec/PASSING'
--- a/testsuite/swfdec/PASSING 2009-10-21 13:17:14 +0000
+++ b/testsuite/swfdec/PASSING 2009-10-22 11:45:49 +0000
@@ -71,6 +71,10 @@
array-sort-on-6.swf:158e931ebd968351b6a4d3d8ca30b516
array-sort-on-7.swf:d2ad844a1b5ebe6c65aae075a8273370
array-sort-on-8.swf:1bed1adba459d3808c64115664767ed3
+array-sort-on-options-5.swf:996b7e65970c35c4aace5d650f12f383
+array-sort-on-options-6.swf:d1e7ba53a8d61b877c325a56fba90a8e
+array-sort-on-options-7.swf:bff70155882c312e985927b0ee5c0cd5
+array-sort-on-options-8.swf:24f55c0c4bf8ad1f297614f457c718ca
array-splice-5.swf:b97cbb0e3bc00703ebaec5c379cb86e7
array-splice-6.swf:8dfba74ba51ae2249fe201959e6c820e
array-splice-7.swf:c00aeda368c996f939f3409bfc5eeef3
@@ -1344,6 +1348,9 @@
text-field-hscroll-8.swf:7f0fb367212b71f3e52076fd743c886b
text-field-html-new-format-5.swf:478143758da9587b72ec18b712b37acb
text-field-init-5.swf:f9c095838e41c5b00de362cef6c20167
+text-field-init-6.swf:f11af63055bb6c9dab80338945abdf27
+text-field-init-7.swf:a593d69ef50883b89ee647d388c7b2c1
+text-field-init-8.swf:5791617ebd27f3750a18c027dae9a030
text-field-init-native-5.swf:881f1f6314617bd2d789fa99f9765e60
text-field-init-native-6.swf:39c6fb4139696e3334b64fb4e48376e1
text-field-init-native-7.swf:167277605ec309a453caa1b713afbc45
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] /srv/bzr/gnash/trunk r11583: Add more tests for TextFields and their construction.,
Benjamin Wolsey <=