webengine/osswebengine/JavaScriptCore/kjs/function.cpp
changeset 0 dd21522fd290
child 8 7c90e6132015
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 // -*- c-basic-offset: 2 -*-
       
     2 /*
       
     3  *  This file is part of the KDE libraries
       
     4  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
       
     5  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
       
     6  *  Copyright (C) 2003 Apple Computer, Inc.
       
     7  *  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
       
     8  *
       
     9  *  This library is free software; you can redistribute it and/or
       
    10  *  modify it under the terms of the GNU Library General Public
       
    11  *  License as published by the Free Software Foundation; either
       
    12  *  version 2 of the License, or (at your option) any later version.
       
    13  *
       
    14  *  This library is distributed in the hope that it will be useful,
       
    15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    17  *  Library General Public License for more details.
       
    18  *
       
    19  *  You should have received a copy of the GNU Library General Public License
       
    20  *  along with this library; see the file COPYING.LIB.  If not, write to
       
    21  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    22  *  Boston, MA 02110-1301, USA.
       
    23  *
       
    24  */
       
    25 
       
    26 #include "config.h"
       
    27 #include "function.h"
       
    28 
       
    29 #include "dtoa.h"
       
    30 #include "internal.h"
       
    31 #include "function_object.h"
       
    32 #include "lexer.h"
       
    33 #include "nodes.h"
       
    34 #include "operations.h"
       
    35 #include "debugger.h"
       
    36 #include "context.h"
       
    37 
       
    38 #include <stdio.h>
       
    39 #include <errno.h>
       
    40 #include <stdlib.h>
       
    41 #include <assert.h>
       
    42 #include <string.h>
       
    43 #include <ctype.h>
       
    44 
       
    45 #include <wtf/unicode/Unicode.h>
       
    46 
       
    47 using namespace WTF;
       
    48 using namespace Unicode;
       
    49 
       
    50 namespace KJS {
       
    51 
       
    52 // ----------------------------- FunctionImp ----------------------------------
       
    53 
       
    54 const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
       
    55 
       
    56 FunctionImp::FunctionImp(ExecState* exec, const Identifier& n, FunctionBodyNode* b)
       
    57   : InternalFunctionImp(static_cast<FunctionPrototype*>
       
    58                         (exec->lexicalInterpreter()->builtinFunctionPrototype()), n)
       
    59   , body(b)
       
    60 {
       
    61 }
       
    62 
       
    63 void FunctionImp::mark()
       
    64 {
       
    65     InternalFunctionImp::mark();
       
    66     _scope.mark();
       
    67 }
       
    68 
       
    69 FunctionImp::~FunctionImp()
       
    70 {
       
    71 }
       
    72 
       
    73 JSValue* FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
       
    74 {
       
    75   JSObject* globalObj = exec->dynamicInterpreter()->globalObject();
       
    76 
       
    77   // enter a new execution context
       
    78   Context ctx(globalObj, exec->dynamicInterpreter(), thisObj, body.get(),
       
    79                  codeType(), exec->context(), this, &args);
       
    80   ExecState newExec(exec->dynamicInterpreter(), &ctx);
       
    81   if (exec->hadException())
       
    82     newExec.setException(exec->exception());
       
    83   ctx.setExecState(&newExec);
       
    84 
       
    85   // assign user supplied arguments to parameters
       
    86   passInParameters(&newExec, args);
       
    87   // add variable declarations (initialized to undefined)
       
    88   processVarDecls(&newExec);
       
    89 
       
    90   Debugger* dbg = exec->dynamicInterpreter()->debugger();
       
    91   int sid = -1;
       
    92   int lineno = -1;
       
    93   if (dbg) {
       
    94     if (inherits(&DeclaredFunctionImp::info)) {
       
    95       sid = static_cast<DeclaredFunctionImp*>(this)->body->sourceId();
       
    96       lineno = static_cast<DeclaredFunctionImp*>(this)->body->firstLine();
       
    97     }
       
    98 
       
    99     bool cont = dbg->callEvent(&newExec,sid,lineno,this,args);
       
   100     if (!cont) {
       
   101       dbg->imp()->abort();
       
   102       return jsUndefined();
       
   103     }
       
   104   }
       
   105 
       
   106   Completion comp = execute(&newExec);
       
   107 
       
   108   // if an exception occured, propogate it back to the previous execution object
       
   109   if (newExec.hadException())
       
   110     comp = Completion(Throw, newExec.exception());
       
   111 
       
   112 #ifdef KJS_VERBOSE
       
   113   if (comp.complType() == Throw)
       
   114     printInfo(exec,"throwing", comp.value());
       
   115   else if (comp.complType() == ReturnValue)
       
   116     printInfo(exec,"returning", comp.value());
       
   117   else
       
   118     fprintf(stderr, "returning: undefined\n");
       
   119 #endif
       
   120 
       
   121   // The debugger may have been deallocated by now if the WebFrame
       
   122   // we were running in has been destroyed, so refetch it.
       
   123   // See http://bugs.webkit.org/show_bug.cgi?id=9477
       
   124   dbg = exec->dynamicInterpreter()->debugger();
       
   125 
       
   126   if (dbg) {
       
   127     if (inherits(&DeclaredFunctionImp::info))
       
   128       lineno = static_cast<DeclaredFunctionImp*>(this)->body->lastLine();
       
   129 
       
   130     if (comp.complType() == Throw)
       
   131         newExec.setException(comp.value());
       
   132 
       
   133     int cont = dbg->returnEvent(&newExec,sid,lineno,this);
       
   134     if (!cont) {
       
   135       dbg->imp()->abort();
       
   136       return jsUndefined();
       
   137     }
       
   138   }
       
   139 
       
   140   if (comp.complType() == Throw) {
       
   141     exec->setException(comp.value());
       
   142     return comp.value();
       
   143   }
       
   144   else if (comp.complType() == ReturnValue)
       
   145     return comp.value();
       
   146   else
       
   147     return jsUndefined();
       
   148 }
       
   149 
       
   150 // ECMA 10.1.3q
       
   151 inline void FunctionImp::passInParameters(ExecState* exec, const List& args)
       
   152 {
       
   153     Vector<Parameter>& parameters = body->parameters();
       
   154 
       
   155     JSObject* variable = exec->context()->variableObject();
       
   156 
       
   157 #ifdef KJS_VERBOSE
       
   158     fprintf(stderr, "---------------------------------------------------\n"
       
   159           "processing parameters for %s call\n",
       
   160           functionName().isEmpty() ? "(internal)" : functionName().ascii());
       
   161 #endif
       
   162 
       
   163     size_t size = parameters.size();
       
   164     for (size_t i = 0; i < size; ++i) {
       
   165 #ifdef KJS_VERBOSE
       
   166       fprintf(stderr, "setting parameter %s ", parameters.at(i).name.ascii());
       
   167       printInfo(exec, "to", args[i]);
       
   168 #endif
       
   169       variable->put(exec, parameters[i].name, args[i]);
       
   170     }
       
   171 }
       
   172 
       
   173 void FunctionImp::processVarDecls(ExecState*)
       
   174 {
       
   175 }
       
   176 
       
   177 JSValue* FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
       
   178 {
       
   179   FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
       
   180   Context* context = exec->m_context;
       
   181   while (context) {
       
   182     if (context->function() == thisObj)
       
   183       return static_cast<ActivationImp*>(context->activationObject())->get(exec, propertyName);
       
   184     context = context->callingContext();
       
   185   }
       
   186   return jsNull();
       
   187 }
       
   188 
       
   189 JSValue* FunctionImp::callerGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
       
   190 {
       
   191     FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
       
   192     Context* context = exec->m_context;
       
   193     while (context) {
       
   194         if (context->function() == thisObj)
       
   195             break;
       
   196         context = context->callingContext();
       
   197     }
       
   198 
       
   199     if (!context)
       
   200         return jsNull();
       
   201     
       
   202     Context* callingContext = context->callingContext();
       
   203     if (!callingContext)
       
   204         return jsNull();
       
   205     
       
   206     FunctionImp* callingFunction = callingContext->function();
       
   207     if (!callingFunction)
       
   208         return jsNull();
       
   209 
       
   210     return callingFunction;
       
   211 }
       
   212 
       
   213 JSValue* FunctionImp::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
       
   214 {
       
   215     FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
       
   216     return jsNumber(thisObj->body->numParams());
       
   217 }
       
   218 
       
   219 bool FunctionImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
       
   220 {
       
   221     // Find the arguments from the closest context.
       
   222     if (propertyName == exec->propertyNames().arguments) {
       
   223         slot.setCustom(this, argumentsGetter);
       
   224         return true;
       
   225     }
       
   226 
       
   227     // Compute length of parameters.
       
   228     if (propertyName == exec->propertyNames().length) {
       
   229         slot.setCustom(this, lengthGetter);
       
   230         return true;
       
   231     }
       
   232 
       
   233     if (propertyName == exec->propertyNames().caller) {
       
   234         slot.setCustom(this, callerGetter);
       
   235         return true;
       
   236     }
       
   237 
       
   238     return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
       
   239 }
       
   240 
       
   241 void FunctionImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
       
   242 {
       
   243     if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
       
   244         return;
       
   245     InternalFunctionImp::put(exec, propertyName, value, attr);
       
   246 }
       
   247 
       
   248 bool FunctionImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
       
   249 {
       
   250     if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
       
   251         return false;
       
   252     return InternalFunctionImp::deleteProperty(exec, propertyName);
       
   253 }
       
   254 
       
   255 /* Returns the parameter name corresponding to the given index. eg:
       
   256  * function f1(x, y, z): getParameterName(0) --> x
       
   257  *
       
   258  * If a name appears more than once, only the last index at which
       
   259  * it appears associates with it. eg:
       
   260  * function f2(x, x): getParameterName(0) --> null
       
   261  */
       
   262 Identifier FunctionImp::getParameterName(int index)
       
   263 {
       
   264     Vector<Parameter>& parameters = body->parameters();
       
   265 
       
   266     if (static_cast<size_t>(index) >= body->numParams())
       
   267         return CommonIdentifiers::shared()->nullIdentifier;
       
   268   
       
   269     Identifier name = parameters[index].name;
       
   270 
       
   271     // Are there any subsequent parameters with the same name?
       
   272     size_t size = parameters.size();
       
   273     for (size_t i = index + 1; i < size; ++i)
       
   274         if (parameters[i].name == name)
       
   275             return CommonIdentifiers::shared()->nullIdentifier;
       
   276 
       
   277     return name;
       
   278 }
       
   279 
       
   280 // ------------------------------ DeclaredFunctionImp --------------------------
       
   281 
       
   282 // ### is "Function" correct here?
       
   283 const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
       
   284 
       
   285 DeclaredFunctionImp::DeclaredFunctionImp(ExecState* exec, const Identifier& n,
       
   286                                          FunctionBodyNode* b, const ScopeChain& sc)
       
   287   : FunctionImp(exec, n, b)
       
   288 {
       
   289   setScope(sc);
       
   290 }
       
   291 
       
   292 bool DeclaredFunctionImp::implementsConstruct() const
       
   293 {
       
   294   return true;
       
   295 }
       
   296 
       
   297 // ECMA 13.2.2 [[Construct]]
       
   298 JSObject* DeclaredFunctionImp::construct(ExecState* exec, const List& args)
       
   299 {
       
   300   JSObject* proto;
       
   301   JSValue* p = get(exec, exec->propertyNames().prototype);
       
   302   if (p->isObject())
       
   303     proto = static_cast<JSObject*>(p);
       
   304   else
       
   305     proto = exec->lexicalInterpreter()->builtinObjectPrototype();
       
   306 
       
   307   JSObject* obj(new JSObject(proto));
       
   308 
       
   309   JSValue* res = call(exec,obj,args);
       
   310 
       
   311   if (res->isObject())
       
   312     return static_cast<JSObject*>(res);
       
   313   else
       
   314     return obj;
       
   315 }
       
   316 
       
   317 Completion DeclaredFunctionImp::execute(ExecState* exec)
       
   318 {
       
   319   Completion result = body->execute(exec);
       
   320 
       
   321   if (result.complType() == Throw || result.complType() == ReturnValue)
       
   322       return result;
       
   323   return Completion(Normal, jsUndefined()); // TODO: or ReturnValue ?
       
   324 }
       
   325 
       
   326 void DeclaredFunctionImp::processVarDecls(ExecState* exec)
       
   327 {
       
   328   body->processVarDecls(exec);
       
   329 }
       
   330 
       
   331 // ------------------------------ IndexToNameMap ---------------------------------
       
   332 
       
   333 // We map indexes in the arguments array to their corresponding argument names. 
       
   334 // Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x"). 
       
   335 
       
   336 // Once we have an argument name, we can get and set the argument's value in the 
       
   337 // activation object.
       
   338 
       
   339 // We use Identifier::null to indicate that a given argument's value
       
   340 // isn't stored in the activation object.
       
   341 
       
   342 IndexToNameMap::IndexToNameMap(FunctionImp* func, const List& args)
       
   343 {
       
   344   _map = new Identifier[args.size()];
       
   345   this->size = args.size();
       
   346   
       
   347   int i = 0;
       
   348   ListIterator iterator = args.begin(); 
       
   349   for (; iterator != args.end(); i++, iterator++)
       
   350     _map[i] = func->getParameterName(i); // null if there is no corresponding parameter
       
   351 }
       
   352 
       
   353 IndexToNameMap::~IndexToNameMap() {
       
   354   delete [] _map;
       
   355 }
       
   356 
       
   357 bool IndexToNameMap::isMapped(const Identifier& index) const
       
   358 {
       
   359   bool indexIsNumber;
       
   360   int indexAsNumber = index.toUInt32(&indexIsNumber);
       
   361   
       
   362   if (!indexIsNumber)
       
   363     return false;
       
   364   
       
   365   if (indexAsNumber >= size)
       
   366     return false;
       
   367 
       
   368   if (_map[indexAsNumber].isNull())
       
   369     return false;
       
   370   
       
   371   return true;
       
   372 }
       
   373 
       
   374 void IndexToNameMap::unMap(const Identifier& index)
       
   375 {
       
   376   bool indexIsNumber;
       
   377   int indexAsNumber = index.toUInt32(&indexIsNumber);
       
   378 
       
   379   assert(indexIsNumber && indexAsNumber < size);
       
   380   
       
   381   _map[indexAsNumber] = CommonIdentifiers::shared()->nullIdentifier;
       
   382 }
       
   383 
       
   384 Identifier& IndexToNameMap::operator[](int index)
       
   385 {
       
   386   return _map[index];
       
   387 }
       
   388 
       
   389 Identifier& IndexToNameMap::operator[](const Identifier& index)
       
   390 {
       
   391   bool indexIsNumber;
       
   392   int indexAsNumber = index.toUInt32(&indexIsNumber);
       
   393 
       
   394   assert(indexIsNumber && indexAsNumber < size);
       
   395   
       
   396   return (*this)[indexAsNumber];
       
   397 }
       
   398 
       
   399 // ------------------------------ Arguments ---------------------------------
       
   400 
       
   401 const ClassInfo Arguments::info = {"Arguments", 0, 0, 0};
       
   402 
       
   403 // ECMA 10.1.8
       
   404 Arguments::Arguments(ExecState* exec, FunctionImp* func, const List& args, ActivationImp* act)
       
   405 : JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()), 
       
   406 _activationObject(act),
       
   407 indexToNameMap(func, args)
       
   408 {
       
   409   putDirect(exec->propertyNames().callee, func, DontEnum);
       
   410   putDirect(exec->propertyNames().length, args.size(), DontEnum);
       
   411   
       
   412   int i = 0;
       
   413   ListIterator iterator = args.begin(); 
       
   414   for (; iterator != args.end(); i++, iterator++) {
       
   415     if (!indexToNameMap.isMapped(Identifier::from(i))) {
       
   416       JSObject::put(exec, Identifier::from(i), *iterator, DontEnum);
       
   417     }
       
   418   }
       
   419 }
       
   420 
       
   421 void Arguments::mark() 
       
   422 {
       
   423   JSObject::mark();
       
   424   if (_activationObject && !_activationObject->marked())
       
   425     _activationObject->mark();
       
   426 }
       
   427 
       
   428 JSValue* Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
       
   429 {
       
   430   Arguments* thisObj = static_cast<Arguments*>(slot.slotBase());
       
   431   return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
       
   432 }
       
   433 
       
   434 bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
       
   435 {
       
   436   if (indexToNameMap.isMapped(propertyName)) {
       
   437     slot.setCustom(this, mappedIndexGetter);
       
   438     return true;
       
   439   }
       
   440 
       
   441   return JSObject::getOwnPropertySlot(exec, propertyName, slot);
       
   442 }
       
   443 
       
   444 void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
       
   445 {
       
   446   if (indexToNameMap.isMapped(propertyName)) {
       
   447     _activationObject->put(exec, indexToNameMap[propertyName], value, attr);
       
   448   } else {
       
   449     JSObject::put(exec, propertyName, value, attr);
       
   450   }
       
   451 }
       
   452 
       
   453 bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName) 
       
   454 {
       
   455   if (indexToNameMap.isMapped(propertyName)) {
       
   456     indexToNameMap.unMap(propertyName);
       
   457     return true;
       
   458   } else {
       
   459     return JSObject::deleteProperty(exec, propertyName);
       
   460   }
       
   461 }
       
   462 
       
   463 // ------------------------------ ActivationImp --------------------------------
       
   464 
       
   465 const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
       
   466 
       
   467 // ECMA 10.1.6
       
   468 ActivationImp::ActivationImp(FunctionImp* function, const List& arguments)
       
   469     : _function(function), _arguments(arguments), _argumentsObject(0)
       
   470 {
       
   471   // FIXME: Do we need to support enumerating the arguments property?
       
   472 }
       
   473 
       
   474 JSValue* ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
       
   475 {
       
   476   ActivationImp* thisObj = static_cast<ActivationImp*>(slot.slotBase());
       
   477 
       
   478   // default: return builtin arguments array
       
   479   if (!thisObj->_argumentsObject)
       
   480     thisObj->createArgumentsObject(exec);
       
   481   
       
   482   return thisObj->_argumentsObject;
       
   483 }
       
   484 
       
   485 PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
       
   486 {
       
   487   return ActivationImp::argumentsGetter;
       
   488 }
       
   489 
       
   490 bool ActivationImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
       
   491 {
       
   492     // do this first so property map arguments property wins over the below
       
   493     // we don't call JSObject because we won't have getter/setter properties
       
   494     // and we don't want to support __proto__
       
   495 
       
   496     if (JSValue** location = getDirectLocation(propertyName)) {
       
   497         slot.setValueSlot(this, location);
       
   498         return true;
       
   499     }
       
   500 
       
   501     if (propertyName == exec->propertyNames().arguments) {
       
   502         slot.setCustom(this, getArgumentsGetter());
       
   503         return true;
       
   504     }
       
   505 
       
   506     return false;
       
   507 }
       
   508 
       
   509 bool ActivationImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
       
   510 {
       
   511     if (propertyName == exec->propertyNames().arguments)
       
   512         return false;
       
   513     return JSObject::deleteProperty(exec, propertyName);
       
   514 }
       
   515 
       
   516 void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value, int attr)
       
   517 {
       
   518   // There's no way that an activation object can have a prototype or getter/setter properties
       
   519   assert(!_prop.hasGetterSetterProperties());
       
   520   assert(prototype() == jsNull());
       
   521 
       
   522   _prop.put(propertyName, value, attr, (attr == None || attr == DontDelete));
       
   523 }
       
   524 
       
   525 void ActivationImp::mark()
       
   526 {
       
   527     if (_function && !_function->marked()) 
       
   528         _function->mark();
       
   529     if (_argumentsObject && !_argumentsObject->marked())
       
   530         _argumentsObject->mark();
       
   531     JSObject::mark();
       
   532 }
       
   533 
       
   534 void ActivationImp::createArgumentsObject(ExecState* exec)
       
   535 {
       
   536   _argumentsObject = new Arguments(exec, _function, _arguments, const_cast<ActivationImp*>(this));
       
   537   // The arguments list is only needed to create the arguments object, so discard it now
       
   538   _arguments.reset();
       
   539 }
       
   540 
       
   541 // ------------------------------ GlobalFunc -----------------------------------
       
   542 
       
   543 
       
   544 GlobalFuncImp::GlobalFuncImp(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
       
   545   : InternalFunctionImp(funcProto, name)
       
   546   , id(i)
       
   547 {
       
   548   putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum);
       
   549 }
       
   550 
       
   551 CodeType GlobalFuncImp::codeType() const
       
   552 {
       
   553   return id == Eval ? EvalCode : codeType();
       
   554 }
       
   555 
       
   556 static JSValue* encode(ExecState* exec, const List& args, const char* do_not_escape)
       
   557 {
       
   558   UString r = "", s, str = args[0]->toString(exec);
       
   559   CString cstr = str.UTF8String();
       
   560   const char* p = cstr.c_str();
       
   561   for (size_t k = 0; k < cstr.size(); k++, p++) {
       
   562     char c = *p;
       
   563     if (c && strchr(do_not_escape, c)) {
       
   564       r.append(c);
       
   565     } else {
       
   566       char tmp[4];
       
   567       sprintf(tmp, "%%%02X", (unsigned char)c);
       
   568       r += tmp;
       
   569     }
       
   570   }
       
   571   return jsString(r);
       
   572 }
       
   573 
       
   574 static JSValue* decode(ExecState* exec, const List& args, const char* do_not_unescape, bool strict)
       
   575 {
       
   576   UString s = "", str = args[0]->toString(exec);
       
   577   int k = 0, len = str.size();
       
   578   const UChar* d = str.data();
       
   579   UChar u;
       
   580   while (k < len) {
       
   581     const UChar* p = d + k;
       
   582     UChar c = *p;
       
   583     if (c == '%') {
       
   584       int charLen = 0;
       
   585       if (k <= len - 3 && isxdigit(p[1].uc) && isxdigit(p[2].uc)) {
       
   586         const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
       
   587         const int sequenceLen = UTF8SequenceLength(b0);
       
   588         if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
       
   589           charLen = sequenceLen * 3;
       
   590           char sequence[5];
       
   591           sequence[0] = b0;
       
   592           for (int i = 1; i < sequenceLen; ++i) {
       
   593             const UChar* q = p + i * 3;
       
   594             if (q[0] == '%' && isxdigit(q[1].uc) && isxdigit(q[2].uc))
       
   595               sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
       
   596             else {
       
   597               charLen = 0;
       
   598               break;
       
   599             }
       
   600           }
       
   601           if (charLen != 0) {
       
   602             sequence[sequenceLen] = 0;
       
   603             const int character = decodeUTF8Sequence(sequence);
       
   604             if (character < 0 || character >= 0x110000) {
       
   605               charLen = 0;
       
   606             } else if (character >= 0x10000) {
       
   607               // Convert to surrogate pair.
       
   608               s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
       
   609               u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
       
   610             } else {
       
   611               u = static_cast<unsigned short>(character);
       
   612             }
       
   613           }
       
   614         }
       
   615       }
       
   616       if (charLen == 0) {
       
   617         if (strict)
       
   618           return throwError(exec, URIError);
       
   619         // The only case where we don't use "strict" mode is the "unescape" function.
       
   620         // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
       
   621         if (k <= len - 6 && p[1] == 'u'
       
   622             && isxdigit(p[2].uc) && isxdigit(p[3].uc)
       
   623             && isxdigit(p[4].uc) && isxdigit(p[5].uc)) {
       
   624           charLen = 6;
       
   625           u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
       
   626         }
       
   627       }
       
   628       if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
       
   629         c = u;
       
   630         k += charLen - 1;
       
   631       }
       
   632     }
       
   633     k++;
       
   634     s.append(c);
       
   635   }
       
   636   return jsString(s);
       
   637 }
       
   638 
       
   639 static bool isStrWhiteSpace(unsigned short c)
       
   640 {
       
   641     switch (c) {
       
   642         case 0x0009:
       
   643         case 0x000A:
       
   644         case 0x000B:
       
   645         case 0x000C:
       
   646         case 0x000D:
       
   647         case 0x0020:
       
   648         case 0x00A0:
       
   649         case 0x2028:
       
   650         case 0x2029:
       
   651             return true;
       
   652         default:
       
   653             return isSeparatorSpace(c);
       
   654     }
       
   655 }
       
   656 
       
   657 static int parseDigit(unsigned short c, int radix)
       
   658 {
       
   659     int digit = -1;
       
   660 
       
   661     if (c >= '0' && c <= '9') {
       
   662         digit = c - '0';
       
   663     } else if (c >= 'A' && c <= 'Z') {
       
   664         digit = c - 'A' + 10;
       
   665     } else if (c >= 'a' && c <= 'z') {
       
   666         digit = c - 'a' + 10;
       
   667     }
       
   668 
       
   669     if (digit >= radix)
       
   670         return -1;
       
   671     return digit;
       
   672 }
       
   673 
       
   674 double parseIntOverflow(const char* s, int length, int radix)
       
   675 {
       
   676     double number = 0.0;
       
   677     double radixMultiplier = 1.0;
       
   678 
       
   679     for (const char* p = s + length - 1; p >= s; p--) {
       
   680         if (radixMultiplier == Inf) {
       
   681             if (*p != '0') {
       
   682                 number = Inf;
       
   683                 break;
       
   684             }
       
   685         } else {
       
   686             int digit = parseDigit(*p, radix);
       
   687             number += digit * radixMultiplier;
       
   688         }
       
   689 
       
   690         radixMultiplier *= radix;
       
   691     }
       
   692 
       
   693     return number;
       
   694 }
       
   695 
       
   696 static double parseInt(const UString& s, int radix)
       
   697 {
       
   698     int length = s.size();
       
   699     int p = 0;
       
   700 
       
   701     while (p < length && isStrWhiteSpace(s[p].uc)) {
       
   702         ++p;
       
   703     }
       
   704 
       
   705     double sign = 1;
       
   706     if (p < length) {
       
   707         if (s[p] == '+') {
       
   708             ++p;
       
   709         } else if (s[p] == '-') {
       
   710             sign = -1;
       
   711             ++p;
       
   712         }
       
   713     }
       
   714 
       
   715     if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
       
   716         radix = 16;
       
   717         p += 2;
       
   718     } else if (radix == 0) {
       
   719         if (p < length && s[p] == '0')
       
   720             radix = 8;
       
   721         else
       
   722             radix = 10;
       
   723     }
       
   724 
       
   725     if (radix < 2 || radix > 36)
       
   726         return NaN;
       
   727 
       
   728     int firstDigitPosition = p;
       
   729     bool sawDigit = false;
       
   730     double number = 0;
       
   731     while (p < length) {
       
   732         int digit = parseDigit(s[p].uc, radix);
       
   733         if (digit == -1)
       
   734             break;
       
   735         sawDigit = true;
       
   736         number *= radix;
       
   737         number += digit;
       
   738         ++p;
       
   739     }
       
   740 
       
   741     if (number >= mantissaOverflowLowerBound) {
       
   742         if (radix == 10)
       
   743             number = kjs_strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
       
   744         else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
       
   745             number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
       
   746     }
       
   747 
       
   748     if (!sawDigit)
       
   749         return NaN;
       
   750 
       
   751     return sign * number;
       
   752 }
       
   753 
       
   754 static double parseFloat(const UString& s)
       
   755 {
       
   756     // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
       
   757     // Need to skip any whitespace and then one + or - sign.
       
   758     int length = s.size();
       
   759     int p = 0;
       
   760     while (p < length && isStrWhiteSpace(s[p].uc)) {
       
   761         ++p;
       
   762     }
       
   763     if (p < length && (s[p] == '+' || s[p] == '-')) {
       
   764         ++p;
       
   765     }
       
   766     if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
       
   767         return 0;
       
   768     }
       
   769 
       
   770     return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
       
   771 }
       
   772 
       
   773 JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
       
   774 {
       
   775   JSValue* res = jsUndefined();
       
   776 
       
   777   static const char do_not_escape[] =
       
   778     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
       
   779     "abcdefghijklmnopqrstuvwxyz"
       
   780     "0123456789"
       
   781     "*+-./@_";
       
   782 
       
   783   static const char do_not_escape_when_encoding_URI_component[] =
       
   784     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
       
   785     "abcdefghijklmnopqrstuvwxyz"
       
   786     "0123456789"
       
   787     "!'()*-._~";
       
   788   static const char do_not_escape_when_encoding_URI[] =
       
   789     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
       
   790     "abcdefghijklmnopqrstuvwxyz"
       
   791     "0123456789"
       
   792     "!#$&'()*+,-./:;=?@_~";
       
   793   static const char do_not_unescape_when_decoding_URI[] =
       
   794     "#$&+,/:;=?@";
       
   795 
       
   796   switch (id) {
       
   797     case Eval: { // eval()
       
   798       JSValue* x = args[0];
       
   799       if (!x->isString())
       
   800         return x;
       
   801       else {
       
   802         UString s = x->toString(exec);
       
   803         
       
   804         int sid;
       
   805         int errLine;
       
   806         UString errMsg;
       
   807         RefPtr<ProgramNode> progNode(Parser::parse(UString(), 0, s.data(),s.size(),&sid,&errLine,&errMsg));
       
   808 
       
   809         Debugger* dbg = exec->dynamicInterpreter()->debugger();
       
   810         if (dbg) {
       
   811           bool cont = dbg->sourceParsed(exec, sid, UString(), s, 0, errLine, errMsg);
       
   812           if (!cont)
       
   813             return jsUndefined();
       
   814         }
       
   815 
       
   816         // no program node means a syntax occurred
       
   817         if (!progNode)
       
   818           return throwError(exec, SyntaxError, errMsg, errLine, sid, NULL);
       
   819 
       
   820         bool switchGlobal = thisObj && exec->dynamicInterpreter()->isGlobalObject(thisObj) && thisObj != exec->dynamicInterpreter()->globalObject();
       
   821           
       
   822         // enter a new execution context
       
   823         Interpreter* interpreter = switchGlobal ? exec->dynamicInterpreter()->interpreterForGlobalObject(thisObj) : exec->dynamicInterpreter();
       
   824         JSObject* thisVal = static_cast<JSObject*>(exec->context()->thisValue());
       
   825         Context ctx(interpreter->globalObject(),
       
   826                        interpreter,
       
   827                        thisVal,
       
   828                        progNode.get(),
       
   829                        EvalCode,
       
   830                        exec->context());
       
   831         ExecState newExec(interpreter, &ctx);
       
   832         if (exec->hadException())
       
   833             newExec.setException(exec->exception());
       
   834         ctx.setExecState(&newExec);
       
   835           
       
   836         if (switchGlobal) {
       
   837             ctx.pushScope(thisObj);
       
   838             ctx.setVariableObject(thisObj);
       
   839         }
       
   840         
       
   841         // execute the code
       
   842         progNode->processVarDecls(&newExec);
       
   843         Completion c = progNode->execute(&newExec);
       
   844           
       
   845         if (switchGlobal)
       
   846             ctx.popScope();
       
   847 
       
   848         // if an exception occured, propogate it back to the previous execution object
       
   849         if (newExec.hadException())
       
   850           exec->setException(newExec.exception());
       
   851 
       
   852         res = jsUndefined();
       
   853         if (c.complType() == Throw)
       
   854           exec->setException(c.value());
       
   855         else if (c.isValueCompletion())
       
   856             res = c.value();
       
   857       }
       
   858       break;
       
   859     }
       
   860   case ParseInt:
       
   861     res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
       
   862     break;
       
   863   case ParseFloat:
       
   864     res = jsNumber(parseFloat(args[0]->toString(exec)));
       
   865     break;
       
   866   case IsNaN:
       
   867     res = jsBoolean(isNaN(args[0]->toNumber(exec)));
       
   868     break;
       
   869   case IsFinite: {
       
   870     double n = args[0]->toNumber(exec);
       
   871     res = jsBoolean(!isNaN(n) && !isInf(n));
       
   872     break;
       
   873   }
       
   874   case DecodeURI:
       
   875     res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
       
   876     break;
       
   877   case DecodeURIComponent:
       
   878     res = decode(exec, args, "", true);
       
   879     break;
       
   880   case EncodeURI:
       
   881     res = encode(exec, args, do_not_escape_when_encoding_URI);
       
   882     break;
       
   883   case EncodeURIComponent:
       
   884     res = encode(exec, args, do_not_escape_when_encoding_URI_component);
       
   885     break;
       
   886   case Escape:
       
   887     {
       
   888       UString r = "", s, str = args[0]->toString(exec);
       
   889       const UChar* c = str.data();
       
   890       for (int k = 0; k < str.size(); k++, c++) {
       
   891         int u = c->uc;
       
   892         if (u > 255) {
       
   893           char tmp[7];
       
   894           sprintf(tmp, "%%u%04X", u);
       
   895           s = UString(tmp);
       
   896         } else if (u != 0 && strchr(do_not_escape, (char)u)) {
       
   897           s = UString(c, 1);
       
   898         } else {
       
   899           char tmp[4];
       
   900           sprintf(tmp, "%%%02X", u);
       
   901           s = UString(tmp);
       
   902         }
       
   903         r += s;
       
   904       }
       
   905       res = jsString(r);
       
   906       break;
       
   907     }
       
   908   case UnEscape:
       
   909     {
       
   910       UString s = "", str = args[0]->toString(exec);
       
   911       int k = 0, len = str.size();
       
   912       while (k < len) {
       
   913         const UChar* c = str.data() + k;
       
   914         UChar u;
       
   915         if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
       
   916           if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
       
   917               Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
       
   918           u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
       
   919                                     (c+4)->uc, (c+5)->uc);
       
   920           c = &u;
       
   921           k += 5;
       
   922           }
       
   923         } else if (*c == UChar('%') && k <= len - 3 &&
       
   924                    Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
       
   925           u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
       
   926           c = &u;
       
   927           k += 2;
       
   928         }
       
   929         k++;
       
   930         s += UString(c, 1);
       
   931       }
       
   932       res = jsString(s);
       
   933       break;
       
   934     }
       
   935 #ifndef NDEBUG
       
   936   case KJSPrint:
       
   937     puts(args[0]->toString(exec).ascii());
       
   938     break;
       
   939 #endif
       
   940   }
       
   941 
       
   942   return res;
       
   943 }
       
   944 
       
   945 UString escapeStringForPrettyPrinting(const UString& s)
       
   946 {
       
   947     UString escapedString;
       
   948     
       
   949     for (int i = 0; i < s.size(); i++) {
       
   950         unsigned short c = s.data()[i].unicode();
       
   951         
       
   952         switch (c) {
       
   953         case '\"':
       
   954             escapedString += "\\\"";
       
   955             break;
       
   956         case '\n':
       
   957             escapedString += "\\n";
       
   958             break;
       
   959         case '\r':
       
   960             escapedString += "\\r";
       
   961             break;
       
   962         case '\t':
       
   963             escapedString += "\\t";
       
   964             break;
       
   965         case '\\':
       
   966             escapedString += "\\\\";
       
   967             break;
       
   968         default:
       
   969             if (c < 128 && isPrintableChar(c))
       
   970                 escapedString.append(c);
       
   971             else {
       
   972                 char hexValue[7];
       
   973             
       
   974 #if PLATFORM(WIN_OS)
       
   975                 _snprintf(hexValue, 7, "\\u%04x", c);
       
   976 #else
       
   977                 snprintf(hexValue, 7, "\\u%04x", c);
       
   978 #endif
       
   979                 escapedString += hexValue;
       
   980             }
       
   981         }
       
   982     }
       
   983     
       
   984     return escapedString;    
       
   985 }
       
   986 
       
   987 } // namespace