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

Last change on this file since 27022 was 27022, checked in by eseidel, 18 years ago

2007-10-24 Eric Seidel <[email protected]>

Reviewed by Maciej.


Add a JSGlobalObject class and remove the InterpreterMap
http://bugs.webkit.org/show_bug.cgi?id=15681


This required making JSCallbackObject a template class to allow for
JSGlobalObjects with JSCallbackObject functionality.


SunSpider claims this was a 0.5% speedup.

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