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

Last change on this file since 27359 was 27359, checked in by ggaren, 18 years ago

JavaScriptCore:

Reviewed by Maciej Stachowiak.

Fixed http://bugs.webkit.org/show_bug.cgi?id=15785
REGRESSION(r27344): Crash on load at finance.yahoo.com


Reverted a small portion of my last check-in. (The speedup and the List
removal are still there, though.)


ActivationImp needs to hold a pointer to its function, and mark that
pointer (rather than accessing its function through its ExecState, and
counting on the active scope to mark its function) because a closure
can cause an ActivationImp to outlive its ExecState along with any
active scope.

  • kjs/ExecState.cpp: (KJS::ExecState::ExecState):
  • kjs/function.cpp: (KJS::FunctionImp::~FunctionImp): (KJS::ActivationImp::ActivationImp):
  • kjs/function.h: (KJS::ActivationImp::ActivationImpPrivate::ActivationImpPrivate):

Also made HashTable a little more crash-happy in debug builds, so
problems like this will show up earlier:


  • wtf/HashTable.h: (WTF::HashTable::~HashTable):

LayoutTests:

Reviewed by Maciej Stachowiak.


Layout test for http://bugs.webkit.org/show_bug.cgi?id=15785
REGRESSION(r27344): Crash on load at finance.yahoo.com

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