File indexing completed on 2024-05-12 15:43:21

0001 // krazy:excludeall=doublequote_chars (UStrings aren't QStrings)
0002 /*
0003  *  This file is part of the KDE libraries
0004  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
0005  *  Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
0006  *
0007  *  This library is free software; you can redistribute it and/or
0008  *  modify it under the terms of the GNU Lesser General Public
0009  *  License as published by the Free Software Foundation; either
0010  *  version 2 of the License, or (at your option) any later version.
0011  *
0012  *  This library is distributed in the hope that it will be useful,
0013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  *  Lesser General Public License for more details.
0016  *
0017  *  You should have received a copy of the GNU Lesser General Public
0018  *  License along with this library; if not, write to the Free Software
0019  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0020  *
0021  */
0022 
0023 #include "function_object.h"
0024 #include "internal.h"
0025 #include "scriptfunction.h"
0026 #include "array_object.h"
0027 #include "nodes.h"
0028 #include "lexer.h"
0029 #include "debugger.h"
0030 #include "object.h"
0031 
0032 #include <assert.h>
0033 #include <stdio.h>
0034 #include <string.h>
0035 
0036 using namespace KJS;
0037 
0038 // ------------------------------ FunctionPrototype -------------------------
0039 
0040 FunctionPrototype::FunctionPrototype(ExecState *exec)
0041 {
0042     static const Identifier *applyPropertyName = new Identifier("apply");
0043     static const Identifier *callPropertyName = new Identifier("call");
0044     static const Identifier *bindPropertyName = new Identifier("bind");
0045 
0046     putDirect(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
0047     putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::ToString, 0, exec->propertyNames().toString), DontEnum);
0048     putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Apply, 2, *applyPropertyName), DontEnum);
0049     putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Call, 1, *callPropertyName), DontEnum);
0050     putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Bind, 1, *bindPropertyName), DontEnum);
0051 }
0052 
0053 FunctionPrototype::~FunctionPrototype()
0054 {
0055 }
0056 
0057 // ECMA 15.3.4
0058 JSValue *FunctionPrototype::callAsFunction(ExecState * /*exec*/, JSObject * /*thisObj*/, const List &/*args*/)
0059 {
0060     return jsUndefined();
0061 }
0062 
0063 // ------------------------------ FunctionProtoFunc -------------------------
0064 
0065 FunctionProtoFunc::FunctionProtoFunc(ExecState *exec, FunctionPrototype *funcProto, int i, int len, const Identifier &name)
0066     : InternalFunctionImp(funcProto, name)
0067     , id(i)
0068 {
0069     putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum);
0070 }
0071 
0072 JSValue *FunctionProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
0073 {
0074     JSValue *result = nullptr;
0075 
0076     switch (id) {
0077     case ToString:
0078         if (!thisObj || !thisObj->inherits(&InternalFunctionImp::info)) {
0079 #ifndef NDEBUG
0080             fprintf(stderr, "attempted toString() call on null or non-function object\n");
0081 #endif
0082             return throwError(exec, TypeError);
0083         }
0084         if (thisObj->inherits(&FunctionImp::info)) {
0085             return jsString(static_cast<FunctionImp *>(thisObj)->toSource());
0086         } else if (thisObj->inherits(&InternalFunctionImp::info) &&
0087                    !static_cast<InternalFunctionImp *>(thisObj)->functionName().isNull()) {
0088             result = jsString("\nfunction " + static_cast<InternalFunctionImp *>(thisObj)->functionName().ustring() + "() {\n"
0089                               "    [native code]\n}\n");
0090         } else {
0091             result = jsString("[function]");
0092         }
0093         break;
0094     case Apply: {
0095         JSValue *thisArg = args[0];
0096         JSValue *argArray = args[1];
0097         JSObject *func = thisObj;
0098 
0099         if (!func->implementsCall()) {
0100             return throwError(exec, TypeError);
0101         }
0102 
0103         JSObject *applyThis;
0104         if (JSValue::isUndefinedOrNull(thisArg)) {
0105             applyThis = exec->dynamicInterpreter()->globalObject();
0106         } else {
0107             applyThis = JSValue::toObject(thisArg, exec);
0108         }
0109 
0110         List applyArgs;
0111         if (!JSValue::isUndefinedOrNull(argArray)) {
0112             if (JSValue::isObject(argArray) &&
0113                     (static_cast<JSObject *>(argArray)->inherits(&ArrayInstance::info) ||
0114                      static_cast<JSObject *>(argArray)->inherits(&Arguments::info))) {
0115 
0116                 JSObject *argArrayObj = static_cast<JSObject *>(argArray);
0117                 unsigned int length = JSValue::toUInt32(argArrayObj->get(exec, exec->propertyNames().length), exec);
0118                 for (unsigned int i = 0; i < length; i++) {
0119                     applyArgs.append(argArrayObj->get(exec, i));
0120                 }
0121             } else {
0122                 return throwError(exec, TypeError);
0123             }
0124         }
0125         result = func->call(exec, applyThis, applyArgs);
0126     }
0127     break;
0128     case Call: {
0129         JSValue *thisArg = args[0];
0130         JSObject *func = thisObj;
0131 
0132         if (!func->implementsCall()) {
0133             return throwError(exec, TypeError);
0134         }
0135 
0136         JSObject *callThis;
0137         if (JSValue::isUndefinedOrNull(thisArg)) {
0138             callThis = exec->dynamicInterpreter()->globalObject();
0139         } else {
0140             callThis = JSValue::toObject(thisArg, exec);
0141         }
0142 
0143         result = func->call(exec, callThis, args.copyTail());
0144     }
0145     break;
0146     case Bind: { //ECMA Edition 5.1r6 - 15.3.4.5
0147         JSObject *target(thisObj);
0148         if (!target->implementsCall()) {
0149             return throwError(exec, TypeError, "object is not callable");
0150         }
0151 
0152         List newArgs;
0153         for (int i = 1; i < args.size(); ++i) {
0154             newArgs.append(args[i]);
0155         }
0156 
0157         JSObject *boundThis = nullptr;
0158 
0159         // As call does not accept JSValue(undefined/null),
0160         // do it like in call and use the global object
0161         if (JSValue::isUndefinedOrNull(args[0])) {
0162             boundThis = exec->dynamicInterpreter()->globalObject();
0163         } else {
0164             boundThis = JSValue::toObject(args[0], exec);
0165         }
0166 
0167         BoundFunction *bfunc = new BoundFunction(exec, target, boundThis, newArgs);
0168 
0169         unsigned length;
0170         if (target->inherits(&FunctionImp::info)) {
0171             double L = JSValue::getNumber(target->get(exec, exec->propertyNames().length)) - newArgs.size();
0172             length = (unsigned)std::max<int>((int)L, 0);
0173         } else {
0174             length = 0;
0175         }
0176         bfunc->put(exec, exec->propertyNames().length, jsNumber(length), ReadOnly | DontEnum | DontDelete);
0177 
0178         JSObject *thrower = new Thrower(TypeError);
0179         PropertyDescriptor callerDesc;
0180 
0181         GetterSetterImp *gs = new GetterSetterImp();
0182         gs->setGetter(thrower);
0183         gs->setSetter(thrower);
0184 
0185         callerDesc.setPropertyDescriptorValues(exec, gs, DontEnum | DontDelete);
0186         bfunc->defineOwnProperty(exec, exec->propertyNames().caller, callerDesc, false);
0187 
0188         PropertyDescriptor argumentsDesc;
0189         argumentsDesc.setPropertyDescriptorValues(exec, gs, DontEnum | DontDelete);
0190         bfunc->defineOwnProperty(exec, exec->propertyNames().arguments, argumentsDesc, false);
0191 
0192         return bfunc;
0193     }
0194     break;
0195     }
0196 
0197     return result;
0198 }
0199 
0200 // ------------------------------ FunctionObjectImp ----------------------------
0201 
0202 FunctionObjectImp::FunctionObjectImp(ExecState *exec, FunctionPrototype *funcProto)
0203     : InternalFunctionImp(funcProto)
0204 {
0205     putDirect(exec->propertyNames().prototype, funcProto, DontEnum | DontDelete | ReadOnly);
0206 
0207     // no. of arguments for constructor
0208     putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum);
0209 }
0210 
0211 FunctionObjectImp::~FunctionObjectImp()
0212 {
0213 }
0214 
0215 bool FunctionObjectImp::implementsConstruct() const
0216 {
0217     return true;
0218 }
0219 
0220 // ECMA 15.3.2 The Function Constructor
0221 JSObject *FunctionObjectImp::construct(ExecState *exec, const List &args, const Identifier &functionName, const UString &sourceURL, int lineNumber)
0222 {
0223     UString p("");
0224     UString body;
0225     int argsSize = args.size();
0226     if (argsSize == 0) {
0227         body = "";
0228     } else if (argsSize == 1) {
0229         body = JSValue::toString(args[0], exec);
0230     } else {
0231         p = JSValue::toString(args[0], exec);
0232         for (int k = 1; k < argsSize - 1; k++) {
0233             p += "," + JSValue::toString(args[k], exec);
0234         }
0235         body = JSValue::toString(args[argsSize - 1], exec);
0236     }
0237 
0238     // parse the source code
0239     int sourceId;
0240     int errLine;
0241     UString errMsg;
0242     RefPtr<FunctionBodyNode> functionBody = parser().parseFunctionBody(sourceURL, lineNumber, body.data(), body.size(), &sourceId, &errLine, &errMsg);
0243 
0244     // notify debugger that source has been parsed
0245     Debugger *dbg = exec->dynamicInterpreter()->debugger();
0246     if (dbg) {
0247         // make sure to pass in sourceURL, since it's useful for lazy event listeners, and empty for actual function ctor
0248         dbg->reportSourceParsed(exec, functionBody.get(), sourceId, sourceURL, body, lineNumber, errLine, errMsg);
0249     }
0250 
0251     // no program node == syntax error - throw a syntax error
0252     if (!functionBody)
0253         // we can't return a Completion(Throw) here, so just set the exception
0254         // and return it
0255     {
0256         return throwError(exec, SyntaxError, errMsg, errLine, sourceId, sourceURL);
0257     }
0258 
0259     ScopeChain scopeChain;
0260     scopeChain.push(exec->lexicalInterpreter()->globalObject());
0261 
0262     FunctionImp *fimp = new FunctionImp(exec, functionName, functionBody.get(), scopeChain);
0263 
0264     // parse parameter list. throw syntax error on illegal identifiers
0265     int len = p.size();
0266     const UChar *c = p.data();
0267     int i = 0, params = 0;
0268     UString param;
0269     while (i < len) {
0270         while (*c == ' ' && i < len) {
0271             c++, i++;
0272         }
0273         if (Lexer::isIdentStart(c->uc)) {  // else error
0274             param = UString(c, 1);
0275             c++, i++;
0276             while (i < len && (Lexer::isIdentPart(c->uc))) {
0277                 param += UString(c, 1);
0278                 c++, i++;
0279             }
0280             while (i < len && *c == ' ') {
0281                 c++, i++;
0282             }
0283             if (i == len) {
0284                 functionBody->addParam(Identifier(param));
0285                 params++;
0286                 break;
0287             } else if (*c == ',') {
0288                 functionBody->addParam(Identifier(param));
0289                 params++;
0290                 c++, i++;
0291                 continue;
0292             } // else error
0293         }
0294         return throwError(exec, SyntaxError, "Syntax error in parameter list");
0295     }
0296 
0297     List consArgs;
0298 
0299     JSObject *objCons = exec->lexicalInterpreter()->builtinObject();
0300     JSObject *prototype = objCons->construct(exec, List::empty());
0301     prototype->put(exec, exec->propertyNames().constructor, fimp, DontEnum | DontDelete | ReadOnly);
0302     // ECMA Edition 5.1r6 - 15.3.5.2 - [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false
0303     fimp->put(exec, exec->propertyNames().prototype, prototype, Internal | DontDelete | DontEnum);
0304     return fimp;
0305 }
0306 
0307 // ECMA 15.3.2 The Function Constructor
0308 JSObject *FunctionObjectImp::construct(ExecState *exec, const List &args)
0309 {
0310     return construct(exec, args, "anonymous", UString(), 0);
0311 }
0312 
0313 // ECMA 15.3.1 The Function Constructor Called as a Function
0314 JSValue *FunctionObjectImp::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &args)
0315 {
0316     return construct(exec, args);
0317 }