61b9647d2a48d74de879e5130c034a7e9ac65b38
[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 static_cast<ActivationImp*>(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 ActivationImp::ActivationImp(ExecState* exec)
362     : d(new ActivationImpPrivate(exec))
363     , m_symbolTable(&exec->function()->body->symbolTable())
364 {
365 }
366
367 JSValue* ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
368 {
369   ActivationImp* thisObj = static_cast<ActivationImp*>(slot.slotBase());
370   ActivationImpPrivate* d = thisObj->d.get();
371   
372   if (!d->argumentsObject)
373     thisObj->createArgumentsObject(exec);
374   
375   return d->argumentsObject;
376 }
377
378 PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
379 {
380   return ActivationImp::argumentsGetter;
381 }
382
383 bool ActivationImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
384 {
385     // We don't call through to JSObject because there's no way to give an 
386     // activation object getter/setter properties, and __proto__ is a 
387     // non-standard extension that other implementations do not expose in the 
388     // activation object.
389     ASSERT(!_prop.hasGetterSetterProperties());
390
391     // it's more efficient to just get and check for a special empty
392     // value than to do a separate contains check
393     size_t index = m_symbolTable->get(propertyName.ustring().rep());
394     if (index != missingSymbolMarker()) {
395         slot.setValueSlot(this, &d->localStorage[index].value);
396         return true;
397     }
398
399     if (JSValue** location = getDirectLocation(propertyName)) {
400         slot.setValueSlot(this, location);
401         return true;
402     }
403
404     // Only return the built-in arguments object if it wasn't overridden above.
405     if (propertyName == exec->propertyNames().arguments) {
406         slot.setCustom(this, getArgumentsGetter());
407         return true;
408     }
409
410     return false;
411 }
412
413 bool ActivationImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
414 {
415     if (propertyName == exec->propertyNames().arguments)
416         return false;
417
418     if (m_symbolTable->contains(propertyName.ustring().rep()))
419         return false;
420
421     return JSObject::deleteProperty(exec, propertyName);
422 }
423
424 void ActivationImp::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
425 {
426     SymbolTable::const_iterator::Keys end = m_symbolTable->end().keys();
427     for (SymbolTable::const_iterator::Keys it = m_symbolTable->begin().keys(); it != end; ++it)
428         propertyNames.add(Identifier(*it));
429
430     JSObject::getPropertyNames(exec, propertyNames);
431 }
432
433 void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value, int attr)
434 {
435   // There's no way that an activation object can have a prototype or getter/setter properties.
436   ASSERT(!_prop.hasGetterSetterProperties());
437   ASSERT(prototype() == jsNull());
438
439   // it's more efficient to just get and check for a special empty
440   // value than to do a separate contains check
441   size_t index = m_symbolTable->get(propertyName.ustring().rep());
442   if (index != missingSymbolMarker()) {
443     LocalStorageEntry& entry = d->localStorage[index];
444     entry.value = value;
445     entry.attributes = attr;
446     return;
447   }
448
449   _prop.put(propertyName, value, attr, (attr == None || attr == DontDelete));
450 }
451
452 void ActivationImp::mark()
453 {
454     JSObject::mark();
455
456     size_t size = d->localStorage.size();
457     for (size_t i = 0; i < size; ++i) {
458         JSValue* value = d->localStorage[i].value;
459         if (!value->marked())
460             value->mark();
461     }
462     
463     ASSERT(d->function);
464     if (!d->function->marked())
465         d->function->mark();
466
467     if (d->argumentsObject && !d->argumentsObject->marked())
468         d->argumentsObject->mark();
469 }
470
471 void ActivationImp::createArgumentsObject(ExecState* exec)
472 {
473     // Since "arguments" is only accessible while a function is being called,
474     // we can retrieve our argument list from the ExecState for our function 
475     // call instead of storing the list ourselves.
476     d->argumentsObject = new Arguments(exec, d->function, *d->exec->arguments(), this);
477 }
478
479 // ------------------------------ GlobalFunc -----------------------------------
480
481
482 GlobalFuncImp::GlobalFuncImp(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
483   : InternalFunctionImp(funcProto, name)
484   , id(i)
485 {
486   putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum);
487 }
488
489 static JSValue* encode(ExecState* exec, const List& args, const char* do_not_escape)
490 {
491   UString r = "", s, str = args[0]->toString(exec);
492   CString cstr = str.UTF8String(true);
493   if (!cstr.c_str())
494     return throwError(exec, URIError, "String contained an illegal UTF-16 sequence.");
495   const char* p = cstr.c_str();
496   for (size_t k = 0; k < cstr.size(); k++, p++) {
497     char c = *p;
498     if (c && strchr(do_not_escape, c)) {
499       r.append(c);
500     } else {
501       char tmp[4];
502       sprintf(tmp, "%%%02X", (unsigned char)c);
503       r += tmp;
504     }
505   }
506   return jsString(r);
507 }
508
509 static JSValue* decode(ExecState* exec, const List& args, const char* do_not_unescape, bool strict)
510 {
511   UString s = "", str = args[0]->toString(exec);
512   int k = 0, len = str.size();
513   const UChar* d = str.data();
514   UChar u;
515   while (k < len) {
516     const UChar* p = d + k;
517     UChar c = *p;
518     if (c == '%') {
519       int charLen = 0;
520       if (k <= len - 3 && isASCIIHexDigit(p[1].uc) && isASCIIHexDigit(p[2].uc)) {
521         const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
522         const int sequenceLen = UTF8SequenceLength(b0);
523         if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
524           charLen = sequenceLen * 3;
525           char sequence[5];
526           sequence[0] = b0;
527           for (int i = 1; i < sequenceLen; ++i) {
528             const UChar* q = p + i * 3;
529             if (q[0] == '%' && isASCIIHexDigit(q[1].uc) && isASCIIHexDigit(q[2].uc))
530               sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
531             else {
532               charLen = 0;
533               break;
534             }
535           }
536           if (charLen != 0) {
537             sequence[sequenceLen] = 0;
538             const int character = decodeUTF8Sequence(sequence);
539             if (character < 0 || character >= 0x110000) {
540               charLen = 0;
541             } else if (character >= 0x10000) {
542               // Convert to surrogate pair.
543               s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
544               u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
545             } else {
546               u = static_cast<unsigned short>(character);
547             }
548           }
549         }
550       }
551       if (charLen == 0) {
552         if (strict)
553           return throwError(exec, URIError);
554         // The only case where we don't use "strict" mode is the "unescape" function.
555         // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
556         if (k <= len - 6 && p[1] == 'u'
557             && isASCIIHexDigit(p[2].uc) && isASCIIHexDigit(p[3].uc)
558             && isASCIIHexDigit(p[4].uc) && isASCIIHexDigit(p[5].uc)) {
559           charLen = 6;
560           u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
561         }
562       }
563       if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
564         c = u;
565         k += charLen - 1;
566       }
567     }
568     k++;
569     s.append(c);
570   }
571   return jsString(s);
572 }
573
574 static bool isStrWhiteSpace(unsigned short c)
575 {
576     switch (c) {
577         case 0x0009:
578         case 0x000A:
579         case 0x000B:
580         case 0x000C:
581         case 0x000D:
582         case 0x0020:
583         case 0x00A0:
584         case 0x2028:
585         case 0x2029:
586             return true;
587         default:
588             return isSeparatorSpace(c);
589     }
590 }
591
592 static int parseDigit(unsigned short c, int radix)
593 {
594     int digit = -1;
595
596     if (c >= '0' && c <= '9') {
597         digit = c - '0';
598     } else if (c >= 'A' && c <= 'Z') {
599         digit = c - 'A' + 10;
600     } else if (c >= 'a' && c <= 'z') {
601         digit = c - 'a' + 10;
602     }
603
604     if (digit >= radix)
605         return -1;
606     return digit;
607 }
608
609 double parseIntOverflow(const char* s, int length, int radix)
610 {
611     double number = 0.0;
612     double radixMultiplier = 1.0;
613
614     for (const char* p = s + length - 1; p >= s; p--) {
615         if (radixMultiplier == Inf) {
616             if (*p != '0') {
617                 number = Inf;
618                 break;
619             }
620         } else {
621             int digit = parseDigit(*p, radix);
622             number += digit * radixMultiplier;
623         }
624
625         radixMultiplier *= radix;
626     }
627
628     return number;
629 }
630
631 static double parseInt(const UString& s, int radix)
632 {
633     int length = s.size();
634     int p = 0;
635
636     while (p < length && isStrWhiteSpace(s[p].uc)) {
637         ++p;
638     }
639
640     double sign = 1;
641     if (p < length) {
642         if (s[p] == '+') {
643             ++p;
644         } else if (s[p] == '-') {
645             sign = -1;
646             ++p;
647         }
648     }
649
650     if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
651         radix = 16;
652         p += 2;
653     } else if (radix == 0) {
654         if (p < length && s[p] == '0')
655             radix = 8;
656         else
657             radix = 10;
658     }
659
660     if (radix < 2 || radix > 36)
661         return NaN;
662
663     int firstDigitPosition = p;
664     bool sawDigit = false;
665     double number = 0;
666     while (p < length) {
667         int digit = parseDigit(s[p].uc, radix);
668         if (digit == -1)
669             break;
670         sawDigit = true;
671         number *= radix;
672         number += digit;
673         ++p;
674     }
675
676     if (number >= mantissaOverflowLowerBound) {
677         if (radix == 10)
678             number = kjs_strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
679         else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
680             number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
681     }
682
683     if (!sawDigit)
684         return NaN;
685
686     return sign * number;
687 }
688
689 static double parseFloat(const UString& s)
690 {
691     // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
692     // Need to skip any whitespace and then one + or - sign.
693     int length = s.size();
694     int p = 0;
695     while (p < length && isStrWhiteSpace(s[p].uc)) {
696         ++p;
697     }
698     if (p < length && (s[p] == '+' || s[p] == '-')) {
699         ++p;
700     }
701     if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
702         return 0;
703     }
704
705     return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
706 }
707
708 JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
709 {
710   JSValue* res = jsUndefined();
711
712   static const char do_not_escape[] =
713     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
714     "abcdefghijklmnopqrstuvwxyz"
715     "0123456789"
716     "*+-./@_";
717
718   static const char do_not_escape_when_encoding_URI_component[] =
719     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
720     "abcdefghijklmnopqrstuvwxyz"
721     "0123456789"
722     "!'()*-._~";
723   static const char do_not_escape_when_encoding_URI[] =
724     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
725     "abcdefghijklmnopqrstuvwxyz"
726     "0123456789"
727     "!#$&'()*+,-./:;=?@_~";
728   static const char do_not_unescape_when_decoding_URI[] =
729     "#$&+,/:;=?@";
730
731   switch (id) {
732     case Eval: { // eval()
733       JSValue* x = args[0];
734       if (!x->isString())
735         return x;
736       else {
737         UString s = x->toString(exec);
738         
739         int sourceId;
740         int errLine;
741         UString errMsg;
742         RefPtr<ProgramNode> progNode(parser().parseProgram(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg));
743
744         Debugger* dbg = exec->dynamicGlobalObject()->debugger();
745         if (dbg) {
746           bool cont = dbg->sourceParsed(exec, sourceId, UString(), s, 0, errLine, errMsg);
747           if (!cont)
748             return jsUndefined();
749         }
750
751         // no program node means a syntax occurred
752         if (!progNode)
753           return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
754
755         bool switchGlobal = thisObj && thisObj != exec->dynamicGlobalObject() && thisObj->isGlobalObject();
756
757         // enter a new execution context
758         JSGlobalObject* globalObject = switchGlobal ? static_cast<JSGlobalObject*>(thisObj) : exec->dynamicGlobalObject();
759         JSObject* thisVal = static_cast<JSObject*>(exec->thisValue());
760         ExecState newExec(globalObject, thisVal, progNode.get(), EvalCode, exec, globalObject->currentExec());
761         if (exec->hadException())
762             newExec.setException(exec->exception());
763           
764         if (switchGlobal) {
765             newExec.pushScope(thisObj);
766             newExec.setVariableObject(thisObj);
767         }
768         
769         Completion c = progNode->execute(&newExec);
770           
771         if (switchGlobal)
772             newExec.popScope();
773
774         // if an exception occured, propogate it back to the previous execution object
775         if (newExec.hadException())
776           exec->setException(newExec.exception());
777
778         res = jsUndefined();
779         if (c.complType() == Throw)
780           exec->setException(c.value());
781         else if (c.isValueCompletion())
782             res = c.value();
783       }
784       break;
785     }
786   case ParseInt:
787     res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
788     break;
789   case ParseFloat:
790     res = jsNumber(parseFloat(args[0]->toString(exec)));
791     break;
792   case IsNaN:
793     res = jsBoolean(isnan(args[0]->toNumber(exec)));
794     break;
795   case IsFinite: {
796     double n = args[0]->toNumber(exec);
797     res = jsBoolean(!isnan(n) && !isinf(n));
798     break;
799   }
800   case DecodeURI:
801     res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
802     break;
803   case DecodeURIComponent:
804     res = decode(exec, args, "", true);
805     break;
806   case EncodeURI:
807     res = encode(exec, args, do_not_escape_when_encoding_URI);
808     break;
809   case EncodeURIComponent:
810     res = encode(exec, args, do_not_escape_when_encoding_URI_component);
811     break;
812   case Escape:
813     {
814       UString r = "", s, str = args[0]->toString(exec);
815       const UChar* c = str.data();
816       for (int k = 0; k < str.size(); k++, c++) {
817         int u = c->uc;
818         if (u > 255) {
819           char tmp[7];
820           sprintf(tmp, "%%u%04X", u);
821           s = UString(tmp);
822         } else if (u != 0 && strchr(do_not_escape, (char)u)) {
823           s = UString(c, 1);
824         } else {
825           char tmp[4];
826           sprintf(tmp, "%%%02X", u);
827           s = UString(tmp);
828         }
829         r += s;
830       }
831       res = jsString(r);
832       break;
833     }
834   case UnEscape:
835     {
836       UString s = "", str = args[0]->toString(exec);
837       int k = 0, len = str.size();
838       while (k < len) {
839         const UChar* c = str.data() + k;
840         UChar u;
841         if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
842           if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
843               Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
844           u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
845                                     (c+4)->uc, (c+5)->uc);
846           c = &u;
847           k += 5;
848           }
849         } else if (*c == UChar('%') && k <= len - 3 &&
850                    Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
851           u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
852           c = &u;
853           k += 2;
854         }
855         k++;
856         s += UString(c, 1);
857       }
858       res = jsString(s);
859       break;
860     }
861 #ifndef NDEBUG
862   case KJSPrint:
863     puts(args[0]->toString(exec).ascii());
864     break;
865 #endif
866   }
867
868   return res;
869 }
870
871 } // namespace