gnash-commit
[Top][All Lists]
Advanced

[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


reply via email to

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