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 "Function.h"
20 
21 #include <algorithm>
22 
23 #include "log.h"
24 #include "fn_call.h"
25 #include "action_buffer.h"
26 #include "ActionExec.h"
27 #include "VM.h"
28 #include "NativeFunction.h"
29 #include "Global_as.h"
30 #include "namedStrings.h"
31 #include "CallStack.h"
32 #include "DisplayObject.h"
33 
34 namespace gnash {
35 
Function(const action_buffer & ab,as_environment & env,size_t start,ScopeStack scopeStack)36 Function::Function(const action_buffer& ab, as_environment& env,
37             size_t start, ScopeStack scopeStack)
38     :
39     UserFunction(getGlobal(env)),
40     _env(env),
41     _pool(getVM(env).getConstantPool()),
42     _action_buffer(ab),
43     _scopeStack(std::move(scopeStack)),
44     _startPC(start),
45     _length(0)
46 {
47     assert( _startPC < _action_buffer.size() );
48 }
49 
TargetGuard(as_environment & e,DisplayObject * ch,DisplayObject * och)50 TargetGuard::TargetGuard(as_environment& e, DisplayObject* ch,
51         DisplayObject* och)
52     :
53     env(e),
54     from(env.target()),
55     from_orig(env.get_original_target())
56 {
57     env.set_target(ch);
58     env.set_original_target(och);
59 }
60 
61 
~TargetGuard()62 TargetGuard::~TargetGuard()
63 {
64     env.set_target(from);
65     env.set_original_target(from_orig);
66 }
67 
68 // Dispatch.
69 as_value
call(const fn_call & fn)70 Function::call(const fn_call& fn)
71 {
72     // Extract caller before pushing ourself on the call stack
73     VM& vm = getVM(fn);
74 
75     as_object* caller = vm.calling() ? &vm.currentCall().function() : nullptr;
76 
77     // Set up local stack frame, for parameters and locals.
78     FrameGuard guard(getVM(fn), *this);
79     CallFrame& cf = guard.callFrame();
80 
81     DisplayObject* target = _env.target();
82     DisplayObject* orig_target = _env.get_original_target();
83 
84     // Some features are version-dependant.
85     const int swfversion = getSWFVersion(fn);
86 
87     if (swfversion < 6) {
88         // In SWF5, when 'this' is a DisplayObject it becomes
89         // the target for this function call.
90         // See actionscript.all/setProperty.as
91         DisplayObject* ch = get<DisplayObject>(fn.this_ptr);
92         if (ch) {
93             target = ch;
94             orig_target = ch;
95         }
96     }
97 
98     /// This is only needed for SWF5 (temp switch of target)
99     /// We do always and base 'target' value on SWF version.
100     /// TODO: simplify code by maybe using a custom as_environment
101     ///       instead, so to get an "original" target being
102     ///       the one set now (rather then the really original one)
103     /// TODO: test scope when calling functions defined in another timeline
104     ///       (target, in particular).
105     TargetGuard targetGuard(_env, target, orig_target);
106 
107     // Temporarely restore the ConstantPool which was
108     // in effect at the time of function definition
109     PoolGuard poolGuard(getVM(_env), _pool);
110 
111     // Push the arguments onto the local frame.
112     for (size_t i = 0, n = _args.size(); i < n; ++i) {
113 
114         assert(_args[i].reg == 0);
115         if (i < fn.nargs) {
116             setLocal(cf, _args[i].name, fn.arg(i));
117         }
118         else {
119             // Still declare named arguments, even if
120             // they are not passed from caller
121             // See bug #22203
122             declareLocal(cf, _args[i].name);
123         }
124     }
125 
126     // Add 'this'
127     setLocal(cf, NSV::PROP_THIS, fn.this_ptr ? fn.this_ptr : as_value());
128 
129     as_object* super = fn.super ? fn.super :
130         fn.this_ptr ? fn.this_ptr->get_super() : nullptr;
131 
132     // Add 'super' (SWF6+ only)
133     if (super && swfversion > 5) {
134         setLocal(cf, NSV::PROP_SUPER, super);
135     }
136 
137     // Add 'arguments'
138     as_object* args = getGlobal(fn).createArray();
139 
140     // Put 'arguments' in a local var.
141     setLocal(cf, NSV::PROP_ARGUMENTS, getArguments(*this, *args, fn, caller));
142 
143     // Execute the actions.
144     as_value result;
145     ActionExec(*this, _env, &result, fn.this_ptr)();
146     return result;
147 }
148 
149 void
setLength(size_t len)150 Function::setLength(size_t len)
151 {
152     assert(_startPC + len <= _action_buffer.size());
153     _length = len;
154 }
155 
156 void
markReachableResources() const157 Function::markReachableResources() const
158 {
159     std::for_each(_scopeStack.begin(), _scopeStack.end(),
160         std::mem_fun(&as_object::setReachable));
161 
162     _env.markReachableResources();
163 
164     // Invoke parent class marker
165     as_object::markReachableResources();
166 }
167 
168 as_object*
getArguments(Function & callee,as_object & args,const fn_call & fn,as_object * caller)169 getArguments(Function& callee, as_object& args, const fn_call& fn,
170         as_object* caller)
171 {
172 
173     for (size_t i = 0; i < fn.nargs; ++i) {
174         callMethod(&args, NSV::PROP_PUSH, fn.arg(i));
175     }
176 
177     args.init_member(NSV::PROP_CALLEE, &callee);
178     args.init_member(NSV::PROP_CALLER, caller);
179     return &args;
180 
181 }
182 
183 } // end of gnash namespace
184 
185