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