dcce892cad91c2755fb41979f38b7eed5d63a8d9
[WebKit.git] / JavaScriptCore / kjs / function.cpp
1 // -*- c-basic-offset: 2 -*-
2 /*
3  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
4  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
5  *  Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
6  *  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
7  *  Copyright (C) 2007 Maks Orlovich
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 "ExecState.h"
30 #include "JSGlobalObject.h"
31 #include "Parser.h"
32 #include "PropertyNameArray.h"
33 #include "debugger.h"
34 #include "dtoa.h"
35 #include "function_object.h"
36 #include "internal.h"
37 #include "lexer.h"
38 #include "nodes.h"
39 #include "operations.h"
40 #include <errno.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <wtf/ASCIICType.h>
45 #include <wtf/Assertions.h>
46 #include <wtf/MathExtras.h>
47 #include <wtf/unicode/UTF8.h>
48
49 using namespace WTF;
50 using namespace Unicode;
51
52 namespace KJS {
53
54 // ----------------------------- FunctionImp ----------------------------------
55
56 const ClassInfo FunctionImp::info = { "Function", &InternalFunctionImp::info, 0 };
57
58 FunctionImp::FunctionImp(ExecState* exec, const Identifier& name, FunctionBodyNode* b, const ScopeChain& sc)
59   : InternalFunctionImp(exec->lexicalGlobalObject()->functionPrototype(), name)
60   , body(b)
61   , _scope(sc)
62 {
63 }
64
65 void FunctionImp::mark()
66 {
67     InternalFunctionImp::mark();
68     _scope.mark();
69 }
70
71 JSValue* FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
72 {
73     ExecState newExec(exec->dynamicGlobalObject(), thisObj, body.get(), exec, this, args);
74     JSValue* result = body->execute(&newExec);
75     if (newExec.completionType() == Throw) {
76         exec->setException(result);
77         return result;
78     }
79     if (newExec.completionType() == ReturnValue)
80         return result;
81     return jsUndefined();
82 }
83
84 JSValue* FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
85 {
86   FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
87   ExecState* e = exec;
88   while (e) {
89     if (e->function() == thisObj)
90       return e->activationObject()->get(exec, propertyName);
91     e = e->callingExecState();
92   }
93   return jsNull();
94 }
95
96 JSValue* FunctionImp::callerGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
97 {
98     FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
99     ExecState* e = exec;
100     while (e) {
101         if (e->function() == thisObj)
102             break;
103         e = e->callingExecState();
104     }
105
106     if (!e)
107         return jsNull();
108     
109     ExecState* callingExecState = e->callingExecState();
110     if (!callingExecState)
111         return jsNull();
112     
113     FunctionImp* callingFunction = callingExecState->function();
114     if (!callingFunction)
115         return jsNull();
116
117     return callingFunction;
118 }
119
120 JSValue* FunctionImp::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
121 {
122     FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
123     return jsNumber(thisObj->body->parameters().size());
124 }
125
126 bool FunctionImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
127 {
128     // Find the arguments from the closest context.
129     if (propertyName == exec->propertyNames().arguments) {
130         slot.setCustom(this, argumentsGetter);
131         return true;
132     }
133
134     // Compute length of parameters.
135     if (propertyName == exec->propertyNames().length) {
136         slot.setCustom(this, lengthGetter);
137         return true;
138     }
139
140     if (propertyName == exec->propertyNames().caller) {
141         slot.setCustom(this, callerGetter);
142         return true;
143     }
144
145     return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
146 }
147
148 void FunctionImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
149 {
150     if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
151         return;
152     InternalFunctionImp::put(exec, propertyName, value, attr);
153 }
154
155 bool FunctionImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
156 {
157     if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
158         return false;
159     return InternalFunctionImp::deleteProperty(exec, propertyName);
160 }
161
162 /* Returns the parameter name corresponding to the given index. eg:
163  * function f1(x, y, z): getParameterName(0) --> x
164  *
165  * If a name appears more than once, only the last index at which
166  * it appears associates with it. eg:
167  * function f2(x, x): getParameterName(0) --> null
168  */
169 Identifier FunctionImp::getParameterName(int index)
170 {
171     Vector<Identifier>& parameters = body->parameters();
172
173     if (static_cast<size_t>(index) >= body->parameters().size())
174         return CommonIdentifiers::shared()->nullIdentifier;
175   
176     Identifier name = parameters[index];
177
178     // Are there any subsequent parameters with the same name?
179     size_t size = parameters.size();
180     for (size_t i = index + 1; i < size; ++i)
181         if (parameters[i] == name)
182             return CommonIdentifiers::shared()->nullIdentifier;
183
184     return name;
185 }
186
187 // ECMA 13.2.2 [[Construct]]
188 JSObject* FunctionImp::construct(ExecState* exec, const List& args)
189 {
190   JSObject* proto;
191   JSValue* p = get(exec, exec->propertyNames().prototype);
192   if (p->isObject())
193     proto = static_cast<JSObject*>(p);
194   else
195     proto = exec->lexicalGlobalObject()->objectPrototype();
196
197   JSObject* obj(new JSObject(proto));
198
199   JSValue* res = call(exec,obj,args);
200
201   if (res->isObject())
202     return static_cast<JSObject*>(res);
203   else
204     return obj;
205 }
206
207 // ------------------------------ IndexToNameMap ---------------------------------
208
209 // We map indexes in the arguments array to their corresponding argument names. 
210 // Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x"). 
211
212 // Once we have an argument name, we can get and set the argument's value in the 
213 // activation object.
214
215 // We use Identifier::null to indicate that a given argument's value
216 // isn't stored in the activation object.
217
218 IndexToNameMap::IndexToNameMap(FunctionImp* func, const List& args)
219 {
220   _map = new Identifier[args.size()];
221   this->size = args.size();
222   
223   int i = 0;
224   List::const_iterator end = args.end();
225   for (List::const_iterator it = args.begin(); it != end; ++i, ++it)
226     _map[i] = func->getParameterName(i); // null if there is no corresponding parameter
227 }
228
229 IndexToNameMap::~IndexToNameMap() {
230   delete [] _map;
231 }
232
233 bool IndexToNameMap::isMapped(const Identifier& index) const
234 {
235   bool indexIsNumber;
236   int indexAsNumber = index.toUInt32(&indexIsNumber);
237   
238   if (!indexIsNumber)
239     return false;
240   
241   if (indexAsNumber >= size)
242     return false;
243
244   if (_map[indexAsNumber].isNull())
245     return false;
246   
247   return true;
248 }
249
250 void IndexToNameMap::unMap(const Identifier& index)
251 {
252   bool indexIsNumber;
253   int indexAsNumber = index.toUInt32(&indexIsNumber);
254
255   ASSERT(indexIsNumber && indexAsNumber < size);
256   
257   _map[indexAsNumber] = CommonIdentifiers::shared()->nullIdentifier;
258 }
259
260 Identifier& IndexToNameMap::operator[](int index)
261 {
262   return _map[index];
263 }
264
265 Identifier& IndexToNameMap::operator[](const Identifier& index)
266 {
267   bool indexIsNumber;
268   int indexAsNumber = index.toUInt32(&indexIsNumber);
269
270   ASSERT(indexIsNumber && indexAsNumber < size);
271   
272   return (*this)[indexAsNumber];
273 }
274
275 // ------------------------------ Arguments ---------------------------------
276
277 const ClassInfo Arguments::info = { "Arguments", 0, 0 };
278
279 // ECMA 10.1.8
280 Arguments::Arguments(ExecState* exec, FunctionImp* func, const List& args, ActivationImp* act)
281 : JSObject(exec->lexicalGlobalObject()->objectPrototype()), 
282 _activationObject(act),
283 indexToNameMap(func, args)
284 {
285   putDirect(exec->propertyNames().callee, func, DontEnum);
286   putDirect(exec->propertyNames().length, args.size(), DontEnum);
287   
288   int i = 0;
289   List::const_iterator end = args.end();
290   for (List::const_iterator it = args.begin(); it != end; ++it, ++i) {
291     if (!indexToNameMap.isMapped(Identifier::from(i))) {
292       JSObject::put(exec, Identifier::from(i), *it, DontEnum);
293     }
294   }
295 }
296
297 void Arguments::mark() 
298 {
299   JSObject::mark();
300   if (_activationObject && !_activationObject->marked())
301     _activationObject->mark();
302 }
303
304 JSValue* Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
305 {
306   Arguments* thisObj = static_cast<Arguments*>(slot.slotBase());
307   return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
308 }
309
310 bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
311 {
312   if (indexToNameMap.isMapped(propertyName)) {
313     slot.setCustom(this, mappedIndexGetter);
314     return true;
315   }
316
317   return JSObject::getOwnPropertySlot(exec, propertyName, slot);
318 }
319
320 void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
321 {
322   if (indexToNameMap.isMapped(propertyName)) {
323     _activationObject->put(exec, indexToNameMap[propertyName], value, attr);
324   } else {
325     JSObject::put(exec, propertyName, value, attr);
326   }
327 }
328
329 bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName) 
330 {
331   if (indexToNameMap.isMapped(propertyName)) {
332     indexToNameMap.unMap(propertyName);
333     return true;
334   } else {
335     return JSObject::deleteProperty(exec, propertyName);
336   }
337 }
338
339 // ------------------------------ ActivationImp --------------------------------
340
341 const ClassInfo ActivationImp::info = { "Activation", 0, 0 };
342
343 JSValue* ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
344 {
345   ActivationImp* thisObj = static_cast<ActivationImp*>(slot.slotBase());
346   
347   if (!thisObj->d()->argumentsObject)
348     thisObj->createArgumentsObject(exec);
349   
350   return thisObj->d()->argumentsObject;
351 }
352
353 PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
354 {
355   return ActivationImp::argumentsGetter;
356 }
357
358 bool ActivationImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
359 {
360     if (symbolTableGet(propertyName, slot))
361         return true;
362
363     if (JSValue** location = getDirectLocation(propertyName)) {
364         slot.setValueSlot(this, location);
365         return true;
366     }
367
368     // Only return the built-in arguments object if it wasn't overridden above.
369     if (propertyName == exec->propertyNames().arguments) {
370         slot.setCustom(this, getArgumentsGetter());
371         return true;
372     }
373
374     // We don't call through to JSObject because there's no way to give an 
375     // activation object getter properties or a prototype.
376     ASSERT(!_prop.hasGetterSetterProperties());
377     ASSERT(prototype() == jsNull());
378     return false;
379 }
380
381 bool ActivationImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
382 {
383     if (propertyName == exec->propertyNames().arguments)
384         return false;
385
386     return JSVariableObject::deleteProperty(exec, propertyName);
387 }
388
389 void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value, int attr)
390 {
391     if (symbolTablePut(propertyName, value, attr))
392         return;
393
394     // We don't call through to JSObject because __proto__ and getter/setter 
395     // properties are non-standard extensions that other implementations do not
396     // expose in the activation object.
397     ASSERT(!_prop.hasGetterSetterProperties());
398     _prop.put(propertyName, value, attr, (attr == None || attr == DontDelete));
399 }
400
401 void ActivationImp::mark()
402 {
403     JSVariableObject::mark();
404     
405     if (!d()->function->marked())
406         d()->function->mark();
407
408     if (d()->argumentsObject && !d()->argumentsObject->marked())
409         d()->argumentsObject->mark();
410 }
411
412 void ActivationImp::createArgumentsObject(ExecState* exec)
413 {
414     // Since "arguments" is only accessible while a function is being called,
415     // we can retrieve our argument list from the ExecState for our function 
416     // call instead of storing the list ourselves.
417     d()->argumentsObject = new Arguments(exec, d()->exec->function(), *d()->exec->arguments(), this);
418 }
419
420 // ------------------------------ GlobalFunc -----------------------------------
421
422
423 GlobalFuncImp::GlobalFuncImp(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
424   : InternalFunctionImp(funcProto, name)
425   , id(i)
426 {
427   putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum);
428 }
429
430 static JSValue* encode(ExecState* exec, const List& args, const char* do_not_escape)
431 {
432   UString r = "", s, str = args[0]->toString(exec);
433   CString cstr = str.UTF8String(true);
434   if (!cstr.c_str())
435     return throwError(exec, URIError, "String contained an illegal UTF-16 sequence.");
436   const char* p = cstr.c_str();
437   for (size_t k = 0; k < cstr.size(); k++, p++) {
438     char c = *p;
439     if (c && strchr(do_not_escape, c)) {
440       r.append(c);
441     } else {
442       char tmp[4];
443       sprintf(tmp, "%%%02X", (unsigned char)c);
444       r += tmp;
445     }
446   }
447   return jsString(r);
448 }
449
450 static JSValue* decode(ExecState* exec, const List& args, const char* do_not_unescape, bool strict)
451 {
452   UString s = "", str = args[0]->toString(exec);
453   int k = 0, len = str.size();
454   const UChar* d = str.data();
455   UChar u;
456   while (k < len) {
457     const UChar* p = d + k;
458     UChar c = *p;
459     if (c == '%') {
460       int charLen = 0;
461       if (k <= len - 3 && isASCIIHexDigit(p[1].uc) && isASCIIHexDigit(p[2].uc)) {
462         const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
463         const int sequenceLen = UTF8SequenceLength(b0);
464         if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
465           charLen = sequenceLen * 3;
466           char sequence[5];
467           sequence[0] = b0;
468           for (int i = 1; i < sequenceLen; ++i) {
469             const UChar* q = p + i * 3;
470             if (q[0] == '%' && isASCIIHexDigit(q[1].uc) && isASCIIHexDigit(q[2].uc))
471               sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
472             else {
473               charLen = 0;
474               break;
475             }
476           }
477           if (charLen != 0) {
478             sequence[sequenceLen] = 0;
479             const int character = decodeUTF8Sequence(sequence);
480             if (character < 0 || character >= 0x110000) {
481               charLen = 0;
482             } else if (character >= 0x10000) {
483               // Convert to surrogate pair.
484               s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
485               u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
486             } else {
487               u = static_cast<unsigned short>(character);
488             }
489           }
490         }
491       }
492       if (charLen == 0) {
493         if (strict)
494           return throwError(exec, URIError);
495         // The only case where we don't use "strict" mode is the "unescape" function.
496         // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
497         if (k <= len - 6 && p[1] == 'u'
498             && isASCIIHexDigit(p[2].uc) && isASCIIHexDigit(p[3].uc)
499             && isASCIIHexDigit(p[4].uc) && isASCIIHexDigit(p[5].uc)) {
500           charLen = 6;
501           u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
502         }
503       }
504       if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
505         c = u;
506         k += charLen - 1;
507       }
508     }
509     k++;
510     s.append(c);
511   }
512   return jsString(s);
513 }
514
515 static bool isStrWhiteSpace(unsigned short c)
516 {
517     switch (c) {
518         case 0x0009:
519         case 0x000A:
520         case 0x000B:
521         case 0x000C:
522         case 0x000D:
523         case 0x0020:
524         case 0x00A0:
525         case 0x2028:
526         case 0x2029:
527             return true;
528         default:
529             return isSeparatorSpace(c);
530     }
531 }
532
533 static int parseDigit(unsigned short c, int radix)
534 {
535     int digit = -1;
536
537     if (c >= '0' && c <= '9') {
538         digit = c - '0';
539     } else if (c >= 'A' && c <= 'Z') {
540         digit = c - 'A' + 10;
541     } else if (c >= 'a' && c <= 'z') {
542         digit = c - 'a' + 10;
543     }
544
545     if (digit >= radix)
546         return -1;
547     return digit;
548 }
549
550 double parseIntOverflow(const char* s, int length, int radix)
551 {
552     double number = 0.0;
553     double radixMultiplier = 1.0;
554
555     for (const char* p = s + length - 1; p >= s; p--) {
556         if (radixMultiplier == Inf) {
557             if (*p != '0') {
558                 number = Inf;
559                 break;
560             }
561         } else {
562             int digit = parseDigit(*p, radix);
563             number += digit * radixMultiplier;
564         }
565
566         radixMultiplier *= radix;
567     }
568
569     return number;
570 }
571
572 static double parseInt(const UString& s, int radix)
573 {
574     int length = s.size();
575     int p = 0;
576
577     while (p < length && isStrWhiteSpace(s[p].uc)) {
578         ++p;
579     }
580
581     double sign = 1;
582     if (p < length) {
583         if (s[p] == '+') {
584             ++p;
585         } else if (s[p] == '-') {
586             sign = -1;
587             ++p;
588         }
589     }
590
591     if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
592         radix = 16;
593         p += 2;
594     } else if (radix == 0) {
595         if (p < length && s[p] == '0')
596             radix = 8;
597         else
598             radix = 10;
599     }
600
601     if (radix < 2 || radix > 36)
602         return NaN;
603
604     int firstDigitPosition = p;
605     bool sawDigit = false;
606     double number = 0;
607     while (p < length) {
608         int digit = parseDigit(s[p].uc, radix);
609         if (digit == -1)
610             break;
611         sawDigit = true;
612         number *= radix;
613         number += digit;
614         ++p;
615     }
616
617     if (number >= mantissaOverflowLowerBound) {
618         if (radix == 10)
619             number = kjs_strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
620         else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
621             number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
622     }
623
624     if (!sawDigit)
625         return NaN;
626
627     return sign * number;
628 }
629
630 static double parseFloat(const UString& s)
631 {
632     // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
633     // Need to skip any whitespace and then one + or - sign.
634     int length = s.size();
635     int p = 0;
636     while (p < length && isStrWhiteSpace(s[p].uc)) {
637         ++p;
638     }
639     if (p < length && (s[p] == '+' || s[p] == '-')) {
640         ++p;
641     }
642     if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
643         return 0;
644     }
645
646     return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
647 }
648
649 JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
650 {
651   JSValue* res = jsUndefined();
652
653   static const char do_not_escape[] =
654     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
655     "abcdefghijklmnopqrstuvwxyz"
656     "0123456789"
657     "*+-./@_";
658
659   static const char do_not_escape_when_encoding_URI_component[] =
660     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
661     "abcdefghijklmnopqrstuvwxyz"
662     "0123456789"
663     "!'()*-._~";
664   static const char do_not_escape_when_encoding_URI[] =
665     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
666     "abcdefghijklmnopqrstuvwxyz"
667     "0123456789"
668     "!#$&'()*+,-./:;=?@_~";
669   static const char do_not_unescape_when_decoding_URI[] =
670     "#$&+,/:;=?@";
671
672   switch (id) {
673     case Eval: { // eval()
674       JSValue* x = args[0];
675       if (!x->isString())
676         return x;
677       else {
678         UString s = x->toString(exec);
679         
680         int sourceId;
681         int errLine;
682         UString errMsg;
683         RefPtr<EvalNode> evalNode = parser().parse<EvalNode>(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg);
684
685         Debugger* dbg = exec->dynamicGlobalObject()->debugger();
686         if (dbg) {
687           bool cont = dbg->sourceParsed(exec, sourceId, UString(), s, 0, errLine, errMsg);
688           if (!cont)
689             return jsUndefined();
690         }
691
692         // no program node means a syntax occurred
693         if (!evalNode)
694           return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
695
696         bool switchGlobal = thisObj && thisObj != exec->dynamicGlobalObject() && thisObj->isGlobalObject();
697
698         // enter a new execution context
699         JSGlobalObject* globalObject = switchGlobal ? static_cast<JSGlobalObject*>(thisObj) : exec->dynamicGlobalObject();
700         ExecState newExec(globalObject, evalNode.get(), exec);
701           
702         if (switchGlobal) {
703             newExec.pushScope(thisObj);
704             newExec.setVariableObject(static_cast<JSGlobalObject*>(thisObj));
705         }
706         JSValue* value = evalNode->execute(&newExec);
707         if (switchGlobal)
708             newExec.popScope();
709
710         if (newExec.completionType() == Throw) {
711             exec->setException(value);
712             return value;
713         }
714         return value ? value : jsUndefined();
715       }
716     }
717   case ParseInt:
718     res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
719     break;
720   case ParseFloat:
721     res = jsNumber(parseFloat(args[0]->toString(exec)));
722     break;
723   case IsNaN:
724     res = jsBoolean(isnan(args[0]->toNumber(exec)));
725     break;
726   case IsFinite: {
727     double n = args[0]->toNumber(exec);
728     res = jsBoolean(!isnan(n) && !isinf(n));
729     break;
730   }
731   case DecodeURI:
732     res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
733     break;
734   case DecodeURIComponent:
735     res = decode(exec, args, "", true);
736     break;
737   case EncodeURI:
738     res = encode(exec, args, do_not_escape_when_encoding_URI);
739     break;
740   case EncodeURIComponent:
741     res = encode(exec, args, do_not_escape_when_encoding_URI_component);
742     break;
743   case Escape:
744     {
745       UString r = "", s, str = args[0]->toString(exec);
746       const UChar* c = str.data();
747       for (int k = 0; k < str.size(); k++, c++) {
748         int u = c->uc;
749         if (u > 255) {
750           char tmp[7];
751           sprintf(tmp, "%%u%04X", u);
752           s = UString(tmp);
753         } else if (u != 0 && strchr(do_not_escape, (char)u)) {
754           s = UString(c, 1);
755         } else {
756           char tmp[4];
757           sprintf(tmp, "%%%02X", u);
758           s = UString(tmp);
759         }
760         r += s;
761       }
762       res = jsString(r);
763       break;
764     }
765   case UnEscape:
766     {
767       UString s = "", str = args[0]->toString(exec);
768       int k = 0, len = str.size();
769       while (k < len) {
770         const UChar* c = str.data() + k;
771         UChar u;
772         if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
773           if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
774               Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
775           u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
776                                     (c+4)->uc, (c+5)->uc);
777           c = &u;
778           k += 5;
779           }
780         } else if (*c == UChar('%') && k <= len - 3 &&
781                    Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
782           u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
783           c = &u;
784           k += 2;
785         }
786         k++;
787         s += UString(c, 1);
788       }
789       res = jsString(s);
790       break;
791     }
792 #ifndef NDEBUG
793   case KJSPrint:
794     puts(args[0]->toString(exec).ascii());
795     break;
796 #endif
797   }
798
799   return res;
800 }
801
802 } // namespace