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

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

Reviewed by Darin Adler.

Removed KJS_VERBOSE because it was getting in the way of readability,
and the messages didn't seem very helpful.

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