Tweak getProperty stats some more: trigger every 10Millions lookups of __proto__...
[gnash.git] / libcore / PropertyList.cpp
blob46c40070b2d8045efb14bdc9f3880d6ab0ec4bec
1 // PropertyList.cpp: ActionScript property lists, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4 // 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.
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
21 #include "PropertyList.h"
22 #include "Property.h"
23 #include "as_environment.h"
24 #include "log.h"
25 #include "as_function.h"
26 #include "as_value.h" // for enumerateValues
27 #include "VM.h" // For string_table
28 #include "string_table.h"
29 #include "GnashAlgorithm.h"
30 #ifdef HAVE_CONFIG_H
31 #include "gnashconfig.h" // GNASH_STATS_PROPERTY_LOOKUPS
32 #endif
34 #include <utility> // for std::make_pair
35 #include <boost/bind.hpp>
37 // Define the following to enable printing address of each property added
38 //#define DEBUG_PROPERTY_ALLOC
40 // Define this to get verbosity of properties insertion and flags setting
41 //#define GNASH_DEBUG_PROPERTY 1
43 // Define this to get stats of property lookups
44 //#define GNASH_STATS_PROPERTY_LOOKUPS 1
47 #ifdef GNASH_STATS_PROPERTY_LOOKUPS
48 # include "Stats.h"
49 # include "namedStrings.h"
50 #endif
52 namespace gnash {
54 namespace {
56 inline
57 PropertyList::const_iterator
58 iterator_find(const PropertyList::container& p, const ObjectURI& uri, VM& vm)
60 const bool caseless = vm.getSWFVersion() < 7;
62 if (!caseless) {
63 return p.project<0>(p.get<1>().find(uri));
66 ObjectURI uri2 = uri;
67 ObjectURI uri3 ( uri );
69 string_table& st = vm.getStringTable();
70 const string_table::key nocase = uri.noCase(st);
71 return p.project<0>(p.get<2>().find(nocase));
76 bool
77 PropertyList::setValue(const ObjectURI& uri, const as_value& val,
78 const PropFlags& flagsIfMissing)
80 const_iterator found = iterator_find(_props, uri, getVM(_owner));
82 string_table& st = getStringTable(_owner);
84 if (found == _props.end())
86 // create a new member
87 Property a(uri, val, flagsIfMissing);
88 // Non slot properties are negative ordering in insertion order
89 _props.push_back(std::make_pair(a, uri.noCase(st)));
90 #ifdef GNASH_DEBUG_PROPERTY
91 ObjectURI::Logger l(getStringTable(_owner));
92 log_debug("Simple AS property %s inserted with flags %s",
93 l(uri), a.getFlags());
94 #endif
95 return true;
98 const Property& prop = found->first;
99 if (readOnly(prop) && ! prop.isDestructive())
101 ObjectURI::Logger l(getStringTable(_owner));
102 log_error(_("Property %s is read-only %s, not setting it to %s"),
103 l(uri), prop.getFlags(), val);
104 return false;
107 prop.setValue(_owner, val);
109 return true;
112 void
113 PropertyList::setFlags(const ObjectURI& uri, int setFlags, int clearFlags)
115 iterator found = iterator_find(_props, uri, getVM(_owner));
116 if (found == _props.end()) return;
117 PropFlags f = found->first.getFlags();
118 f.set_flags(setFlags, clearFlags);
119 found->first.setFlags(f);
123 void
124 PropertyList::setFlagsAll(int setFlags, int clearFlags)
126 for (const_iterator it = _props.begin(); it != _props.end(); ++it) {
127 PropFlags f = it->first.getFlags();
128 f.set_flags(setFlags, clearFlags);
129 it->first.setFlags(f);
133 Property*
134 PropertyList::getProperty(const ObjectURI& uri) const
136 #ifdef GNASH_STATS_PROPERTY_LOOKUPS
137 // HINT: can add a final arg to KeyLookup ctor, like NSV::PROP_ON_MOUSE_MOVE
138 // to have *that* property lookup drive dump triggers
139 static stats::KeyLookup kcl("getProperty",
140 getStringTable(_owner), 10000000, NSV::PROP_uuPROTOuu, 10);
141 kcl.check(uri.name);
142 #endif // GNASH_STATS_PROPERTY_LOOKUPS
143 iterator found = iterator_find(_props, uri, getVM(_owner));
144 if (found == _props.end()) return 0;
145 return const_cast<Property*>(&(found->first));
148 std::pair<bool,bool>
149 PropertyList::delProperty(const ObjectURI& uri)
151 //GNASH_REPORT_FUNCTION;
152 iterator found = iterator_find(_props, uri, getVM(_owner));
153 if (found == _props.end()) {
154 return std::make_pair(false, false);
157 // check if member is protected from deletion
158 if (found->first.getFlags().get_dont_delete()) {
159 return std::make_pair(true, false);
162 _props.erase(found);
163 return std::make_pair(true, true);
166 void
167 PropertyList::dump(std::map<std::string, as_value>& to)
169 ObjectURI::Logger l(getStringTable(_owner));
171 for (const_iterator i=_props.begin(), ie=_props.end();
172 i != ie; ++i)
174 to.insert(std::make_pair(l(i->first.uri()), i->first.getValue(_owner)));
178 void
179 PropertyList::enumerateKeys(as_environment& env, PropertyTracker& donelist)
180 const
182 string_table& st = getStringTable(_owner);
184 // We should enumerate in order of creation, not lexicographically.
185 for (const_iterator i = _props.begin(),
186 ie = _props.end(); i != ie; ++i) {
188 if (i->first.getFlags().get_dont_enum()) continue;
190 const ObjectURI& uri = i->first.uri();
192 if (donelist.insert(uri).second) {
193 const std::string& qname = st.value(getName(uri));
194 env.push(qname);
199 void
200 PropertyList::dump()
202 ObjectURI::Logger l(getStringTable(_owner));
203 for (const_iterator it=_props.begin(), itEnd=_props.end();
204 it != itEnd; ++it) {
205 log_debug(" %s: %s", l(it->first.uri()), it->first.getValue(_owner));
209 bool
210 PropertyList::addGetterSetter(const ObjectURI& uri, as_function& getter,
211 as_function* setter, const as_value& cacheVal,
212 const PropFlags& flagsIfMissing)
214 Property a(uri, &getter, setter, flagsIfMissing);
215 iterator found = iterator_find(_props, uri, getVM(_owner));
217 string_table& st = getStringTable(_owner);
218 if (found != _props.end())
220 // copy flags from previous member (even if it's a normal member ?)
221 a.setFlags(found->first.getFlags());
222 a.setCache(found->first.getCache());
223 _props.replace(found, std::make_pair(a, uri.noCase(st)));
225 #ifdef GNASH_DEBUG_PROPERTY
226 ObjectURI::Logger l(getStringTable(_owner));
227 log_debug("AS GetterSetter %s replaced copying flags %s", l(uri),
228 a.getFlags());
229 #endif
232 else
234 a.setCache(cacheVal);
235 _props.push_back(std::make_pair(a, uri.noCase(st)));
236 #ifdef GNASH_DEBUG_PROPERTY
237 ObjectURI::Logger l(getStringTable(_owner));
238 log_debug("AS GetterSetter %s inserted with flags %s", l(uri),
239 a.getFlags());
240 #endif
243 return true;
246 bool
247 PropertyList::addGetterSetter(const ObjectURI& uri, as_c_function_ptr getter,
248 as_c_function_ptr setter, const PropFlags& flagsIfMissing)
250 Property a(uri, getter, setter, flagsIfMissing);
252 string_table& st = getStringTable(_owner);
253 const_iterator found = iterator_find(_props, uri, getVM(_owner));
254 if (found != _props.end())
256 // copy flags from previous member (even if it's a normal member ?)
257 a.setFlags(found->first.getFlags());
258 _props.replace(found, std::make_pair(a, uri.noCase(st)));
260 #ifdef GNASH_DEBUG_PROPERTY
261 ObjectURI::Logger l(getStringTable(_owner));
262 log_debug("Native GetterSetter %s replaced copying flags %s", l(uri),
263 a.getFlags());
264 #endif
267 else
269 _props.push_back(std::make_pair(a, uri.noCase(st)));
270 #ifdef GNASH_DEBUG_PROPERTY
271 string_table& st = getStringTable(_owner);
272 log_debug("Native GetterSetter %s in namespace %s inserted with "
273 "flags %s", st.value(key), st.value(nsId), a.getFlags());
274 #endif
277 return true;
280 bool
281 PropertyList::addDestructiveGetter(const ObjectURI& uri, as_function& getter,
282 const PropFlags& flagsIfMissing)
284 const_iterator found = iterator_find(_props, uri, getVM(_owner));
285 if (found != _props.end())
287 ObjectURI::Logger l(getStringTable(_owner));
288 log_error("Property %s already exists, can't addDestructiveGetter",
289 l(uri));
290 return false; // Already exists.
293 // destructive getter don't need a setter
294 Property a(uri, &getter, (as_function*)0, flagsIfMissing, true);
296 string_table& st = getStringTable(_owner);
297 _props.push_back(std::make_pair(a, uri.noCase(st)));
299 #ifdef GNASH_DEBUG_PROPERTY
300 ObjectURI::Logger l(getStringTable(_owner));
301 log_debug("Destructive AS property %s inserted with flags %s",
302 l(uri), a.getFlags());
303 #endif
305 return true;
308 bool
309 PropertyList::addDestructiveGetter(const ObjectURI& uri,
310 as_c_function_ptr getter, const PropFlags& flagsIfMissing)
312 iterator found = iterator_find(_props, uri, getVM(_owner));
313 if (found != _props.end()) return false;
315 // destructive getter don't need a setter
316 Property a(uri, getter, (as_c_function_ptr)0, flagsIfMissing, true);
317 string_table& st = getStringTable(_owner);
318 _props.push_back(std::make_pair(a, uri.noCase(st)));
320 #ifdef GNASH_DEBUG_PROPERTY
321 ObjectURI::Logger l(getStringTable(_owner));
322 log_debug("Destructive native property %s with flags %s", l(uri),
323 a.getFlags());
324 #endif
325 return true;
328 void
329 PropertyList::clear()
331 _props.clear();
334 void
335 PropertyList::setReachable() const
337 foreachFirst(_props.begin(), _props.end(),
338 boost::mem_fn(&Property::setReachable));
341 } // namespace gnash