FunctionPrototype.cpp 8.78 KB
Newer Older
1
/*
2
 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
darin@apple.com's avatar
darin@apple.com committed
3
 *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4
5
6
7
8
9
10
11
12
13
14
15
16
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
mjs's avatar
mjs committed
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18
 *
19
20
 */

mjs's avatar
mjs committed
21
#include "config.h"
darin@apple.com's avatar
darin@apple.com committed
22
#include "FunctionPrototype.h"
ggaren's avatar
ggaren committed
23

mjs's avatar
mjs committed
24
#include "JSGlobalObject.h"
25
#include "Parser.h"
26
#include "debugger.h"
27
#include "JSArray.h"
darin@apple.com's avatar
darin@apple.com committed
28
29
#include "JSFunction.h"
#include "JSString.h"
ggaren's avatar
ggaren committed
30
31
#include "lexer.h"
#include "nodes.h"
darin@apple.com's avatar
darin@apple.com committed
32
#include "JSObject.h"
33
34
#include <stdio.h>
#include <string.h>
ggaren's avatar
ggaren committed
35
#include <wtf/Assertions.h>
36

weinig@apple.com's avatar
weinig@apple.com committed
37
namespace KJS {
38

darin's avatar
darin committed
39
// ------------------------------ FunctionPrototype -------------------------
40

darin@apple.com's avatar
darin@apple.com committed
41
42
43
static JSValue* functionProtoFuncToString(ExecState*, JSObject*, JSValue*, const ArgList&);
static JSValue* functionProtoFuncApply(ExecState*, JSObject*, JSValue*, const ArgList&);
static JSValue* functionProtoFuncCall(ExecState*, JSObject*, JSValue*, const ArgList&);
weinig@apple.com's avatar
weinig@apple.com committed
44
45

FunctionPrototype::FunctionPrototype(ExecState* exec)
46
{
ap@webkit.org's avatar
ap@webkit.org committed
47
    putDirect(exec->propertyNames().length, jsNumber(exec, 0), DontDelete | ReadOnly | DontEnum);
48

ap@webkit.org's avatar
ap@webkit.org committed
49
50
51
    putDirectFunction(new (exec) PrototypeFunction(exec, this, 0, exec->propertyNames().toString, functionProtoFuncToString), DontEnum);
    putDirectFunction(new (exec) PrototypeFunction(exec, this, 2, exec->propertyNames().apply, functionProtoFuncApply), DontEnum);
    putDirectFunction(new (exec) PrototypeFunction(exec, this, 1, exec->propertyNames().call, functionProtoFuncCall), DontEnum);
52
53
}

darin@apple.com's avatar
darin@apple.com committed
54
static JSValue* callFunctionPrototype(ExecState*, JSObject*, JSValue*, const ArgList&)
55
{
weinig@apple.com's avatar
weinig@apple.com committed
56
    return jsUndefined();
57
58
}

darin@apple.com's avatar
darin@apple.com committed
59
60
61
62
63
64
65
// ECMA 15.3.4
CallType FunctionPrototype::getCallData(CallData& callData)
{
    callData.native.function = callFunctionPrototype;
    return CallTypeNative;
}

weinig@apple.com's avatar
weinig@apple.com committed
66
// Functions
67

darin@apple.com's avatar
darin@apple.com committed
68
JSValue* functionProtoFuncToString(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
69
{
darin@apple.com's avatar
darin@apple.com committed
70
    if (!thisValue->isObject(&InternalFunction::info))
weinig@apple.com's avatar
weinig@apple.com committed
71
72
        return throwError(exec, TypeError);

darin@apple.com's avatar
darin@apple.com committed
73
74
75
76
    InternalFunction* function = static_cast<InternalFunction*>(thisValue);

    if (function->inherits(&JSFunction::info)) {
        JSFunction* fi = static_cast<JSFunction*>(thisValue);
ap@webkit.org's avatar
ap@webkit.org committed
77
        return jsString(exec, "function " + fi->functionName().ustring() + "(" + fi->body->paramString() + ") " + fi->body->toSourceString());
78
79
    }

darin@apple.com's avatar
darin@apple.com committed
80
    return jsString(exec, "function " + function->functionName().ustring() + "() {\n    [native code]\n}");
weinig@apple.com's avatar
weinig@apple.com committed
81
82
}

darin@apple.com's avatar
darin@apple.com committed
83
JSValue* functionProtoFuncApply(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
weinig@apple.com's avatar
weinig@apple.com committed
84
{
darin@apple.com's avatar
darin@apple.com committed
85
86
87
    CallData callData;
    CallType callType = thisValue->getCallData(callData);
    if (callType == CallTypeNone)
weinig@apple.com's avatar
weinig@apple.com committed
88
        return throwError(exec, TypeError);
89

weinig@apple.com's avatar
weinig@apple.com committed
90
91
92
    JSValue* thisArg = args[0];
    JSValue* argArray = args[1];

darin@apple.com's avatar
darin@apple.com committed
93
    JSValue* applyThis;
darin's avatar
darin committed
94
    if (thisArg->isUndefinedOrNull())
weinig@apple.com's avatar
weinig@apple.com committed
95
        applyThis = exec->globalThisValue();
96
    else
weinig@apple.com's avatar
weinig@apple.com committed
97
        applyThis = thisArg->toObject(exec);
98

darin@apple.com's avatar
darin@apple.com committed
99
    ArgList applyArgs;
darin's avatar
darin committed
100
    if (!argArray->isUndefinedOrNull()) {
weinig@apple.com's avatar
weinig@apple.com committed
101
        if (argArray->isObject() &&
darin@apple.com's avatar
darin@apple.com committed
102
            (static_cast<JSObject*>(argArray)->inherits(&JSArray::info) ||
weinig@apple.com's avatar
weinig@apple.com committed
103
104
105
106
107
108
109
110
             static_cast<JSObject*>(argArray)->inherits(&Arguments::info))) {

            JSObject* argArrayObj = static_cast<JSObject*>(argArray);
            unsigned int length = argArrayObj->get(exec, exec->propertyNames().length)->toUInt32(exec);
            for (unsigned int i = 0; i < length; i++)
                applyArgs.append(argArrayObj->get(exec, i));
        } else
            return throwError(exec, TypeError);
111
112
    }

darin@apple.com's avatar
darin@apple.com committed
113
    return call(exec, thisValue, callType, callData, applyThis, applyArgs);
weinig@apple.com's avatar
weinig@apple.com committed
114
115
}

darin@apple.com's avatar
darin@apple.com committed
116
JSValue* functionProtoFuncCall(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
weinig@apple.com's avatar
weinig@apple.com committed
117
{
darin@apple.com's avatar
darin@apple.com committed
118
119
120
    CallData callData;
    CallType callType = thisValue->getCallData(callData);
    if (callType == CallTypeNone)
weinig@apple.com's avatar
weinig@apple.com committed
121
122
123
        return throwError(exec, TypeError);

    JSValue* thisArg = args[0];
124

weinig@apple.com's avatar
weinig@apple.com committed
125
    JSObject* callThis;
darin's avatar
darin committed
126
    if (thisArg->isUndefinedOrNull())
weinig@apple.com's avatar
weinig@apple.com committed
127
        callThis = exec->globalThisValue();
128
    else
weinig@apple.com's avatar
weinig@apple.com committed
129
        callThis = thisArg->toObject(exec);
130

darin@apple.com's avatar
darin@apple.com committed
131
    ArgList argsTail;
ggaren's avatar
ggaren committed
132
    args.getSlice(1, argsTail);
darin@apple.com's avatar
darin@apple.com committed
133
    return call(exec, thisValue, callType, callData, callThis, argsTail);
134
135
}

darin@apple.com's avatar
darin@apple.com committed
136
// ------------------------------ FunctionConstructor ----------------------------
137

darin@apple.com's avatar
darin@apple.com committed
138
FunctionConstructor::FunctionConstructor(ExecState* exec, FunctionPrototype* functionPrototype)
ap@webkit.org's avatar
ap@webkit.org committed
139
    : InternalFunction(functionPrototype, Identifier(exec, functionPrototype->classInfo()->className))
140
{
weinig@apple.com's avatar
weinig@apple.com committed
141
    putDirect(exec->propertyNames().prototype, functionPrototype, DontEnum | DontDelete | ReadOnly);
142

weinig@apple.com's avatar
weinig@apple.com committed
143
    // Number of arguments for constructor
ap@webkit.org's avatar
ap@webkit.org committed
144
    putDirect(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontDelete | DontEnum);
145
146
}

darin@apple.com's avatar
darin@apple.com committed
147
static JSObject* constructWithFunctionConstructor(ExecState* exec, JSObject*, const ArgList& args)
148
{
darin@apple.com's avatar
darin@apple.com committed
149
150
151
152
153
154
    return constructFunction(exec, args);
}

ConstructType FunctionConstructor::getConstructData(ConstructData& constructData)
{
    constructData.native.function = constructWithFunctionConstructor;
155
    return ConstructTypeNative;
156
157
}

darin@apple.com's avatar
darin@apple.com committed
158
159
160
161
162
163
164
165
166
167
168
169
static JSValue* callFunctionConstructor(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
{
    return constructFunction(exec, args);
}

// ECMA 15.3.1 The Function Constructor Called as a Function
CallType FunctionConstructor::getCallData(CallData& callData)
{
    callData.native.function = callFunctionConstructor;
    return CallTypeNative;
}

170
// ECMA 15.3.2 The Function Constructor
darin@apple.com's avatar
darin@apple.com committed
171
JSObject* constructFunction(ExecState* exec, const ArgList& args, const Identifier& functionName, const UString& sourceURL, int lineNumber)
172
{
weinig@apple.com's avatar
weinig@apple.com committed
173
174
175
176
177
178
179
180
181
182
183
184
    UString p("");
    UString body;
    int argsSize = args.size();
    if (argsSize == 0)
        body = "";
    else if (argsSize == 1)
        body = args[0]->toString(exec);
    else {
        p = args[0]->toString(exec);
        for (int k = 1; k < argsSize - 1; k++)
            p += "," + args[k]->toString(exec);
        body = args[argsSize - 1]->toString(exec);
185
    }
186

weinig@apple.com's avatar
weinig@apple.com committed
187
188
189
190
    // parse the source code
    int sourceId;
    int errLine;
    UString errMsg;
191
    RefPtr<SourceProvider> source = UStringSourceProvider::create(body);
ap@webkit.org's avatar
ap@webkit.org committed
192
    RefPtr<FunctionBodyNode> functionBody = exec->parser()->parse<FunctionBodyNode>(exec, sourceURL, lineNumber, source, &sourceId, &errLine, &errMsg);
193

weinig@apple.com's avatar
weinig@apple.com committed
194
195
196
197
198
    // No program node == syntax error - throw a syntax error
    if (!functionBody)
        // We can't return a Completion(Throw) here, so just set the exception
        // and return it
        return throwError(exec, SyntaxError, errMsg, errLine, sourceId, sourceURL);
199
200
    
    functionBody->setSource(SourceRange(source, 0, source->length()));
mjs@apple.com's avatar
mjs@apple.com committed
201
    ScopeChain scopeChain(exec->lexicalGlobalObject(), exec->globalThisValue());
weinig@apple.com's avatar
weinig@apple.com committed
202

darin@apple.com's avatar
darin@apple.com committed
203
    JSFunction* function = new (exec) JSFunction(exec, functionName, functionBody.get(), scopeChain.node());
weinig@apple.com's avatar
weinig@apple.com committed
204
205
206
207
208
209
210
211
212

    // parse parameter list. throw syntax error on illegal identifiers
    int len = p.size();
    const UChar* c = p.data();
    int i = 0, params = 0;
    UString param;
    while (i < len) {
        while (*c == ' ' && i < len)
            c++, i++;
eric@webkit.org's avatar
eric@webkit.org committed
213
        if (Lexer::isIdentStart(c[0])) {  // else error
weinig@apple.com's avatar
weinig@apple.com committed
214
215
            param = UString(c, 1);
            c++, i++;
eric@webkit.org's avatar
eric@webkit.org committed
216
            while (i < len && (Lexer::isIdentPart(c[0]))) {
217
                param.append(*c);
weinig@apple.com's avatar
weinig@apple.com committed
218
219
220
221
222
                c++, i++;
            }
            while (i < len && *c == ' ')
                c++, i++;
            if (i == len) {
ap@webkit.org's avatar
ap@webkit.org committed
223
                functionBody->parameters().append(Identifier(exec, param));
weinig@apple.com's avatar
weinig@apple.com committed
224
225
226
                params++;
                break;
            } else if (*c == ',') {
ap@webkit.org's avatar
ap@webkit.org committed
227
                functionBody->parameters().append(Identifier(exec, param));
weinig@apple.com's avatar
weinig@apple.com committed
228
229
230
231
232
233
234
                params++;
                c++, i++;
                continue;
            } // else error
        }
        return throwError(exec, SyntaxError, "Syntax error in parameter list");
    }
235
  
darin@apple.com's avatar
darin@apple.com committed
236
237
238
239
    JSObject* prototype = constructEmptyObject(exec);
    prototype->putDirect(exec->propertyNames().constructor, function, DontEnum);
    function->putDirect(exec->propertyNames().prototype, prototype, DontDelete);
    return function;
240
241
}

kdecker's avatar
kdecker committed
242
// ECMA 15.3.2 The Function Constructor
darin@apple.com's avatar
darin@apple.com committed
243
JSObject* constructFunction(ExecState* exec, const ArgList& args)
244
{
darin@apple.com's avatar
darin@apple.com committed
245
    return constructFunction(exec, args, Identifier(exec, "anonymous"), UString(), 1);
246
}
weinig@apple.com's avatar
weinig@apple.com committed
247
248

} // namespace KJS