update copyright date
[gnash.git] / libcore / as_object.cpp
blob45b8c62f87d825bbffcb11b0ec0bfcaaa4a10887
1 // as_object.cpp: ActionScript Object class and its properties, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
4 // 2011 Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "as_object.h"
22 #include <set>
23 #include <string>
24 #include <boost/algorithm/string/case_conv.hpp>
25 #include <utility> // for std::pair
27 #include "RunResources.h"
28 #include "log.h"
29 #include "smart_ptr.h"
30 #include "as_function.h"
31 #include "as_environment.h"
32 #include "movie_root.h"
33 #include "event_id.h"
34 #include "Property.h"
35 #include "VM.h"
36 #include "GnashException.h"
37 #include "fn_call.h"
38 #include "Array_as.h"
39 #include "as_function.h"
40 #include "Global_as.h"
41 #include "GnashAlgorithm.h"
42 #include "DisplayObject.h"
43 #include "namedStrings.h"
45 namespace gnash {
46 template<typename T>
47 class
48 as_object::PrototypeRecursor
50 public:
51 PrototypeRecursor(as_object* top, const ObjectURI& uri, T cmp = T())
53 _object(top),
54 _uri(uri),
55 _iterations(0),
56 _condition(cmp)
58 _visited.insert(top);
61 /// Iterate to the next object in the inheritance chain.
63 /// This function throws an ActionLimitException when the maximum
64 /// number of recursions is reached.
66 /// @return false if there is no next object. In this case calling
67 /// the other functions will abort.
68 bool operator()()
70 ++_iterations;
72 // See swfdec/prototype-recursion-get-?.swf
73 if (_iterations > 256) {
74 throw ActionLimitException("Lookup depth exceeded.");
77 _object = _object->get_prototype();
79 // TODO: there is recursion prevention anyway; is this extra
80 // check for circularity really necessary?
81 if (!_visited.insert(_object).second) return 0;
82 return _object && !_object->displayObject();
85 /// Return the wanted property if it exists and satisfies the predicate.
87 /// This will abort if there is no current object.
88 Property* getProperty(as_object** owner = 0) const {
90 assert(_object);
91 Property* prop = _object->_members.getProperty(_uri);
93 if (prop && _condition(*prop)) {
94 if (owner) *owner = _object;
95 return prop;
97 return 0;
100 private:
101 as_object* _object;
102 const ObjectURI& _uri;
103 std::set<const as_object*> _visited;
104 size_t _iterations;
105 T _condition;
108 // Anonymous namespace used for module-static defs
109 namespace {
111 as_function*
112 getConstructor(as_object& o)
114 as_value ctorVal;
115 if (!o.get_member(NSV::PROP_uuCONSTRUCTORuu, &ctorVal)) {
116 return 0;
118 return ctorVal.to_function();
121 /// 'super' is a special kind of object
123 /// See http://wiki.gnashdev.org/wiki/index.php/ActionScriptSuper
125 /// We make it derive from as_function instead of as_object
126 /// to avoid touching too many files (ie: an as_object is not considered
127 /// something that can be called by current Gnash code). We may want
128 /// to change this in the future to implement what ECMA-262 refers to
129 /// as the [[Call]] property of objects.
131 class as_super : public as_object
133 public:
135 as_super(Global_as& gl, as_object* super)
137 as_object(gl),
138 _super(super)
140 set_prototype(prototype());
143 virtual bool isSuper() const { return true; }
145 virtual as_object* get_super(const ObjectURI& fname);
147 // Fetching members from 'super' yelds a lookup on the associated prototype
148 virtual bool get_member(const ObjectURI& uri, as_value* val)
150 as_object* proto = prototype();
151 if (proto) return proto->get_member(uri, val);
152 log_debug("Super has no associated prototype");
153 return false;
156 /// Dispatch.
157 virtual as_value call(const fn_call& fn)
160 // TODO: this is a hack to make sure objects are constructed, not
161 // converted (fn.isInstantiation() must be true).
162 fn_call::Args::container_type argsIn(fn.getArgs());
163 fn_call::Args args;
164 args.swap(argsIn);
166 fn_call fn2(fn.this_ptr, fn.env(), args, fn.super, true);
167 assert(fn2.isInstantiation());
168 as_function* ctor = constructor();
169 if (ctor) return ctor->call(fn2);
170 log_debug("Super has no associated constructor");
171 return as_value();
174 protected:
176 virtual void markReachableResources() const
178 if (_super) _super->setReachable();
179 as_object::markReachableResources();
182 private:
184 as_object* prototype() {
185 return _super ? _super->get_prototype() : 0;
188 as_function* constructor() {
189 return _super ? getConstructor(*_super) : 0;
192 as_object* _super;
195 as_object*
196 as_super::get_super(const ObjectURI& fname)
198 // Super references the super class of our class prototype.
199 // Our class prototype is __proto__.
200 // Our class superclass prototype is __proto__.__proto__
202 // Our class prototype is __proto__.
203 as_object* proto = get_prototype();
204 if (!proto) return new as_super(getGlobal(*this), 0);
206 if (fname.empty() || getSWFVersion(*this) <= 6) {
207 return new as_super(getGlobal(*this), proto);
210 as_object* owner = 0;
211 proto->findProperty(fname, &owner);
212 if (!owner) return 0;
214 if (owner == proto) return new as_super(getGlobal(*this), proto);
216 as_object* tmp = proto;
217 while (tmp && tmp->get_prototype() != owner) {
218 tmp = tmp->get_prototype();
220 // ok, now 'tmp' should be the object whose __proto__ member
221 // contains the actual named method.
223 // in the C:B:A:F case this would be B when calling
224 // super.myName() from C.prototype.myName()
226 // well, since we found the property, it must be somewhere!
227 assert(tmp);
229 if (tmp != proto) { return new as_super(getGlobal(*this), tmp); }
230 return new as_super(getGlobal(*this), owner);
234 /// A PropertyList visitor copying properties to an object
235 class PropsCopier : public PropertyVisitor
238 public:
240 /// \brief
241 /// Initialize a PropsCopier instance associating it
242 /// with a target object (an object whose members has to be set)
244 PropsCopier(as_object& tgt)
246 _tgt(tgt)
250 /// Set *inherited* properties of the given target object
251 bool accept(const ObjectURI& uri, const as_value& val) {
252 if (getName(uri) == NSV::PROP_uuPROTOuu) return true;
253 _tgt.set_member(uri, val);
254 return true;
256 private:
257 as_object& _tgt;
260 class PropertyEnumerator : public PropertyVisitor
262 public:
263 PropertyEnumerator(SortedPropertyList& to)
265 _to(to)
268 bool accept(const ObjectURI& uri, const as_value& val) {
269 _to.push_back(std::make_pair(uri, val));
270 return true;
272 private:
273 SortedPropertyList& _to;
276 } // anonymous namespace
279 const int as_object::DefaultFlags;
281 as_object::as_object(const Global_as& gl)
283 GcResource(getRoot(gl).gc()),
284 _displayObject(0),
285 _array(false),
286 _relay(0),
287 _vm(getVM(gl)),
288 _members(*this)
292 as_object::as_object(VM& vm)
294 GcResource(vm.getRoot().gc()),
295 _displayObject(0),
296 _array(false),
297 _relay(0),
298 _vm(vm),
299 _members(*this)
303 as_value
304 as_object::call(const fn_call& /*fn*/)
306 throw ActionTypeError();
309 std::string
310 as_object::stringValue() const
312 return "[object Object]";
315 std::pair<bool,bool>
316 as_object::delProperty(const ObjectURI& uri)
318 return _members.delProperty(uri);
322 void
323 as_object::add_property(const std::string& name, as_function& getter,
324 as_function* setter)
326 const ObjectURI& uri = getURI(vm(), name);
328 Property* prop = _members.getProperty(uri);
330 if (prop) {
331 const as_value& cacheVal = prop->getCache();
332 // Used to return the return value of addGetterSetter, but this
333 // is always true.
334 _members.addGetterSetter(uri, getter, setter, cacheVal);
335 return;
336 // NOTE: watch triggers not called when adding a new
337 // getter-setter property
339 else {
341 _members.addGetterSetter(uri, getter, setter, as_value());
343 // Nothing more to do if there are no triggers.
344 if (!_trigs.get()) return;
346 // check if we have a trigger, if so, invoke it
347 // and set val to its return
348 TriggerContainer::iterator trigIter = _trigs->find(uri);
350 if (trigIter != _trigs->end()) {
352 Trigger& trig = trigIter->second;
354 log_debug("add_property: property %s is being watched" , name);
355 as_value v = trig.call(as_value(), as_value(), *this);
357 // The trigger call could have deleted the property,
358 // so we check for its existence again, and do NOT put
359 // it back in if it was deleted
360 prop = _members.getProperty(uri);
361 if (!prop) {
362 log_debug("Property %s deleted by trigger on create "
363 "(getter-setter)", name);
364 return;
366 prop->setCache(v);
368 return;
373 /// Order of property lookup:
375 /// 1. Visible own properties.
376 /// 2. If DisplayObject, magic properties
377 /// 3. Visible own properties of all __proto__ objects (a DisplayObject
378 /// ends the chain).
379 /// 4. __resolve property of this object and all __proto__ objects (a Display
380 /// Object ends the chain). This should ignore visibility but doesn't.
381 bool
382 as_object::get_member(const ObjectURI& uri, as_value* val)
384 assert(val);
386 const int version = getSWFVersion(*this);
388 PrototypeRecursor<IsVisible> pr(this, uri, IsVisible(version));
390 Property* prop = pr.getProperty();
391 if (!prop) {
392 if (displayObject()) {
393 DisplayObject* d = displayObject();
394 if (getDisplayObjectProperty(*d, uri, *val)) return true;
396 while (pr()) {
397 if ((prop = pr.getProperty())) break;
401 // If the property isn't found or doesn't apply to any objects in the
402 // inheritance chain, try the __resolve property.
403 if (!prop) {
405 Property* res = findProperty(NSV::PROP_uuRESOLVE);
407 // No __resolve
408 if (!res) return false;
410 // If __resolve exists, call it with the name of the undefined
411 // property.
412 string_table& st = getStringTable(*this);
413 const std::string& undefinedName = st.value(getName(uri));
415 fn_call::Args args;
416 args += undefinedName;
418 // Invoke the __resolve property.
419 *val = invoke(res->getValue(*this), as_environment(getVM(*this)),
420 this, args);
422 return true;
425 try {
426 *val = prop->getValue(*this);
427 return true;
429 catch (ActionLimitException& exc) {
430 // will be logged by outer catcher
431 throw;
433 catch (ActionTypeError& exc) {
434 IF_VERBOSE_ASCODING_ERRORS(
435 log_aserror(_("Caught exception: %s"), exc.what());
437 return false;
443 as_object*
444 as_object::get_super(const ObjectURI& fname)
446 // Super references the super class of our class prototype.
447 // Our class prototype is __proto__.
448 // Our class superclass prototype is __proto__.__proto__
450 // Our class prototype is __proto__.
451 as_object* proto = get_prototype();
453 if ( ! fname.empty() && getSWFVersion(*this) > 6) {
454 as_object* owner = 0;
455 findProperty(fname, &owner);
456 // should be 0 if findProperty returned 0
457 if (owner != this) proto = owner;
460 as_object* super = new as_super(getGlobal(*this), proto);
462 return super;
465 as_object*
466 as_object::get_super()
468 // Our class prototype is __proto__.
469 as_object* proto = get_prototype();
470 as_object* super = new as_super(getGlobal(*this), proto);
472 return super;
475 Property*
476 as_object::findProperty(const ObjectURI& uri, as_object** owner)
479 const int version = getSWFVersion(*this);
481 PrototypeRecursor<IsVisible> pr(this, uri, IsVisible(version));
483 do {
484 Property* prop = pr.getProperty(owner);
485 if (prop) return prop;
486 } while (pr());
488 // No Property found
489 return 0;
492 Property*
493 as_object::findUpdatableProperty(const ObjectURI& uri)
496 PrototypeRecursor<Exists> pr(this, uri);
498 Property* prop = pr.getProperty();
500 // We won't scan the inheritance chain if we find a member,
501 // even if invisible.
502 if (prop) return prop;
504 const int swfVersion = getSWFVersion(*this);
506 while (pr()) {
507 if ((prop = pr.getProperty())) {
508 if (prop->isGetterSetter() && visible(*prop, swfVersion)) {
509 return prop;
513 return 0;
516 void
517 as_object::set_prototype(const as_value& proto)
519 // TODO: check what happens if __proto__ is set as a user-defined
520 // getter/setter
521 // TODO: check triggers !!
522 _members.setValue(NSV::PROP_uuPROTOuu, proto, as_object::DefaultFlags);
525 void
526 as_object::executeTriggers(Property* prop, const ObjectURI& uri,
527 const as_value& val)
530 // check if we have a trigger, if so, invoke it
531 // and set val to its return
532 TriggerContainer::iterator trigIter;
534 // If there are no triggers or the trigger is not found, just set
535 // the property.
536 if (!_trigs.get() || (trigIter = _trigs->find(uri)) == _trigs->end()) {
537 if (prop) {
538 prop->setValue(*this, val);
539 prop->clearVisible(getSWFVersion(*this));
541 return;
544 Trigger& trig = trigIter->second;
546 if (trig.dead()) {
547 _trigs->erase(trigIter);
548 return;
551 // WARNING: getValue might itself invoke a trigger
552 // (getter-setter)... ouch ?
553 // TODO: in this case, return the underlying value !
554 const as_value& curVal = prop ? prop->getCache() : as_value();
555 const as_value& newVal = trig.call(curVal, val, *this);
557 // This is a particularly clear and concise way of removing dead triggers.
558 EraseIf(*_trigs, boost::bind(boost::mem_fn(&Trigger::dead),
559 boost::bind(SecondElement<TriggerContainer::value_type>(), _1)));
561 // The trigger call could have deleted the property,
562 // so we check for its existence again, and do NOT put
563 // it back in if it was deleted
564 prop = findUpdatableProperty(uri);
565 if (!prop) return;
567 prop->setValue(*this, newVal);
568 prop->clearVisible(getSWFVersion(*this));
572 /// Order of property lookup:
574 /// 0. MovieClip textfield variables. TODO: this is a hack and should be
575 /// eradicated.
576 /// 1. Own properties even if invisible or not getter-setters.
577 /// 2. If DisplayObject, magic properties
578 /// 3. Visible own getter-setter properties of all __proto__ objects
579 /// (a DisplayObject ends the chain).
580 bool
581 as_object::set_member(const ObjectURI& uri, const as_value& val, bool ifFound)
584 bool tfVarFound = false;
585 if (displayObject()) {
586 MovieClip* mc = dynamic_cast<MovieClip*>(displayObject());
587 if (mc) tfVarFound = mc->setTextFieldVariables(uri, val);
588 // We still need to set the member.
591 // Handle the length property for arrays. NB: checkArrayLength() will
592 // call this function again if the key is a valid index.
593 if (array()) checkArrayLength(*this, uri, val);
595 PrototypeRecursor<Exists> pr(this, uri);
597 Property* prop = pr.getProperty();
599 // We won't scan the inheritance chain if we find a member,
600 // even if invisible.
601 if (!prop) {
603 if (displayObject()) {
604 DisplayObject* d = displayObject();
605 if (setDisplayObjectProperty(*d, uri, val)) return true;
606 // TODO: should we execute triggers?
609 const int version = getSWFVersion(*this);
610 while (pr()) {
611 if ((prop = pr.getProperty())) {
612 if ((prop->isGetterSetter()) && visible(*prop, version)) {
613 break;
615 else prop = 0;
620 if (prop) {
621 if (readOnly(*prop)) {
622 IF_VERBOSE_ASCODING_ERRORS(
623 ObjectURI::Logger l(getStringTable(*this));
624 log_aserror(_("Attempt to set read-only property '%s'"),
625 l(uri));
627 return true;
630 try {
631 executeTriggers(prop, uri, val);
633 catch (const ActionTypeError& exc) {
634 IF_VERBOSE_ASCODING_ERRORS(
635 log_aserror(
636 _("%s: %s"), getStringTable(*this).value(getName(uri)), exc.what());
640 return true;
643 // Else, add new property...
644 if (ifFound) return false;
646 // Property does not exist, so it won't be read-only. Set it.
647 if (!_members.setValue(uri, val)) {
649 IF_VERBOSE_ASCODING_ERRORS(
650 ObjectURI::Logger l(getStringTable(*this));
651 log_aserror(_("Unknown failure in setting property '%s' on "
652 "object '%p'"), l(uri), (void*) this);
654 return false;
657 executeTriggers(prop, uri, val);
659 // Return true if we found a textfield variable.
660 if (tfVarFound) return true;
662 return false;
666 void
667 as_object::init_member(const std::string& key1, const as_value& val, int flags)
669 const ObjectURI& uri(getURI(vm(), key1));
670 init_member(uri, val, flags);
673 void
674 as_object::init_member(const ObjectURI& uri, const as_value& val, int flags)
677 // Set (or create) a SimpleProperty
678 if (!_members.setValue(uri, val, flags)) {
679 ObjectURI::Logger l(getStringTable(*this));
680 log_error(_("Attempt to initialize read-only property '%s'"
681 " on object '%p' twice"), l(uri), (void*)this);
682 // We shouldn't attempt to initialize a member twice, should we ?
683 abort();
687 void
688 as_object::init_property(const std::string& name, as_function& getter,
689 as_function& setter, int flags)
691 const ObjectURI& uri = getURI(vm(), name);
692 init_property(uri, getter, setter, flags);
695 void
696 as_object::init_property(const ObjectURI& uri, as_function& getter,
697 as_function& setter, int flags)
699 _members.addGetterSetter(uri, getter, &setter, as_value(), flags);
702 void
703 as_object::init_property(const std::string& name, as_c_function_ptr getter,
704 as_c_function_ptr setter, int flags)
706 const ObjectURI& uri = getURI(vm(), name);
707 init_property(uri, getter, setter, flags);
710 void
711 as_object::init_property(const ObjectURI& uri, as_c_function_ptr getter,
712 as_c_function_ptr setter, int flags)
714 _members.addGetterSetter(uri, getter, setter, flags);
717 bool
718 as_object::init_destructive_property(const ObjectURI& uri, as_function& getter,
719 int flags)
721 return _members.addDestructiveGetter(uri, getter, flags);
724 bool
725 as_object::init_destructive_property(const ObjectURI& uri,
726 as_c_function_ptr getter, int flags)
728 return _members.addDestructiveGetter(uri, getter, flags);
731 void
732 as_object::init_readonly_property(const std::string& name, as_function& getter,
733 int initflags)
735 const ObjectURI& uri = getURI(vm(), name);
737 init_property(uri, getter, getter, initflags | PropFlags::readOnly);
738 assert(_members.getProperty(uri));
741 void
742 as_object::init_readonly_property(const std::string& name,
743 as_c_function_ptr getter, int initflags)
745 const ObjectURI& uri = getURI(vm(), name);
746 init_property(uri, getter, getter, initflags | PropFlags::readOnly);
747 assert(_members.getProperty(uri));
750 void
751 as_object::set_member_flags(const ObjectURI& uri, int setTrue, int setFalse)
753 _members.setFlags(uri, setTrue, setFalse);
756 void
757 as_object::addInterface(as_object* obj)
759 assert(obj);
760 if (std::find(_interfaces.begin(), _interfaces.end(), obj) ==
761 _interfaces.end()) {
762 _interfaces.push_back(obj);
766 bool
767 as_object::instanceOf(as_object* ctor)
770 /// An object is never an instance of a null prototype.
771 if (!ctor) return false;
773 as_value protoVal;
774 if (!ctor->get_member(NSV::PROP_PROTOTYPE, &protoVal)) {
775 #ifdef GNASH_DEBUG_INSTANCE_OF
776 log_debug("Object %p can't be an instance of an object (%p) "
777 "with no 'prototype'",
778 (void*)this, (void*)ctor);
779 #endif
780 return false;
783 as_object* ctorProto = toObject(protoVal, getVM(*this));
784 if (!ctorProto) {
785 #ifdef GNASH_DEBUG_INSTANCE_OF
786 log_debug("Object %p can't be an instance of an object (%p) "
787 "with non-object 'prototype' (%s)",
788 (void*)this, (void*)ctor, protoVal);
789 #endif
790 return false;
793 // TODO: cleanup the iteration, make it more readable ...
794 std::set<as_object*> visited;
796 as_object* obj = this;
797 while (obj && visited.insert(obj).second) {
798 as_object* thisProto = obj->get_prototype();
799 if (!thisProto) {
800 break;
803 // Check our proto
804 if (thisProto == ctorProto) {
805 #ifdef GNASH_DEBUG_INSTANCE_OF
806 log_debug("Object %p is an instance of constructor %p as "
807 "the constructor exposes our __proto__ %p",
808 (void*)obj, (void*)ctor, (void*)thisProto);
809 #endif
810 return true;
813 // Check our proto interfaces
814 if (std::find(thisProto->_interfaces.begin(),
815 thisProto->_interfaces.end(), ctorProto)
816 != thisProto->_interfaces.end()) {
818 #ifdef GNASH_DEBUG_INSTANCE_OF
819 log_debug("Object %p __proto__ %p had one interface matching "
820 "with the constructor prototype %p",
821 (void*)obj, (void*)thisProto, (void*)ctorProto);
822 #endif
823 return true;
826 obj = thisProto;
829 return false;
832 bool
833 as_object::prototypeOf(as_object& instance)
835 as_object* obj = &instance;
837 std::set<as_object*> visited;
839 while (obj && visited.insert(obj).second ) {
840 if (obj->get_prototype() == this) return true;
841 obj = obj->get_prototype();
844 // See actionscript.all/Inheritance.as for a way to trigger this
845 IF_VERBOSE_ASCODING_ERRORS(
846 if (obj) log_aserror(_("Circular inheritance chain detected "
847 "during isPrototypeOf call"));
850 return false;
853 void
854 as_object::dump_members()
856 log_debug(_("%d members of object %p follow"), _members.size(),
857 static_cast<const void*>(this));
858 _members.dump();
861 void
862 as_object::setPropFlags(const as_value& props_val, int set_false, int set_true)
865 if (props_val.is_null()) {
866 // Take all the members of the object
867 _members.setFlagsAll(set_true, set_false);
868 return;
871 std::string propstr = props_val.to_string();
873 for (;;) {
875 std::string prop;
876 size_t next_comma=propstr.find(",");
877 if (next_comma == std::string::npos) {
878 prop = propstr;
880 else {
881 prop = propstr.substr(0,next_comma);
882 propstr = propstr.substr(next_comma+1);
885 // set_member_flags will take care of case conversion
886 set_member_flags(getURI(vm(), prop), set_true, set_false);
888 if (next_comma == std::string::npos) {
889 break;
892 return;
896 void
897 as_object::copyProperties(const as_object& o)
899 PropsCopier copier(*this);
901 // TODO: check if non-visible properties should be also copied !
902 o.visitProperties<Exists>(copier);
905 void
906 as_object::visitKeys(KeyVisitor& visitor) const
908 // Hack to handle MovieClips.
909 if (displayObject()) {
910 displayObject()->visitNonProperties(visitor);
913 // this set will keep track of visited objects,
914 // to avoid infinite loops
915 std::set<const as_object*> visited;
917 PropertyList::PropertyTracker doneList;
919 const as_object* current(this);
920 while (current && visited.insert(current).second) {
921 current->_members.visitKeys(visitor, doneList);
922 current = current->get_prototype();
927 Property*
928 as_object::getOwnProperty(const ObjectURI& uri)
930 return _members.getProperty(uri);
933 as_object*
934 as_object::get_prototype() const
936 int swfVersion = getSWFVersion(*this);
938 Property* prop = _members.getProperty(NSV::PROP_uuPROTOuu);
939 if (!prop) return 0;
940 if (!visible(*prop, swfVersion)) return 0;
942 const as_value& proto = prop->getValue(*this);
944 return toObject(proto, getVM(*this));
947 std::string
948 getURLEncodedVars(as_object& o)
950 SortedPropertyList props = enumerateProperties(o);
952 std::string data;
953 string_table& st = getStringTable(o);
955 for (SortedPropertyList::const_reverse_iterator i = props.rbegin(),
956 e = props.rend(); i != e; ++i) {
958 const std::string& name = i->first.toString(st);
959 const std::string& value = i->second.to_string();
961 // see bug #22006
962 if (!name.empty() && name[0] == '$') continue;
964 URL::encode(value);
965 if (i != props.rbegin()) data += '&';
967 data += name + "=" + value;
970 return data;
973 bool
974 as_object::watch(const ObjectURI& uri, as_function& trig,
975 const as_value& cust)
978 std::string propname = getStringTable(*this).value(getName(uri));
980 if (!_trigs.get()) _trigs.reset(new TriggerContainer);
982 TriggerContainer::iterator it = _trigs->find(uri);
983 if (it == _trigs->end()) {
984 return _trigs->insert(
985 std::make_pair(uri, Trigger(propname, trig, cust))).second;
987 it->second = Trigger(propname, trig, cust);
988 return true;
991 bool
992 as_object::unwatch(const ObjectURI& uri)
994 if (!_trigs.get()) return false;
996 TriggerContainer::iterator trigIter = _trigs->find(uri);
997 if (trigIter == _trigs->end()) {
998 log_debug("No watch for property %s",
999 getStringTable(*this).value(getName(uri)));
1000 return false;
1002 Property* prop = _members.getProperty(uri);
1003 if (prop && prop->isGetterSetter()) {
1004 log_debug("Watch on %s not removed (is a getter-setter)",
1005 getStringTable(*this).value(getName(uri)));
1006 return false;
1008 trigIter->second.kill();
1009 return true;
1012 void
1013 as_object::markReachableResources() const
1015 _members.setReachable();
1017 if (_trigs.get()) {
1018 for (TriggerContainer::const_iterator it = _trigs->begin();
1019 it != _trigs->end(); ++it) {
1020 it->second.setReachable();
1024 // Mark interfaces reachable.
1025 std::for_each(_interfaces.begin(), _interfaces.end(),
1026 std::mem_fun(&as_object::setReachable));
1028 // Proxy objects can contain references to other as_objects.
1029 if (_relay) _relay->setReachable();
1030 if (_displayObject) _displayObject->setReachable();
1033 void
1034 Trigger::setReachable() const
1036 _func->setReachable();
1037 _customArg.setReachable();
1040 as_value
1041 Trigger::call(const as_value& oldval, const as_value& newval,
1042 as_object& this_obj)
1044 assert(!_dead);
1046 if (_executing) return newval;
1048 _executing = true;
1050 try {
1052 const as_environment env(getVM(this_obj));
1054 fn_call::Args args;
1055 args += _propname, oldval, newval, _customArg;
1057 fn_call fn(&this_obj, env, args);
1058 as_value ret = _func->call(fn);
1059 _executing = false;
1061 return ret;
1064 catch (const GnashException&) {
1065 _executing = false;
1066 throw;
1070 SortedPropertyList
1071 enumerateProperties(as_object& obj)
1074 // this set will keep track of visited objects,
1075 // to avoid infinite loops
1076 std::set<as_object*> visited;
1078 SortedPropertyList to;
1079 PropertyEnumerator e(to);
1080 as_object* current(&obj);
1082 while (current && visited.insert(current).second) {
1083 current->visitProperties<IsEnumerable>(e);
1084 current = current->get_prototype();
1086 return to;
1090 as_object*
1091 getPathElement(as_object& o, const ObjectURI& uri)
1093 as_value tmp;
1094 if (!o.get_member(uri, &tmp)) return 0;
1095 if (!tmp.is_object()) return 0;
1096 return toObject(tmp, getVM(o));
1100 void
1101 sendEvent(as_object& o, const as_environment& env, const ObjectURI& name)
1103 Property* prop = o.findProperty(name);
1104 if (prop) {
1105 fn_call::Args args;
1106 invoke(prop->getValue(o), env, &o, args);
1110 as_object*
1111 getObjectWithPrototype(Global_as& gl, const ObjectURI& c)
1113 as_object* ctor = toObject(getMember(gl, c), getVM(gl));
1114 as_object* proto = ctor ?
1115 toObject(getMember(*ctor, NSV::PROP_PROTOTYPE), getVM(gl)) : 0;
1117 as_object* o = createObject(gl);
1118 o->set_prototype(proto ? proto : as_value());
1119 return o;
1122 /// Get the VM from an as_object
1124 getVM(const as_object& o)
1126 return o.vm();
1129 /// Get the movie_root from an as_object
1130 movie_root&
1131 getRoot(const as_object& o)
1133 return o.vm().getRoot();
1136 /// Get the string_table from an as_object
1137 string_table&
1138 getStringTable(const as_object& o)
1140 return o.vm().getStringTable();
1143 const RunResources&
1144 getRunResources(const as_object& o)
1146 return o.vm().getRoot().runResources();
1150 getSWFVersion(const as_object& o)
1152 return o.vm().getSWFVersion();
1155 Global_as&
1156 getGlobal(const as_object& o)
1158 return *o.vm().getGlobal();
1161 } // end of gnash namespace
1163 // local Variables:
1164 // mode: C++
1165 // indent-tabs-mode: nil
1166 // End: