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 #include "Function2.h"
20 
21 #include "log.h"
22 #include "fn_call.h"
23 #include "action_buffer.h"
24 #include "ActionExec.h"
25 #include "VM.h"
26 #include "NativeFunction.h"
27 #include "Global_as.h"
28 #include "namedStrings.h"
29 #include "CallStack.h"
30 #include "MovieClip.h"
31 #include "DisplayObject.h"
32 
33 namespace gnash {
34 
Function2(const action_buffer & ab,as_environment & env,size_t start,const ScopeStack & scopeStack)35 Function2::Function2(const action_buffer& ab, as_environment& env,
36 			size_t start, const ScopeStack& scopeStack)
37 	:
38 	Function(ab, env, start, scopeStack),
39 	_registerCount(0),
40 	_function2Flags(0)
41 {
42 }
43 
44 // Dispatch.
45 as_value
call(const fn_call & fn)46 Function2::call(const fn_call& fn)
47 {
48     // Extract caller before pushing ourself on the call stack
49     VM& vm = getVM(fn);
50 
51     as_object* caller = vm.calling() ? &vm.currentCall().function() : nullptr;
52 
53 	// Set up local stack frame, for parameters and locals.
54 	FrameGuard guard(getVM(fn), *this);
55     CallFrame& cf = guard.callFrame();
56 
57 	DisplayObject* target = _env.target();
58 	DisplayObject* orig_target = _env.get_original_target();
59 
60 	// Some features are version-dependant.
61 	const int swfversion = getSWFVersion(fn);
62 
63 	if (swfversion < 6) {
64 		// In SWF5, when 'this' is a DisplayObject it becomes
65 		// the target for this function call.
66 		// See actionscript.all/setProperty.as
67 		//
68 		if (fn.this_ptr) {
69 			DisplayObject* ch = get<DisplayObject>(fn.this_ptr);
70 			if (ch) {
71 				target = ch;
72 				orig_target = ch;
73 			}
74 		}
75 	}
76 
77 	/// This is only needed for SWF5 (temp switch of target)
78 	/// We do always and base 'target' value on SWF version.
79 	/// TODO: simplify code by maybe using a custom as_environment
80 	///       instead, so to get an "original" target being
81 	///       the one set now (rather then the really original one)
82 	/// TODO: test scope when calling functions defined in another timeline
83 	///       (target, in particular).
84 	TargetGuard targetGuard(_env, target, orig_target);
85 
86     // Temporarely restore the ConstantPool which was
87     // in effect at the time of function definition
88     PoolGuard poolGuard(getVM(_env), _pool);
89 
90     // function2: most args go in registers; any others get pushed.
91 
92     // Handle the implicit args.
93     // @@ why start at 1 ? Note that starting at 0 makes
94     // intro.swf movie fail to play correctly.
95     size_t current_reg(1);
96 
97     // This is us. TODO: why do we have to query the VM to get
98     // what are effectively our own resources?
99 
100     // If this is not suppressed it is either placed in a register
101     // or set as a local variable, but not both.
102     if (!(_function2Flags & SUPPRESS_THIS)) {
103         if (_function2Flags & PRELOAD_THIS) {
104             // preload 'this' into a register.
105             // TODO: check whether it should be undefined or null
106             // if this_ptr is null.
107             cf.setLocalRegister(current_reg, fn.this_ptr);
108             ++current_reg;
109         }
110         else {
111             // Put 'this' in a local var.
112             setLocal(cf, NSV::PROP_THIS,
113                     fn.this_ptr ? fn.this_ptr : as_value());
114         }
115     }
116 
117     // This works slightly differently from 'super' and 'this'. The
118     // arguments are only ever either placed in the register or a
119     // local variable, but if both preload and suppress arguments flags
120     // are set, an empty array is still placed to the register.
121     // This seems like a bug in the reference player.
122     if (!(_function2Flags & SUPPRESS_ARGUMENTS) ||
123             (_function2Flags & PRELOAD_ARGUMENTS)) {
124 
125         as_object* args = getGlobal(fn).createArray();
126 
127         if (!(_function2Flags & SUPPRESS_ARGUMENTS)) {
128             getArguments(*this, *args, fn, caller);
129         }
130 
131         if (_function2Flags & PRELOAD_ARGUMENTS) {
132             // preload 'arguments' into a register.
133             cf.setLocalRegister(current_reg, args);
134             ++current_reg;
135         }
136         else {
137             // Put 'arguments' in a local var.
138             setLocal(cf, NSV::PROP_ARGUMENTS, args);
139         }
140 
141     }
142 
143     // If super is not suppressed it is either placed in a register
144     // or set as a local variable, but not both.
145     if (swfversion > 5 && !(_function2Flags & SUPPRESS_SUPER)) {
146 
147         // Put 'super' in a register (SWF6+ only).
148         // TOCHECK: should we still set it if not available ?
149         as_object* super = fn.super ? fn.super :
150             fn.this_ptr ? fn.this_ptr->get_super() : nullptr;
151 
152         if (super && (_function2Flags & PRELOAD_SUPER)) {
153             cf.setLocalRegister(current_reg, super);
154             current_reg++;
155         }
156         else if (super) {
157             setLocal(cf, NSV::PROP_SUPER, super);
158         }
159     }
160 
161     if (_function2Flags & PRELOAD_ROOT) {
162         // Put '_root' (if any) in a register.
163         DisplayObject* tgtch = _env.target();
164         if (tgtch) {
165             // NOTE: _lockroot will be handled by getAsRoot()
166             as_object* r = getObject(tgtch->getAsRoot());
167             cf.setLocalRegister(current_reg, r);
168             ++current_reg;
169         }
170     }
171 
172     if (_function2Flags & PRELOAD_PARENT) {
173         DisplayObject* tgtch = _env.target();
174         if (tgtch) {
175             as_object* p = getObject(tgtch->parent());
176             cf.setLocalRegister(current_reg, p);
177             ++current_reg;
178         }
179     }
180 
181     if (_function2Flags & PRELOAD_GLOBAL) {
182         // Put '_global' in a register.
183         as_object* global = vm.getGlobal();
184         cf.setLocalRegister(current_reg, global);
185         ++current_reg;
186     }
187 
188     // Handle the explicit args.
189     // This must be done after implicit ones,
190     // as the explicit override the implicits:
191     // see swfdec/definefunction2-override
192     for (size_t i = 0, n = _args.size(); i < n; ++i) {
193         // not a register, declare as local
194         if (!_args[i].reg) {
195             if (i < fn.nargs) {
196                 // Conventional arg passing: create a local var.
197                 setLocal(cf, _args[i].name, fn.arg(i));
198             }
199             else {
200                 // Still declare named arguments, even if
201                 // they are not passed from caller
202                 // See bug #22203
203                 declareLocal(cf, _args[i].name);
204             }
205         }
206         else {
207             if (i < fn.nargs) {
208                 // Pass argument into a register.
209                 const int reg = _args[i].reg;
210                 cf.setLocalRegister(reg, fn.arg(i));
211             }
212             // If no argument was passed, no need to setup a register
213             // I guess.
214         }
215     }
216 
217 	// Execute the actions.
218     as_value result;
219     ActionExec(*this, _env, &result, fn.this_ptr)();
220     return result;
221 }
222 
223 } // end of gnash namespace
224 
225