1 | // -*- c-basic-offset: 2 -*- |
2 | // krazy:excludeall=doublequote_chars (UStrings aren't QStrings) |
3 | /* |
4 | * This file is part of the KDE libraries |
5 | * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) |
6 | * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. |
7 | * |
8 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2 of the License, or (at your option) any later version. |
12 | * |
13 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with this library; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | * |
22 | */ |
23 | |
24 | #include "function_object.h" |
25 | #include <config-kjs.h> |
26 | #include "internal.h" |
27 | #include "function.h" |
28 | #include "scriptfunction.h" |
29 | #include "array_object.h" |
30 | #include "nodes.h" |
31 | #include "lexer.h" |
32 | #include "debugger.h" |
33 | #include "object.h" |
34 | |
35 | #include <assert.h> |
36 | #include <stdio.h> |
37 | #include <string.h> |
38 | |
39 | using namespace KJS; |
40 | |
41 | // ------------------------------ FunctionPrototype ------------------------- |
42 | |
43 | FunctionPrototype::FunctionPrototype(ExecState *exec) |
44 | { |
45 | static const Identifier* applyPropertyName = new Identifier("apply" ); |
46 | static const Identifier* callPropertyName = new Identifier("call" ); |
47 | static const Identifier* bindPropertyName = new Identifier("bind" ); |
48 | |
49 | putDirect(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum); |
50 | putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::ToString, 0, exec->propertyNames().toString), DontEnum); |
51 | putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Apply, 2, *applyPropertyName), DontEnum); |
52 | putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Call, 1, *callPropertyName), DontEnum); |
53 | putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Bind, 1, *bindPropertyName), DontEnum); |
54 | } |
55 | |
56 | FunctionPrototype::~FunctionPrototype() |
57 | { |
58 | } |
59 | |
60 | // ECMA 15.3.4 |
61 | JSValue *FunctionPrototype::callAsFunction(ExecState * /*exec*/, JSObject * /*thisObj*/, const List &/*args*/) |
62 | { |
63 | return jsUndefined(); |
64 | } |
65 | |
66 | // ------------------------------ FunctionProtoFunc ------------------------- |
67 | |
68 | FunctionProtoFunc::FunctionProtoFunc(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name) |
69 | : InternalFunctionImp(funcProto, name) |
70 | , id(i) |
71 | { |
72 | putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum); |
73 | } |
74 | |
75 | JSValue* FunctionProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args) |
76 | { |
77 | JSValue* result = NULL; |
78 | |
79 | switch (id) { |
80 | case ToString: |
81 | if (!thisObj || !thisObj->inherits(&InternalFunctionImp::info)) { |
82 | #ifndef NDEBUG |
83 | fprintf(stderr,"attempted toString() call on null or non-function object\n" ); |
84 | #endif |
85 | return throwError(exec, TypeError); |
86 | } |
87 | if (thisObj->inherits(&FunctionImp::info)) { |
88 | return jsString(static_cast<FunctionImp*>(thisObj)->toSource()); |
89 | } else if (thisObj->inherits(&InternalFunctionImp::info) && |
90 | !static_cast<InternalFunctionImp*>(thisObj)->functionName().isNull()) { |
91 | result = jsString("\nfunction " + static_cast<InternalFunctionImp*>(thisObj)->functionName().ustring() + "() {\n" |
92 | " [native code]\n}\n" ); |
93 | } else { |
94 | result = jsString("[function]" ); |
95 | } |
96 | break; |
97 | case Apply: { |
98 | JSValue *thisArg = args[0]; |
99 | JSValue *argArray = args[1]; |
100 | JSObject *func = thisObj; |
101 | |
102 | if (!func->implementsCall()) |
103 | return throwError(exec, TypeError); |
104 | |
105 | JSObject *applyThis; |
106 | if (thisArg->isUndefinedOrNull()) |
107 | applyThis = exec->dynamicInterpreter()->globalObject(); |
108 | else |
109 | applyThis = thisArg->toObject(exec); |
110 | |
111 | List applyArgs; |
112 | if (!argArray->isUndefinedOrNull()) { |
113 | if (argArray->isObject() && |
114 | (static_cast<JSObject *>(argArray)->inherits(&ArrayInstance::info) || |
115 | static_cast<JSObject *>(argArray)->inherits(&Arguments::info))) { |
116 | |
117 | JSObject *argArrayObj = static_cast<JSObject *>(argArray); |
118 | unsigned int length = argArrayObj->get(exec, exec->propertyNames().length)->toUInt32(exec); |
119 | for (unsigned int i = 0; i < length; i++) |
120 | applyArgs.append(argArrayObj->get(exec,i)); |
121 | } |
122 | else |
123 | return throwError(exec, TypeError); |
124 | } |
125 | result = func->call(exec,applyThis,applyArgs); |
126 | } |
127 | break; |
128 | case Call: { |
129 | JSValue *thisArg = args[0]; |
130 | JSObject *func = thisObj; |
131 | |
132 | if (!func->implementsCall()) |
133 | return throwError(exec, TypeError); |
134 | |
135 | JSObject *callThis; |
136 | if (thisArg->isUndefinedOrNull()) |
137 | callThis = exec->dynamicInterpreter()->globalObject(); |
138 | else |
139 | callThis = thisArg->toObject(exec); |
140 | |
141 | result = func->call(exec,callThis,args.copyTail()); |
142 | } |
143 | break; |
144 | case Bind: { //ECMA Edition 5.1r6 - 15.3.4.5 |
145 | JSObject* target(thisObj); |
146 | if (!target->implementsCall()) |
147 | return throwError(exec, TypeError, "object is not callable" ); |
148 | |
149 | List newArgs; |
150 | for (int i = 1; i < args.size(); ++i) |
151 | newArgs.append(args[i]); |
152 | |
153 | JSObject* boundThis = 0; |
154 | |
155 | // As call does not accept JSValue(undefined/null), |
156 | // do it like in call and use the global object |
157 | if (args[0]->isUndefinedOrNull()) |
158 | boundThis = exec->dynamicInterpreter()->globalObject(); |
159 | else |
160 | boundThis = args[0]->toObject(exec); |
161 | |
162 | BoundFunction* bfunc = new BoundFunction(exec, target, boundThis, newArgs); |
163 | |
164 | unsigned length; |
165 | if (target->inherits(&FunctionImp::info)) { |
166 | double L = target->get(exec, exec->propertyNames().length)->getNumber() - newArgs.size(); |
167 | length = (unsigned)std::max<int>((int)L, 0); |
168 | } else { |
169 | length = 0; |
170 | } |
171 | bfunc->put(exec, exec->propertyNames().length, jsNumber(length), ReadOnly|DontEnum|DontDelete); |
172 | |
173 | JSObject *thrower = new Thrower(TypeError); |
174 | PropertyDescriptor callerDesc; |
175 | |
176 | GetterSetterImp* gs = new GetterSetterImp(); |
177 | gs->setGetter(thrower); |
178 | gs->setSetter(thrower); |
179 | |
180 | callerDesc.setPropertyDescriptorValues(exec, gs, DontEnum|DontDelete); |
181 | bfunc->defineOwnProperty(exec, exec->propertyNames().caller, callerDesc, false); |
182 | |
183 | PropertyDescriptor argumentsDesc; |
184 | argumentsDesc.setPropertyDescriptorValues(exec, gs, DontEnum|DontDelete); |
185 | bfunc->defineOwnProperty(exec, exec->propertyNames().arguments, argumentsDesc, false); |
186 | |
187 | return bfunc; |
188 | } |
189 | break; |
190 | } |
191 | |
192 | return result; |
193 | } |
194 | |
195 | // ------------------------------ FunctionObjectImp ---------------------------- |
196 | |
197 | FunctionObjectImp::FunctionObjectImp(ExecState* exec, FunctionPrototype* funcProto) |
198 | : InternalFunctionImp(funcProto) |
199 | { |
200 | putDirect(exec->propertyNames().prototype, funcProto, DontEnum|DontDelete|ReadOnly); |
201 | |
202 | // no. of arguments for constructor |
203 | putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum); |
204 | } |
205 | |
206 | FunctionObjectImp::~FunctionObjectImp() |
207 | { |
208 | } |
209 | |
210 | bool FunctionObjectImp::implementsConstruct() const |
211 | { |
212 | return true; |
213 | } |
214 | |
215 | // ECMA 15.3.2 The Function Constructor |
216 | JSObject* FunctionObjectImp::construct(ExecState* exec, const List& args, const Identifier& functionName, const UString& sourceURL, int lineNumber) |
217 | { |
218 | UString p("" ); |
219 | UString body; |
220 | int argsSize = args.size(); |
221 | if (argsSize == 0) { |
222 | body = "" ; |
223 | } else if (argsSize == 1) { |
224 | body = args[0]->toString(exec); |
225 | } else { |
226 | p = args[0]->toString(exec); |
227 | for (int k = 1; k < argsSize - 1; k++) |
228 | p += "," + args[k]->toString(exec); |
229 | body = args[argsSize-1]->toString(exec); |
230 | } |
231 | |
232 | // parse the source code |
233 | int sourceId; |
234 | int errLine; |
235 | UString errMsg; |
236 | RefPtr<FunctionBodyNode> functionBody = parser().parseFunctionBody(sourceURL, lineNumber, body.data(), body.size(), &sourceId, &errLine, &errMsg); |
237 | |
238 | // notify debugger that source has been parsed |
239 | Debugger *dbg = exec->dynamicInterpreter()->debugger(); |
240 | if (dbg) { |
241 | // make sure to pass in sourceURL, since it's useful for lazy event listeners, and empty for actual function ctor |
242 | dbg->reportSourceParsed(exec, functionBody.get(), sourceId, sourceURL, body, lineNumber, errLine, errMsg); |
243 | } |
244 | |
245 | // no program node == syntax error - throw a syntax error |
246 | if (!functionBody) |
247 | // we can't return a Completion(Throw) here, so just set the exception |
248 | // and return it |
249 | return throwError(exec, SyntaxError, errMsg, errLine, sourceId, sourceURL); |
250 | |
251 | ScopeChain scopeChain; |
252 | scopeChain.push(exec->lexicalInterpreter()->globalObject()); |
253 | |
254 | FunctionImp* fimp = new FunctionImp(exec, functionName, functionBody.get(), scopeChain); |
255 | |
256 | // parse parameter list. throw syntax error on illegal identifiers |
257 | int len = p.size(); |
258 | const UChar *c = p.data(); |
259 | int i = 0, params = 0; |
260 | UString param; |
261 | while (i < len) { |
262 | while (*c == ' ' && i < len) |
263 | c++, i++; |
264 | if (Lexer::isIdentStart(c->uc)) { // else error |
265 | param = UString(c, 1); |
266 | c++, i++; |
267 | while (i < len && (Lexer::isIdentPart(c->uc))) { |
268 | param += UString(c, 1); |
269 | c++, i++; |
270 | } |
271 | while (i < len && *c == ' ') |
272 | c++, i++; |
273 | if (i == len) { |
274 | functionBody->addParam(Identifier(param)); |
275 | params++; |
276 | break; |
277 | } else if (*c == ',') { |
278 | functionBody->addParam(Identifier(param)); |
279 | params++; |
280 | c++, i++; |
281 | continue; |
282 | } // else error |
283 | } |
284 | return throwError(exec, SyntaxError, "Syntax error in parameter list" ); |
285 | } |
286 | |
287 | List consArgs; |
288 | |
289 | JSObject* objCons = exec->lexicalInterpreter()->builtinObject(); |
290 | JSObject* prototype = objCons->construct(exec,List::empty()); |
291 | prototype->put(exec, exec->propertyNames().constructor, fimp, DontEnum|DontDelete|ReadOnly); |
292 | // ECMA Edition 5.1r6 - 15.3.5.2 - [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false |
293 | fimp->put(exec, exec->propertyNames().prototype, prototype, Internal|DontDelete|DontEnum); |
294 | return fimp; |
295 | } |
296 | |
297 | // ECMA 15.3.2 The Function Constructor |
298 | JSObject* FunctionObjectImp::construct(ExecState* exec, const List& args) |
299 | { |
300 | return construct(exec, args, "anonymous" , UString(), 0); |
301 | } |
302 | |
303 | // ECMA 15.3.1 The Function Constructor Called as a Function |
304 | JSValue* FunctionObjectImp::callAsFunction(ExecState* exec, JSObject* /*thisObj*/, const List &args) |
305 | { |
306 | return construct(exec, args); |
307 | } |
308 | |