1
// as_object.cpp: ActionScript Object class and its properties, for Gnash.
3
// Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
// This program is free software; you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation; either version 3 of the License, or
8
// (at your option) any later version.
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
// GNU General Public License for more details.
15
// You should have received a copy of the GNU General Public License
16
// along with this program; if not, write to the Free Software
17
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
#include "gnashconfig.h"
25
#include "smart_ptr.h" // GNASH_USE_GC
26
#include "as_object.h"
27
#include "as_function.h"
28
#include "as_environment.h" // for enumerateProperties
29
#include "Property.h" // for findGetterSetter
31
#include "GnashException.h"
32
#include "fn_call.h" // for generic methods
33
#include "Object.h" // for getObjectInterface
34
#include "action.h" // for call_method
35
#include "array.h" // for setPropFlags
36
#include "as_function.h" // for inheritance of as_super
40
#include <boost/algorithm/string/case_conv.hpp>
41
#include <utility> // for std::pair
42
#include "namedStrings.h"
46
// Anonymous namespace used for module-static defs
49
using namespace gnash;
51
/// 'super' is a special kind of object
53
/// See http://wiki.gnashdev.org/wiki/index.php/ActionScriptSuper
55
/// We make it derive from as_function instead of as_object
56
/// to avoid touching too many files (ie: an as_object is not considered
57
/// something that can be called by current Gnash code). We may want
58
/// to change this in the future to implement what ECMA-262 refers to
59
/// as the [[Call]] property of objects.
61
class as_super : public as_function
65
as_super(as_function* ctor, as_object* proto)
71
//log_debug("as_super %p constructed with ctor %p and proto %p", this, ctor, proto);
74
virtual bool isSuper() const { return true; }
76
virtual as_object* get_super(const char* fname=0);
78
std::string get_text_value() const
80
return "[object Object]";
83
// Fetching members from 'super' yelds a lookup on the associated prototype
84
virtual bool get_member(string_table::key name, as_value* val,
85
string_table::key nsname = 0)
87
//log_debug("as_super::get_member %s called - _proto is %p", getVM().getStringTable().value(name), _proto);
88
if ( _proto ) return _proto->get_member(name, val, nsname);
89
log_debug("Super has no associated prototype");
93
// Setting members on 'super' is a no-op
94
virtual void set_member(string_table::key /*key*/, const as_value& /*val*/,
95
string_table::key /*nsname*/ = 0)
97
// can't assign to super
98
IF_VERBOSE_ASCODING_ERRORS(
99
log_aserror("Can't set members on the 'super' object");
104
virtual as_value operator()(const fn_call& fn)
106
//log_debug("Super call operator. fn.this_ptr is %p", fn.this_ptr);
107
if ( _ctor ) return _ctor->call(fn);
108
log_debug("Super has no associated constructor");
114
virtual void markReachableResources() const
116
if ( _ctor ) _ctor->setReachable();
117
if ( _proto ) _proto->setReachable();
118
markAsFunctionReachable();
128
as_super::get_super(const char* fname)
130
// Super references the super class of our class prototype.
131
// Our class prototype is __proto__.
132
// Our class superclass prototype is __proto__.__proto__
134
// Our class prototype is __proto__.
135
as_object* proto = get_prototype().get();
138
//log_debug("We (a super) have no associated prototype, returning a null-referencing as_super from get_super()");
139
return new as_super(0, 0);
142
// proto's __proto__ is superProto
143
as_object* superProto = proto->get_prototype().get();
145
// proto's __constructor__ is superCtor
146
as_function* superCtor = proto->get_constructor();
147
assert(superCtor == get_constructor());
149
//log_debug("super %p proto is %p, its prototype %p", this, proto, proto->get_prototype());
152
if ( fname && vm.getSWFVersion() > 6)
154
as_object* owner = 0;
155
string_table& st = vm.getStringTable();
156
string_table::key k = st.find(fname);
158
proto->findProperty(k, 0, &owner);
161
//log_debug("get_super: can't find property %s", fname);
165
//log_debug("object containing method %s is %p, its __proto__ is %p", fname, owner, owner->get_prototype());
169
if ( owner != proto )
171
as_object* tmp = proto;
172
while (tmp && tmp->get_prototype() != owner) tmp = tmp->get_prototype().get();
173
// ok, now 'tmp' should be the object whose __proto__ member contains
174
// the actual named method.
176
// in the C:B:A:F case this would be B when calling super.myName() from
177
// C.prototype.myName()
180
assert(tmp); // well, since we found the property, it must be somewhere!
182
//log_debug("tmp is %p", tmp);
186
//assert(superProto == tmp->get_prototype().get());
188
//superCtor = superProto->get_constructor();
189
superCtor = tmp->get_constructor();
190
//if ( ! superCtor ) log_debug("superProto (owner) has no __constructor__");
194
//log_debug("tmp == proto");
195
superCtor = owner->get_constructor(); // most likely..
196
if ( superProto ) superProto = superProto->get_prototype().get();
201
// TODO: check if we've anything to do here...
202
//log_debug("owner == proto == %p", owner);
203
//if ( superProto ) superProto = superProto->get_prototype().get();
204
//superCtor = superProto->get_constructor();
207
// superCtor = superProto->get_constructor();
208
//} // else superCtor = NULL ?
212
as_object* super = new as_super(superCtor, superProto);
218
/// A PropertyList visitor copying properties to an object
219
class PropsCopier : public AbstractPropertyVisitor {
226
/// Initialize a PropsCopier instance associating it
227
/// with a target object (an object whose members has to be set)
229
PropsCopier(as_object& tgt)
235
/// Use the set_member function to properly set *inherited* properties
236
/// of the given target object
238
void accept(string_table::key name, const as_value& val)
240
if (name == NSV::PROP_uuPROTOuu) return;
241
//log_debug(_("Setting member '%s' to value '%s'"), name, val);
242
_tgt.set_member(name, val);
246
} // end of anonymous namespace
252
as_object::add_property(const std::string& name, as_function& getter,
255
string_table &st = _vm.getStringTable();
256
string_table::key k = st.find(name);
260
Property* prop = _members.getProperty(k);
263
cacheVal = prop->getCache();
264
return _members.addGetterSetter(k, getter, setter, cacheVal);
266
// NOTE: watch triggers not called when adding a new getter-setter property
271
bool ret = _members.addGetterSetter(k, getter, setter, cacheVal);
272
if (!ret) return false;
275
// check if we have a trigger, if so, invoke it
276
// and set val to it's return
277
TriggerContainer::iterator trigIter = _trigs.find(std::make_pair(k, 0));
278
if ( trigIter != _trigs.end() )
280
Trigger& trig = trigIter->second;
282
log_debug("add_property: property %s is being watched, current val: %s", name, cacheVal);
283
cacheVal = trig.call(cacheVal, as_value(), *this);
285
// The trigger call could have deleted the property,
286
// so we check for its existance again, and do NOT put
287
// it back in if it was deleted
288
prop = _members.getProperty(k);
291
log_debug("Property %s deleted by trigger on create (getter-setter)", name);
292
return false; // or true ?
294
prop->setCache(cacheVal);
295
//prop->setValue(*this, cacheVal);
305
as_object::get_member(string_table::key name, as_value* val,
306
string_table::key nsname)
310
Property* prop = findProperty(name, nsname);
316
*val = prop->getValue(*this);
319
catch (ActionLimitException& exc)
321
// will be logged by outer catcher
324
catch (ActionTypeError& exc)
326
// TODO: check if this should be an 'as' error.. (log_aserror)
327
log_error(_("Caught exception: %s"), exc.what());
334
as_object::getByIndex(int index)
336
// The low byte is used to contain the depth of the property.
337
unsigned char depth = index & 0xFF;
338
index /= 256; // Signed
339
as_object *obj = this;
342
obj = obj->get_prototype().get();
347
return obj->_members.getPropertyByOrder(index);
351
as_object::get_super(const char* fname)
353
// Super references the super class of our class prototype.
354
// Our class prototype is __proto__.
355
// Our class superclass prototype is __proto__.__proto__
357
// Our class prototype is __proto__.
358
as_object* proto = get_prototype().get();
361
if ( fname && vm.getSWFVersion() > 6)
363
as_object* owner = 0;
364
string_table& st = vm.getStringTable();
365
string_table::key k = st.find(fname);
366
/*Property* p =*/ findProperty(k, 0, &owner);
367
if ( owner != this ) proto = owner; // should be 0 if findProperty returned 0
370
// proto's __proto__ is superProto
371
as_object* superProto = proto ? proto->get_prototype().get() : 0;
373
// proto's __constructor__ is superCtor
374
as_function* superCtor = proto ? proto->get_constructor() : 0;
376
as_object* super = new as_super(superCtor, superProto);
382
as_object::get_constructor()
385
if ( ! get_member(NSV::PROP_uuCONSTRUCTORuu, &ctorVal) )
387
//log_debug("Object %p has no __constructor__ member");
390
//log_debug("%p.__constructor__ is %s", ctorVal);
391
return ctorVal.to_as_function();
395
as_object::nextIndex(int index, as_object **owner)
398
unsigned char depth = index & 0xFF;
399
unsigned char i = depth;
400
index /= 256; // Signed
401
as_object *obj = this;
404
obj = obj->get_prototype().get();
409
const Property *p = obj->_members.getOrderAfter(index);
412
obj = obj->get_prototype().get();
415
p = obj->_members.getOrderAfter(0);
420
if (findProperty(p->getName(), p->getNamespace()) != p)
422
index = p->getOrder() * 256 | depth;
423
goto skip_duplicates; // Faster than recursion.
427
return p->getOrder() * 256 | depth;
434
as_object::findProperty(string_table::key key, string_table::key nsname,
437
int swfVersion = _vm.getSWFVersion();
439
// don't enter an infinite loop looking for __proto__ ...
440
if (key == NSV::PROP_uuPROTOuu && !nsname)
442
Property* prop = _members.getProperty(key, nsname);
443
// TODO: add ignoreVisibility parameter to allow using __proto__ even when not visible ?
444
if (prop && prop->isVisible(swfVersion))
453
// keep track of visited objects, avoid infinite loops.
454
std::set<as_object*> visited;
458
boost::intrusive_ptr<as_object> obj = this;
460
// This recursion prevention seems not to exist in the PP.
461
// Instead, it stops when its general timeout for the
462
// execution of scripts is reached.
463
while (obj && visited.insert(obj.get()).second)
466
if ((i > 255 && swfVersion == 5) || i > 257)
467
throw ActionLimitException("Lookup depth exceeded.");
469
Property* prop = obj->_members.getProperty(key);
470
if (prop && prop->isVisible(swfVersion) )
477
obj = obj->get_prototype();
485
as_object::findUpdatableProperty(string_table::key key, string_table::key nsname)
487
int swfVersion = _vm.getSWFVersion();
489
Property* prop = _members.getProperty(key, nsname);
491
// We won't scan the inheritance chain if we find a member,
492
// even if invisible.
494
if ( prop ) return prop; // TODO: what about isVisible ?
496
// don't enter an infinite loop looking for __proto__ ...
497
if (key == NSV::PROP_uuPROTOuu) return NULL;
499
std::set<as_object*> visited;
500
visited.insert(this);
504
boost::intrusive_ptr<as_object> obj = get_prototype();
506
// TODO: does this recursion protection exist in the PP?
507
while (obj && visited.insert(obj.get()).second)
510
if ((i > 255 && swfVersion == 5) || i > 257)
511
throw ActionLimitException("Property lookup depth exceeded.");
513
Property* p = obj->_members.getProperty(key, nsname);
514
if (p && (p->isGetterSetter() | p->isStatic()) && p->isVisible(swfVersion))
516
return p; // What should we do if this is not a getter/setter ?
518
obj = obj->get_prototype();
525
as_object::set_prototype(boost::intrusive_ptr<as_object> proto, int flags)
527
static string_table::key key = NSV::PROP_uuPROTOuu;
529
// TODO: check what happens if __proto__ is set as a user-defined getter/setter
530
// TODO: check triggers !!
531
_members.setValue(key, as_value(proto.get()), *this, 0, flags);
535
as_object::reserveSlot(string_table::key name, string_table::key nsId,
536
unsigned short slotId)
538
_members.reserveSlot(name, nsId, slotId);
541
// Handles read_only and static properties properly.
543
as_object::set_member(string_table::key key, const as_value& val,
544
string_table::key nsname, bool ifFound)
546
//log_debug(_("set_member_default(%s)"), key);
547
Property* prop = findUpdatableProperty(key, nsname);
550
if (prop->isReadOnly())
552
IF_VERBOSE_ASCODING_ERRORS(log_aserror(_(""
553
"Attempt to set read-only property '%s'"),
554
_vm.getStringTable().value(key)););
560
// check if we have a trigger, if so, invoke it
561
// and set val to it's return
562
TriggerContainer::iterator trigIter = _trigs.find(std::make_pair(key, nsname));
563
if ( trigIter != _trigs.end() )
565
Trigger& trig = trigIter->second;
567
// WARNING: getValue might itself invoke a trigger
568
// (getter-setter)... ouch ?
569
// TODO: in this case, return the underlying value !
570
as_value curVal = prop->getCache(); // getValue(*this);
572
log_debug("Existing property %s is being watched: firing trigger on update (current val:%s, new val:%s)",
573
_vm.getStringTable().value(key), curVal, val);
574
as_value newVal = trig.call(curVal, val, *this);
575
// The trigger call could have deleted the property,
576
// so we check for its existance again, and do NOT put
577
// it back in if it was deleted
578
prop = findUpdatableProperty(key, nsname);
581
log_debug("Property %s deleted by trigger on update", _vm.getStringTable().value(key));
585
//if ( prop->isGetterSetter() ) prop->setCache(newVal);
586
prop->setValue(*this, newVal);
590
// log_debug("No trigger for key %d ns %d", key, nsname);
591
prop->setValue(*this, val);
594
prop->clearVisible(_vm.getSWFVersion());
596
catch (ActionTypeError& exc)
598
log_aserror(_("%s: Exception %s. Will create a new member"),
599
_vm.getStringTable().value(key), exc.what());
605
// Else, add new property...
606
if ( ifFound ) return false;
608
// Property does not exist, so it won't be read-only. Set it.
609
if (!_members.setValue(key, val, *this, nsname))
611
IF_VERBOSE_ASCODING_ERRORS(
612
log_aserror(_("Unknown failure in setting property '%s' on "
613
"object '%p'"), _vm.getStringTable().value(key), (void*) this);
618
// Now check if we have a trigger, if so, invoke it
619
// and reset val to it's return
620
// NOTE that we do this *after* setting it in first place
621
// as the trigger seems allowed to delete the property again
622
TriggerContainer::iterator trigIter = _trigs.find(std::make_pair(key, nsname));
623
if ( trigIter != _trigs.end() )
625
Trigger& trig = trigIter->second;
627
log_debug("Property %s is being watched, calling trigger on create", _vm.getStringTable().value(key));
629
// NOTE: the trigger call might delete the propery being added
630
// so we first add the property, then call the trigger
631
// and finally check if the property still exists (ufff...)
634
as_value curVal; // undefined, didn't exist...
635
as_value newVal = trig.call(curVal, val, *this);
636
Property* prop = _members.getProperty(key);
639
log_debug("Property %s deleted by trigger on create", _vm.getStringTable().value(key));
643
prop->setValue(*this, newVal);
652
as_object::update_member(string_table::key key, const as_value& val,
653
string_table::key nsname)
655
std::pair<bool,bool> ret; // first is found, second is updated
657
//log_debug(_("set_member_default(%s)"), key);
658
Property* prop = findUpdatableProperty(key, nsname);
661
if (prop->isReadOnly())
663
IF_VERBOSE_ASCODING_ERRORS(log_aserror(_(""
664
"Attempt to set read-only property '%s'"),
665
_vm.getStringTable().value(key)););
666
return std::make_pair(true, false);
671
as_value newVal = val;
673
// check if we have a trigger, if so, invoke it
674
// and set val to it's return
675
TriggerContainer::iterator trigIter = _trigs.find(std::make_pair(key, nsname));
676
if ( trigIter != _trigs.end() )
678
Trigger& trig = trigIter->second;
679
// WARNING: getValue might itself invoke a trigger (getter-setter)... ouch ?
680
as_value curVal = prop->getCache(); // Value(*this);
681
log_debug("Property %s is being watched: firing trigger on update (current val:%s, new val:%s",
682
_vm.getStringTable().value(key),
684
newVal = trig.call(curVal, val, *this);
685
// The trigger call could have deleted the property,
686
// so we check for its existance again, and do NOT put
687
// it back in if it was deleted
688
prop = findUpdatableProperty(key, nsname);
691
return std::make_pair(true, true);
695
prop->setValue(*this, newVal);
696
return std::make_pair(true, true);
698
catch (ActionTypeError& exc)
700
log_debug(_("%s: Exception %s. Will create a new member"),
701
_vm.getStringTable().value(key), exc.what());
704
return std::make_pair(true, false);
707
return std::make_pair(false, false);
712
as_object::init_member(const std::string& key1, const as_value& val, int flags,
713
string_table::key nsname)
715
init_member(_vm.getStringTable().find(PROPNAME(key1)), val, flags, nsname);
719
as_object::init_member(string_table::key key, const as_value& val, int flags,
720
string_table::key nsname, int order)
722
//log_debug(_("Initializing member %s for object %p"), _vm.getStringTable().value(key), (void*) this);
724
if (order >= 0 && !_members.
725
reserveSlot(static_cast<unsigned short>(order), key, nsname))
727
log_error(_("Attempt to set a slot for either a slot or a property "
728
"which already exists."));
732
// Set (or create) a SimpleProperty
733
if (! _members.setValue(key, val, *this, nsname, flags) )
735
log_error(_("Attempt to initialize read-only property ``%s''"
736
" on object ``%p'' twice"),
737
_vm.getStringTable().value(key), (void*)this);
738
// We shouldn't attempt to initialize a member twice, should we ?
744
as_object::init_property(const std::string& key, as_function& getter,
745
as_function& setter, int flags, string_table::key nsname)
747
string_table::key k = _vm.getStringTable().find(PROPNAME(key));
748
init_property(k, getter, setter, flags, nsname);
752
as_object::init_property(string_table::key key, as_function& getter,
753
as_function& setter, int flags, string_table::key nsname)
758
success = _members.addGetterSetter(key, getter, &setter, cacheValue, flags, nsname);
760
// We shouldn't attempt to initialize a property twice, should we ?
763
//log_debug(_("Initialized property '%s'"), name);
765
// TODO: optimize this, don't scan again !
766
//_members.setFlags(key, flags, nsname);
771
as_object::init_property(const std::string& key, as_c_function_ptr getter,
772
as_c_function_ptr setter, int flags, string_table::key nsname)
774
string_table::key k = _vm.getStringTable().find(PROPNAME(key));
775
init_property(k, getter, setter, flags, nsname);
779
as_object::init_property(string_table::key key, as_c_function_ptr getter,
780
as_c_function_ptr setter, int flags, string_table::key nsname)
783
success = _members.addGetterSetter(key, getter, setter, nsname);
785
// We shouldn't attempt to initialize a property twice, should we ?
788
//log_debug(_("Initialized property '%s'"), name);
790
// TODO: optimize this, don't scan again !
791
_members.setFlags(key, flags, nsname);
796
as_object::init_destructive_property(string_table::key key, as_function& getter,
797
int flags, string_table::key nsname)
801
// No case check, since we've already got the key.
802
success = _members.addDestructiveGetter(key, getter, nsname, flags);
807
as_object::init_destructive_property(string_table::key key, as_c_function_ptr getter,
808
int flags, string_table::key nsname)
812
// No case check, since we've already got the key.
813
success = _members.addDestructiveGetter(key, getter, nsname, flags);
818
as_object::init_readonly_property(const std::string& key, as_function& getter,
819
int initflags, string_table::key nsname)
821
string_table::key k = _vm.getStringTable().find(PROPNAME(key));
823
init_property(k, getter, getter, initflags | as_prop_flags::readOnly
824
| as_prop_flags::isProtected, nsname);
825
assert(_members.getProperty(k, nsname));
829
as_object::init_readonly_property(const string_table::key& k, as_function& getter,
830
int initflags, string_table::key nsname)
832
init_property(k, getter, getter, initflags | as_prop_flags::readOnly
833
| as_prop_flags::isProtected, nsname);
834
assert(_members.getProperty(k, nsname));
838
as_object::init_readonly_property(const std::string& key, as_c_function_ptr getter,
839
int initflags, string_table::key nsname)
841
string_table::key k = _vm.getStringTable().find(PROPNAME(key));
843
init_property(k, getter, getter, initflags | as_prop_flags::readOnly
844
| as_prop_flags::isProtected, nsname);
845
assert(_members.getProperty(k, nsname));
849
as_object::init_readonly_property(const string_table::key& k, as_c_function_ptr getter,
850
int initflags, string_table::key nsname)
852
init_property(k, getter, getter, initflags | as_prop_flags::readOnly
853
| as_prop_flags::isProtected, nsname);
854
assert(_members.getProperty(k, nsname));
858
as_object::asPropName(string_table::key name)
860
std::string orig = _vm.getStringTable().value(name);
862
return PROPNAME(orig); // why is PROPNAME needed here ?
867
as_object::set_member_flags(string_table::key name,
868
int setTrue, int setFalse, string_table::key nsname)
870
return _members.setFlags(name, setTrue, setFalse, nsname);
874
as_object::add_interface(as_object* obj)
878
if (std::find(mInterfaces.begin(), mInterfaces.end(), obj) == mInterfaces.end())
879
mInterfaces.push_back(obj);
883
as_object::instanceOf(as_object* ctor)
885
//#define GNASH_DEBUG_INSTANCE_OF 1
888
if ( ! ctor->get_member(NSV::PROP_PROTOTYPE, &protoVal) )
890
#ifdef GNASH_DEBUG_INSTANCE_OF
891
log_debug("Object %p can't be an instance of an object (%p) w/out 'prototype'",
892
(void*)this, (void*)ctor);
896
as_object* ctorProto = protoVal.to_object().get();
899
#ifdef GNASH_DEBUG_INSTANCE_OF
900
log_debug("Object %p can't be an instance of an object (%p) with non-object 'prototype' (%s)",
901
(void*)this, (void*)ctor, protoVal);
906
// TODO: cleanup the iteration, make it more readable ...
908
std::set< as_object* > visited;
910
as_object* obj = this;
911
while (obj && visited.insert(obj).second )
913
as_object* thisProto = obj->get_prototype().get();
920
if ( thisProto == ctorProto )
922
#ifdef GNASH_DEBUG_INSTANCE_OF
923
log_debug("Object %p is an instance of constructor %p as the constructor exposes our __proto__ %p",
924
(void*)obj, (void*)ctor, (void*)thisProto);
929
// Check our proto interfaces
930
if (std::find(thisProto->mInterfaces.begin(), thisProto->mInterfaces.end(), ctorProto) != thisProto->mInterfaces.end())
932
#ifdef GNASH_DEBUG_INSTANCE_OF
933
log_debug("Object %p __proto__ %p had one interface matching with the constructor prototype %p",
934
(void*)obj, (void*)thisProto, (void*)ctorProto);
946
as_object::prototypeOf(as_object& instance)
948
boost::intrusive_ptr<as_object> obj = &instance;
950
std::set< as_object* > visited;
952
while (obj && visited.insert(obj.get()).second )
954
if ( obj->get_prototype() == this ) return true;
955
obj = obj->get_prototype();
958
// See actionscript.all/Inheritance.as for a way to trigger this
959
IF_VERBOSE_ASCODING_ERRORS(
960
if ( obj ) log_aserror(_("Circular inheritance chain detected during isPrototypeOf call"));
967
as_object::dump_members()
969
log_debug(_("%d members of object %p follow"),
970
_members.size(), (const void*)this);
971
_members.dump(*this);
975
as_object::dump_members(std::map<std::string, as_value>& to)
977
_members.dump(*this, to);
980
class FlagsSetterVisitor {
986
FlagsSetterVisitor(string_table& st, PropertyList& pl, int setTrue, int setFalse)
994
void visit(as_value& v)
996
string_table::key key = _st.find(v.to_string());
997
_pl.setFlags(key, _setTrue, _setFalse);
1002
as_object::setPropFlags(const as_value& props_val, int set_false, int set_true)
1004
if (props_val.is_string())
1006
std::string propstr = PROPNAME(props_val.to_string());
1011
size_t next_comma=propstr.find(",");
1012
if ( next_comma == std::string::npos )
1018
prop=propstr.substr(0,next_comma);
1019
propstr=propstr.substr(next_comma+1);
1022
// set_member_flags will take care of case conversion
1023
if (!set_member_flags(_vm.getStringTable().find(prop), set_true, set_false) )
1025
IF_VERBOSE_ASCODING_ERRORS(
1026
log_aserror(_("Can't set propflags on object "
1028
"(either not found or protected)"), prop);
1032
if ( next_comma == std::string::npos )
1040
// Evan: it seems that if set_true == 0 and set_false == 0,
1041
// this function acts as if the parameters were (object, null, 0x1, 0)
1042
#if 0 // bullshit, see actionscript.all/Global.as
1043
if (set_false == 0 && set_true == 0)
1045
props_val.set_null();
1051
if (props_val.is_null())
1053
// Take all the members of the object
1054
//std::pair<size_t, size_t> result =
1055
_members.setFlagsAll(set_true, set_false);
1057
// Are we sure we need to descend to __proto__ ?
1058
// should we recurse then ?
1062
m_prototype->_members.setFlagsAll(set_true, set_false);
1068
boost::intrusive_ptr<as_object> props = props_val.to_object();
1069
Array_as* ary = dynamic_cast<Array_as*>(props.get());
1072
IF_VERBOSE_ASCODING_ERRORS(
1073
log_aserror(_("Invalid call to AsSetPropFlags: "
1074
"invalid second argument %s "
1075
"(expected string, null or an array)"),
1081
// The passed argument has to be considered an array
1082
//std::pair<size_t, size_t> result =
1083
FlagsSetterVisitor visitor(getVM().getStringTable(), _members, set_true, set_false);
1084
ary->visitAll(visitor);
1085
//_members.setFlagsAll(props->_members, set_true, set_false);
1090
as_object::copyProperties(const as_object& o)
1092
PropsCopier copier(*this);
1094
// TODO: check if non-visible properties should be also copied !
1095
o.visitPropertyValues(copier);
1099
as_object::enumerateProperties(as_environment& env) const
1101
assert( env.top(0).is_null() );
1103
enumerateNonProperties(env);
1105
// this set will keep track of visited objects,
1106
// to avoid infinite loops
1107
std::set< const as_object* > visited;
1108
PropertyList::propNameSet named;
1110
boost::intrusive_ptr<const as_object> obj(this);
1112
while ( obj && visited.insert(obj.get()).second )
1114
obj->_members.enumerateKeys(env, named);
1115
obj = obj->get_prototype();
1118
// This happens always since top object in hierarchy
1119
// is always Object, which in turn derives from itself
1120
//if ( obj ) log_error(_("prototype loop during Enumeration"));
1124
as_object::enumerateProperties(std::map<std::string, std::string>& to) const
1127
// this set will keep track of visited objects,
1128
// to avoid infinite loops
1129
std::set< const as_object* > visited;
1131
boost::intrusive_ptr<const as_object> obj(this);
1132
while ( obj && visited.insert(obj.get()).second )
1134
obj->_members.enumerateKeyValue(*this, to);
1135
obj = obj->get_prototype();
1140
as_object::as_object()
1144
//, m_prototype(NULL)
1148
as_object::as_object(as_object* proto)
1152
//, m_prototype(proto)
1154
init_member(NSV::PROP_uuPROTOuu, as_value(proto));
1157
as_object::as_object(boost::intrusive_ptr<as_object> proto)
1161
//, m_prototype(proto)
1163
//set_prototype(proto);
1164
init_member(NSV::PROP_uuPROTOuu, as_value(proto));
1167
as_object::as_object(const as_object& other)
1169
#ifndef GNASH_USE_GC
1174
_members(other._members),
1176
//, m_prototype(other.m_prototype) // done by _members copy
1180
std::pair<bool,bool>
1181
as_object::delProperty(string_table::key name, string_table::key nsname)
1183
return _members.delProperty(name, nsname);
1187
as_object::getOwnProperty(string_table::key key, string_table::key nsname)
1189
return _members.getProperty(key, nsname);
1193
as_object::hasOwnProperty(string_table::key key, string_table::key nsname)
1195
return getOwnProperty(key, nsname) != NULL;
1199
as_object::tostring_method(const fn_call& fn)
1201
boost::intrusive_ptr<as_object> obj = fn.this_ptr;
1203
std::string text_val = obj->get_text_value();
1204
return as_value(text_val);
1208
as_object::valueof_method(const fn_call& fn)
1210
boost::intrusive_ptr<as_object> obj = fn.this_ptr;
1212
return obj->get_primitive_value();
1215
boost::intrusive_ptr<as_object>
1216
as_object::get_prototype()
1220
if ( ! get_member(NSV::PROP_uuPROTOuu, &val) )
1222
//log_debug("Object %p has no __proto__ member");
1225
//log_debug("%p.__proto__ is %s", val);
1226
return val.to_object().get();
1228
static string_table::key key = NSV::PROP_uuPROTOuu;
1230
int swfVersion = _vm.getSWFVersion();
1232
boost::intrusive_ptr<as_object> nullRet = NULL;
1234
Property* prop = _members.getProperty(key);
1235
if ( ! prop ) return nullRet;
1236
if ( ! prop->isVisible(swfVersion) ) return nullRet;
1238
as_value tmp = prop->getValue(*this);
1240
return tmp.to_object();
1245
as_object::on_event(const event_id& id )
1247
as_value event_handler;
1249
if (get_member(id.get_function_key(), &event_handler) )
1251
call_method0(event_handler, NULL, this);
1259
as_object::getMember(string_table::key name, string_table::key nsname)
1262
get_member(name, &ret, nsname);
1263
//get_member(PROPNAME(name), &ret);
1268
as_object::callMethod(string_table::key methodName)
1273
if (! get_member(methodName, &method))
1278
as_environment env(_vm);
1280
return call_method0(method, &env, this);
1284
as_object::callMethod(string_table::key methodName, const as_value& arg0)
1289
if (!get_member(methodName, &method))
1294
as_environment env(_vm);
1296
std::auto_ptr< std::vector<as_value> > args ( new std::vector<as_value> );
1297
args->push_back(arg0);
1299
ret = call_method(method, &env, this, args);
1305
as_object::callMethod(string_table::key methodName,
1306
const as_value& arg0, const as_value& arg1)
1311
if (! get_member(methodName, &method))
1316
as_environment env(_vm);
1318
std::auto_ptr< std::vector<as_value> > args ( new std::vector<as_value> );
1319
args->push_back(arg0);
1320
args->push_back(arg1);
1322
ret = call_method(method, &env, this, args);
1328
as_object::callMethod(string_table::key methodName,
1329
const as_value& arg0, const as_value& arg1, const as_value& arg2)
1334
if (! get_member(methodName, &method))
1339
as_environment env(_vm);
1341
std::auto_ptr< std::vector<as_value> > args ( new std::vector<as_value> );
1342
args->push_back(arg0);
1343
args->push_back(arg1);
1344
args->push_back(arg2);
1346
ret = call_method(method, &env, this, args);
1352
as_object::callMethod(string_table::key methodName,
1353
const as_value& arg0, const as_value& arg1,
1354
const as_value& arg2, const as_value& arg3)
1359
if (! get_member(methodName, &method))
1364
as_environment env(_vm);
1366
std::auto_ptr< std::vector<as_value> > args ( new std::vector<as_value> );
1367
args->push_back(arg0);
1368
args->push_back(arg1);
1369
args->push_back(arg2);
1370
args->push_back(arg3);
1372
ret = call_method(method, &env, this, args);
1378
as_object::get_path_element(string_table::key key)
1380
//#define DEBUG_TARGET_FINDING 1
1383
if ( ! get_member(key, &tmp ) )
1385
#ifdef DEBUG_TARGET_FINDING
1386
log_debug("Member %s not found in object %p",
1387
_vm.getStringTable().value(key), (void*)this);
1391
if ( ! tmp.is_object() )
1393
#ifdef DEBUG_TARGET_FINDING
1394
log_debug("Member %s of object %p is not an object (%s)",
1395
_vm.getStringTable().value(key), (void*)this, tmp);
1400
return tmp.to_object().get();
1404
as_object::getURLEncodedVars(std::string& data)
1406
typedef std::map<std::string, std::string> PropMap;
1408
enumerateProperties(props);
1413
for (PropMap::const_iterator i=props.begin(), e=props.end(); i!=e; ++i)
1415
std::string name = i->first;
1416
std::string value = i->second;
1417
if ( ! name.empty() && name[0] == '$' ) continue; // see bug #22006
1420
data += del + name + "=" + value;
1429
as_object::watch(string_table::key key, as_function& trig,
1430
const as_value& cust, string_table::key ns)
1433
FQkey k = std::make_pair(key, ns);
1434
std::string propname = VM::get().getStringTable().value(key);
1436
TriggerContainer::iterator it = _trigs.find(k);
1437
if ( it == _trigs.end() )
1439
return _trigs.insert(std::make_pair(k, Trigger(propname, trig, cust))).second;
1441
it->second = Trigger(propname, trig, cust);
1446
as_object::unwatch(string_table::key key, string_table::key ns)
1448
TriggerContainer::iterator trigIter = _trigs.find(std::make_pair(key, ns));
1449
if ( trigIter == _trigs.end() )
1451
log_debug("No watch for property %s", getVM().getStringTable().value(key));
1454
Property* prop = _members.getProperty(key, ns);
1455
if ( prop && prop->isGetterSetter() )
1457
log_debug("Watch on %s not removed (is a getter-setter)", getVM().getStringTable().value(key));
1460
_trigs.erase(trigIter);
1466
as_object::markAsObjectReachable() const
1468
_members.setReachable();
1470
for (TriggerContainer::const_iterator it = _trigs.begin();
1471
it != _trigs.end(); ++it)
1473
it->second.setReachable();
1476
#endif // GNASH_USE_GC
1479
Trigger::setReachable() const
1481
_func->setReachable();
1482
_customArg.setReachable();
1486
Trigger::call(const as_value& oldval, const as_value& newval, as_object& this_obj)
1488
if ( _executing ) return newval;
1493
as_environment env(VM::get()); // TODO: get VM in some other way
1495
std::auto_ptr< std::vector<as_value> > args ( new std::vector<as_value> );
1496
args->push_back(_propname);
1497
args->push_back(oldval);
1498
args->push_back(newval);
1499
args->push_back(_customArg);
1501
fn_call fn(&this_obj, &env, args);
1503
as_value ret = _func->call(fn);
1518
as_object::visitPropertyValues(AbstractPropertyVisitor& visitor) const
1520
_members.visitValues(visitor, *this);
1524
as_object::visitNonHiddenPropertyValues(AbstractPropertyVisitor& visitor) const
1526
_members.visitNonHiddenValues(visitor, *this);
1531
} // end of gnash namespace