gnash-commit
[Top][All Lists]
Advanced

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

[Gnash-commit] gnash ChangeLog server/as_object.cpp server/as_...


From: Sandro Santilli
Subject: [Gnash-commit] gnash ChangeLog server/as_object.cpp server/as_...
Date: Sat, 05 Apr 2008 10:19:28 +0000

CVSROOT:        /sources/gnash
Module name:    gnash
Changes by:     Sandro Santilli <strk>  08/04/05 10:19:27

Modified files:
        .              : ChangeLog 
        server         : as_object.cpp as_object.h 
        server/asobj   : Object.cpp 
        testsuite/actionscript.all: Object.as 
        testsuite/swfdec: PASSING 

Log message:
                * server/as_object.{cpp,h}: add initial support for
                  property modify/create triggers (watches).
                * server/asobj/Object.cpp: implement Object.watch/unwatch.
                * testsuite/actionscript.all/Object.as: watch/unwatch successes
                  (not complete yet).
                * testsuite/swfdec/PASSING: object-watch-segv-*.swf all pass 
now.

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/ChangeLog?cvsroot=gnash&r1=1.6186&r2=1.6187
http://cvs.savannah.gnu.org/viewcvs/gnash/server/as_object.cpp?cvsroot=gnash&r1=1.111&r2=1.112
http://cvs.savannah.gnu.org/viewcvs/gnash/server/as_object.h?cvsroot=gnash&r1=1.102&r2=1.103
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/Object.cpp?cvsroot=gnash&r1=1.50&r2=1.51
http://cvs.savannah.gnu.org/viewcvs/gnash/testsuite/actionscript.all/Object.as?cvsroot=gnash&r1=1.55&r2=1.56
http://cvs.savannah.gnu.org/viewcvs/gnash/testsuite/swfdec/PASSING?cvsroot=gnash&r1=1.120&r2=1.121

Patches:
Index: ChangeLog
===================================================================
RCS file: /sources/gnash/gnash/ChangeLog,v
retrieving revision 1.6186
retrieving revision 1.6187
diff -u -b -r1.6186 -r1.6187
--- ChangeLog   5 Apr 2008 10:10:03 -0000       1.6186
+++ ChangeLog   5 Apr 2008 10:19:24 -0000       1.6187
@@ -1,5 +1,14 @@
 2008-04-04 Sandro Santilli <address@hidden>
 
+       * server/as_object.{cpp,h}: add initial support for
+         property modify/create triggers (watches).
+       * server/asobj/Object.cpp: implement Object.watch/unwatch.
+       * testsuite/actionscript.all/Object.as: watch/unwatch successes
+         (not complete yet).
+       * testsuite/swfdec/PASSING: object-watch-segv-*.swf all pass now.
+
+2008-04-04 Sandro Santilli <address@hidden>
+
        * testsuite/actionscript.all/Object.as: add test for "underlying
          value" for getter-setter, add tests for watch/unwatch.
 

Index: server/as_object.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/as_object.cpp,v
retrieving revision 1.111
retrieving revision 1.112
diff -u -b -r1.111 -r1.112
--- server/as_object.cpp        3 Apr 2008 16:00:47 -0000       1.111
+++ server/as_object.cpp        5 Apr 2008 10:19:25 -0000       1.112
@@ -159,7 +159,12 @@
                as_function* setter)
 {
        string_table &st = _vm.getStringTable();
-       return _members.addGetterSetter(st.find(PROPNAME(name)), getter, 
setter);
+       string_table::key k = st.find(name);
+
+       // TODO: if the named property already exist, store it's value
+       //       into the getter-setter as "underlying value"
+
+       return _members.addGetterSetter(k, getter, setter);
 }
 
 bool
@@ -492,6 +497,7 @@
        static string_table::key key = NSV::PROP_uuPROTOuu;
 
        // TODO: check what happens if __proto__ is set as a user-defined 
getter/setter
+       // TODO: check triggers !!
        _members.setValue(key, as_value(proto.get()), *this, 0, flags);
 }
 
@@ -521,7 +527,37 @@
 
                try
                {
+                       // check if we have a trigger, if so, invoke it
+                       // and set val to it's return
+                       TriggerContainer::iterator trigIter = 
_trigs.find(std::make_pair(key, nsname));
+                       if ( trigIter != _trigs.end() )
+                       {
+                               Trigger& trig = trigIter->second;
+
+                               // WARNING: getValue might itself invoke a 
trigger
+                               // (getter-setter)... ouch ?
+                               // TODO: in this case, return the underlying 
value !
+                               as_value curVal = prop->getValue(*this); 
+
+                               log_debug("Property %s is being watched, 
current val: %s", _vm.getStringTable().value(key), curVal.to_debug_string());
+                               as_value newVal = trig.call(curVal, val, *this);
+                               // The trigger call could have deleted the 
property,
+                               // so we check for its existance again, and do 
NOT put
+                               // it back in if it was deleted
+                               prop = findUpdatableProperty(key, nsname);
+                               if ( ! prop )
+                               {
+                                       log_debug("Property %s deleted by 
trigger on update", _vm.getStringTable().value(key));
+                                       return;
+                               }
+                               prop->setValue(*this, newVal);
+                       }
+                       else
+                       {
+                               // log_debug("No trigger for key %d ns %d", 
key, nsname);
                        prop->setValue(*this, val);
+                       }
+
                        prop->clearVisible(_vm.getSWFVersion());
                        return;
                }
@@ -543,7 +579,36 @@
                        log_aserror(_("Unknown failure in setting property '%s' 
on "
                        "object '%p'"), _vm.getStringTable().value(key).c_str(),
                        (void*) this););
+               return;
+       }
+
+       // Now check if we have a trigger, if so, invoke it
+       // and reset val to it's return
+       // NOTE that we do this *after* setting it in first place 
+       // as the trigger seems allowed to delete the property again
+       TriggerContainer::iterator trigIter = _trigs.find(std::make_pair(key, 
nsname));
+       if ( trigIter != _trigs.end() )
+       {
+               Trigger& trig = trigIter->second;
+
+               log_debug("Property %s is being watched, calling trigger on 
create", _vm.getStringTable().value(key));
+
+               // NOTE: the trigger call might delete the propery being added
+               //       so we first add the property, then call the trigger
+               //       and finally check if the property still exists 
(ufff...)
+               //
+
+               as_value curVal; // undefined, didn't exist...
+               as_value newVal = trig.call(curVal, val, *this);
+               Property* prop = _members.getProperty(key);
+               if ( ! prop )
+               {
+                       log_debug("Property %s deleted by trigger on create", 
_vm.getStringTable().value(key));
+                       return;
+               }
+               prop->setValue(*this, newVal);
        }
+
 }
 
 std::pair<bool,bool>
@@ -566,7 +631,29 @@
 
                try
                {
-                       prop->setValue(*this, val);
+                       as_value newVal = val;
+
+                       // check if we have a trigger, if so, invoke it
+                       // and set val to it's return
+                       TriggerContainer::iterator trigIter = 
_trigs.find(std::make_pair(key, nsname));
+                       if ( trigIter != _trigs.end() )
+                       {
+                               Trigger& trig = trigIter->second;
+                               // WARNING: getValue might itself invoke a 
trigger (getter-setter)... ouch ?
+                               as_value curVal = prop->getValue(*this); 
+                               log_debug("Property %s is being watched, 
current val: %s", _vm.getStringTable().value(key), curVal.to_debug_string());
+                               newVal = trig.call(curVal, val, *this);
+                               // The trigger call could have deleted the 
property,
+                               // so we check for its existance again, and do 
NOT put
+                               // it back in if it was deleted
+                               prop = findUpdatableProperty(key, nsname);
+                               if ( ! prop )
+                               {
+                                       return std::make_pair(true, true);
+                               }
+                       }
+
+                       prop->setValue(*this, newVal);
                        return std::make_pair(true, true);
                }
                catch (ActionException& exc)
@@ -1257,5 +1344,91 @@
     
 }
 
+bool
+as_object::watch(string_table::key key, as_function& trig,
+               const as_value& cust, string_table::key ns)
+{
+       
+       FQkey k = std::make_pair(key, ns);
+       std::string propname = VM::get().getStringTable().value(key);
+
+       TriggerContainer::iterator it = _trigs.find(k);
+       if ( it == _trigs.end() )
+       {
+               return _trigs.insert(std::make_pair(k, Trigger(propname, trig, 
cust))).second;
+       }
+       it->second = Trigger(propname, trig, cust);
+       return true;
+}
+
+bool
+as_object::unwatch(string_table::key key, string_table::key ns)
+{
+       TriggerContainer::iterator trigIter = _trigs.find(std::make_pair(key, 
ns));
+       if ( trigIter == _trigs.end() ) return false;
+       _trigs.erase(trigIter);
+       return true;
+}
+
+#ifdef GNASH_USE_GC
+void
+as_object::markAsObjectReachable() const
+{
+       _members.setReachable();
+
+       for (TriggerContainer::const_iterator it = _trigs.begin();
+                       it != _trigs.end(); ++it)
+       {
+               it->second.setReachable();
+       }
+}
+#endif // GNASH_USE_GC
+
+void
+Trigger::setReachable() const
+{
+       _func->setReachable();
+       _customArg.setReachable();
+}
+
+as_value
+Trigger::call(const as_value& oldval, const as_value& newval, as_object& 
this_obj)
+{
+       if ( _executing ) return newval;
+
+       _executing = true;
+
+       try {
+               as_environment env;
+
+#ifndef NDEBUG
+               size_t origStackSize = env.stack_size();
+#endif
+
+               env.push(_customArg);
+               env.push(newval);
+               env.push(oldval);
+               env.push(_propname);
+               fn_call fn(const_cast<as_object*>(&this_obj), &env, 4, 
env.stack_size()-1);
+               as_value ret = _func->call(fn);
+               env.drop(4);
+
+#ifndef NDEBUG
+               assert(origStackSize == env.stack_size());
+#endif
+
+               _executing = false;
+
+               return ret;
+
+       }
+       catch (...)
+       {
+               _executing = false;
+               throw;
+       }
+}
+
+
 
 } // end of gnash namespace

Index: server/as_object.h
===================================================================
RCS file: /sources/gnash/gnash/server/as_object.h,v
retrieving revision 1.102
retrieving revision 1.103
diff -u -b -r1.102 -r1.103
--- server/as_object.h  3 Apr 2008 16:00:47 -0000       1.102
+++ server/as_object.h  5 Apr 2008 10:19:26 -0000       1.103
@@ -58,6 +58,56 @@
 class asClass;
 class asName;
 
+/// A trigger that can be associated with a property name
+class Trigger
+{
+       /// Name of the property
+       //
+       /// By storing a string_table::key we'd save CPU cycles
+       /// while adding/removing triggers and some memory
+       /// on each trigger, but at the cost of looking up
+       /// the string_table on every invocation of the watch...
+       ///
+       std::string _propname;
+
+       /// The trigger function 
+       as_function* _func;
+
+       /// A custom argument to pass to the trigger
+       /// after old and new value.
+       as_value _customArg;
+
+       /// Flag to protect from infinite loops
+       bool _executing;
+
+public:
+
+       Trigger(const std::string& propname, as_function& trig, const as_value& 
customArg)
+               :
+               _propname(propname),
+               _func(&trig),
+               _customArg(customArg),
+               _executing(false)
+       {}
+
+       /// Call the trigger
+       //
+       /// @param oldval
+       ///     Old value being modified
+       ///
+       /// @param newval
+       ///     New value requested
+       /// 
+       /// @param this_obj
+       ///     Object of which the property is being changed
+       ///
+       as_value call(const as_value& oldval, const as_value& newval, 
as_object& this_obj);
+
+       void setReachable() const;
+
+};
+
+
 /// \brief
 /// A generic bag of attributes. Base class for all ActionScript-able objects.
 //
@@ -221,7 +271,7 @@
 
        /// Update an existing member value
        //
-       /// NOTE that getter-setter in the inheritance chaing are
+       /// NOTE that getter-setter in the inheritance chain are
        /// considered as existing members. See with.as and Object.as
        /// testcases under actionscript.all.
        /// Also be aware that 'special' (non-proper) properties
@@ -237,7 +287,7 @@
        /// @param nsname
        ///     Id of the namespace.
        ///
-       /// @return a pair in which first element express wheter the 
property01apl0mb
+       /// @return a pair in which first element express wheter the property
        ///         was found and the second wheter it was set (won't set if 
read-only).
        ///
        std::pair<bool,bool> update_member(string_table::key key, const 
as_value& val,
@@ -495,6 +545,43 @@
                        int 
flags=as_prop_flags::dontDelete|as_prop_flags::dontEnum,
                        string_table::key nsname = 0);
 
+       /// \brief
+       /// Add a watch trigger, overriding any other defined for same name.
+       //
+       /// @param key
+       ///     property name (key)
+       ///
+       /// @param ns
+       ///     property namespace.
+       ///
+       /// @param trig
+       ///     A function to invoke when this property value is assigned to.
+       ///     The function will be called with old val, new val and the custom
+       ///     value below. It's return code will be used to set actual value
+       ///
+       /// @param cust
+       ///     Custom value to always pass to the trigger as third arg
+       ///
+       /// @return true if the trigger was successfully added, false
+       ///         otherwise (error? should always be possible to add...)
+       ///
+       bool watch(string_table::key key, as_function& trig,
+               const as_value& cust, string_table::key ns = 0);
+
+       /// \brief
+       /// Remove a watch trigger.
+       //
+       /// @param key
+       ///     property name (key)
+       ///
+       /// @param ns
+       ///     property namespace.
+       ///
+       /// @return true if the trigger was successfully removed, false
+       ///         otherwise (no such trigger...)
+       ///
+       bool unwatch(string_table::key key, string_table::key ns = 0);
+
        /// Get a member as_value by name
        //
        /// The default behaviour is to call set_member_default,
@@ -997,12 +1084,8 @@
                markAsObjectReachable();
        }
 
-       /// Mark properties and __proto__ as reachable (for the GC)
-       void markAsObjectReachable() const
-       {
-               _members.setReachable();
-               //if ( m_prototype.get() ) m_prototype->setReachable();
-       }
+       /// Mark properties and triggers list as reachable (for the GC)
+       void markAsObjectReachable() const;
 #endif // GNASH_USE_GC
 
        /// The Virtual Machine used to create this object
@@ -1017,6 +1100,10 @@
        /// Reference to this object's '__proto__'
        //boost::intrusive_ptr<as_object> m_prototype;
 
+
+       typedef std::pair< string_table::key, string_table::key > FQkey;
+       typedef std::map< FQkey, Trigger > TriggerContainer;
+       TriggerContainer _trigs;
 };
 
 /// Template which does a dynamic cast for as_object pointers. It throws an

Index: server/asobj/Object.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/Object.cpp,v
retrieving revision 1.50
retrieving revision 1.51
diff -u -b -r1.50 -r1.51
--- server/asobj/Object.cpp     3 Apr 2008 16:00:48 -0000       1.50
+++ server/asobj/Object.cpp     5 Apr 2008 10:19:26 -0000       1.51
@@ -65,7 +65,7 @@
 
        // Then will attach to the prototype based on version
 
-       int target_version = vm.getSWFVersion();
+       //int target_version = vm.getSWFVersion();
 
        // Object.valueOf()
        o.init_member("valueOf", vm.getNative(101, 3));
@@ -428,25 +428,65 @@
 }
 
 as_value
-object_watch(const fn_call&)
+object_watch(const fn_call& fn)
 {
-       static bool warned = false;
-       if ( ! warned ) {
-               log_unimpl (__FUNCTION__);
-               warned=true;
+       as_object* obj = fn.this_ptr.get();
+
+       if ( fn.nargs < 2 )
+       {
+               IF_VERBOSE_ASCODING_ERRORS(
+               std::stringstream ss; fn.dump_args(ss);
+               log_aserror(_("Object.watch(%s): missing arguments"));
+               );
+               return as_value(false);
        }
-       return as_value();
+
+       as_value& propval = fn.arg(0);
+       as_value& funcval = fn.arg(1);
+
+       if ( ! funcval.is_function() )
+       {
+               IF_VERBOSE_ASCODING_ERRORS(
+               std::stringstream ss; fn.dump_args(ss);
+               log_aserror(_("Object.watch(%s): second argument is not a 
function"));
+               );
+               return as_value(false);
+       }
+
+       VM& vm = obj->getVM();
+       string_table& st = vm.getStringTable();
+
+       std::string propname = propval.to_string();
+       string_table::key propkey = st.find(propname);
+       as_function* trig = funcval.to_as_function();
+       as_value cust; if ( fn.nargs > 2 ) cust = fn.arg(2);
+
+       return as_value(obj->watch(propkey, *trig, cust));
 }
 
 as_value
-object_unwatch(const fn_call&)
+object_unwatch(const fn_call& fn)
 {
-       static bool warned = false;
-       if ( ! warned ) {
-               log_unimpl (__FUNCTION__);
-               warned=true;
+       as_object* obj = fn.this_ptr.get();
+
+       if ( fn.nargs < 1 )
+       {
+               IF_VERBOSE_ASCODING_ERRORS(
+               std::stringstream ss; fn.dump_args(ss);
+               log_aserror(_("Object.unwatch(%s): missing argument"));
+               );
+               return as_value(false);
        }
-       return as_value();
+
+       as_value& propval = fn.arg(0);
+
+       VM& vm = obj->getVM();
+       string_table& st = vm.getStringTable();
+
+       std::string propname = propval.to_string();
+       string_table::key propkey = st.find(propname);
+
+       return as_value(obj->unwatch(propkey));
 }
 
 as_value

Index: testsuite/actionscript.all/Object.as
===================================================================
RCS file: /sources/gnash/gnash/testsuite/actionscript.all/Object.as,v
retrieving revision 1.55
retrieving revision 1.56
diff -u -b -r1.55 -r1.56
--- testsuite/actionscript.all/Object.as        5 Apr 2008 10:10:05 -0000       
1.55
+++ testsuite/actionscript.all/Object.as        5 Apr 2008 10:19:26 -0000       
1.56
@@ -21,7 +21,7 @@
 // execute it like this gnash -1 -r 0 -v out.swf
 
 
-rcsid="$Id: Object.as,v 1.55 2008/04/05 10:10:05 strk Exp $";
+rcsid="$Id: Object.as,v 1.56 2008/04/05 10:19:26 strk Exp $";
 #include "check.as"
 
 // Test things in Class Object (swf5~swf8)
@@ -650,34 +650,34 @@
        return _root.ret;
 };
 r = o.watch('l', simplewatch, 'cust');
-xcheck(r); // can watch unexisting prop
+check(r); // can watch unexisting prop
 _root.ret = 2;
 o.l = 5;
-xcheck_equals(o.l, 2); // returned by watcher
-xcheck_equals(_root.info.nam, 'l');
+check_equals(o.l, 2); // returned by watcher
+check_equals(_root.info.nam, 'l');
 check_equals(typeof(_root.info.ov), 'undefined');
-xcheck_equals(_root.info.nv, 5);
-xcheck_equals(_root.info.d, 'cust');
-xcheck_equals(_root.info.tv, o);
+check_equals(_root.info.nv, 5);
+check_equals(_root.info.d, 'cust');
+check_equals(_root.info.tv, o);
 delete _root.info;
 check(delete o.l);
 o.p = 4;
 check(!o.unwatch('p')); // can not unwatch not-watched props
 check(!o.unwatch('r')); // can not unwatch non-watched props
-xcheck(o.unwatch('l')); // can unwatch non-existing but watched vars
+check(o.unwatch('l')); // can unwatch non-existing but watched vars
 
 // watch a getter-setter 
 
 get_l = function() { _root.get_l_calls++; return this.l; };
 set_l = function(v) { _root.set_l_calls++; this.l=v; };
 r = o.watch('l', simplewatch, 'cust2');
-xcheck(r);
+check(r);
 check_equals(typeof(_root.info), 'undefined'); // just checking...
 _root.ret = 'return from watch';
 _root.get_l_calls=_root.set_l_calls=0;
 r = o.addProperty("l", get_l, set_l);
 check(r);
-xcheck_equals(_root.info.nam, 'l');
+xcheck_equals(_root.info.nam, 'l'); // gnash fails calling the watch trigger 
at all here
 check_equals(typeof(_root.info.ov), 'undefined');
 check_equals(typeof(_root.info.nv), 'undefined'); // underlying value of 
getter-setter was undefined
 xcheck_equals(_root.info.d, 'cust2');
@@ -697,16 +697,16 @@
 #if OUTPUT_VERSION < 7
   xcheck_equals(_root.info.ov, 'return from watch'); // old value
   xcheck_equals(_root.info.nv, 'ciao'); // we requested this
-  xcheck_equals(_root.info.d, 'cust2'); 
-  xcheck_equals(_root.info.tv, o); 
-  check_equals(_root.get_l_calls, 0);
+  check_equals(_root.info.d, 'cust2'); 
+  check_equals(_root.info.tv, o); 
+  xcheck_equals(_root.get_l_calls, 0); // should get underlying value, not 
invoke getter
   check_equals(_root.set_l_calls, 1);
 #else
   xcheck_equals(_root.info.ov, 'return from watch'); // old value
-  xcheck_equals(_root.info.nv, 'return from watch'); // mmm ?
-  xcheck_equals(_root.info.d, 'cust2'); 
-  xcheck_equals(_root.info.tv, o); 
-  check_equals(_root.get_l_calls, 0);
+  check_equals(_root.info.nv, 'return from watch'); // mmm ?
+  check_equals(_root.info.d, 'cust2'); 
+  check_equals(_root.info.tv, o); 
+  xcheck_equals(_root.get_l_calls, 0); // should get underlying value, not 
invoke getter
   xcheck_equals(_root.set_l_calls, 65);
 #endif
 

Index: testsuite/swfdec/PASSING
===================================================================
RCS file: /sources/gnash/gnash/testsuite/swfdec/PASSING,v
retrieving revision 1.120
retrieving revision 1.121
diff -u -b -r1.120 -r1.121
--- testsuite/swfdec/PASSING    3 Apr 2008 16:00:48 -0000       1.120
+++ testsuite/swfdec/PASSING    5 Apr 2008 10:19:27 -0000       1.121
@@ -441,6 +441,9 @@
 object-valueof-8.swf:d9f385078fd32f289837897667323fd7
 object-watch-5.swf:12bd406ac79e6ce18130f854626e9003
 object-watch-segv-5.swf:990733edee1b528931b182c03bea8b6e
+object-watch-segv-6.swf:3c8d7fa8d3c9ee9f3f35d2d7227d7497
+object-watch-segv-7.swf:ecbe214987d0f2fec33ba37a75131ddf
+object-watch-segv-8.swf:acba6fcf2bd1dfa9ae5f88e21d522291
 onload-childparent.swf:657c404873bde57d597a8a8c43f7c008
 onresize-5.swf:76f88b683c640279239628f030ad8291
 onresize-6.swf:322df0e4ac0cbb3beb5c86026be8c716




reply via email to

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