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

Last change on this file since 27842 was 27842, checked in by [email protected], 18 years ago

Reviewed by Eric Seidel.

Another round of grammar / parsing cleanup.


  1. Created distinct parser calls for parsing function bodies vs programs. This will help later with optimizing global variable access.


  1. Turned Parser into a singleton. Cleaned up Lexer's singleton interface.


  1. Modified Lexer to free a little more memory when done lexing. (Added FIXMEs for similar issues that I didn't fix.)


  1. Changed Lexer::makeIdentifier and Lexer::makeUString to start respecting the arguments passed to them. (No behavior change, but this problem could have caused serious problems for an unsuspecting user of these functions.)


  1. Removed KJS_DEBUG_MEM because it was bit-rotted.


  1. Removed Parser::prettyPrint because the same work was simpler to do at the call site.


  1. Some renames:


"Parser::accept" => "Parser::didFinishParsing"
"Parser::sid" => "Parser::m_sourceID"
"Lexer::doneParsing" => "Lexer::clear"
"sid" => "sourceId"
"lineno" => "lineNo"


  • JavaScriptCore.exp:
  • kjs/Parser.cpp: (KJS::Parser::Parser): (KJS::Parser::parseProgram): (KJS::Parser::parseFunctionBody): (KJS::Parser::parse): (KJS::Parser::didFinishParsing): (KJS::parser):
  • kjs/Parser.h: (KJS::Parser::sourceId):
  • kjs/function.cpp: (KJS::GlobalFuncImp::callAsFunction):
  • kjs/function_object.cpp: (FunctionObjectImp::construct):
  • kjs/grammar.y:
  • kjs/interpreter.cpp: (KJS::Interpreter::checkSyntax): (KJS::Interpreter::evaluate):
  • kjs/interpreter.h:
  • kjs/lexer.cpp: (kjsyylex): (KJS::lexer): (KJS::Lexer::Lexer): (KJS::Lexer::~Lexer): (KJS::Lexer::scanRegExp): (KJS::Lexer::doneParsing): (KJS::Lexer::makeIdentifier): (KJS::Lexer::makeUString):
  • kjs/lexer.h: (KJS::Lexer::pattern): (KJS::Lexer::flags): (KJS::Lexer::sawError):
  • kjs/nodes.cpp: (KJS::Node::Node): (KJS::FunctionBodyNode::FunctionBodyNode):
  • kjs/nodes.h:
  • kjs/testkjs.cpp: (prettyPrintScript): (kjsmain):
  • kjs/ustring.cpp:
  • kjs/ustring.h:
  • Property svn:eol-style set to native
File size: 26.2 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/UTF8.h>
46
47using namespace WTF;
48using namespace Unicode;
49
50namespace KJS {
51
52// ----------------------------- FunctionImp ----------------------------------
53
54const ClassInfo FunctionImp::info = { "Function", &InternalFunctionImp::info, 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 sourceId = -1;
81 int lineNo = -1;
82 if (dbg) {
83 sourceId = body->sourceId();
84 lineNo = body->firstLine();
85
86 bool cont = dbg->callEvent(&newExec, sourceId, 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, sourceId, 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 List::const_iterator end = args.end();
277 for (List::const_iterator it = args.begin(); it != end; ++i, ++it)
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 };
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 List::const_iterator end = args.end();
342 for (List::const_iterator it = args.begin(); it != end; ++it, ++i) {
343 if (!indexToNameMap.isMapped(Identifier::from(i))) {
344 JSObject::put(exec, Identifier::from(i), *it, 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 };
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(true);
518 if (!cstr.c_str())
519 return throwError(exec, URIError, "String contained an illegal UTF-16 sequence.");
520 const char* p = cstr.c_str();
521 for (size_t k = 0; k < cstr.size(); k++, p++) {
522 char c = *p;
523 if (c && strchr(do_not_escape, c)) {
524 r.append(c);
525 } else {
526 char tmp[4];
527 sprintf(tmp, "%%%02X", (unsigned char)c);
528 r += tmp;
529 }
530 }
531 return jsString(r);
532}
533
534static JSValue* decode(ExecState* exec, const List& args, const char* do_not_unescape, bool strict)
535{
536 UString s = "", str = args[0]->toString(exec);
537 int k = 0, len = str.size();
538 const UChar* d = str.data();
539 UChar u;
540 while (k < len) {
541 const UChar* p = d + k;
542 UChar c = *p;
543 if (c == '%') {
544 int charLen = 0;
545 if (k <= len - 3 && isASCIIHexDigit(p[1].uc) && isASCIIHexDigit(p[2].uc)) {
546 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
547 const int sequenceLen = UTF8SequenceLength(b0);
548 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
549 charLen = sequenceLen * 3;
550 char sequence[5];
551 sequence[0] = b0;
552 for (int i = 1; i < sequenceLen; ++i) {
553 const UChar* q = p + i * 3;
554 if (q[0] == '%' && isASCIIHexDigit(q[1].uc) && isASCIIHexDigit(q[2].uc))
555 sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
556 else {
557 charLen = 0;
558 break;
559 }
560 }
561 if (charLen != 0) {
562 sequence[sequenceLen] = 0;
563 const int character = decodeUTF8Sequence(sequence);
564 if (character < 0 || character >= 0x110000) {
565 charLen = 0;
566 } else if (character >= 0x10000) {
567 // Convert to surrogate pair.
568 s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
569 u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
570 } else {
571 u = static_cast<unsigned short>(character);
572 }
573 }
574 }
575 }
576 if (charLen == 0) {
577 if (strict)
578 return throwError(exec, URIError);
579 // The only case where we don't use "strict" mode is the "unescape" function.
580 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
581 if (k <= len - 6 && p[1] == 'u'
582 && isASCIIHexDigit(p[2].uc) && isASCIIHexDigit(p[3].uc)
583 && isASCIIHexDigit(p[4].uc) && isASCIIHexDigit(p[5].uc)) {
584 charLen = 6;
585 u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
586 }
587 }
588 if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
589 c = u;
590 k += charLen - 1;
591 }
592 }
593 k++;
594 s.append(c);
595 }
596 return jsString(s);
597}
598
599static bool isStrWhiteSpace(unsigned short c)
600{
601 switch (c) {
602 case 0x0009:
603 case 0x000A:
604 case 0x000B:
605 case 0x000C:
606 case 0x000D:
607 case 0x0020:
608 case 0x00A0:
609 case 0x2028:
610 case 0x2029:
611 return true;
612 default:
613 return isSeparatorSpace(c);
614 }
615}
616
617static int parseDigit(unsigned short c, int radix)
618{
619 int digit = -1;
620
621 if (c >= '0' && c <= '9') {
622 digit = c - '0';
623 } else if (c >= 'A' && c <= 'Z') {
624 digit = c - 'A' + 10;
625 } else if (c >= 'a' && c <= 'z') {
626 digit = c - 'a' + 10;
627 }
628
629 if (digit >= radix)
630 return -1;
631 return digit;
632}
633
634double parseIntOverflow(const char* s, int length, int radix)
635{
636 double number = 0.0;
637 double radixMultiplier = 1.0;
638
639 for (const char* p = s + length - 1; p >= s; p--) {
640 if (radixMultiplier == Inf) {
641 if (*p != '0') {
642 number = Inf;
643 break;
644 }
645 } else {
646 int digit = parseDigit(*p, radix);
647 number += digit * radixMultiplier;
648 }
649
650 radixMultiplier *= radix;
651 }
652
653 return number;
654}
655
656static double parseInt(const UString& s, int radix)
657{
658 int length = s.size();
659 int p = 0;
660
661 while (p < length && isStrWhiteSpace(s[p].uc)) {
662 ++p;
663 }
664
665 double sign = 1;
666 if (p < length) {
667 if (s[p] == '+') {
668 ++p;
669 } else if (s[p] == '-') {
670 sign = -1;
671 ++p;
672 }
673 }
674
675 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
676 radix = 16;
677 p += 2;
678 } else if (radix == 0) {
679 if (p < length && s[p] == '0')
680 radix = 8;
681 else
682 radix = 10;
683 }
684
685 if (radix < 2 || radix > 36)
686 return NaN;
687
688 int firstDigitPosition = p;
689 bool sawDigit = false;
690 double number = 0;
691 while (p < length) {
692 int digit = parseDigit(s[p].uc, radix);
693 if (digit == -1)
694 break;
695 sawDigit = true;
696 number *= radix;
697 number += digit;
698 ++p;
699 }
700
701 if (number >= mantissaOverflowLowerBound) {
702 if (radix == 10)
703 number = kjs_strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
704 else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
705 number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
706 }
707
708 if (!sawDigit)
709 return NaN;
710
711 return sign * number;
712}
713
714static double parseFloat(const UString& s)
715{
716 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
717 // Need to skip any whitespace and then one + or - sign.
718 int length = s.size();
719 int p = 0;
720 while (p < length && isStrWhiteSpace(s[p].uc)) {
721 ++p;
722 }
723 if (p < length && (s[p] == '+' || s[p] == '-')) {
724 ++p;
725 }
726 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
727 return 0;
728 }
729
730 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
731}
732
733JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
734{
735 JSValue* res = jsUndefined();
736
737 static const char do_not_escape[] =
738 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
739 "abcdefghijklmnopqrstuvwxyz"
740 "0123456789"
741 "*+-./@_";
742
743 static const char do_not_escape_when_encoding_URI_component[] =
744 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
745 "abcdefghijklmnopqrstuvwxyz"
746 "0123456789"
747 "!'()*-._~";
748 static const char do_not_escape_when_encoding_URI[] =
749 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
750 "abcdefghijklmnopqrstuvwxyz"
751 "0123456789"
752 "!#$&'()*+,-./:;=?@_~";
753 static const char do_not_unescape_when_decoding_URI[] =
754 "#$&+,/:;=?@";
755
756 switch (id) {
757 case Eval: { // eval()
758 JSValue* x = args[0];
759 if (!x->isString())
760 return x;
761 else {
762 UString s = x->toString(exec);
763
764 int sourceId;
765 int errLine;
766 UString errMsg;
767 RefPtr<ProgramNode> progNode(parser().parseProgram(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg));
768
769 Debugger* dbg = exec->dynamicInterpreter()->debugger();
770 if (dbg) {
771 bool cont = dbg->sourceParsed(exec, sourceId, UString(), s, 0, errLine, errMsg);
772 if (!cont)
773 return jsUndefined();
774 }
775
776 // no program node means a syntax occurred
777 if (!progNode)
778 return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
779
780 bool switchGlobal = thisObj && thisObj != exec->dynamicInterpreter()->globalObject();
781
782 // enter a new execution context
783 Interpreter* interpreter = switchGlobal ? static_cast<JSGlobalObject*>(thisObj)->interpreter() : exec->dynamicInterpreter();
784 JSObject* thisVal = static_cast<JSObject*>(exec->thisValue());
785 ExecState newExec(interpreter, interpreter->globalObject(), thisVal, progNode.get(), EvalCode, exec);
786 if (exec->hadException())
787 newExec.setException(exec->exception());
788
789 if (switchGlobal) {
790 newExec.pushScope(thisObj);
791 newExec.setVariableObject(thisObj);
792 }
793
794 Completion c = progNode->execute(&newExec);
795
796 if (switchGlobal)
797 newExec.popScope();
798
799 // if an exception occured, propogate it back to the previous execution object
800 if (newExec.hadException())
801 exec->setException(newExec.exception());
802
803 res = jsUndefined();
804 if (c.complType() == Throw)
805 exec->setException(c.value());
806 else if (c.isValueCompletion())
807 res = c.value();
808 }
809 break;
810 }
811 case ParseInt:
812 res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
813 break;
814 case ParseFloat:
815 res = jsNumber(parseFloat(args[0]->toString(exec)));
816 break;
817 case IsNaN:
818 res = jsBoolean(isnan(args[0]->toNumber(exec)));
819 break;
820 case IsFinite: {
821 double n = args[0]->toNumber(exec);
822 res = jsBoolean(!isnan(n) && !isinf(n));
823 break;
824 }
825 case DecodeURI:
826 res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
827 break;
828 case DecodeURIComponent:
829 res = decode(exec, args, "", true);
830 break;
831 case EncodeURI:
832 res = encode(exec, args, do_not_escape_when_encoding_URI);
833 break;
834 case EncodeURIComponent:
835 res = encode(exec, args, do_not_escape_when_encoding_URI_component);
836 break;
837 case Escape:
838 {
839 UString r = "", s, str = args[0]->toString(exec);
840 const UChar* c = str.data();
841 for (int k = 0; k < str.size(); k++, c++) {
842 int u = c->uc;
843 if (u > 255) {
844 char tmp[7];
845 sprintf(tmp, "%%u%04X", u);
846 s = UString(tmp);
847 } else if (u != 0 && strchr(do_not_escape, (char)u)) {
848 s = UString(c, 1);
849 } else {
850 char tmp[4];
851 sprintf(tmp, "%%%02X", u);
852 s = UString(tmp);
853 }
854 r += s;
855 }
856 res = jsString(r);
857 break;
858 }
859 case UnEscape:
860 {
861 UString s = "", str = args[0]->toString(exec);
862 int k = 0, len = str.size();
863 while (k < len) {
864 const UChar* c = str.data() + k;
865 UChar u;
866 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
867 if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
868 Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
869 u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
870 (c+4)->uc, (c+5)->uc);
871 c = &u;
872 k += 5;
873 }
874 } else if (*c == UChar('%') && k <= len - 3 &&
875 Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
876 u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
877 c = &u;
878 k += 2;
879 }
880 k++;
881 s += UString(c, 1);
882 }
883 res = jsString(s);
884 break;
885 }
886#ifndef NDEBUG
887 case KJSPrint:
888 puts(args[0]->toString(exec).ascii());
889 break;
890#endif
891 }
892
893 return res;
894}
895
896} // namespace
Note: See TracBrowser for help on using the repository browser.