1 // as_object.cpp:  ActionScript Object class and its properties, for Gnash.
2 //
3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 //   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
19 
20 #include "as_object.h"
21 
22 #include <set>
23 #include <string>
24 #include <boost/algorithm/string/case_conv.hpp>
25 #include <utility> // for std::pair
26 
27 #include "RunResources.h"
28 #include "log.h"
29 #include "as_function.h"
30 #include "as_environment.h"
31 #include "movie_root.h"
32 #include "event_id.h"
33 #include "Property.h"
34 #include "VM.h"
35 #include "GnashException.h"
36 #include "fn_call.h"
37 #include "Array_as.h"
38 #include "as_function.h"
39 #include "Global_as.h"
40 #include "GnashAlgorithm.h"
41 #include "DisplayObject.h"
42 #include "namedStrings.h"
43 
44 namespace gnash {
45 template<typename T>
46 class
47 as_object::PrototypeRecursor
48 {
49 public:
PrototypeRecursor(as_object * top,const ObjectURI & uri,T cmp=T ())50     PrototypeRecursor(as_object* top, const ObjectURI& uri, T cmp = T())
51         :
52         _object(top),
53         _uri(uri),
54         _iterations(0),
55         _condition(std::move(cmp))
56     {
57         _visited.insert(top);
58     }
59 
60     /// Iterate to the next object in the inheritance chain.
61     //
62     /// This function throws an ActionLimitException when the maximum
63     /// number of recursions is reached.
64     //
65     /// @return     false if there is no next object. In this case calling
66     ///             the other functions will abort.
operator ()()67     bool operator()()
68     {
69         ++_iterations;
70 
71         // See swfdec/prototype-recursion-get-?.swf
72 		if (_iterations > 256) {
73 			throw ActionLimitException("Lookup depth exceeded.");
74         }
75 
76         _object = _object->get_prototype();
77 
78         // TODO: there is recursion prevention anyway; is this extra
79         // check for circularity really necessary?
80         if (!_visited.insert(_object).second) return 0;
81         return _object && !_object->displayObject();
82     }
83 
84     /// Return the wanted property if it exists and satisfies the predicate.
85     //
86     /// This will abort if there is no current object.
getProperty(as_object ** owner=nullptr) const87     Property* getProperty(as_object** owner = nullptr) const {
88 
89         assert(_object);
90         Property* prop = _object->_members.getProperty(_uri);
91 
92         if (prop && _condition(*prop)) {
93             if (owner) *owner = _object;
94             return prop;
95         }
96         return nullptr;
97     }
98 
99 private:
100     as_object* _object;
101     const ObjectURI& _uri;
102     std::set<const as_object*> _visited;
103     size_t _iterations;
104     T _condition;
105 };
106 
107 // Anonymous namespace used for module-static defs
108 namespace {
109 
110 as_function*
getConstructor(as_object & o)111 getConstructor(as_object& o)
112 {
113 	as_value ctorVal;
114 	if (!o.get_member(NSV::PROP_uuCONSTRUCTORuu, &ctorVal)) {
115 		return nullptr;
116 	}
117 	return ctorVal.to_function();
118 }
119 
120 /// 'super' is a special kind of object
121 //
122 /// See http://wiki.gnashdev.org/wiki/index.php/ActionScriptSuper
123 ///
124 /// We make it derive from as_function instead of as_object
125 /// to avoid touching too many files (ie: an as_object is not considered
126 /// something that can be called by current Gnash code). We may want
127 /// to change this in the future to implement what ECMA-262 refers to
128 /// as the [[Call]] property of objects.
129 ///
130 class as_super : public as_object
131 {
132 public:
133 
as_super(Global_as & gl,as_object * super)134     as_super(Global_as& gl, as_object* super)
135         :
136         as_object(gl),
137         _super(super)
138 	{
139         set_prototype(prototype());
140 	}
141 
isSuper() const142     virtual bool isSuper() const { return true; }
143 
144     virtual as_object* get_super(const ObjectURI& fname);
145 
146     // Fetching members from 'super' yelds a lookup on the associated prototype
get_member(const ObjectURI & uri,as_value * val)147     virtual bool get_member(const ObjectURI& uri, as_value* val)
148 	{
149         as_object* proto = prototype();
150         if (proto) return proto->get_member(uri, val);
151         log_debug("Super has no associated prototype");
152         return false;
153 	}
154 
155     /// Dispatch.
call(const fn_call & fn)156     virtual as_value call(const fn_call& fn)
157 	{
158 
159         // TODO: this is a hack to make sure objects are constructed, not
160         // converted (fn.isInstantiation() must be true).
161         fn_call::Args::container_type argsIn(fn.getArgs());
162         fn_call::Args args;
163         args.swap(argsIn);
164 
165         fn_call fn2(fn.this_ptr, fn.env(), args, fn.super, true);
166         assert(fn2.isInstantiation());
167         as_function* ctor = constructor();
168         if (ctor) return ctor->call(fn2);
169         log_debug("Super has no associated constructor");
170         return as_value();
171 	}
172 
173 protected:
174 
markReachableResources() const175     virtual void markReachableResources() const
176 	{
177         if (_super) _super->setReachable();
178         as_object::markReachableResources();
179 	}
180 
181 private:
182 
prototype()183     as_object* prototype() {
184         return _super ? _super->get_prototype() : nullptr;
185     }
186 
constructor()187     as_function* constructor() {
188         return _super ? getConstructor(*_super) : nullptr;
189     }
190 
191     as_object* _super;
192 };
193 
194 as_object*
get_super(const ObjectURI & fname)195 as_super::get_super(const ObjectURI& fname)
196 {
197     // Super references the super class of our class prototype.
198     // Our class prototype is __proto__.
199     // Our class superclass prototype is __proto__.__proto__
200 
201     // Our class prototype is __proto__.
202     as_object* proto = get_prototype();
203     if (!proto) return new as_super(getGlobal(*this), nullptr);
204 
205     if (fname.empty() || getSWFVersion(*this) <= 6) {
206         return new as_super(getGlobal(*this), proto);
207     }
208 
209     as_object* owner = nullptr;
210     proto->findProperty(fname, &owner);
211     if (!owner) return nullptr;
212 
213     if (owner == proto) return new as_super(getGlobal(*this), proto);
214 
215     as_object* tmp = proto;
216     while (tmp && tmp->get_prototype() != owner) {
217         tmp = tmp->get_prototype();
218     }
219     // ok, now 'tmp' should be the object whose __proto__ member
220     // contains the actual named method.
221     //
222     // in the C:B:A:F case this would be B when calling
223     // super.myName() from C.prototype.myName()
224 
225     // well, since we found the property, it must be somewhere!
226     assert(tmp);
227 
228     if (tmp != proto) { return new as_super(getGlobal(*this), tmp); }
229     return new as_super(getGlobal(*this), owner);
230 
231 }
232 
233 /// A PropertyList visitor copying properties to an object
234 class PropsCopier : public PropertyVisitor
235 {
236 
237 public:
238 
239     /// \brief
240     /// Initialize a PropsCopier instance associating it
241     /// with a target object (an object whose members has to be set)
242     ///
PropsCopier(as_object & tgt)243     PropsCopier(as_object& tgt)
244         :
245         _tgt(tgt)
246 	{
247     }
248 
249     /// Set *inherited* properties of the given target object
accept(const ObjectURI & uri,const as_value & val)250     bool accept(const ObjectURI& uri, const as_value& val) {
251         if (getName(uri) == NSV::PROP_uuPROTOuu) return true;
252         _tgt.set_member(uri, val);
253         return true;
254     }
255 private:
256     as_object& _tgt;
257 };
258 
259 class PropertyEnumerator : public PropertyVisitor
260 {
261 public:
PropertyEnumerator(SortedPropertyList & to)262     PropertyEnumerator(SortedPropertyList& to)
263         :
264         _to(to)
265     {}
266 
accept(const ObjectURI & uri,const as_value & val)267     bool accept(const ObjectURI& uri, const as_value& val) {
268         _to.push_back(std::make_pair(uri, val));
269         return true;
270     }
271 private:
272     SortedPropertyList& _to;
273 };
274 
275 } // anonymous namespace
276 
277 
278 const int as_object::DefaultFlags;
279 
as_object(const Global_as & gl)280 as_object::as_object(const Global_as& gl)
281     :
282     GcResource(getRoot(gl).gc()),
283     _displayObject(nullptr),
284     _array(false),
285     _vm(getVM(gl)),
286     _members(*this)
287 {
288 }
289 
as_object(VM & vm)290 as_object::as_object(VM& vm)
291     :
292     GcResource(vm.getRoot().gc()),
293     _displayObject(nullptr),
294     _array(false),
295     _vm(vm),
296     _members(*this)
297 {
298 }
299 
300 as_value
call(const fn_call &)301 as_object::call(const fn_call& /*fn*/)
302 {
303     throw ActionTypeError();
304 }
305 
306 std::string
stringValue() const307 as_object::stringValue() const
308 {
309     return "[object Object]";
310 }
311 
312 std::pair<bool,bool>
delProperty(const ObjectURI & uri)313 as_object::delProperty(const ObjectURI& uri)
314 {
315     return _members.delProperty(uri);
316 }
317 
318 
319 void
add_property(const std::string & name,as_function & getter,as_function * setter)320 as_object::add_property(const std::string& name, as_function& getter,
321                         as_function* setter)
322 {
323     const ObjectURI& uri = getURI(vm(), name);
324 
325     Property* prop = _members.getProperty(uri);
326 
327     if (prop) {
328         const as_value& cacheVal = prop->getCache();
329         // Used to return the return value of addGetterSetter, but this
330         // is always true.
331         _members.addGetterSetter(uri, getter, setter, cacheVal);
332         return;
333         // NOTE: watch triggers not called when adding a new
334         // getter-setter property
335     }
336     else {
337 
338         _members.addGetterSetter(uri, getter, setter, as_value());
339 
340         // Nothing more to do if there are no triggers.
341         if (!_trigs.get()) return;
342 
343         // check if we have a trigger, if so, invoke it
344         // and set val to its return
345         TriggerContainer::iterator trigIter = _trigs->find(uri);
346 
347         if (trigIter != _trigs->end()) {
348 
349             Trigger& trig = trigIter->second;
350 
351             log_debug("add_property: property %s is being watched", name);
352             as_value v = trig.call(as_value(), as_value(), *this);
353 
354             // The trigger call could have deleted the property,
355             // so we check for its existence again, and do NOT put
356             // it back in if it was deleted
357             prop = _members.getProperty(uri);
358             if (!prop) {
359                 log_debug("Property %s deleted by trigger on create (getter-setter)", name);
360                 return;
361             }
362             prop->setCache(v);
363         }
364         return;
365     }
366 }
367 
368 
369 /// Order of property lookup:
370 //
371 /// 1. Visible own properties.
372 /// 2. If DisplayObject, magic properties
373 /// 3. Visible own properties of all __proto__ objects (a DisplayObject
374 ///    ends the chain).
375 /// 4. __resolve property of this object and all __proto__ objects (a Display
376 ///    Object ends the chain). This should ignore visibility but doesn't.
377 bool
get_member(const ObjectURI & uri,as_value * val)378 as_object::get_member(const ObjectURI& uri, as_value* val)
379 {
380     assert(val);
381 
382     const int version = getSWFVersion(*this);
383 
384     PrototypeRecursor<IsVisible> pr(this, uri, IsVisible(version));
385 
386     Property* prop = pr.getProperty();
387     if (!prop) {
388         if (displayObject()) {
389             DisplayObject* d = displayObject();
390             if (getDisplayObjectProperty(*d, uri, *val)) return true;
391         }
392         while (pr()) {
393             if ((prop = pr.getProperty())) break;
394         }
395     }
396 
397     // If the property isn't found or doesn't apply to any objects in the
398     // inheritance chain, try the __resolve property.
399     if (!prop) {
400 
401         PrototypeRecursor<Exists> pr(this, NSV::PROP_uuRESOLVE);
402 
403         as_value resolve;
404 
405         for (;;) {
406             Property* res = pr.getProperty();
407             if (res) {
408                 resolve = res->isGetterSetter() ? res->getCache() :
409                                                   res->getValue(*this);
410                 if (version < 7) break;
411                 if (resolve.is_object()) break;
412             }
413             // Finished searching.
414             if (!pr()) return false;
415         }
416 
417         // If __resolve exists, call it with the name of the undefined
418         // property.
419         string_table& st = getStringTable(*this);
420         const std::string& undefinedName = st.value(getName(uri));
421 
422         fn_call::Args args;
423         args += undefinedName;
424 
425         // Invoke the __resolve property.
426         *val = invoke(resolve, as_environment(getVM(*this)), this, args);
427 
428         return true;
429     }
430 
431     try {
432         *val = prop->getValue(*this);
433         return true;
434     }
435     catch (const ActionTypeError& exc) {
436         IF_VERBOSE_ASCODING_ERRORS(
437             log_aserror(_("Caught exception: %s"), exc.what());
438             );
439         return false;
440     }
441 }
442 
443 
444 as_object*
get_super(const ObjectURI & fname)445 as_object::get_super(const ObjectURI& fname)
446 {
447     // Super references the super class of our class prototype.
448     // Our class prototype is __proto__.
449     // Our class superclass prototype is __proto__.__proto__
450 
451     // Our class prototype is __proto__.
452     as_object* proto = get_prototype();
453 
454     if ( ! fname.empty() && getSWFVersion(*this) > 6) {
455         as_object* owner = nullptr;
456         findProperty(fname, &owner);
457         // should be 0 if findProperty returned 0
458         if (owner != this) proto = owner;
459     }
460 
461     as_object* super = new as_super(getGlobal(*this), proto);
462 
463     return super;
464 }
465 
466 as_object*
get_super()467 as_object::get_super()
468 {
469     // Our class prototype is __proto__.
470     as_object* proto = get_prototype();
471     as_object* super = new as_super(getGlobal(*this), proto);
472 
473     return super;
474 }
475 
476 Property*
findProperty(const ObjectURI & uri,as_object ** owner)477 as_object::findProperty(const ObjectURI& uri, as_object** owner)
478 {
479 
480     const int version = getSWFVersion(*this);
481 
482     PrototypeRecursor<IsVisible> pr(this, uri, IsVisible(version));
483 
484     do {
485         Property* prop = pr.getProperty(owner);
486         if (prop) return prop;
487     } while (pr());
488 
489     // No Property found
490     return nullptr;
491 }
492 
493 Property*
findUpdatableProperty(const ObjectURI & uri)494 as_object::findUpdatableProperty(const ObjectURI& uri)
495 {
496 
497     PrototypeRecursor<Exists> pr(this, uri);
498 
499     Property* prop = pr.getProperty();
500 
501     // We won't scan the inheritance chain if we find a member,
502     // even if invisible.
503     if (prop) return prop;
504 
505     const int swfVersion = getSWFVersion(*this);
506 
507     while (pr()) {
508         if ((prop = pr.getProperty())) {
509             if (prop->isGetterSetter() && visible(*prop, swfVersion)) {
510                 return prop;
511             }
512         }
513     }
514     return nullptr;
515 }
516 
517 void
set_prototype(const as_value & proto)518 as_object::set_prototype(const as_value& proto)
519 {
520     // TODO: check what happens if __proto__ is set as a user-defined
521     // getter/setter
522     // TODO: check triggers !!
523     _members.setValue(NSV::PROP_uuPROTOuu, proto, as_object::DefaultFlags);
524 }
525 
526 void
executeTriggers(Property * prop,const ObjectURI & uri,const as_value & val)527 as_object::executeTriggers(Property* prop, const ObjectURI& uri,
528                            const as_value& val)
529 {
530 
531     // check if we have a trigger, if so, invoke it
532     // and set val to its return
533     TriggerContainer::iterator trigIter;
534 
535     // If there are no triggers or the trigger is not found, just set
536     // the property.
537     if (!_trigs.get() || (trigIter = _trigs->find(uri)) == _trigs->end()) {
538         if (prop) {
539             prop->setValue(*this, val);
540             prop->clearVisible(getSWFVersion(*this));
541         }
542         return;
543     }
544 
545     Trigger& trig = trigIter->second;
546 
547     if (trig.dead()) {
548         _trigs->erase(trigIter);
549         return;
550     }
551 
552     // WARNING: getValue might itself invoke a trigger
553     // (getter-setter)... ouch ?
554     // TODO: in this case, return the underlying value !
555     const as_value& curVal = prop ? prop->getCache() : as_value();
556     const as_value& newVal = trig.call(curVal, val, *this);
557 
558     // This is a particularly clear and concise way of removing dead triggers.
559     EraseIf(*_trigs, std::bind(std::mem_fn(&Trigger::dead),
560              std::bind(&TriggerContainer::value_type::second,
561              std::placeholders::_1)));
562 
563     // The trigger call could have deleted the property,
564     // so we check for its existence again, and do NOT put
565     // it back in if it was deleted
566     prop = findUpdatableProperty(uri);
567     if (!prop) return;
568 
569     prop->setValue(*this, newVal);
570     prop->clearVisible(getSWFVersion(*this));
571 
572 }
573 
574 /// Order of property lookup:
575 //
576 /// 0. MovieClip textfield variables. TODO: this is a hack and should be
577 ///    eradicated.
578 /// 1. Own properties even if invisible or not getter-setters.
579 /// 2. If DisplayObject, magic properties
580 /// 3. Visible own getter-setter properties of all __proto__ objects
581 ///    (a DisplayObject ends the chain).
582 bool
set_member(const ObjectURI & uri,const as_value & val,bool ifFound)583 as_object::set_member(const ObjectURI& uri, const as_value& val, bool ifFound)
584 {
585 
586     bool tfVarFound = false;
587     if (displayObject()) {
588         MovieClip* mc = dynamic_cast<MovieClip*>(displayObject());
589         if (mc) tfVarFound = mc->setTextFieldVariables(uri, val);
590         // We still need to set the member.
591     }
592 
593     // Handle the length property for arrays. NB: checkArrayLength() will
594     // call this function again if the key is a valid index.
595     if (array()) checkArrayLength(*this, uri, val);
596 
597     PrototypeRecursor<Exists> pr(this, uri);
598 
599     Property* prop = pr.getProperty();
600 
601     // We won't scan the inheritance chain if we find a member,
602     // even if invisible.
603     if (!prop) {
604 
605         if (displayObject()) {
606             DisplayObject* d = displayObject();
607             if (setDisplayObjectProperty(*d, uri, val)) return true;
608             // TODO: should we execute triggers?
609         }
610 
611         const int version = getSWFVersion(*this);
612         while (pr()) {
613             if ((prop = pr.getProperty())) {
614                 if ((prop->isGetterSetter()) && visible(*prop, version)) {
615                     break;
616                 }
617                 else prop = nullptr;
618             }
619         }
620     }
621 
622     if (prop) {
623         if (readOnly(*prop)) {
624             IF_VERBOSE_ASCODING_ERRORS(
625                 ObjectURI::Logger l(getStringTable(*this));
626                 log_aserror(_("Attempt to set read-only property '%s'"),
627                             l(uri));
628                 );
629             return true;
630         }
631 
632         try {
633             executeTriggers(prop, uri, val);
634         }
635         catch (const ActionTypeError& exc) {
636             IF_VERBOSE_ASCODING_ERRORS(
637                 log_aserror(
638                 _("%s: %s"), getStringTable(*this).value(getName(uri)), exc.what());
639             );
640         }
641 
642         return true;
643     }
644 
645     // Else, add new property...
646     if (ifFound) return false;
647 
648     // Property does not exist, so it won't be read-only. Set it.
649     if (!_members.setValue(uri, val)) {
650 
651         IF_VERBOSE_ASCODING_ERRORS(
652             ObjectURI::Logger l(getStringTable(*this));
653             log_aserror(_("Unknown failure in setting property '%s' on "
654                           "object '%p'"), l(uri), (void*) this);
655             );
656         return false;
657     }
658 
659     executeTriggers(prop, uri, val);
660 
661     // Return true if we found a textfield variable.
662     if (tfVarFound) return true;
663 
664     return false;
665 }
666 
667 
668 void
init_member(const std::string & key1,const as_value & val,int flags)669 as_object::init_member(const std::string& key1, const as_value& val, int flags)
670 {
671     const ObjectURI& uri(getURI(vm(), key1));
672     init_member(uri, val, flags);
673 }
674 
675 void
init_member(const ObjectURI & uri,const as_value & val,int flags)676 as_object::init_member(const ObjectURI& uri, const as_value& val, int flags)
677 {
678 
679     // Set (or create) a SimpleProperty
680     if (!_members.setValue(uri, val, flags)) {
681         ObjectURI::Logger l(getStringTable(*this));
682         log_error(_("Attempt to initialize read-only property '%s'"
683                     " on object '%p' twice"), l(uri), (void*)this);
684         // We shouldn't attempt to initialize a member twice, should we ?
685         abort();
686     }
687 }
688 
689 void
init_property(const std::string & name,as_function & getter,as_function & setter,int flags)690 as_object::init_property(const std::string& name, as_function& getter,
691                          as_function& setter, int flags)
692 {
693     const ObjectURI& uri = getURI(vm(), name);
694     init_property(uri, getter, setter, flags);
695 }
696 
697 void
init_property(const ObjectURI & uri,as_function & getter,as_function & setter,int flags)698 as_object::init_property(const ObjectURI& uri, as_function& getter,
699                          as_function& setter, int flags)
700 {
701     _members.addGetterSetter(uri, getter, &setter, as_value(), flags);
702 }
703 
704 void
init_property(const std::string & name,as_c_function_ptr getter,as_c_function_ptr setter,int flags)705 as_object::init_property(const std::string& name, as_c_function_ptr getter,
706                          as_c_function_ptr setter, int flags)
707 {
708     const ObjectURI& uri = getURI(vm(), name);
709     init_property(uri, getter, setter, flags);
710 }
711 
712 void
init_property(const ObjectURI & uri,as_c_function_ptr getter,as_c_function_ptr setter,int flags)713 as_object::init_property(const ObjectURI& uri, as_c_function_ptr getter,
714                          as_c_function_ptr setter, int flags)
715 {
716     _members.addGetterSetter(uri, getter, setter, flags);
717 }
718 
719 bool
init_destructive_property(const ObjectURI & uri,as_function & getter,int flags)720 as_object::init_destructive_property(const ObjectURI& uri, as_function& getter,
721                                      int flags)
722 {
723     return _members.addDestructiveGetter(uri, getter, flags);
724 }
725 
726 bool
init_destructive_property(const ObjectURI & uri,as_c_function_ptr getter,int flags)727 as_object::init_destructive_property(const ObjectURI& uri,
728                                      as_c_function_ptr getter, int flags)
729 {
730     return _members.addDestructiveGetter(uri, getter, flags);
731 }
732 
733 void
init_readonly_property(const std::string & name,as_function & getter,int initflags)734 as_object::init_readonly_property(const std::string& name, as_function& getter,
735                                   int initflags)
736 {
737     const ObjectURI& uri = getURI(vm(), name);
738 
739     init_property(uri, getter, getter, initflags | PropFlags::readOnly);
740     assert(_members.getProperty(uri));
741 }
742 
743 void
init_readonly_property(const std::string & name,as_c_function_ptr getter,int initflags)744 as_object::init_readonly_property(const std::string& name,
745                                   as_c_function_ptr getter, int initflags)
746 {
747     const ObjectURI& uri = getURI(vm(), name);
748     init_property(uri, getter, getter, initflags | PropFlags::readOnly);
749     assert(_members.getProperty(uri));
750 }
751 
752 void
set_member_flags(const ObjectURI & uri,int setTrue,int setFalse)753 as_object::set_member_flags(const ObjectURI& uri, int setTrue, int setFalse)
754 {
755     _members.setFlags(uri, setTrue, setFalse);
756 }
757 
758 void
addInterface(as_object * obj)759 as_object::addInterface(as_object* obj)
760 {
761     assert(obj);
762     if (std::find(_interfaces.begin(), _interfaces.end(), obj) ==
763         _interfaces.end()) {
764         _interfaces.push_back(obj);
765     }
766 }
767 
768 bool
instanceOf(as_object * ctor)769 as_object::instanceOf(as_object* ctor)
770 {
771 
772     /// An object is never an instance of a null prototype.
773     if (!ctor) return false;
774 
775     as_value protoVal;
776     if (!ctor->get_member(NSV::PROP_PROTOTYPE, &protoVal)) {
777 #ifdef GNASH_DEBUG_INSTANCE_OF
778         log_debug("Object %p can't be an instance of an object (%p) with no 'prototype'",
779                   (void*)this, (void*)ctor);
780 #endif
781         return false;
782     }
783 
784     as_object* ctorProto = toObject(protoVal, getVM(*this));
785     if (!ctorProto) {
786 #ifdef GNASH_DEBUG_INSTANCE_OF
787         log_debug("Object %p can't be an instance of an object (%p) with non-object 'prototype' (%s)",
788                   (void*)this, (void*)ctor, protoVal);
789 #endif
790         return false;
791     }
792 
793     // TODO: cleanup the iteration, make it more readable ...
794     std::set<as_object*> visited;
795 
796     as_object* obj = this;
797     while (obj && visited.insert(obj).second) {
798         as_object* thisProto = obj->get_prototype();
799         if (!thisProto) {
800             break;
801         }
802 
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 the constructor exposes our __proto__ %p",
807                       (void*)obj, (void*)ctor, (void*)thisProto);
808 #endif
809             return true;
810         }
811 
812         // Check our proto interfaces
813         if (std::find(thisProto->_interfaces.begin(),
814                       thisProto->_interfaces.end(), ctorProto)
815             != thisProto->_interfaces.end()) {
816 
817 #ifdef GNASH_DEBUG_INSTANCE_OF
818             log_debug("Object %p __proto__ %p had one interface matching with the constructor prototype %p",
819                       (void*)obj, (void*)thisProto, (void*)ctorProto);
820 #endif
821             return true;
822         }
823 
824         obj = thisProto;
825     }
826 
827     return false;
828 }
829 
830 bool
prototypeOf(as_object & instance)831 as_object::prototypeOf(as_object& instance)
832 {
833     as_object* obj = &instance;
834 
835     std::set<as_object*> visited;
836 
837     while (obj && visited.insert(obj).second ) {
838         if (obj->get_prototype() == this) return true;
839         obj = obj->get_prototype();
840 	}
841 
842     // See actionscript.all/Inheritance.as for a way to trigger this
843     IF_VERBOSE_ASCODING_ERRORS(
844         if (obj) log_aserror(_("Circular inheritance chain detected "
845                                "during isPrototypeOf call"));
846 	);
847 
848     return false;
849 }
850 
851 void
dump_members()852 as_object::dump_members()
853 {
854     log_debug("%d members of object %p follow", _members.size(),
855             static_cast<const void*>(this));
856     _members.dump();
857 }
858 
859 void
setPropFlags(const as_value & props_val,int set_false,int set_true)860 as_object::setPropFlags(const as_value& props_val, int set_false, int set_true)
861 {
862 
863     if (props_val.is_null()) {
864         // Take all the members of the object
865         _members.setFlagsAll(set_true, set_false);
866         return;
867     }
868 
869     std::string propstr = props_val.to_string();
870 
871     for (;;) {
872 
873         std::string prop;
874         size_t next_comma=propstr.find(",");
875         if (next_comma == std::string::npos) {
876             prop = propstr;
877         }
878         else {
879             prop = propstr.substr(0,next_comma);
880             propstr = propstr.substr(next_comma+1);
881         }
882 
883         // set_member_flags will take care of case conversion
884         set_member_flags(getURI(vm(), prop), set_true, set_false);
885 
886         if (next_comma == std::string::npos) {
887             break;
888         }
889     }
890     return;
891 }
892 
893 
894 void
copyProperties(const as_object & o)895 as_object::copyProperties(const as_object& o)
896 {
897     PropsCopier copier(*this);
898 
899     // TODO: check if non-visible properties should be also copied !
900     o.visitProperties<Exists>(copier);
901 }
902 
903 void
visitKeys(KeyVisitor & visitor) const904 as_object::visitKeys(KeyVisitor& visitor) const
905 {
906     // Hack to handle MovieClips.
907     if (displayObject()) {
908         displayObject()->visitNonProperties(visitor);
909     }
910 
911     // this set will keep track of visited objects,
912     // to avoid infinite loops
913     std::set<const as_object*> visited;
914 
915     PropertyList::PropertyTracker doneList;
916 
917     const as_object* current(this);
918     while (current && visited.insert(current).second) {
919         current->_members.visitKeys(visitor, doneList);
920         current = current->get_prototype();
921     }
922 }
923 
924 
925 Property*
getOwnProperty(const ObjectURI & uri)926 as_object::getOwnProperty(const ObjectURI& uri)
927 {
928     return _members.getProperty(uri);
929 }
930 
931 as_object*
get_prototype() const932 as_object::get_prototype() const
933 {
934     int swfVersion = getSWFVersion(*this);
935 
936     Property* prop = _members.getProperty(NSV::PROP_uuPROTOuu);
937     if (!prop) return nullptr;
938     if (!visible(*prop, swfVersion)) return nullptr;
939 
940     const as_value& proto = prop->getValue(*this);
941 
942     return toObject(proto, getVM(*this));
943 }
944 
945 std::string
getURLEncodedVars(as_object & o)946 getURLEncodedVars(as_object& o)
947 {
948     SortedPropertyList props = enumerateProperties(o);
949 
950     std::string data;
951     string_table& st = getStringTable(o);
952 
953     for (SortedPropertyList::const_reverse_iterator i = props.rbegin(),
954             e = props.rend(); i != e; ++i) {
955 
956         const std::string& name = i->first.toString(st);
957         const std::string& value = i->second.to_string();
958 
959         // see bug #22006
960         if (!name.empty() && name[0] == '$') continue;
961 
962         URL::encode(value);
963         if (i != props.rbegin()) data += '&';
964 
965         data += name + "=" + value;
966 
967     }
968     return data;
969 }
970 
971 bool
watch(const ObjectURI & uri,as_function & trig,const as_value & cust)972 as_object::watch(const ObjectURI& uri, as_function& trig,
973 		const as_value& cust)
974 {
975 
976     std::string propname = getStringTable(*this).value(getName(uri));
977 
978     if (!_trigs.get()) _trigs.reset(new TriggerContainer);
979 
980     TriggerContainer::iterator it = _trigs->find(uri);
981     if (it == _trigs->end()) {
982         return _trigs->insert(
983             std::make_pair(uri, Trigger(propname, trig, cust))).second;
984     }
985     it->second = Trigger(propname, trig, cust);
986     return true;
987 }
988 
989 bool
unwatch(const ObjectURI & uri)990 as_object::unwatch(const ObjectURI& uri)
991 {
992     if (!_trigs.get()) return false;
993 
994     TriggerContainer::iterator trigIter = _trigs->find(uri);
995     if (trigIter == _trigs->end()) {
996         log_debug("No watch for property %s",
997                   getStringTable(*this).value(getName(uri)));
998         return false;
999     }
1000     Property* prop = _members.getProperty(uri);
1001     if (prop && prop->isGetterSetter()) {
1002         log_debug("Watch on %s not removed (is a getter-setter)",
1003                   getStringTable(*this).value(getName(uri)));
1004         return false;
1005     }
1006     trigIter->second.kill();
1007     return true;
1008 }
1009 
1010 void
markReachableResources() const1011 as_object::markReachableResources() const
1012 {
1013     _members.setReachable();
1014 
1015     if (_trigs.get()) {
1016         for (TriggerContainer::const_iterator it = _trigs->begin();
1017              it != _trigs->end(); ++it) {
1018             it->second.setReachable();
1019         }
1020     }
1021 
1022     // Mark interfaces reachable.
1023     std::for_each(_interfaces.begin(), _interfaces.end(),
1024                   std::mem_fun(&as_object::setReachable));
1025 
1026     // Proxy objects can contain references to other as_objects.
1027     if (_relay) _relay->setReachable();
1028     if (_displayObject) _displayObject->setReachable();
1029 }
1030 
1031 void
setReachable() const1032 Trigger::setReachable() const
1033 {
1034 	_func->setReachable();
1035 	_customArg.setReachable();
1036 }
1037 
1038 as_value
call(const as_value & oldval,const as_value & newval,as_object & this_obj)1039 Trigger::call(const as_value& oldval, const as_value& newval,
1040         as_object& this_obj)
1041 {
1042     assert(!_dead);
1043 
1044     if (_executing) return newval;
1045 
1046     _executing = true;
1047 
1048     try {
1049 
1050         const as_environment env(getVM(this_obj));
1051 
1052         fn_call::Args args;
1053         args += _propname, oldval, newval, _customArg;
1054 
1055         fn_call fn(&this_obj, env, args);
1056         as_value ret = _func->call(fn);
1057         _executing = false;
1058 
1059         return ret;
1060 
1061     }
1062     catch (const GnashException&) {
1063         _executing = false;
1064         throw;
1065     }
1066 }
1067 
1068 SortedPropertyList
enumerateProperties(as_object & obj)1069 enumerateProperties(as_object& obj)
1070 {
1071 
1072     // this set will keep track of visited objects,
1073     // to avoid infinite loops
1074     std::set<as_object*> visited;
1075 
1076     SortedPropertyList to;
1077     PropertyEnumerator e(to);
1078     as_object* current(&obj);
1079 
1080     while (current && visited.insert(current).second) {
1081         current->visitProperties<IsEnumerable>(e);
1082         current = current->get_prototype();
1083     }
1084     return to;
1085 
1086 }
1087 
1088 as_object*
getPathElement(as_object & o,const ObjectURI & uri)1089 getPathElement(as_object& o, const ObjectURI& uri)
1090 {
1091     as_value tmp;
1092     if (!o.get_member(uri, &tmp)) return nullptr;
1093     if (!tmp.is_object()) return nullptr;
1094     return toObject(tmp, getVM(o));
1095 }
1096 
1097 
1098 void
sendEvent(as_object & o,const as_environment & env,const ObjectURI & name)1099 sendEvent(as_object& o, const as_environment& env, const ObjectURI& name)
1100 {
1101     Property* prop = o.findProperty(name);
1102     if (prop) {
1103         fn_call::Args args;
1104         invoke(prop->getValue(o), env, &o, args);
1105     }
1106 }
1107 
1108 as_object*
getObjectWithPrototype(Global_as & gl,const ObjectURI & c)1109 getObjectWithPrototype(Global_as& gl, const ObjectURI& c)
1110 {
1111     as_object* ctor = toObject(getMember(gl, c), getVM(gl));
1112     as_object* proto = ctor ?
1113         toObject(getMember(*ctor, NSV::PROP_PROTOTYPE), getVM(gl)) : nullptr;
1114 
1115     as_object* o = createObject(gl);
1116     o->set_prototype(proto ? proto : as_value());
1117     return o;
1118 }
1119 
1120 /// Get the VM from an as_object
1121 VM&
getVM(const as_object & o)1122 getVM(const as_object& o)
1123 {
1124     return o.vm();
1125 }
1126 
1127 /// Get the movie_root from an as_object
1128 movie_root&
getRoot(const as_object & o)1129 getRoot(const as_object& o)
1130 {
1131     return o.vm().getRoot();
1132 }
1133 
1134 /// Get the string_table from an as_object
1135 string_table&
getStringTable(const as_object & o)1136 getStringTable(const as_object& o)
1137 {
1138     return o.vm().getStringTable();
1139 }
1140 
1141 const RunResources&
getRunResources(const as_object & o)1142 getRunResources(const as_object& o)
1143 {
1144     return o.vm().getRoot().runResources();
1145 }
1146 
1147 int
getSWFVersion(const as_object & o)1148 getSWFVersion(const as_object& o)
1149 {
1150     return o.vm().getSWFVersion();
1151 }
1152 
1153 Global_as&
getGlobal(const as_object & o)1154 getGlobal(const as_object& o)
1155 {
1156     return *o.vm().getGlobal();
1157 }
1158 
1159 } // end of gnash namespace
1160 
1161 // local Variables:
1162 // mode: C++
1163 // indent-tabs-mode: nil
1164 // End:
1165