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

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

JavaScriptCore:

Reviewed by Sam Weinig.

Split this:


FunctionBodyNode


|

ProgramNode


into this:


ScopeNode


| | |

FunctionBodyNode ProgramNode EvalNode

in preparation for specializing each class more while optimizing global
variable access.


Also removed some cruft from the FunctionBodyNode interface to simplify
things.


SunSpider says this patch is a .8% speedup, which seems reasonable,
since it eliminates a few branches and adds KJS_FAST_CALL in a few
places.


Layout tests and JS tests pass. Also, this baby builds on Windows! (Qt
mileage may vary...)

WebCore:

Reviewed by Sam Weinig.

Updated for rename in JavaScriptCore.

  • bridge/mac/WebCoreScriptDebugger.mm: (-[WebCoreScriptCallFrame scopeChain]): (-[WebCoreScriptCallFrame functionName]): (-[WebCoreScriptCallFrame evaluateWebScript:]):

WebKit/win:

Reviewed by Sam Weinig.

Updated for rename in JavaScriptCore.

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