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