[Top][All Lists]
[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
- [Gnash-commit] gnash ChangeLog server/as_object.cpp server/as_...,
Sandro Santilli <=