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