# kjs Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# function.cpp

    00001 // -*- c-basic-offset: 2 -*-
    00002 /*
    00003  *  This file is part of the KDE libraries
    00004  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
    00005  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
    00006  *
    00007  *  This library is free software; you can redistribute it and/or
    00008  *  modify it under the terms of the GNU Lesser General Public
    00009  *  License as published by the Free Software Foundation; either
    00010  *  version 2 of the License, or (at your option) any later version.
    00011  *
    00012  *  This library is distributed in the hope that it will be useful,
    00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00015  *  Lesser General Public License for more details.
    00016  *
    00017  *  You should have received a copy of the GNU Lesser General Public License
    00018  *  along with this library; see the file COPYING.LIB.  If not, write to
    00019  *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00020  *  Boston, MA 02111-1307, USA.
    00021  *
    00022  */
    00023
    00024 #include "function.h"
    00025
    00026 #include "internal.h"
    00027 #include "function_object.h"
    00028 #include "lexer.h"
    00029 #include "nodes.h"
    00030 #include "operations.h"
    00031 #include "debugger.h"
    00032
    00033 #include <stdio.h>
    00034 #include <stdlib.h>
    00035 #include <assert.h>
    00036 #include <string.h>
    00037 #include <errno.h>
    00038 #include <math.h>
    00039 #include <ctype.h>
    00040
    00041 using namespace KJS;
    00042
    00043 // ----------------------------- FunctionImp ----------------------------------
    00044
    00045 const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
    00046
    00047 namespace KJS {
    00048   class Parameter {
    00049   public:
    00050     Parameter(const UString &n) : name(n), next(0L) { }
    00051     ~Parameter() { delete next; }
    00052     UString name;
    00053     Parameter *next;
    00054   };
    00055 }
    00056
    00057 FunctionImp::FunctionImp(ExecState *exec, const UString &n)
    00058   : InternalFunctionImp(
    00059       static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
    00060       ), param(0L), ident(n), argStack(0)
    00061 {
    00062   Value protect(this);
    00063   argStack = new ListImp();
    00064   Value protectArgStack( argStack ); // this also calls setGcAllowed on argStack
    00065   //fprintf(stderr,"FunctionImp::FunctionImp this=%p argStack=%p\n");
    00066   put(exec,"arguments",Null(),ReadOnly|DontDelete|DontEnum);
    00067 }
    00068
    00069 FunctionImp::~FunctionImp()
    00070 {
    00071   // The function shouldn't be deleted while it is still executed; argStack
    00072   // should be set to 0 by the last call to popArgs()
    00073   //assert(argStack->isEmpty());
    00074   // Accessing argStack from here is a problem though.
    00075   // When the function isn't used anymore, it's not marked, and neither is the
    00076   // argStack, so both can be deleted - in any order!
    00077   delete param;
    00078 }
    00079
    00080 void FunctionImp::mark()
    00081 {
    00082   InternalFunctionImp::mark();
    00083   if (argStack && !argStack->marked())
    00084     argStack->mark();
    00085 }
    00086
    00087 bool FunctionImp::implementsCall() const
    00088 {
    00089   return true;
    00090 }
    00091
    00092 Value FunctionImp::call(ExecState *exec, Object &thisObj, const List &args)
    00093 {
    00094   Object globalObj = exec->interpreter()->globalObject();
    00095
    00096   Debugger *dbg = exec->interpreter()->imp()->debugger();
    00097   int sid = -1;
    00098   int lineno = -1;
    00099   if (dbg) {
    00100     if (inherits(&DeclaredFunctionImp::info)) {
    00101       sid = static_cast<DeclaredFunctionImp*>(this)->body->sourceId();
    00102       lineno = static_cast<DeclaredFunctionImp*>(this)->body->firstLine();
    00103     }
    00104
    00105     Object func(this);
    00106     bool cont = dbg->callEvent(exec,sid,lineno,func,args);
    00107     if (!cont) {
    00108       dbg->imp()->abort();
    00109       return Undefined();
    00110     }
    00111   }
    00112
    00113   // enter a new execution context
    00114   ContextImp ctx(globalObj, exec, thisObj, codeType(),
    00115                  exec->context().imp(), this, args);
    00116   ExecState newExec(exec->interpreter(), &ctx);
    00117   newExec.setException(exec->exception()); // could be null
    00118
    00119   // In order to maintain our "arguments" property, we maintain a list of arguments
    00120   // properties from earlier in the execution stack. Upon return, we restore the
    00121   // previous arguments object using popArgs().
    00122   // Note: this does not appear to be part of the spec
    00123   if (codeType() == FunctionCode) {
    00124     assert(ctx.activationObject().inherits(&ActivationImp::info));
    00125     Object argsObj = static_cast<ActivationImp*>(ctx.activationObject().imp())->argumentsObject();
    00126     put(&newExec, "arguments", argsObj, DontDelete|DontEnum|ReadOnly);
    00127     pushArgs(&newExec, argsObj);
    00128   }
    00129
    00130   // assign user supplied arguments to parameters
    00131   processParameters(&newExec, args);
    00132   // add variable declarations (initialized to undefined)
    00133   processVarDecls(&newExec);
    00134
    00135   Completion comp = execute(&newExec);
    00136
    00137   // if an exception occured, propogate it back to the previous execution object
    00138   if (newExec.hadException())
    00139     exec->setException(newExec.exception());
    00140   if (codeType() == FunctionCode)
    00141     popArgs(&newExec);
    00142
    00143 #ifdef KJS_VERBOSE
    00144   CString n = ident.isEmpty() ? CString("(internal)") : ident.cstring();
    00145   if (comp.complType() == Throw) {
    00146     n += " throws";
    00147     printInfo(exec, n.c_str(), comp.value());
    00148   } else if (comp.complType() == ReturnValue) {
    00149     n += " returns";
    00150     printInfo(exec, n.c_str(), comp.value());
    00151   } else
    00152     fprintf(stderr, "%s returns: undefined\n", n.c_str());
    00153 #endif
    00154
    00155   if (dbg) {
    00156     Object func(this);
    00157     int cont = dbg->returnEvent(exec,sid,lineno,func);
    00158     if (!cont) {
    00159       dbg->imp()->abort();
    00160       return Undefined();
    00161     }
    00162   }
    00163
    00164   if (comp.complType() == Throw) {
    00165     exec->setException(comp.value());
    00166     return comp.value();
    00167   }
    00168   else if (comp.complType() == ReturnValue)
    00169     return comp.value();
    00170   else
    00171     return Undefined();
    00172 }
    00173
    00174 void FunctionImp::addParameter(const UString &n)
    00175 {
    00176   Parameter **p = &param;
    00177   while (*p)
    00178     p = &(*p)->next;
    00179
    00180   *p = new Parameter(n);
    00181 }
    00182
    00183 UString FunctionImp::parameterString() const
    00184 {
    00185   UString s;
    00186   const Parameter * const *p = &param;
    00187   while (*p) {
    00188     if (!s.isEmpty())
    00189         s += ", ";
    00190     s += (*p)->name;
    00191     p = &(*p)->next;
    00192   }
    00193
    00194   return s;
    00195 }
    00196
    00197
    00198 // ECMA 10.1.3q
    00199 void FunctionImp::processParameters(ExecState *exec, const List &args)
    00200 {
    00201   Object variable = exec->context().imp()->variableObject();
    00202
    00203 #ifdef KJS_VERBOSE
    00204   fprintf(stderr, "---------------------------------------------------\n"
    00205           "processing parameters for %s call\n",
    00206           name().isEmpty() ? "(internal)" : name().ascii());
    00207 #endif
    00208
    00209   if (param) {
    00210     ListIterator it = args.begin();
    00211     Parameter **p = &param;
    00212     while (*p) {
    00213       if (it != args.end()) {
    00214 #ifdef KJS_VERBOSE
    00215         fprintf(stderr, "setting parameter %s ", (*p)->name.ascii());
    00216         printInfo(exec,"to", *it);
    00217 #endif
    00218         variable.put(exec,(*p)->name, *it);
    00219         it++;
    00220       } else
    00221         variable.put(exec,(*p)->name, Undefined());
    00222       p = &(*p)->next;
    00223     }
    00224   }
    00225 #ifdef KJS_VERBOSE
    00226   else {
    00227     for (int i = 0; i < args.size(); i++)
    00228       printInfo(exec,"setting argument", args[i]);
    00229   }
    00230 #endif
    00231 }
    00232
    00233 void FunctionImp::processVarDecls(ExecState */*exec*/)
    00234 {
    00235 }
    00236
    00237 void FunctionImp::pushArgs(ExecState *exec, const Object &args)
    00238 {
    00239   argStack->append(args);
    00240   put(exec,"arguments",args,ReadOnly|DontDelete|DontEnum);
    00241 }
    00242
    00243 void FunctionImp::popArgs(ExecState *exec)
    00244 {
    00245   argStack->removeLast();
    00246   if (argStack->isEmpty()) {
    00247     put(exec,"arguments",Null(),ReadOnly|DontDelete|DontEnum);
    00248   }
    00249   else
    00250     put(exec,"arguments",argStack->at(argStack->size()-1),ReadOnly|DontDelete|DontEnum);
    00251 }
    00252
    00253 // ------------------------------ DeclaredFunctionImp --------------------------
    00254
    00255 // ### is "Function" correct here?
    00256 const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
    00257
    00258 DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const UString &n,
    00259                                          FunctionBodyNode *b, const List &sc)
    00260   : FunctionImp(exec,n), body(b)
    00261 {
    00262   Value protect(this);
    00263   body->ref();
    00264   setScope(sc.copy());
    00265 }
    00266
    00267 DeclaredFunctionImp::~DeclaredFunctionImp()
    00268 {
    00269   if ( body->deref() )
    00270     delete body;
    00271 }
    00272
    00273 bool DeclaredFunctionImp::implementsConstruct() const
    00274 {
    00275   return true;
    00276 }
    00277
    00278 // ECMA 13.2.2 [[Construct]]
    00279 Object DeclaredFunctionImp::construct(ExecState *exec, const List &args)
    00280 {
    00281   Object proto;
    00282   Value p = get(exec,"prototype");
    00283   if (p.type() == ObjectType)
    00284     proto = Object(static_cast<ObjectImp*>(p.imp()));
    00285   else
    00286     proto = exec->interpreter()->builtinObjectPrototype();
    00287
    00288   Object obj(new ObjectImp(proto));
    00289
    00290   Value res = call(exec,obj,args);
    00291
    00292   if (res.type() == ObjectType)
    00293     return Object::dynamicCast(res);
    00294   else
    00295     return obj;
    00296 }
    00297
    00298 Completion DeclaredFunctionImp::execute(ExecState *exec)
    00299 {
    00300   Completion result = body->execute(exec);
    00301
    00302   if (result.complType() == Throw || result.complType() == ReturnValue)
    00303       return result;
    00304   return Completion(Normal, Undefined()); // TODO: or ReturnValue ?
    00305 }
    00306
    00307 void DeclaredFunctionImp::processVarDecls(ExecState *exec)
    00308 {
    00309   body->processVarDecls(exec);
    00310 }
    00311
    00312 // ------------------------------ ArgumentsImp ---------------------------------
    00313
    00314 const ClassInfo ArgumentsImp::info = {"Arguments", 0, 0, 0};
    00315
    00316 // ECMA 10.1.8
    00317 ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func, const List &args)
    00318   : ObjectImp(exec->interpreter()->builtinObjectPrototype())
    00319 {
    00320   Value protect(this);
    00321   put(exec,"callee", Object(func), DontEnum);
    00322   put(exec,"length", Number(args.size()), DontEnum);
    00323   if (!args.isEmpty()) {
    00324     ListIterator arg = args.begin();
    00325     for (int i = 0; arg != args.end(); arg++, i++) {
    00326       put(exec,UString::from(i), *arg, DontEnum);
    00327     }
    00328   }
    00329 }
    00330
    00331 // ------------------------------ ActivationImp --------------------------------
    00332
    00333 const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
    00334
    00335 // ECMA 10.1.6
    00336 ActivationImp::ActivationImp(ExecState *exec, FunctionImp *f, const List &args)
    00337   : ObjectImp()
    00338 {
    00339   Value protect(this);
    00340   arguments = new ArgumentsImp(exec,f, args);
    00341   put(exec, "arguments", Object(arguments), Internal|DontDelete);
    00342 }
    00343
    00344 ActivationImp::~ActivationImp()
    00345 {
    00346   arguments->inlinedSetGcAllowed();
    00347 }
    00348
    00349 // ------------------------------ GlobalFunc -----------------------------------
    00350
    00351
    00352 GlobalFuncImp::GlobalFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto, int i, int len)
    00353   : InternalFunctionImp(funcProto), id(i)
    00354 {
    00355   Value protect(this);
    00356   put(exec,"length",Number(len),DontDelete|ReadOnly|DontEnum);
    00357 }
    00358
    00359 CodeType GlobalFuncImp::codeType() const
    00360 {
    00361   return id == Eval ? EvalCode : codeType();
    00362 }
    00363
    00364 bool GlobalFuncImp::implementsCall() const
    00365 {
    00366   return true;
    00367 }
    00368
    00369 Value GlobalFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
    00370 {
    00371   Value res;
    00372
    00373   static const char non_escape[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    00374                                    "abcdefghijklmnopqrstuvwxyz"
    00375                                    "0123456789@*_+-./";
    00376
    00377   switch (id) {
    00378   case Eval: { // eval()
    00379     Value x = args[0];
    00380     if (x.type() != StringType)
    00381       return x;
    00382     else {
    00383       UString s = x.toString(exec);
    00384
    00385       int sid;
    00386       int errLine;
    00387       UString errMsg;
    00388 #ifdef KJS_VERBOSE
    00389       fprintf(stderr, "eval(): %s\n", s.ascii());
    00390 #endif
    00391       ProgramNode *progNode = Parser::parse(s.data(),s.size(),&sid,&errLine,&errMsg);
    00392
    00393       // no program node means a syntax occurred
    00394       if (!progNode) {
    00395         Object err = Error::create(exec,SyntaxError,errMsg.ascii(),errLine);
    00396         err.put(exec,"sid",Number(sid));
    00397         exec->setException(err);
    00398         return err;
    00399       }
    00400
    00401       progNode->ref();
    00402
    00403       // enter a new execution context
    00404       Object glob(exec->interpreter()->globalObject());
    00405       ContextImp ctx(glob, exec, thisObj, EvalCode, exec->context().imp());
    00406       ExecState newExec(exec->interpreter(), &ctx);
    00407       newExec.setException(exec->exception()); // could be null
    00408
    00409       // execute the code
    00410       Completion c = progNode->execute(&newExec);
    00411
    00412       // if an exception occured, propogate it back to the previous execution object
    00413       if (newExec.hadException())
    00414         exec->setException(newExec.exception());
    00415
    00416       if ( progNode->deref() )
    00417           delete progNode;
    00418       if (c.complType() == ReturnValue)
    00419           return c;
    00420       // ### setException() on throw?
    00421       else if (c.complType() == Normal) {
    00422           if (c.isValueCompletion())
    00423               return c.value();
    00424           else
    00425               return Undefined();
    00426       } else {
    00427           return c;
    00428       }
    00429     }
    00430     break;
    00431   }
    00432   case ParseInt: { // ECMA 15.1.2.2
    00433     CString cstr = args[0].toString(exec).cstring();
    00434     const char* startptr = cstr.c_str();
    00435     while ( *startptr && isspace( *startptr ) ) // first, skip leading spaces
    00436       ++startptr;
    00437     char* endptr;
    00438     errno = 0;
    00439     //fprintf( stderr, "ParseInt: parsing string %s\n", startptr );
    00440     int base = 0;
    00441     // Figure out the base
    00442     if ( args.size() > 1 )
    00443       base = args[ 1 ].toInt32( exec );
    00444     if ( base == 0 ) {
    00445       // default base is 10, unless the number starts with 0x or 0X
    00446       if ( *startptr == '0' && toupper( *(startptr+1) ) == 'X' )
    00447         base = 16;
    00448       else
    00449         base = 10;
    00450     }
    00451     //fprintf( stderr, "base=%d\n",base );
    00452     if ( base != 10 )
    00453     {
    00454       // We can't use strtod if a non-decimal base was specified...
    00455       long value = strtol(startptr, &endptr, base);
    00456       if (errno || endptr == startptr)
    00457         res = Number(NaN);
    00458       else
    00459         res = Number(value);
    00460     } else {
    00461       // Parse into a double, not an int or long. We must be able to parse
    00462       // huge integers like 16-digits ones (credit card numbers ;)
    00463       // But first, check that it only has digits (after the +/- sign if there's one).
    00464       // That's because strtod will accept .5, but parseInt shouldn't.
    00465       // Also, strtod will parse 0x7, but it should fail here (base==10)
    00466       bool foundSign = false;
    00467       bool foundDot = false;
    00468       bool ok = false;
    00469       for ( const char* ptr = startptr; *ptr; ++ptr ) {
    00470         if ( *ptr >= '0' && *ptr <= '9' )
    00471           ok = true;
    00472         else if ( !foundSign && ( *ptr == '-' || *ptr == '+' ) )
    00473           foundSign = true;
    00474         else if ( ok && !foundDot && *ptr == '.' ) // only accept one dot, and after a digit
    00475           foundDot = true;
    00476         else {
    00477           *const_cast<char *>(ptr) = '\0';   // this will prevent parseInt('0x7',10) from returning 7.
    00478           break; // something else -> stop here.
    00479         }
    00480       }
    00481
    00482       double value = strtod(startptr, &endptr);
    00483       if (!ok || errno || endptr == startptr)
    00484         res = Number(NaN);
    00485       else
    00486         res = Number(floor(value));
    00487     }
    00488     break;
    00489   }
    00490   case ParseFloat:
    00491     res = Number(args[0].toString(exec).toDouble( true /*tolerant*/ ));
    00492     break;
    00493   case IsNaN:
    00494     res = Boolean(isNaN(args[0].toNumber(exec)));
    00495     break;
    00496   case IsFinite: {
    00497     double n = args[0].toNumber(exec);
    00498     res = Boolean(!isNaN(n) && !isInf(n));
    00499     break;
    00500   }
    00501   case Escape: {
    00502     UString r = "", s, str = args[0].toString(exec);
    00503     const UChar *c = str.data();
    00504     for (int k = 0; k < str.size(); k++, c++) {
    00505       int u = c->unicode();
    00506       if (u > 255) {
    00507         char tmp[7];
    00508         sprintf(tmp, "%%u%04X", u);
    00509         s = UString(tmp);
    00510       } else if (strchr(non_escape, (char)u)) {
    00511         s = UString(c, 1);
    00512       } else {
    00513         char tmp[4];
    00514         sprintf(tmp, "%%%02X", u);
    00515         s = UString(tmp);
    00516       }
    00517       r += s;
    00518     }
    00519     res = String(r);
    00520     break;
    00521   }
    00522   case UnEscape: {
    00523     UString s, str = args[0].toString(exec);
    00524     int k = 0, len = str.size();
    00525     while (k < len) {
    00526       const UChar *c = str.data() + k;
    00527       UChar u;
    00528       if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
    00529         u = Lexer::convertUnicode((c+2)->unicode(), (c+3)->unicode(),
    00530                                   (c+4)->unicode(), (c+5)->unicode());
    00531         c = &u;
    00532         k += 5;
    00533       } else if (*c == UChar('%') && k <= len - 3) {
    00534         u = UChar(Lexer::convertHex((c+1)->unicode(), (c+2)->unicode()));
    00535         c = &u;
    00536         k += 2;
    00537       }
    00538       k++;
    00539       s += UString(c, 1);
    00540     }
    00541     res = String(s);
    00542     break;
    00543   }
    00544   }
    00545
    00546   return res;
    00547 }

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:08:20 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001