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

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

Reviewed by Darin.

Fix for http://bugs.webkit.org/show_bug.cgi?id=16901
Convert remaining JS function objects to use the new PrototypeFunction class

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