source: webkit/trunk/JavaScriptCore/kjs/function.cpp@ 14893

Last change on this file since 14893 was 14893, checked in by andersca, 19 years ago

2006-06-17 Anders Carlsson <[email protected]>

Reviewed by Maciej and Geoff.

http://bugzilla.opendarwin.org/show_bug.cgi?id=7080
Provide some way to stop a JavaScript infinite loop


  • kjs/completion.h: (KJS::): Add Interrupted completion type.


  • kjs/function.cpp: (KJS::FunctionImp::callAsFunction): (KJS::GlobalFuncImp::callAsFunction): Only set the exception on the new ExecState if the current one has had one.


  • kjs/interpreter.cpp: (KJS::TimeoutChecker::startTimeoutCheck): (KJS::TimeoutChecker::stopTimeoutCheck): (KJS::TimeoutChecker::alarmHandler): (KJS::TimeoutChecker::pauseTimeoutCheck): (KJS::TimeoutChecker::resumeTimeoutCheck): New TimeoutChecker class which handles setting Interpreter::m_timedOut flag after a given period of time. This currently only works on Unix platforms where setitimer and signals are used.


(KJS::Interpreter::Interpreter):
Initialize new member variables.


(KJS::Interpreter::~Interpreter):
Destroy the timeout checker.


(KJS::Interpreter::startTimeoutCheck):
(KJS::Interpreter::stopTimeoutCheck):
(KJS::Interpreter::pauseTimeoutCheck):
(KJS::Interpreter::resumeTimeoutCheck):
Call the timeout checker.


(KJS::Interpreter::handleTimeout):
Called on timeout. Resets the m_timedOut flag and calls shouldInterruptScript.


  • kjs/interpreter.h: (KJS::Interpreter::setTimeoutTime): New function for setting the timeout time.


(KJS::Interpreter::shouldInterruptScript):
New function. The idea is that this should be overridden by subclasses in order to for example
pop up a dialog asking the user if the script should be interrupted.


(KJS::Interpreter::checkTimeout):
New function which checks the m_timedOut flag and calls handleTimeout if it's set.


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