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 "as_object.h"
26
#include "as_function.h"
27
#include "as_environment.h" // for enumerateProperties
28
#include "Property.h" // for findGetterSetter
30
#include "GnashException.h"
31
#include "fn_call.h" // for generic methods
32
#include "Object.h" // for getObjectInterface
33
#include "action.h" // for call_method
34
#include "array.h" // for setPropFlags
38
#include <boost/algorithm/string/case_conv.hpp>
39
#include <utility> // for std::pair
40
#include "namedStrings.h"
44
// Anonymous namespace used for module-static defs
47
using namespace gnash;
49
// A PropertyList visitor copying properties to an object
57
/// Initialize a PropsCopier instance associating it
58
/// with a target object (an object whose members has to be set)
60
PropsCopier(as_object& tgt)
66
/// Use the set_member function to properly set *inherited* properties
67
/// of the given target object
69
void operator() (string_table::key name, const as_value& val)
71
if (name == NSV::PROP_uuPROTOuu) return;
72
//log_debug(_("Setting member '%s' to value '%s'"), name.c_str(), val.to_debug_string().c_str());
73
_tgt.set_member(name, val);
77
} // end of anonymous namespace
83
as_object::add_property(const std::string& name, as_function& getter,
86
string_table &st = _vm.getStringTable();
87
return _members.addGetterSetter(st.find(PROPNAME(name)), getter, setter);
92
as_object::get_member_default(string_table::key name, as_value* val,
93
string_table::key nsname)
97
Property* prop = findProperty(name, nsname);
103
*val = prop->getValue(*this);
106
catch (ActionLimitException& exc)
108
// will be logged by outer catcher
111
catch (ActionException& exc)
113
// TODO: check if this should be an 'as' error.. (log_aserror)
114
log_error(_("Caught exception: %s"), exc.what());
120
as_object::getByIndex(int index)
122
// The low byte is used to contain the depth of the property.
123
unsigned char depth = index & 0xFF;
124
index /= 256; // Signed
125
as_object *obj = this;
128
obj = obj->get_prototype().get();
133
return const_cast<Property *>(obj->_members.getPropertyByOrder(index));
137
as_object::get_super()
139
static bool getting = false;
140
as_object *owner = NULL;
149
// Super is this.__proto__.__constructor__.prototype
150
as_object *proto = get_prototype().get();
157
// If an object is its own prototype, we stop looking.
164
p = proto->findProperty(NSV::PROP_uuCONSTRUCTORuu, 0, &owner);
171
as_value ctor = p->getValue(*owner);
172
as_object *ctor_obj = ctor.to_object().get();
179
p = ctor_obj->findProperty(NSV::PROP_PROTOTYPE, 0, &owner);
186
as_value ctor_proto = p->getValue(*owner);
187
as_object *super = ctor_proto.to_object().get();
195
as_object::get_constructor()
202
as_object::nextIndex(int index, as_object **owner)
205
unsigned char depth = index & 0xFF;
206
unsigned char i = depth;
207
index /= 256; // Signed
208
as_object *obj = this;
211
obj = obj->get_prototype().get();
216
const Property *p = obj->_members.getOrderAfter(index);
219
obj = obj->get_prototype().get();
222
p = obj->_members.getOrderAfter(0);
227
if (findProperty(p->getName(), p->getNamespace()) != p)
229
index = p->getOrder() * 256 | depth;
230
goto skip_duplicates; // Faster than recursion.
234
return p->getOrder() * 256 | depth;
241
as_object::findProperty(string_table::key key, string_table::key nsname,
244
// don't enter an infinite loop looking for __proto__ ...
245
if (key == NSV::PROP_uuPROTOuu && !nsname)
249
return _members.getProperty(key, nsname);
252
// keep track of visited objects, avoid infinite loops.
253
std::set<as_object*> visited;
255
int swfVersion = _vm.getSWFVersion();
258
boost::intrusive_ptr<as_object> obj = this;
259
while (obj && visited.insert(obj.get()).second)
262
if ((i > 255 && swfVersion == 5) || i > 257)
263
throw ActionLimitException("Lookup depth exceeded.");
265
Property* prop = obj->_members.getProperty(key);
266
if (prop && prop->isVisible(swfVersion) )
273
obj = obj->get_prototype();
281
as_object::findUpdatableProperty(string_table::key key, string_table::key nsname)
283
int swfVersion = _vm.getSWFVersion();
285
Property* prop = _members.getProperty(key, nsname);
287
// We won't scan the inheritance chain if we find a member,
288
// even if invisible.
290
if ( prop ) return prop;
292
// don't enter an infinite loop looking for __proto__ ...
293
if (key == NSV::PROP_uuPROTOuu) return NULL;
295
std::set<as_object*> visited;
296
visited.insert(this);
300
boost::intrusive_ptr<as_object> obj = get_prototype();
301
while (obj && visited.insert(obj.get()).second)
304
if ((i > 255 && swfVersion == 5) || i > 257)
305
throw ActionLimitException("Property lookup depth exceeded.");
307
Property* p = obj->_members.getProperty(key, nsname);
308
if (p && (p->isGetterSetter() | p->isStatic()) && p->isVisible(swfVersion))
310
return p; // What should we do if this is not a getter/setter ?
312
obj = obj->get_prototype();
319
as_object::set_prototype(boost::intrusive_ptr<as_object> proto, int flags)
321
static string_table::key key = NSV::PROP_uuPROTOuu;
323
// TODO: check what happens if __proto__ is set as a user-defined getter/setter
324
if (_members.setValue(key, as_value(proto.get()), *this, 0) )
326
// TODO: optimize this, don't scan again !
327
_members.setFlags(key, flags, 0);
332
as_object::reserveSlot(string_table::key name, string_table::key nsId,
333
unsigned short slotId)
335
_members.reserveSlot(name, nsId, slotId);
338
// Handles read_only and static properties properly.
340
as_object::set_member_default(string_table::key key, const as_value& val,
341
string_table::key nsname)
343
//log_debug(_("set_member_default(%s)"), key.c_str());
344
Property* prop = findUpdatableProperty(key, nsname);
347
if (prop->isReadOnly())
349
IF_VERBOSE_ASCODING_ERRORS(log_aserror(_(""
350
"Attempt to set read-only property '%s'"),
351
_vm.getStringTable().value(key).c_str()););
357
prop->setValue(*this, val);
358
prop->clearVisible(_vm.getSWFVersion());
361
catch (ActionException& exc)
363
log_aserror(_("%s: Exception %s. Will create a new member"),
364
_vm.getStringTable().value(key).c_str(), exc.what());
370
// Else, add new property...
372
// Property does not exist, so it won't be read-only. Set it.
373
if (!_members.setValue(key, const_cast<as_value&>(val), *this, nsname))
375
IF_VERBOSE_ASCODING_ERRORS(
376
log_aserror(_("Unknown failure in setting property '%s' on "
377
"object '%p'"), _vm.getStringTable().value(key).c_str(),
383
as_object::update_member(string_table::key key, const as_value& val,
384
string_table::key nsname)
386
std::pair<bool,bool> ret; // first is found, second is updated
388
//log_debug(_("set_member_default(%s)"), key.c_str());
389
Property* prop = findUpdatableProperty(key, nsname);
392
if (prop->isReadOnly())
394
IF_VERBOSE_ASCODING_ERRORS(log_aserror(_(""
395
"Attempt to set read-only property '%s'"),
396
_vm.getStringTable().value(key).c_str()););
397
return std::make_pair(true, false);
402
prop->setValue(*this, val);
403
return std::make_pair(true, true);
405
catch (ActionException& exc)
407
log_debug(_("%s: Exception %s. Will create a new member"),
408
_vm.getStringTable().value(key).c_str(), exc.what());
411
return std::make_pair(true, false);
414
return std::make_pair(false, false);
419
as_object::init_member(const std::string& key1, const as_value& val, int flags,
420
string_table::key nsname)
422
init_member(_vm.getStringTable().find(PROPNAME(key1)), val, flags, nsname);
426
as_object::init_member(string_table::key key, const as_value& val, int flags,
427
string_table::key nsname, int order)
429
//log_debug(_("Initializing member %s for object %p"), _vm.getStringTable().value(key).c_str(), (void*) this);
431
if (order >= 0 && !_members.
432
reserveSlot(static_cast<unsigned short>(order), key, nsname))
434
log_error(_("Attempt to set a slot for either a slot or a property "
435
"which already exists."));
439
// Set (or create) a SimpleProperty
440
if (! _members.setValue(key, const_cast<as_value&>(val), *this, nsname) )
442
log_error(_("Attempt to initialize read-only property ``%s''"
443
" on object ``%p'' twice"),
444
_vm.getStringTable().value(key).c_str(), (void*)this);
445
// We shouldn't attempt to initialize a member twice, should we ?
448
// TODO: optimize this, don't scan again !
449
_members.setFlags(key, flags, nsname);
453
as_object::init_property(const std::string& key, as_function& getter,
454
as_function& setter, int flags, string_table::key nsname)
456
string_table::key k = _vm.getStringTable().find(PROPNAME(key));
457
init_property(k, getter, setter, flags, nsname);
462
as_object::init_property(string_table::key key, as_function& getter,
463
as_function& setter, int flags, string_table::key nsname)
466
success = _members.addGetterSetter(key, getter, setter, nsname);
468
// We shouldn't attempt to initialize a property twice, should we ?
471
//log_debug(_("Initialized property '%s'"), name.c_str());
473
// TODO: optimize this, don't scan again !
474
_members.setFlags(key, flags, nsname);
479
as_object::init_destructive_property(string_table::key key, as_function& getter,
480
as_function& setter, int flags, string_table::key nsname)
484
// No case check, since we've already got the key.
485
success = _members.addDestructiveGetterSetter(key, getter, setter, nsname);
486
_members.setFlags(key, flags, nsname);
491
as_object::init_readonly_property(const std::string& key, as_function& getter,
492
int initflags, string_table::key nsname)
494
string_table::key k = _vm.getStringTable().find(PROPNAME(key));
496
init_property(k, getter, getter, initflags | as_prop_flags::readOnly
497
| as_prop_flags::isProtected, nsname);
498
assert(_members.getProperty(k, nsname));
502
as_object::asPropName(string_table::key name)
504
std::string orig = _vm.getStringTable().value(name);
506
return PROPNAME(orig); // why is PROPNAME needed here ?
511
as_object::set_member_flags(string_table::key name,
512
int setTrue, int setFalse, string_table::key nsname)
514
return _members.setFlags(name, setTrue, setFalse, nsname);
518
as_object::add_interface(as_object* obj)
522
if (std::find(mInterfaces.begin(), mInterfaces.end(), obj) == mInterfaces.end())
523
mInterfaces.push_back(obj);
527
as_object::instanceOf(as_function* ctor)
529
if (this == ctor->getPrototype())
532
if (!mInterfaces.empty())
534
// TODO: Make this work.
535
if (std::find(mInterfaces.begin(), mInterfaces.end(), ctor->getPrototype()) != mInterfaces.end())
541
as_object *proto = this->get_prototype().get();
543
return proto->instanceOf(ctor);
549
as_object::prototypeOf(as_object& instance)
551
boost::intrusive_ptr<as_object> obj = &instance;
553
std::set< as_object* > visited;
555
while (obj && visited.insert(obj.get()).second )
557
if ( obj->get_prototype() == this ) return true;
558
obj = obj->get_prototype();
561
// See actionscript.all/Inheritance.as for a way to trigger this
562
IF_VERBOSE_ASCODING_ERRORS(
563
if ( obj ) log_aserror(_("Circular inheritance chain detected during isPrototypeOf call"));
570
as_object::dump_members()
572
log_debug(_(SIZET_FMT " members of object %p follow"),
573
_members.size(), (const void*)this);
574
_members.dump(*this);
578
as_object::dump_members(std::map<std::string, as_value>& to)
580
_members.dump(*this, to);
583
class FlagsSetterVisitor {
589
FlagsSetterVisitor(string_table& st, PropertyList& pl, int setTrue, int setFalse)
597
void visit(as_value& v)
599
string_table::key key = _st.find(v.to_string());
600
_pl.setFlags(key, _setTrue, _setFalse);
605
as_object::setPropFlags(as_value& props_val, int set_false, int set_true)
607
if (props_val.is_string())
609
std::string propstr = PROPNAME(props_val.to_string());
614
size_t next_comma=propstr.find(",");
615
if ( next_comma == std::string::npos )
621
prop=propstr.substr(0,next_comma);
622
propstr=propstr.substr(next_comma+1);
625
// set_member_flags will take care of case conversion
626
if (!set_member_flags(_vm.getStringTable().find(prop), set_true, set_false) )
628
IF_VERBOSE_ASCODING_ERRORS(
629
log_aserror(_("Can't set propflags on object "
631
"(either not found or protected)"),
636
if ( next_comma == std::string::npos )
644
// Evan: it seems that if set_true == 0 and set_false == 0,
645
// this function acts as if the parameters were (object, null, 0x1, 0)
646
#if 0 // bullshit, see actionscript.all/Global.as
647
if (set_false == 0 && set_true == 0)
649
props_val.set_null();
655
if (props_val.is_null())
657
// Take all the members of the object
658
//std::pair<size_t, size_t> result =
659
_members.setFlagsAll(set_true, set_false);
661
// Are we sure we need to descend to __proto__ ?
662
// should we recurse then ?
666
m_prototype->_members.setFlagsAll(set_true, set_false);
672
boost::intrusive_ptr<as_object> props = props_val.to_object();
673
as_array_object* ary = dynamic_cast<as_array_object*>(props.get());
676
IF_VERBOSE_ASCODING_ERRORS(
677
log_aserror(_("Invalid call to AsSetPropFlags: "
678
"invalid second argument %s "
679
"(expected string, null or an array)"),
680
props_val.to_debug_string().c_str());
685
// The passed argument has to be considered an array
686
//std::pair<size_t, size_t> result =
687
FlagsSetterVisitor visitor(getVM().getStringTable(), _members, set_true, set_false);
688
ary->visitAll(visitor);
689
//_members.setFlagsAll(props->_members, set_true, set_false);
694
as_object::copyProperties(const as_object& o)
696
PropsCopier copier(*this);
698
// TODO: check if non-visible properties should be also copied !
699
o.visitPropertyValues(copier);
703
as_object::enumerateProperties(as_environment& env) const
705
assert( env.top(0).is_null() );
707
enumerateNonProperties(env);
709
// this set will keep track of visited objects,
710
// to avoid infinite loops
711
std::set< as_object* > visited;
712
PropertyList::propNameSet named;
714
boost::intrusive_ptr<as_object> obj = const_cast<as_object*>(this);
715
while ( obj && visited.insert(obj.get()).second )
717
obj->_members.enumerateKeys(env, named);
718
obj = obj->get_prototype();
721
// This happens always since top object in hierarchy
722
// is always Object, which in turn derives from itself
723
//if ( obj ) log_error(_("prototype loop during Enumeration"));
727
as_object::enumerateProperties(std::map<std::string, std::string>& to)
730
// this set will keep track of visited objects,
731
// to avoid infinite loops
732
std::set< as_object* > visited;
734
boost::intrusive_ptr<as_object> obj = this;
735
while ( obj && visited.insert(obj.get()).second )
737
obj->_members.enumerateKeyValue(*this, to);
738
obj = obj->get_prototype();
743
as_object::as_object()
747
//, m_prototype(NULL)
751
as_object::as_object(as_object* proto)
755
//, m_prototype(proto)
757
init_member("__proto__", as_value(proto));
760
as_object::as_object(boost::intrusive_ptr<as_object> proto)
764
//, m_prototype(proto)
766
//set_prototype(proto);
767
init_member("__proto__", as_value(proto));
770
as_object::as_object(const as_object& other)
777
_members(other._members),
779
//, m_prototype(other.m_prototype) // done by _members copy
784
as_object::delProperty(string_table::key name, string_table::key nsname)
786
return _members.delProperty(name, nsname);
790
as_object::getOwnProperty(string_table::key key, string_table::key nsname)
792
return _members.getProperty(key, nsname);
796
as_object::tostring_method(const fn_call& fn)
798
boost::intrusive_ptr<as_object> obj = fn.this_ptr;
800
std::string text_val = obj->get_text_value();
801
return as_value(text_val);
805
as_object::valueof_method(const fn_call& fn)
807
boost::intrusive_ptr<as_object> obj = fn.this_ptr;
809
return obj->get_primitive_value();
812
boost::intrusive_ptr<as_object>
813
as_object::get_prototype()
815
static string_table::key key = NSV::PROP_uuPROTOuu;
817
int swfVersion = _vm.getSWFVersion();
819
boost::intrusive_ptr<as_object> nullRet = NULL;
821
Property* prop = _members.getProperty(key);
822
if ( ! prop ) return nullRet;
823
if ( ! prop->isVisible(swfVersion) ) return nullRet;
825
as_value tmp = prop->getValue(*this);
827
return tmp.to_object();
831
as_object::on_event(const event_id& id )
833
as_value event_handler;
835
if (get_member(id.get_function_key(), &event_handler) )
837
call_method(event_handler, NULL, this, 0, 0);
845
as_object::getMember(string_table::key name, string_table::key nsname)
848
get_member(name, &ret, nsname);
849
//get_member(PROPNAME(name), &ret);
854
as_object::callMethod(string_table::key methodName)
859
if (! get_member(methodName, &method))
866
return call_method(method, &env, this, 0, env.stack_size());
870
as_object::callMethod(string_table::key methodName, const as_value& arg0)
875
if (!get_member(methodName, &method))
883
ret = call_method(method, &env, this, 1, env.stack_size()-1);
891
as_object::callMethod(string_table::key methodName,
892
const as_value& arg0, const as_value& arg1)
897
if (! get_member(methodName, &method))
905
size_t origStackSize = env.stack_size();
911
ret = call_method(method, &env, this, 2, env.stack_size()-1);
916
assert(origStackSize == env.stack_size());
923
as_object::callMethod(string_table::key methodName,
924
const as_value& arg0, const as_value& arg1, const as_value& arg2)
929
if (! get_member(methodName, &method))
937
size_t origStackSize = env.stack_size();
944
ret = call_method(method, &env, this, 3, env.stack_size()-1);
949
assert(origStackSize == env.stack_size());
956
as_object::callMethod(string_table::key methodName,
957
const as_value& arg0, const as_value& arg1,
958
const as_value& arg2, const as_value& arg3)
963
if (! get_member(methodName, &method))
971
size_t origStackSize = env.stack_size();
979
ret = call_method(method, &env, this, 4, env.stack_size()-1);
984
assert(origStackSize == env.stack_size());
991
as_object::get_path_element(string_table::key key)
993
//#define DEBUG_TARGET_FINDING 1
996
if ( ! get_member(key, &tmp ) )
998
#ifdef DEBUG_TARGET_FINDING
999
log_debug("Member %s not found in object %p",
1000
_vm.getStringTable().value(key).c_str(),
1005
if ( ! tmp.is_object() )
1007
#ifdef DEBUG_TARGET_FINDING
1008
log_debug("Member %s of object %p is not an object (%s)",
1009
_vm.getStringTable().value(key).c_str(), (void*)this,
1010
tmp.to_debug_string().c_str());
1015
return tmp.to_object().get();
1019
as_object::getURLEncodedVars(std::string& data)
1021
typedef std::map<std::string, std::string> PropMap;
1023
enumerateProperties(props);
1028
for (PropMap::const_iterator i=props.begin(), e=props.end(); i!=e; ++i)
1030
std::string name = i->first;
1031
std::string value = i->second;
1032
if ( ! name.empty() && name[0] == '$' ) continue; // see bug #22006
1035
data += del + name + "=" + value;
1044
} // end of gnash namespace