fix logic
[personal-kdelibs.git] / kjs / function.cpp
blob0066b18ee5db401fb29fa797feb182090c968140
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 * Copyright (C) 2007 Maksim Orlovich (maksim@kde.org)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 #include "function.h"
28 #include <config.h>
29 #include "scriptfunction.h"
30 #include "dtoa.h"
31 #include "internal.h"
32 #include "function_object.h"
33 #include "lexer.h"
34 #include "nodes.h"
35 #include "operations.h"
36 #include "debugger.h"
37 #include "PropertyNameArray.h"
39 #include <stdio.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <assert.h>
43 #include <string.h>
44 #include "wtf/DisallowCType.h"
45 #include "wtf/ASCIICType.h"
46 #include "bytecode/machine.h"
48 using namespace WTF;
50 namespace KJS {
52 // ----------------------------- FunctionImp ----------------------------------
54 const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
56 FunctionImp::FunctionImp(ExecState* exec, const Identifier& n, FunctionBodyNode* b, const ScopeChain& sc)
57 : InternalFunctionImp(static_cast<FunctionPrototype*>
58 (exec->lexicalInterpreter()->builtinFunctionPrototype()), n)
59 , body(b)
60 , _scope(sc)
64 void FunctionImp::mark()
66 InternalFunctionImp::mark();
67 _scope.mark();
70 FunctionImp::~FunctionImp()
74 void FunctionImp::initialCompile(ExecState* newExec)
76 FunctionBodyNode* body = this->body.get();
78 // Reserve various slots needed for the activation object. We do it only once,
79 // --- isCompiled() would return true even if debugging state changed
80 body->reserveSlot(ActivationImp::LengthSlot, false);
81 body->reserveSlot(ActivationImp::TearOffNeeded, false);
82 body->reserveSlot(ActivationImp::ScopeLink, false /* will mark via ScopeChain::mark() */);
83 body->reserveSlot(ActivationImp::FunctionSlot, true);
84 body->reserveSlot(ActivationImp::ArgumentsObjectSlot, true);
86 // Create declarations for parameters, and allocate the symbols.
87 // We always just give them sequential positions, to make passInParameters
88 // simple (though perhaps wasting memory in the trivial case)
89 for (size_t i = 0; i < body->numParams(); ++i)
90 body->addSymbolOverwriteID(i + ActivationImp::NumReservedSlots, body->paramName(i), DontDelete);
92 body->processDecls(newExec);
93 body->compile(FunctionCode, newExec->dynamicInterpreter()->debugger() ? Debug : Release);
96 JSValue* FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
98 assert(thisObj);
100 Debugger* dbg = exec->dynamicInterpreter()->debugger();
102 // enter a new execution context
103 FunctionExecState newExec(exec->dynamicInterpreter(), thisObj, body.get(), exec, this);
104 if (exec->hadException())
105 newExec.setException(exec->exception());
107 FunctionBodyNode* body = this->body.get();
109 // The first time we're called, compute the set of local variables,
110 // and compile the body. (note that parameters have been collected
111 // during the AST build)
112 CompileType currentState = body->compileState();
113 if (currentState == NotCompiled) {
114 initialCompile(&newExec);
115 } else {
116 // Otherwise, we may still need to recompile due to debug...
117 CompileType desiredState = dbg ? Debug : Release;
118 if (desiredState != currentState)
119 body->compile(FunctionCode, desiredState);
122 size_t stackSize = 0;
123 LocalStorageEntry* stackSpace = 0;
125 // We always allocate on stack initially, and tearoff only after we're done.
126 int regs = body->numLocalsAndRegisters();
127 stackSize = sizeof(LocalStorageEntry) * regs;
128 stackSpace = (LocalStorageEntry*)exec->dynamicInterpreter()->stackAlloc(stackSize);
130 ActivationImp* activation = static_cast<ActivationImp*>(newExec.activationObject());
131 activation->setup(&newExec, this, &args, stackSpace);
132 activation->tearOffNeededSlot() = body->tearOffAtEnd();
134 if (dbg) {
135 bool cont = dbg->enterContext(&newExec, body->sourceId(), body->firstLine(), this, args);
136 if (!cont) {
137 dbg->imp()->abort();
138 return jsUndefined();
142 newExec.initLocalStorage(stackSpace, regs);
144 JSValue* result = Machine::runBlock(&newExec, body->code(), exec);
146 // If we need to tear off now --- either due to static flag above, or
147 // if execution requested it dynamically --- do so now.
148 if (activation->tearOffNeededSlot()) {
149 activation->performTearOff();
150 } else {
151 // Otherwise, we recycle the activation object; we must clear its
152 // data pointer, though, since that may become dead.
153 // (we also unlink it from the scope chain at this time)
154 activation->scopeLink().deref();
155 activation->localStorage = 0;
156 exec->dynamicInterpreter()->recycleActivation(activation);
159 // Now free the stack space..
160 exec->dynamicInterpreter()->stackFree(stackSize);
162 #ifdef KJS_VERBOSE
163 if (exec->exception())
164 printInfo(exec,"throwing", exec->exception());
165 else
166 printInfo(exec,"returning", result);
167 #endif
169 // The debugger may have been deallocated by now if the WebFrame
170 // we were running in has been destroyed, so refetch it.
171 // See http://bugs.webkit.org/show_bug.cgi?id=9477
172 dbg = exec->dynamicInterpreter()->debugger();
174 if (dbg) {
175 int cont = dbg->exitContext(&newExec, body->sourceId(), body->lastLine(), this);
176 if (!cont) {
177 dbg->imp()->abort();
178 return jsUndefined();
182 return result;
185 JSValue *FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
187 FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
188 ExecState *context = exec;
189 while (context) {
190 if (context->function() == thisObj) {
191 return static_cast<ActivationImp *>(context->activationObject())->get(exec, propertyName);
193 context = context->callingExecState();
195 return jsNull();
198 JSValue *FunctionImp::callerGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
200 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
201 ExecState* context = exec;
202 while (context) {
203 if (context->function() == thisObj)
204 break;
205 context = context->callingExecState();
208 if (!context)
209 return jsNull();
211 ExecState* callingContext = context->callingExecState();
212 if (!callingContext)
213 return jsNull();
215 FunctionImp* callingFunction = callingContext->function();
216 if (!callingFunction)
217 return jsNull();
219 return callingFunction;
222 JSValue *FunctionImp::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
224 FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
225 return jsNumber(thisObj->body->numParams());
228 JSValue* FunctionImp::nameGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
230 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
231 return jsString(thisObj->functionName().ustring());
234 bool FunctionImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
236 // Find the arguments from the closest context.
237 if (propertyName == exec->propertyNames().arguments) {
238 slot.setCustom(this, argumentsGetter);
239 return true;
242 // Compute length of parameters.
243 if (propertyName == exec->propertyNames().length) {
244 slot.setCustom(this, lengthGetter);
245 return true;
248 // Calling function (Mozilla-extension)
249 if (propertyName == exec->propertyNames().caller) {
250 slot.setCustom(this, callerGetter);
251 return true;
254 // Function name (Mozilla-extension)
255 if (propertyName == exec->propertyNames().name) {
256 slot.setCustom(this, nameGetter);
257 return true;
260 return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
263 void FunctionImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
265 if (propertyName == exec->propertyNames().arguments ||
266 propertyName == exec->propertyNames().length ||
267 propertyName == exec->propertyNames().name)
268 return;
269 InternalFunctionImp::put(exec, propertyName, value, attr);
272 bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
274 if (propertyName == exec->propertyNames().arguments ||
275 propertyName == exec->propertyNames().length ||
276 propertyName == exec->propertyNames().name)
277 return false;
278 return InternalFunctionImp::deleteProperty(exec, propertyName);
281 /* Returns the parameter name corresponding to the given index. eg:
282 * function f1(x, y, z): getParameterName(0) --> x
284 * If a name appears more than once, only the last index at which
285 * it appears associates with it. eg:
286 * function f2(x, x): getParameterName(0) --> null
288 Identifier FunctionImp::getParameterName(int index)
290 if (index >= body->numParams())
291 return CommonIdentifiers::shared()->nullIdentifier;
293 Identifier name = body->paramName(index);
295 // Are there any subsequent parameters with the same name?
296 for (int pos = index + 1; pos < body->numParams(); ++pos)
297 if (body->paramName(pos) == name)
298 return CommonIdentifiers::shared()->nullIdentifier;
300 return name;
303 bool FunctionImp::implementsConstruct() const
305 return true;
308 // ECMA 13.2.2 [[Construct]]
309 JSObject *FunctionImp::construct(ExecState *exec, const List &args)
311 JSObject *proto;
312 JSValue *p = get(exec, exec->propertyNames().prototype);
313 if (p->isObject())
314 proto = static_cast<JSObject*>(p);
315 else
316 proto = exec->lexicalInterpreter()->builtinObjectPrototype();
318 JSObject *obj(new JSObject(proto));
320 JSValue *res = call(exec,obj,args);
322 if (res->isObject())
323 return static_cast<JSObject *>(res);
324 else
325 return obj;
328 // ------------------------------ IndexToNameMap ---------------------------------
330 // We map indexes in the arguments array to their corresponding argument names.
331 // Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x").
333 // Once we have an argument name, we can get and set the argument's value in the
334 // activation object.
336 // We use Identifier::null to indicate that a given argument's value
337 // isn't stored in the activation object.
339 IndexToNameMap::IndexToNameMap(FunctionImp *func, const List &args)
341 _map = new Identifier[args.size()];
342 this->size = args.size();
344 int i = 0;
345 ListIterator iterator = args.begin();
346 for (; iterator != args.end(); i++, iterator++)
347 _map[i] = func->getParameterName(i); // null if there is no corresponding parameter
350 IndexToNameMap::~IndexToNameMap() {
351 delete [] _map;
354 bool IndexToNameMap::isMapped(const Identifier &index) const
356 bool indexIsNumber;
357 int indexAsNumber = index.toUInt32(&indexIsNumber);
359 if (!indexIsNumber)
360 return false;
362 if (indexAsNumber >= size)
363 return false;
365 if (_map[indexAsNumber].isNull())
366 return false;
368 return true;
371 void IndexToNameMap::unMap(const Identifier &index)
373 bool indexIsNumber;
374 int indexAsNumber = index.toUInt32(&indexIsNumber);
376 assert(indexIsNumber && indexAsNumber < size);
378 _map[indexAsNumber] = CommonIdentifiers::shared()->nullIdentifier;;
381 Identifier& IndexToNameMap::operator[](int index)
383 return _map[index];
386 Identifier& IndexToNameMap::operator[](const Identifier &index)
388 bool indexIsNumber;
389 int indexAsNumber = index.toUInt32(&indexIsNumber);
391 assert(indexIsNumber && indexAsNumber < size);
393 return (*this)[indexAsNumber];
396 // ------------------------------ Arguments ---------------------------------
398 const ClassInfo Arguments::info = {"Arguments", 0, 0, 0};
400 // ECMA 10.1.8
401 Arguments::Arguments(ExecState *exec, FunctionImp *func, const List &args, ActivationImp *act)
402 : JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()),
403 _activationObject(act),
404 indexToNameMap(func, args)
406 putDirect(exec->propertyNames().callee, func, DontEnum);
407 putDirect(exec->propertyNames().length, args.size(), DontEnum);
409 int i = 0;
410 ListIterator iterator = args.begin();
411 for (; iterator != args.end(); i++, iterator++) {
412 if (!indexToNameMap.isMapped(Identifier::from(i))) {
413 JSObject::put(exec, Identifier::from(i), *iterator, DontEnum);
418 void Arguments::mark()
420 JSObject::mark();
421 if (_activationObject && !_activationObject->marked())
422 _activationObject->mark();
425 JSValue *Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
427 Arguments *thisObj = static_cast<Arguments *>(slot.slotBase());
428 return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
431 bool Arguments::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
433 if (indexToNameMap.isMapped(propertyName)) {
434 slot.setCustom(this, mappedIndexGetter);
435 return true;
438 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
441 void Arguments::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
443 if (indexToNameMap.isMapped(propertyName)) {
444 _activationObject->put(exec, indexToNameMap[propertyName], value, attr);
445 } else {
446 JSObject::put(exec, propertyName, value, attr);
450 bool Arguments::deleteProperty(ExecState *exec, const Identifier &propertyName)
452 if (indexToNameMap.isMapped(propertyName)) {
453 indexToNameMap.unMap(propertyName);
454 return true;
455 } else {
456 return JSObject::deleteProperty(exec, propertyName);
460 // ------------------------------ ActivationImp --------------------------------
462 const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
464 // ECMA 10.1.6
465 void ActivationImp::setup(ExecState* exec, FunctionImp *function,
466 const List* arguments, LocalStorageEntry* entries)
468 FunctionBodyNode* body = function->body.get();
470 size_t total = body->numLocalsAndRegisters();
471 localStorage = entries;
472 lengthSlot() = total;
474 // we can now link ourselves into the scope, which will also fix up our scopeLink().
475 exec->pushVariableObjectScope(this);
477 const FunctionBodyNode::SymbolInfo* symInfo = body->getLocalInfo();
479 // Setup our fields
480 this->arguments = arguments;
481 functionSlot() = function;
482 argumentsObjectSlot() = jsUndefined();
483 symbolTable = &body->symbolTable();
485 // Set the mark/don't mark flags and attributes for everything
486 for (size_t p = 0; p < total; ++p)
487 entries[p].attributes = symInfo[p].attr;
489 // Pass in the parameters (ECMA 10.1.3q)
490 #ifdef KJS_VERBOSE
491 fprintf(stderr, "---------------------------------------------------\n"
492 "processing parameters for %s call\n",
493 function->functionName().isEmpty() ? "(internal)" : function->functionName().ascii());
494 #endif
495 size_t numParams = body->numParams();
496 for (size_t pos = 0; pos < numParams; ++pos) {
497 size_t symNum = pos + ActivationImp::NumReservedSlots;
498 JSValue* v = (*arguments)[pos];
500 entries[symNum].val.valueVal = v;
502 #ifdef KJS_VERBOSE
503 fprintf(stderr, "setting parameter %s", body->paramName(pos).ascii());
504 printInfo(exec, "to", v);
505 #endif
508 // Initialize the rest of the locals to 'undefined'
509 for (size_t pos = numParams + ActivationImp::NumReservedSlots; pos < total; ++pos)
510 entries[pos].val.valueVal = jsUndefined();
512 // Finally, put in the functions. Note that this relies on above
513 // steps to have completed, since it can trigger a GC.
514 size_t numFuns = body->numFunctionLocals();
515 size_t* funsData = body->getFunctionLocalInfo();
516 for (size_t fun = 0; fun < numFuns; ++fun) {
517 size_t id = funsData[fun];
518 entries[id].val.valueVal = symInfo[id].funcDecl->makeFunctionObject(exec);
522 void ActivationImp::performTearOff()
524 // Create a new local array, copy stuff over
525 size_t total = lengthSlot();
526 LocalStorageEntry* entries = new LocalStorageEntry[total];
527 std::memcpy(entries, localStorage, total*sizeof(LocalStorageEntry));
528 localStorage = entries;
531 void ActivationImp::requestTearOff()
533 tearOffNeededSlot() = true;
536 JSValue *ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
538 ActivationImp* thisObj = static_cast<ActivationImp*>(slot.slotBase());
540 if (thisObj->argumentsObjectSlot() == jsUndefined())
541 thisObj->createArgumentsObject(exec);
543 return thisObj->argumentsObjectSlot();
547 PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
549 return ActivationImp::argumentsGetter;
552 bool ActivationImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
554 if (symbolTableGet(propertyName, slot))
555 return true;
557 if (JSValue** location = getDirectLocation(propertyName)) {
558 slot.setValueSlot(this, location);
559 return true;
562 // Only return the built-in arguments object if it wasn't overridden above.
563 if (propertyName == exec->propertyNames().arguments) {
564 slot.setCustom(this, getArgumentsGetter());
565 return true;
568 // We don't call through to JSObject because there's no way to give an
569 // activation object getter properties or a prototype.
570 ASSERT(!_prop.hasGetterSetterProperties());
571 ASSERT(prototype() == jsNull());
572 return false;
575 bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
577 if (propertyName == exec->propertyNames().arguments)
578 return false;
580 return JSVariableObject::deleteProperty(exec, propertyName);
583 void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value, int attr)
585 // If any bits other than DontDelete are set, then we bypass the read-only check.
586 bool checkReadOnly = !(attr & ~DontDelete);
587 if (symbolTablePut(propertyName, value, checkReadOnly))
588 return;
590 // We don't call through to JSObject because __proto__ and getter/setter
591 // properties are non-standard extensions that other implementations do not
592 // expose in the activation object.
593 ASSERT(!_prop.hasGetterSetterProperties());
594 _prop.put(propertyName, value, attr, checkReadOnly);
597 void ActivationImp::createArgumentsObject(ExecState *exec)
599 requestTearOff();
600 argumentsObjectSlot() = new Arguments(exec, static_cast<FunctionImp*>(functionSlot()),
601 *arguments, const_cast<ActivationImp*>(this));
604 // ------------------------------ GlobalFunc -----------------------------------
607 GlobalFuncImp::GlobalFuncImp(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
608 : InternalFunctionImp(funcProto, name)
609 , id(i)
611 putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum);
614 static JSValue *encode(ExecState *exec, const List &args, const char *do_not_escape)
616 UString r = "", s, str = args[0]->toString(exec);
617 CString cstr = str.UTF8String();
618 const char *p = cstr.c_str();
619 for (size_t k = 0; k < cstr.size(); k++, p++) {
620 char c = *p;
621 if (c && strchr(do_not_escape, c)) {
622 r.append(c);
623 } else {
624 char tmp[4];
625 sprintf(tmp, "%%%02X", (unsigned char)c);
626 r += tmp;
629 return jsString(r);
632 static JSValue *decode(ExecState *exec, const List &args, const char *do_not_unescape)
634 UString s = "", str = args[0]->toString(exec);
635 int k = 0, len = str.size();
636 const UChar *d = str.data();
637 UChar u;
638 while (k < len) {
639 const UChar *p = d + k;
640 UChar c = *p;
641 if (c == '%') {
642 int charLen = 0;
643 if (k <= len - 3 && isASCIIHexDigit(p[1].uc) && isASCIIHexDigit(p[2].uc)) {
644 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
645 const int sequenceLen = UTF8SequenceLength(b0);
646 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
647 charLen = sequenceLen * 3;
648 char sequence[5];
649 sequence[0] = b0;
650 for (int i = 1; i < sequenceLen; ++i) {
651 const UChar *q = p + i * 3;
652 if (q[0] == '%' && isASCIIHexDigit(q[1].uc) && isASCIIHexDigit(q[2].uc))
653 sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
654 else {
655 charLen = 0;
656 break;
659 if (charLen != 0) {
660 sequence[sequenceLen] = 0;
661 const int character = decodeUTF8Sequence(sequence);
662 if (character < 0 || character >= 0x110000) {
663 charLen = 0;
664 } else if (character >= 0x10000) {
665 // Convert to surrogate pair.
666 s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
667 u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
668 } else {
669 u = static_cast<unsigned short>(character);
674 if (charLen == 0)
675 return throwError(exec, URIError);
676 if (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low())) {
677 c = u;
678 k += charLen - 1;
681 k++;
682 s.append(c);
684 return jsString(s);
687 static bool isStrWhiteSpace(unsigned short c)
689 switch (c) {
690 case 0x0009:
691 case 0x000A:
692 case 0x000B:
693 case 0x000C:
694 case 0x000D:
695 case 0x2028:
696 case 0x2029:
697 // Unicode category Zs
698 case 0x0020: // SPACE
699 case 0x00A0: // NO-BREAK SPACE
700 case 0x1680: // OGHAM SPACE MARK
701 case 0x180E: // MONGOLIAN VOWEL SEPARATOR
702 case 0x2000: // EN QUAD
703 case 0x2001: // EM QUAD
704 case 0x2002: // EN SPACE
705 case 0x2003: // EM SPACE
706 case 0x2004: // THREE-PER-EM SPACE
707 case 0x2005: // FOUR-PER-EM SPACE
708 case 0x2006: // SIX-PER-EM SPACE
709 case 0x2007: // FIGURE SPACE
710 case 0x2008: // PUNCTUATION SPACE
711 case 0x2009: // THIN SPACE
712 case 0x200A: // HAIR SPACE
713 case 0x202F: // NARROW NO-BREAK SPACE
714 case 0x205F: // MEDIUM MATHEMATICAL SPACE
715 case 0x3000: // IDEOGRAPHIC SPACE
716 return true;
717 default:
718 return false;
722 static int parseDigit(unsigned short c, int radix)
724 int digit = -1;
726 if (c >= '0' && c <= '9') {
727 digit = c - '0';
728 } else if (c >= 'A' && c <= 'Z') {
729 digit = c - 'A' + 10;
730 } else if (c >= 'a' && c <= 'z') {
731 digit = c - 'a' + 10;
734 if (digit >= radix)
735 return -1;
736 return digit;
739 double parseIntOverflow(const char* s, int length, int radix)
741 double number = 0.0;
742 double radixMultiplier = 1.0;
744 for (const char* p = s + length - 1; p >= s; p--) {
745 if (radixMultiplier == Inf) {
746 if (*p != '0') {
747 number = Inf;
748 break;
750 } else {
751 int digit = parseDigit(*p, radix);
752 number += digit * radixMultiplier;
755 radixMultiplier *= radix;
758 return number;
761 static double parseInt(const UString &s, int radix)
763 int length = s.size();
764 int p = 0;
766 while (p < length && isStrWhiteSpace(s[p].uc)) {
767 ++p;
770 double sign = 1;
771 if (p < length) {
772 if (s[p] == '+') {
773 ++p;
774 } else if (s[p] == '-') {
775 sign = -1;
776 ++p;
780 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
781 radix = 16;
782 p += 2;
783 } else if (radix == 0) {
784 if (p < length && s[p] == '0')
785 radix = 8;
786 else
787 radix = 10;
790 if (radix < 2 || radix > 36)
791 return NaN;
793 int firstDigitPosition = p;
794 bool sawDigit = false;
795 double number = 0;
796 while (p < length) {
797 int digit = parseDigit(s[p].uc, radix);
798 if (digit == -1)
799 break;
800 sawDigit = true;
801 number *= radix;
802 number += digit;
803 ++p;
806 if (number >= mantissaOverflowLowerBound) {
807 if (radix == 10)
808 number = kjs_strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
809 else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
810 number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
813 if (!sawDigit)
814 return NaN;
816 return sign * number;
819 static double parseFloat(const UString &s)
821 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
822 // Need to skip any whitespace and then one + or - sign.
823 int length = s.size();
824 int p = 0;
825 while (p < length && isStrWhiteSpace(s[p].uc)) {
826 ++p;
828 if (p < length && (s[p] == '+' || s[p] == '-')) {
829 ++p;
831 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
832 return 0;
835 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
838 JSValue *GlobalFuncImp::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &args)
840 JSValue *res = jsUndefined();
842 static const char do_not_escape[] =
843 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
844 "abcdefghijklmnopqrstuvwxyz"
845 "0123456789"
846 "*+-./@_";
848 static const char do_not_escape_when_encoding_URI_component[] =
849 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
850 "abcdefghijklmnopqrstuvwxyz"
851 "0123456789"
852 "!'()*-._~";
853 static const char do_not_escape_when_encoding_URI[] =
854 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
855 "abcdefghijklmnopqrstuvwxyz"
856 "0123456789"
857 "!#$&'()*+,-./:;=?@_~";
858 static const char do_not_unescape_when_decoding_URI[] =
859 "#$&+,/:;=?@";
861 switch (id) {
862 case Eval: { // eval()
863 JSValue *x = args[0];
864 if (!x->isString())
865 return x;
866 else {
867 UString s = x->toString(exec);
869 int sourceId;
870 int errLine;
871 UString errMsg;
872 RefPtr<ProgramNode> progNode(parser().parseProgram(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg));
874 Debugger *dbg = exec->dynamicInterpreter()->debugger();
875 if (dbg) {
876 dbg->reportSourceParsed(exec, progNode.get(), s, 0, errLine, errMsg);
879 // no program node means a syntax occurred
880 if (!progNode)
881 return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
883 // If the variable object we're working with is an activation, we better
884 // tear it off since stuff inside eval can capture it in a closure
885 if (exec->variableObject()->isActivation())
886 static_cast<ActivationImp*>(exec->variableObject())->requestTearOff();
888 // enter a new execution context
889 EvalExecState newExec(exec->dynamicInterpreter(),
890 exec->dynamicInterpreter()->globalObject(),
891 progNode.get(),
892 exec);
894 if (exec->hadException())
895 newExec.setException(exec->exception());
897 if (dbg) {
898 bool cont = dbg->enterContext(&newExec, sourceId, 0, 0, List::empty());
899 if (!cont) {
900 dbg->imp()->abort();
901 return jsUndefined();
905 // execute the code
906 progNode->processDecls(&newExec);
907 Completion c = progNode->execute(&newExec);
909 dbg = exec->dynamicInterpreter()->debugger();
910 if (dbg) {
911 bool cont = dbg->exitContext(&newExec, sourceId, 0, 0);
912 if (!cont) {
913 dbg->imp()->abort();
914 return jsUndefined();
918 // if an exception occurred, propagate it back to the previous execution object
919 if (newExec.hadException())
920 exec->setException(newExec.exception());
922 res = jsUndefined();
923 if (c.complType() == Throw)
924 exec->setException(c.value());
925 else if (c.isValueCompletion())
926 res = c.value();
928 break;
930 case ParseInt:
931 res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
932 break;
933 case ParseFloat:
934 res = jsNumber(parseFloat(args[0]->toString(exec)));
935 break;
936 case IsNaN:
937 res = jsBoolean(isNaN(args[0]->toNumber(exec)));
938 break;
939 case IsFinite: {
940 double n = args[0]->toNumber(exec);
941 res = jsBoolean(!isNaN(n) && !isInf(n));
942 break;
944 case DecodeURI:
945 res = decode(exec, args, do_not_unescape_when_decoding_URI);
946 break;
947 case DecodeURIComponent:
948 res = decode(exec, args, "");
949 break;
950 case EncodeURI:
951 res = encode(exec, args, do_not_escape_when_encoding_URI);
952 break;
953 case EncodeURIComponent:
954 res = encode(exec, args, do_not_escape_when_encoding_URI_component);
955 break;
956 case Escape:
958 UString r = "", s, str = args[0]->toString(exec);
959 const UChar* c = str.data();
960 for (int k = 0; k < str.size(); k++, c++) {
961 int u = c->uc;
962 if (u > 255) {
963 char tmp[7];
964 sprintf(tmp, "%%u%04X", u);
965 s = UString(tmp);
966 } else if (u != 0 && strchr(do_not_escape, (char)u)) {
967 s = UString(c, 1);
968 } else {
969 char tmp[4];
970 sprintf(tmp, "%%%02X", u);
971 s = UString(tmp);
973 r += s;
975 res = jsString(r);
976 break;
978 case UnEscape:
980 UString s = "", str = args[0]->toString(exec);
981 int k = 0, len = str.size();
982 while (k < len) {
983 const UChar* c = str.data() + k;
984 UChar u;
985 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
986 if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
987 Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
988 u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
989 (c+4)->uc, (c+5)->uc);
990 c = &u;
991 k += 5;
993 } else if (*c == UChar('%') && k <= len - 3 &&
994 Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
995 u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
996 c = &u;
997 k += 2;
999 k++;
1000 s += UString(c, 1);
1002 res = jsString(s);
1003 break;
1005 #ifndef NDEBUG
1006 case KJSPrint:
1007 puts(args[0]->toString(exec).ascii());
1008 break;
1009 #endif
1012 return res;
1015 } // namespace