kjs Library API Documentation

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 }
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Oct 8 12:21:14 2003 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2001