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