1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 // Free Software Foundation, Inc
4 //
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.
9 //
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.
14 //
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
18
19 #ifndef GNASH_FN_CALL_H
20 #define GNASH_FN_CALL_H
21
22 #include <string>
23 #include <vector>
24 #include <cassert>
25 #include <ostream>
26 #include <algorithm>
27
28 #include "utility.h" // for typeName
29 #include "as_object.h"
30 #include "as_value.h"
31 #include "VM.h"
32 #include "GnashException.h"
33 #include "as_environment.h"
34
35
36 // Forward declarations
37 namespace gnash {
38 class movie_definition;
39 }
40
41 namespace gnash {
42
43 /// A class to contain transferable arguments for a fn_call.
44 //
45 /// The operators += and , are implemented for intuitive syntax:
46 //
47 /// FunctionArgs<as_value> args; args += 0.0, "string", NaN.
48 //
49 /// This may have unexpected side effects if it is used in unexpected ways,
50 /// so stick to using such lists, or use operator += repeatedly.
51 //
52 /// The arguments can be moved to another container, and this happens when
53 /// the FunctionArgs object is passed to fn_call. It will still be valid
54 /// afterwards, but will contain no arguments.
55 template<typename T>
56 class FunctionArgs
57 {
58 public:
59
60 typedef typename std::vector<T>::size_type size_type;
61 typedef std::vector<T> container_type;
62 typedef T value_type;
63
64 FunctionArgs() = default;
65 FunctionArgs(FunctionArgs&& other) = default;
66
67 /// The copy constructor copies all the arguments.
68 FunctionArgs(const FunctionArgs& other) = default;
69
70 FunctionArgs& operator+=(T t) {
71 _v.push_back(std::move(t));
72 return *this;
73 }
74
75 FunctionArgs& operator,(T t) {
76 _v.push_back(std::move(t));
77 return *this;
78 }
79
80 template <typename U>
81 FunctionArgs& operator,(U&& u) {
82 _v.emplace_back(std::forward<U>(u));
83 return *this;
84 }
85
86 template <typename U>
87 FunctionArgs& operator+=(U&& u) {
88 _v.emplace_back(std::forward<U>(u));
89 return *this;
90 }
91
92 /// Mark any reachable resources
93 //
94 /// This is only for cases where the lifetime of a FunctionArgs object
95 /// extends beyond a function call.
setReachable()96 void setReachable() const {
97 std::for_each(_v.begin(), _v.end(),
98 std::mem_fun_ref(&as_value::setReachable));
99 }
100
swap(std::vector<T> & to)101 void swap(std::vector<T>& to) {
102 std::swap(_v, to);
103 }
104
size()105 size_type size() const {
106 return _v.size();
107 }
108
109 private:
110 std::vector<T> _v;
111 };
112
113
114 /// \brief
115 /// Parameters/environment for builtin or user-defined functions
116 /// callable from ActionScript.
117 class fn_call
118 {
119 public:
120
121 typedef FunctionArgs<as_value> Args;
122
123 /// Construct a fn_call
124 //
125 /// @param isNew Pass true if this is a constructing fn_call,
126 /// i.e. if it is called as a result of 'new'.
127 /// @param super Pass an overridden super value to the function
128 /// call. If this is 0, the super reference will be
129 /// calculated from the this pointer (if that is not
130 /// null) whenever a function requires it.
131 fn_call(as_object* this_in, const as_environment& env_in,
132 Args& args, as_object* sup = nullptr, bool isNew = false)
133 :
this_ptr(this_in)134 this_ptr(this_in),
135 super(sup),
136 nargs(args.size()),
137 callerDef(nullptr),
138 _env(env_in),
139 _new(isNew)
140 {
141 args.swap(_args);
142 }
143
fn_call(as_object * this_in,const as_environment & env_in)144 fn_call(as_object* this_in, const as_environment& env_in)
145 :
146 this_ptr(this_in),
147 super(nullptr),
148 nargs(0),
149 callerDef(nullptr),
150 _env(env_in),
151 _new(false)
152 {
153 }
154
155 /// Copy constructor
fn_call(const fn_call & fn)156 fn_call(const fn_call& fn)
157 :
158 this_ptr(fn.this_ptr),
159 super(fn.super),
160 nargs(fn.nargs),
161 callerDef(fn.callerDef),
162 _env(fn._env),
163 _args(fn._args),
164 _new(false)
165 {
166 }
167
168 /// The as_object (or a pointer derived thereof) on which this call
169 /// is taking place.
170 as_object* this_ptr;
171
172 /// The "super" object in this function call context
173 //
174 /// If this is 0, the super may be constructed from the this pointer.
175 as_object* super;
176
177 /// Number of arguments to this ActionScript function call.
178 Args::size_type nargs;
179
180 /// Definition containing caller code. 0 if spontaneous (system event).
181 const movie_definition* callerDef;
182
183 /// Return the VM this fn_call is running from
getVM()184 VM& getVM() const {
185 return _env.getVM();
186 }
187
188 /// Return true if this call is an object instantiation
isInstantiation()189 bool isInstantiation() const {
190 return _new;
191 }
192
193 /// Access a particular argument.
arg(unsigned int n)194 const Args::value_type& arg(unsigned int n) const {
195 assert(n < nargs);
196 return _args[n];
197 }
198
getArgs()199 const Args::container_type& getArgs() const {
200 return _args;
201 }
202
drop_bottom()203 void drop_bottom() {
204 assert(!_args.empty());
205 _args.erase(_args.begin());
206 --nargs;
207 }
208
env()209 const as_environment& env() const {
210 return _env;
211 }
212
213 /// Dump arguments to given output stream
dump_args(std::ostream & os)214 void dump_args(std::ostream& os) const {
215 for (size_t i = 0; i < nargs; ++i) {
216 if (i) os << ", ";
217 os << arg(i);
218 }
219 }
220
resetArgs()221 void resetArgs() {
222 nargs = 0;
223 _args.clear();
224 }
225
pushArg(const Args::value_type & arg)226 void pushArg(const Args::value_type& arg) {
227 ++nargs;
228 _args.push_back(arg);
229 }
230
231 private:
232
233 /// The ActionScript environment in which the function call is taking
234 /// place. This contains, among other things, the function arguments.
235 const as_environment& _env;
236
237 /// The actual arguments
238 Args::container_type _args;
239
240 bool _new;
241
242 };
243
244
245 /// Check that the 'this' pointer has a particular native type ('Relay').
246 //
247 /// This is the most likely of the cases to reflect AS behaviour.
248 template<typename T>
249 struct ThisIsNative
250 {
251 typedef T value_type;
operatorThisIsNative252 value_type* operator()(const as_object* o) const {
253 return dynamic_cast<value_type*>(o->relay());
254 }
255 };
256
257 /// Check that the 'this' pointer is a DisplayObject
258 //
259 /// By default this just checks for any DisplayObject type.
260 template<typename T = DisplayObject>
261 struct IsDisplayObject
262 {
263 typedef T value_type;
operatorIsDisplayObject264 value_type* operator()(const as_object* o) const {
265 if (!o) return nullptr;
266 return dynamic_cast<T*>(o->displayObject());
267 }
268 };
269
270 /// Check that the 'this' pointer is not null.
271 struct ValidThis
272 {
273 typedef as_object value_type;
operatorValidThis274 value_type* operator()(as_object* o) const {
275 return o;
276 }
277 };
278
279 /// Templated function to check the validity of a function call.
280 //
281 /// It throws an exception if the condition is not fulfilled, it throws
282 /// an ActionTypeError, resulting in the function call being aborted and
283 /// an undefined as_value returned.
284 //
285 /// Note that not carrying out a function because the this pointer is
286 /// undefined is not ActionScript behaviour in most cases. To avoid
287 /// spreading its usage outside AS function implementations, this function
288 /// now takes a fn_call as an argument.
289 //
290 /// @tparam T A struct defining a value_type and an operator() that
291 /// checks the as_object's validity. A pointer to the
292 /// value_type is returned on success, an exception thrown
293 /// on failure.
294 /// @param fn The function whose 'this' pointer should be checked.
295 /// @return If the cast succeeds, the pointer cast to the
296 /// requested type.
297 template<typename T>
298 typename T::value_type*
ensure(const fn_call & fn)299 ensure(const fn_call& fn)
300 {
301 as_object* obj = fn.this_ptr;
302 if (!obj) throw ActionTypeError();
303
304 typename T::value_type* ret = T()(obj);
305
306 if (!ret) {
307 std::string target = typeName(ret);
308 std::string source = typeName(obj);
309
310 std::string msg = "Function requiring " + target + " as 'this' "
311 "called from " + source + " instance.";
312
313 throw ActionTypeError(msg);
314 }
315 return ret;
316 }
317
318 inline string_table&
getStringTable(const fn_call & fn)319 getStringTable(const fn_call& fn)
320 {
321 return fn.getVM().getStringTable();
322 }
323
324 inline movie_root&
getRoot(const fn_call & fn)325 getRoot(const fn_call& fn)
326 {
327 return fn.getVM().getRoot();
328 }
329
330 inline int
getSWFVersion(const fn_call & fn)331 getSWFVersion(const fn_call& fn)
332 {
333 return fn.getVM().getSWFVersion();
334 }
335
336 inline VM&
getVM(const fn_call & fn)337 getVM(const fn_call& fn)
338 {
339 return fn.getVM();
340 }
341
342 inline Global_as&
getGlobal(const fn_call & fn)343 getGlobal(const fn_call& fn)
344 {
345 return *fn.getVM().getGlobal();
346 }
347
348 } // namespace gnash
349
350
351 #endif
352
353
354 // Local Variables:
355 // mode: C++
356 // indent-tabs-mode: nil
357 // End:
358