Foo::s_info should be Foo::info(), so that you can change how the s_info is actually...
[WebKit-https.git] / Source / JavaScriptCore / runtime / FunctionPrototype.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20
21 #include "config.h"
22 #include "FunctionPrototype.h"
23
24 #include "Arguments.h"
25 #include "JSArray.h"
26 #include "JSBoundFunction.h"
27 #include "JSFunction.h"
28 #include "JSString.h"
29 #include "JSStringBuilder.h"
30 #include "Interpreter.h"
31 #include "Lexer.h"
32 #include "Operations.h"
33
34 namespace JSC {
35
36 ASSERT_HAS_TRIVIAL_DESTRUCTOR(FunctionPrototype);
37
38 const ClassInfo FunctionPrototype::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(FunctionPrototype) };
39
40 static EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState*);
41 static EncodedJSValue JSC_HOST_CALL functionProtoFuncApply(ExecState*);
42 static EncodedJSValue JSC_HOST_CALL functionProtoFuncCall(ExecState*);
43 static EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState*);
44
45 FunctionPrototype::FunctionPrototype(JSGlobalObject* globalObject, Structure* structure)
46     : InternalFunction(globalObject, structure)
47 {
48 }
49
50 void FunctionPrototype::finishCreation(ExecState* exec, const String& name)
51 {
52     Base::finishCreation(exec->vm(), name);
53     putDirectWithoutTransition(exec->vm(), exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
54 }
55
56 void FunctionPrototype::addFunctionProperties(ExecState* exec, JSGlobalObject* globalObject, JSFunction** callFunction, JSFunction** applyFunction)
57 {
58     JSFunction* toStringFunction = JSFunction::create(exec, globalObject, 0, exec->propertyNames().toString.string(), functionProtoFuncToString);
59     putDirectWithoutTransition(exec->vm(), exec->propertyNames().toString, toStringFunction, DontEnum);
60
61     *applyFunction = JSFunction::create(exec, globalObject, 2, exec->propertyNames().apply.string(), functionProtoFuncApply);
62     putDirectWithoutTransition(exec->vm(), exec->propertyNames().apply, *applyFunction, DontEnum);
63
64     *callFunction = JSFunction::create(exec, globalObject, 1, exec->propertyNames().call.string(), functionProtoFuncCall);
65     putDirectWithoutTransition(exec->vm(), exec->propertyNames().call, *callFunction, DontEnum);
66
67     JSFunction* bindFunction = JSFunction::create(exec, globalObject, 1, exec->propertyNames().bind.string(), functionProtoFuncBind);
68     putDirectWithoutTransition(exec->vm(), exec->propertyNames().bind, bindFunction, DontEnum);
69 }
70
71 static EncodedJSValue JSC_HOST_CALL callFunctionPrototype(ExecState*)
72 {
73     return JSValue::encode(jsUndefined());
74 }
75
76 // ECMA 15.3.4
77 CallType FunctionPrototype::getCallData(JSCell*, CallData& callData)
78 {
79     callData.native.function = callFunctionPrototype;
80     return CallTypeHost;
81 }
82
83 // Functions
84
85 // Compatibility hack for the Optimost JavaScript library. (See <rdar://problem/6595040>.)
86 static inline void insertSemicolonIfNeeded(String& functionBody)
87 {
88     ASSERT(functionBody[0] == '{');
89     ASSERT(functionBody[functionBody.length() - 1] == '}');
90
91     for (size_t i = functionBody.length() - 2; i > 0; --i) {
92         UChar ch = functionBody[i];
93         if (!Lexer<UChar>::isWhiteSpace(ch) && !Lexer<UChar>::isLineTerminator(ch)) {
94             if (ch != ';' && ch != '}')
95                 functionBody = makeString(functionBody.substringSharingImpl(0, i + 1), ";", functionBody.substringSharingImpl(i + 1, functionBody.length() - (i + 1)));
96             return;
97         }
98     }
99 }
100
101 EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec)
102 {
103     JSValue thisValue = exec->hostThisValue();
104     if (thisValue.inherits(JSFunction::info())) {
105         JSFunction* function = jsCast<JSFunction*>(thisValue);
106         if (function->isHostFunction())
107             return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n    [native code]\n}"));
108         FunctionExecutable* executable = function->jsExecutable();
109         String sourceString = executable->source().toString();
110         insertSemicolonIfNeeded(sourceString);
111         return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "(", executable->paramString(), ") ", sourceString));
112     }
113
114     if (thisValue.inherits(InternalFunction::info())) {
115         InternalFunction* function = asInternalFunction(thisValue);
116         return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n    [native code]\n}"));
117     }
118
119     return throwVMTypeError(exec);
120 }
121
122 EncodedJSValue JSC_HOST_CALL functionProtoFuncApply(ExecState* exec)
123 {
124     JSValue thisValue = exec->hostThisValue();
125     CallData callData;
126     CallType callType = getCallData(thisValue, callData);
127     if (callType == CallTypeNone)
128         return throwVMTypeError(exec);
129
130     JSValue array = exec->argument(1);
131
132     MarkedArgumentBuffer applyArgs;
133     if (!array.isUndefinedOrNull()) {
134         if (!array.isObject())
135             return throwVMTypeError(exec);
136         if (asObject(array)->classInfo() == Arguments::info()) {
137             if (asArguments(array)->length(exec) > Arguments::MaxArguments)
138                 return JSValue::encode(throwStackOverflowError(exec));
139             asArguments(array)->fillArgList(exec, applyArgs);
140         } else if (isJSArray(array)) {
141             if (asArray(array)->length() > Arguments::MaxArguments)
142                 return JSValue::encode(throwStackOverflowError(exec));
143             asArray(array)->fillArgList(exec, applyArgs);
144         } else {
145             unsigned length = asObject(array)->get(exec, exec->propertyNames().length).toUInt32(exec);
146             if (length > Arguments::MaxArguments)
147                 return JSValue::encode(throwStackOverflowError(exec));
148
149             for (unsigned i = 0; i < length; ++i)
150                 applyArgs.append(asObject(array)->get(exec, i));
151         }
152     }
153     
154     return JSValue::encode(call(exec, thisValue, callType, callData, exec->argument(0), applyArgs));
155 }
156
157 EncodedJSValue JSC_HOST_CALL functionProtoFuncCall(ExecState* exec)
158 {
159     JSValue thisValue = exec->hostThisValue();
160     CallData callData;
161     CallType callType = getCallData(thisValue, callData);
162     if (callType == CallTypeNone)
163         return throwVMTypeError(exec);
164
165     ArgList args(exec);
166     ArgList callArgs;
167     args.getSlice(1, callArgs);
168     return JSValue::encode(call(exec, thisValue, callType, callData, exec->argument(0), callArgs));
169 }
170
171 // 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
172 EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec)
173 {
174     JSGlobalObject* globalObject = exec->callee()->globalObject();
175
176     // Let Target be the this value.
177     JSValue target = exec->hostThisValue();
178
179     // If IsCallable(Target) is false, throw a TypeError exception.
180     CallData callData;
181     CallType callType = getCallData(target, callData);
182     if (callType == CallTypeNone)
183         return throwVMTypeError(exec);
184     // Primitive values are not callable.
185     ASSERT(target.isObject());
186     JSObject* targetObject = asObject(target);
187
188     // Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.
189     size_t numBoundArgs = exec->argumentCount() > 1 ? exec->argumentCount() - 1 : 0;
190     JSArray* boundArgs = JSArray::tryCreateUninitialized(exec->vm(), globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), numBoundArgs);
191     if (!boundArgs)
192         return JSValue::encode(throwOutOfMemoryError(exec));
193
194     for (size_t i = 0; i < numBoundArgs; ++i)
195         boundArgs->initializeIndex(exec->vm(), i, exec->argument(i + 1));
196
197     // If the [[Class]] internal property of Target is "Function", then ...
198     // Else set the length own property of F to 0.
199     unsigned length = 0;
200     if (targetObject->inherits(JSFunction::info())) {
201         ASSERT(target.get(exec, exec->propertyNames().length).isNumber());
202         // a. Let L be the length property of Target minus the length of A.
203         // b. Set the length own property of F to either 0 or L, whichever is larger.
204         unsigned targetLength = (unsigned)target.get(exec, exec->propertyNames().length).asNumber();
205         if (targetLength > numBoundArgs)
206             length = targetLength - numBoundArgs;
207     }
208
209     JSString* name = target.get(exec, exec->propertyNames().name).toString(exec);
210     return JSValue::encode(JSBoundFunction::create(exec, globalObject, targetObject, exec->argument(0), boundArgs, length, name->value(exec)));
211 }
212
213 } // namespace JSC