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

Last change on this file since 28476 was 28468, checked in by [email protected], 17 years ago

JavaScriptCore:

Reviewed by Darin Adler.

Third step in refactoring JSGlobalObject: Moved data members and
functions accessing data members from Interpreter to JSGlobalObject.
Changed Interpreter member functions to static functions.


This resolves a bug in global object bootstrapping, where the global
ExecState could be used when uninitialized.


This is a big change, but it's mostly code motion and renaming.


Layout and JS tests, and testjsglue and testapi, pass. SunSpider reports
a .7% regression, but Shark sees no difference related to this patch,
and SunSpider reported a .7% speedup from an earlier step in this
refactoring, so I think it's fair to call that a wash.

JavaScriptGlue:

Reviewed by Darin Adler.

Third step in refactoring JSGlobalObject: Moved data members and data
member access from Interpreter to JSGlobalObject. Replaced JSInterpreter
subclass with JSGlobalObject subclass.


  • JSRun.cpp: (JSRun::JSRun): (JSRun::Evaluate): (JSRun::CheckSyntax):
  • JSRun.h: (JSGlueGlobalObject::JSGlueGlobalObject):
  • JSUtils.cpp: (KJSValueToCFTypeInternal):
  • JSValueWrapper.cpp: (getThreadGlobalExecState):

WebCore:

Reviewed by Darin Adler.

Third step in refactoring JSGlobalObject: Moved data members and data
member access from Interpreter to JSGlobalObject. Changed Interpreter
member functions to static functions. Same for the subclass,
ScriptInterpreter.


This is a big change, but it's mostly code motion and renaming.

WebKit/mac:

Reviewed by Darin Adler.

Third step in refactoring JSGlobalObject: Moved data members and data
member access from Interpreter to JSGlobalObject.


  • WebView/WebFrame.mm: (-[WebFrame _attachScriptDebugger]):

WebKit/win:

Reviewed by Darin Adler.

Third step in refactoring JSGlobalObject: Moved data members and data
member access from Interpreter to JSGlobalObject.


  • WebFrame.cpp: (WebFrame::globalContext): (WebFrame::attachScriptDebugger): (WebFrame::windowObjectCleared):
  • WebScriptDebugger.cpp: (WebScriptDebugger::WebScriptDebugger):
  • Property svn:eol-style set to native
File size: 25.6 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 "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
48using namespace WTF;
49using namespace Unicode;
50
51namespace KJS {
52
53// ----------------------------- FunctionImp ----------------------------------
54
55const ClassInfo FunctionImp::info = { "Function", &InternalFunctionImp::info, 0 };
56
57FunctionImp::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
64void FunctionImp::mark()
65{
66 InternalFunctionImp::mark();
67 _scope.mark();
68}
69
70JSValue* 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
93JSValue* 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
105JSValue* 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
129JSValue* 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
135bool 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
157void 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
164bool 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 */
178Identifier 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]]
197JSObject* 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
216Completion 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
236IndexToNameMap::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
247IndexToNameMap::~IndexToNameMap() {
248 delete [] _map;
249}
250
251bool 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
268void 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
278Identifier& IndexToNameMap::operator[](int index)
279{
280 return _map[index];
281}
282
283Identifier& 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
295const ClassInfo Arguments::info = { "Arguments", 0, 0 };
296
297// ECMA 10.1.8
298Arguments::Arguments(ExecState* exec, FunctionImp* func, const List& args, ActivationImp* act)
299: JSObject(exec->lexicalGlobalObject()->objectPrototype()),
300_activationObject(act),
301indexToNameMap(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
315void Arguments::mark()
316{
317 JSObject::mark();
318 if (_activationObject && !_activationObject->marked())
319 _activationObject->mark();
320}
321
322JSValue* 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
328bool 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
338void 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
347bool 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
359const ClassInfo ActivationImp::info = { "Activation", 0, 0 };
360
361ActivationImp::ActivationImp(ExecState* exec)
362 : d(new ActivationImpPrivate(exec))
363 , m_symbolTable(&exec->function()->body->symbolTable())
364{
365}
366
367JSValue* 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
378PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
379{
380 return ActivationImp::argumentsGetter;
381}
382
383bool 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
413bool 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
424void 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
433void 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
452void 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
471void 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
482GlobalFuncImp::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
489static 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
509static 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
574static 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
592static 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
609double 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
631static 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
689static 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
708JSValue* 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
Note: See TracBrowser for help on using the repository browser.