1 // Function_as.cpp: ActionScript "Function" class, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software 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.
15 //
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
19 //
20
21 #include "Function_as.h"
22
23 #include "as_object.h"
24 #include "Global_as.h"
25 #include "as_value.h"
26 #include "Array_as.h"
27 #include "NativeFunction.h"
28 #include "fn_call.h"
29 #include "VM.h"
30 #include "log.h"
31 #include "namedStrings.h"
32
33 namespace gnash {
34
35 // Forward declarations
36 namespace {
37 as_value function_apply(const fn_call& fn);
38 as_value function_call(const fn_call& fn);
39 }
40
41 namespace {
42
43 /// Utility struct for pushing args to an array.
44 class PushFunctionArgs
45 {
46 public:
PushFunctionArgs(fn_call & fn)47 PushFunctionArgs(fn_call& fn) : _fn(fn) {}
operator ()(const as_value & val)48 void operator()(const as_value& val) {
49 _fn.pushArg(val);
50 }
51 private:
52 fn_call& _fn;
53 };
54
55 }
56
57 void
registerFunctionNative(as_object & global)58 registerFunctionNative(as_object& global)
59 {
60 VM& vm = getVM(global);
61 vm.registerNative(function_call, 101, 10);
62 vm.registerNative(function_apply, 101, 11);
63 }
64
65 void
function_class_init(as_object & where,const ObjectURI & uri)66 function_class_init(as_object& where, const ObjectURI& uri)
67 {
68 Global_as& gl = getGlobal(where);
69
70 NativeFunction* func = new NativeFunction(gl, emptyFunction);
71 as_object* proto = createObject(gl);
72
73 func->init_member(NSV::PROP_PROTOTYPE, proto);
74 func->init_member(NSV::PROP_CONSTRUCTOR, func);
75 proto->init_member(NSV::PROP_CONSTRUCTOR, func);
76
77 // Register _global.Function, only visible for SWF6 up
78 const int swf6flags = as_object::DefaultFlags | PropFlags::onlySWF6Up;
79 func->init_member(NSV::PROP_uuPROTOuu, proto, swf6flags);
80 where.init_member(uri, func, swf6flags);
81
82 VM& vm = getVM(where);
83
84 // Note: these are the first functions created, and they need the
85 // Function class to be registered.
86 proto->init_member("call", vm.getNative(101, 10), swf6flags);
87 proto->init_member("apply", vm.getNative(101, 11), swf6flags);
88 }
89
90 namespace {
91
92 as_value
function_apply(const fn_call & fn)93 function_apply(const fn_call& fn)
94 {
95
96 as_object* function_obj = ensure<ValidThis>(fn);
97
98 // Copy new function call from old one, we'll modify
99 // the copy only if needed
100 fn_call new_fn_call(fn);
101 new_fn_call.resetArgs();
102
103 if (!fn.nargs) {
104 IF_VERBOSE_ASCODING_ERRORS(
105 log_aserror(_("Function.apply() called with no args"));
106 );
107 new_fn_call.this_ptr = &getGlobal(fn);
108 }
109 else {
110 // Get the object to use as 'this' reference
111 as_object* obj = toObject(fn.arg(0), getVM(fn));
112
113 if (!obj) obj = &getGlobal(fn);
114
115 new_fn_call.this_ptr = obj;
116
117 // Note: do not override fn_call::super by creating a super
118 // object, as it may not be needed. Doing so can have a very
119 // detrimental effect on memory usage!
120 // Normal supers will be created when needed in the function
121 // call.
122 new_fn_call.super = nullptr;
123
124 // Check for second argument ('arguments' array)
125 if (fn.nargs > 1) {
126
127 IF_VERBOSE_ASCODING_ERRORS(
128 if (fn.nargs > 2) {
129 log_aserror(_("Function.apply() got %d args, expected at "
130 "most 2 -- discarding the ones in excess"), fn.nargs);
131 }
132 );
133
134 as_object* arg1 = toObject(fn.arg(1), getVM(fn));
135
136 if (arg1) {
137 PushFunctionArgs pa(new_fn_call);
138 foreachArray(*arg1, pa);
139 }
140 }
141 }
142
143 // Call the function
144 return function_obj->call(new_fn_call);
145 }
146
147 as_value
function_call(const fn_call & fn)148 function_call(const fn_call& fn)
149 {
150 as_object* function_obj = ensure<ValidThis>(fn);
151
152 // Copy new function call from old one, we'll modify
153 // the copy only if needed
154 fn_call new_fn_call(fn);
155
156 as_object* tp;
157
158 if (!fn.nargs || fn.arg(0).is_undefined() || fn.arg(0).is_null()) {
159 tp = &getGlobal(fn);
160 }
161 else tp = toObject(fn.arg(0), getVM(fn));
162
163 new_fn_call.this_ptr = tp;
164 new_fn_call.super = nullptr;
165 if (fn.nargs) new_fn_call.drop_bottom();
166
167 // Call the function
168 return function_obj->call(new_fn_call);
169
170 }
171
172 } // anonymous namespace
173 } // gnash namespace
174