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

Last change on this file since 29067 was 29067, checked in by Darin Adler, 17 years ago

Reviewed by Geoff.

Also included one other speed-up -- remove the call to reserveCapacity from
FunctionBodyNode::processDeclarations in all but the most unusual cases.

Together these make SunSpider 1.016x as fast.

  • JavaScriptCore.exp: Updated.
  • kjs/ExecState.cpp: (KJS::globalEmptyList): Added. Called only when creating global ExecState instances. (KJS::ExecState::ExecState): Broke constructor up into three separate functions, for the three separate node types. Also went through each of the three and streamlined as much as possible, removing dead code. This prevents us from having to access the global in the function body version of the constructor.
  • kjs/ExecState.h: Added emptyList(). Replaced the constructor with a set of three that are specific to the different node types that can create new execution state objects.
  • kjs/array_object.cpp: (KJS::ArrayProtoFuncToLocaleString::callAsFunction): Use exec->emptyList() instead of List::empty(). (KJS::ArrayProtoFuncConcat::callAsFunction): Ditto. (KJS::ArrayProtoFuncSlice::callAsFunction): Ditto. (KJS::ArrayProtoFuncSplice::callAsFunction): Ditto. (KJS::ArrayProtoFuncFilter::callAsFunction): Ditto.
  • kjs/function.cpp: (KJS::FunctionImp::callAsFunction): Updated to call new ExecState constructor. (KJS::GlobalFuncImp::callAsFunction): Ditto (for eval).
  • kjs/function_object.cpp: (FunctionObjectImp::construct): Use exec->emptyList() instead of List::empty().
  • kjs/list.cpp: Removed List::empty.
  • kjs/list.h: Ditto.
  • kjs/nodes.cpp: (KJS::ElementNode::evaluate): Use exec->emptyList() instead of List::empty(). (KJS::ArrayNode::evaluate): Ditto. (KJS::ObjectLiteralNode::evaluate): Ditto. (KJS::PropertyListNode::evaluate): Ditto. (KJS::FunctionBodyNode::processDeclarations): Another speed-up. Check the capacity before calling reserveCapacity, because it doesn't get inlined the local storage vector is almost always big enough -- saving the function call overhead is a big deal. (KJS::FuncDeclNode::makeFunction): Use exec->emptyList() instead of List::empty(). (KJS::FuncExprNode::evaluate): Ditto.
  • kjs/object.cpp: (KJS::tryGetAndCallProperty): Ditto.
  • kjs/property_slot.cpp: (KJS::PropertySlot::functionGetter): Ditto.
  • kjs/string_object.cpp: (KJS::StringProtoFuncSplit::callAsFunction): Ditto.
  • Property svn:eol-style set to native
File size: 23.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 ExecState newExec(exec->dynamicGlobalObject(), thisObj, body.get(), exec, this, args);
74 JSValue* result = body->execute(&newExec);
75 if (newExec.completionType() == Throw) {
76 exec->setException(result);
77 return result;
78 }
79 if (newExec.completionType() == ReturnValue)
80 return result;
81 return jsUndefined();
82}
83
84JSValue* FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
85{
86 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
87 ExecState* e = exec;
88 while (e) {
89 if (e->function() == thisObj)
90 return e->activationObject()->get(exec, propertyName);
91 e = e->callingExecState();
92 }
93 return jsNull();
94}
95
96JSValue* FunctionImp::callerGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
97{
98 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
99 ExecState* e = exec;
100 while (e) {
101 if (e->function() == thisObj)
102 break;
103 e = e->callingExecState();
104 }
105
106 if (!e)
107 return jsNull();
108
109 ExecState* callingExecState = e->callingExecState();
110 if (!callingExecState)
111 return jsNull();
112
113 FunctionImp* callingFunction = callingExecState->function();
114 if (!callingFunction)
115 return jsNull();
116
117 return callingFunction;
118}
119
120JSValue* FunctionImp::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
121{
122 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
123 return jsNumber(thisObj->body->parameters().size());
124}
125
126bool FunctionImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
127{
128 // Find the arguments from the closest context.
129 if (propertyName == exec->propertyNames().arguments) {
130 slot.setCustom(this, argumentsGetter);
131 return true;
132 }
133
134 // Compute length of parameters.
135 if (propertyName == exec->propertyNames().length) {
136 slot.setCustom(this, lengthGetter);
137 return true;
138 }
139
140 if (propertyName == exec->propertyNames().caller) {
141 slot.setCustom(this, callerGetter);
142 return true;
143 }
144
145 return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
146}
147
148void FunctionImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
149{
150 if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
151 return;
152 InternalFunctionImp::put(exec, propertyName, value, attr);
153}
154
155bool FunctionImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
156{
157 if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
158 return false;
159 return InternalFunctionImp::deleteProperty(exec, propertyName);
160}
161
162/* Returns the parameter name corresponding to the given index. eg:
163 * function f1(x, y, z): getParameterName(0) --> x
164 *
165 * If a name appears more than once, only the last index at which
166 * it appears associates with it. eg:
167 * function f2(x, x): getParameterName(0) --> null
168 */
169Identifier FunctionImp::getParameterName(int index)
170{
171 Vector<Identifier>& parameters = body->parameters();
172
173 if (static_cast<size_t>(index) >= body->parameters().size())
174 return CommonIdentifiers::shared()->nullIdentifier;
175
176 Identifier name = parameters[index];
177
178 // Are there any subsequent parameters with the same name?
179 size_t size = parameters.size();
180 for (size_t i = index + 1; i < size; ++i)
181 if (parameters[i] == name)
182 return CommonIdentifiers::shared()->nullIdentifier;
183
184 return name;
185}
186
187// ECMA 13.2.2 [[Construct]]
188JSObject* FunctionImp::construct(ExecState* exec, const List& args)
189{
190 JSObject* proto;
191 JSValue* p = get(exec, exec->propertyNames().prototype);
192 if (p->isObject())
193 proto = static_cast<JSObject*>(p);
194 else
195 proto = exec->lexicalGlobalObject()->objectPrototype();
196
197 JSObject* obj(new JSObject(proto));
198
199 JSValue* res = call(exec,obj,args);
200
201 if (res->isObject())
202 return static_cast<JSObject*>(res);
203 else
204 return obj;
205}
206
207// ------------------------------ IndexToNameMap ---------------------------------
208
209// We map indexes in the arguments array to their corresponding argument names.
210// Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x").
211
212// Once we have an argument name, we can get and set the argument's value in the
213// activation object.
214
215// We use Identifier::null to indicate that a given argument's value
216// isn't stored in the activation object.
217
218IndexToNameMap::IndexToNameMap(FunctionImp* func, const List& args)
219{
220 _map = new Identifier[args.size()];
221 this->size = args.size();
222
223 int i = 0;
224 List::const_iterator end = args.end();
225 for (List::const_iterator it = args.begin(); it != end; ++i, ++it)
226 _map[i] = func->getParameterName(i); // null if there is no corresponding parameter
227}
228
229IndexToNameMap::~IndexToNameMap() {
230 delete [] _map;
231}
232
233bool IndexToNameMap::isMapped(const Identifier& index) const
234{
235 bool indexIsNumber;
236 int indexAsNumber = index.toUInt32(&indexIsNumber);
237
238 if (!indexIsNumber)
239 return false;
240
241 if (indexAsNumber >= size)
242 return false;
243
244 if (_map[indexAsNumber].isNull())
245 return false;
246
247 return true;
248}
249
250void IndexToNameMap::unMap(const Identifier& index)
251{
252 bool indexIsNumber;
253 int indexAsNumber = index.toUInt32(&indexIsNumber);
254
255 ASSERT(indexIsNumber && indexAsNumber < size);
256
257 _map[indexAsNumber] = CommonIdentifiers::shared()->nullIdentifier;
258}
259
260Identifier& IndexToNameMap::operator[](int index)
261{
262 return _map[index];
263}
264
265Identifier& IndexToNameMap::operator[](const Identifier& index)
266{
267 bool indexIsNumber;
268 int indexAsNumber = index.toUInt32(&indexIsNumber);
269
270 ASSERT(indexIsNumber && indexAsNumber < size);
271
272 return (*this)[indexAsNumber];
273}
274
275// ------------------------------ Arguments ---------------------------------
276
277const ClassInfo Arguments::info = { "Arguments", 0, 0 };
278
279// ECMA 10.1.8
280Arguments::Arguments(ExecState* exec, FunctionImp* func, const List& args, ActivationImp* act)
281: JSObject(exec->lexicalGlobalObject()->objectPrototype()),
282_activationObject(act),
283indexToNameMap(func, args)
284{
285 putDirect(exec->propertyNames().callee, func, DontEnum);
286 putDirect(exec->propertyNames().length, args.size(), DontEnum);
287
288 int i = 0;
289 List::const_iterator end = args.end();
290 for (List::const_iterator it = args.begin(); it != end; ++it, ++i) {
291 if (!indexToNameMap.isMapped(Identifier::from(i))) {
292 JSObject::put(exec, Identifier::from(i), *it, DontEnum);
293 }
294 }
295}
296
297void Arguments::mark()
298{
299 JSObject::mark();
300 if (_activationObject && !_activationObject->marked())
301 _activationObject->mark();
302}
303
304JSValue* Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
305{
306 Arguments* thisObj = static_cast<Arguments*>(slot.slotBase());
307 return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
308}
309
310bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
311{
312 if (indexToNameMap.isMapped(propertyName)) {
313 slot.setCustom(this, mappedIndexGetter);
314 return true;
315 }
316
317 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
318}
319
320void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
321{
322 if (indexToNameMap.isMapped(propertyName)) {
323 _activationObject->put(exec, indexToNameMap[propertyName], value, attr);
324 } else {
325 JSObject::put(exec, propertyName, value, attr);
326 }
327}
328
329bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName)
330{
331 if (indexToNameMap.isMapped(propertyName)) {
332 indexToNameMap.unMap(propertyName);
333 return true;
334 } else {
335 return JSObject::deleteProperty(exec, propertyName);
336 }
337}
338
339// ------------------------------ ActivationImp --------------------------------
340
341const ClassInfo ActivationImp::info = { "Activation", 0, 0 };
342
343JSValue* ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
344{
345 ActivationImp* thisObj = static_cast<ActivationImp*>(slot.slotBase());
346
347 if (!thisObj->d()->argumentsObject)
348 thisObj->createArgumentsObject(exec);
349
350 return thisObj->d()->argumentsObject;
351}
352
353PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
354{
355 return ActivationImp::argumentsGetter;
356}
357
358bool ActivationImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
359{
360 if (symbolTableGet(propertyName, slot))
361 return true;
362
363 if (JSValue** location = getDirectLocation(propertyName)) {
364 slot.setValueSlot(this, location);
365 return true;
366 }
367
368 // Only return the built-in arguments object if it wasn't overridden above.
369 if (propertyName == exec->propertyNames().arguments) {
370 slot.setCustom(this, getArgumentsGetter());
371 return true;
372 }
373
374 // We don't call through to JSObject because there's no way to give an
375 // activation object getter properties or a prototype.
376 ASSERT(!_prop.hasGetterSetterProperties());
377 ASSERT(prototype() == jsNull());
378 return false;
379}
380
381bool ActivationImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
382{
383 if (propertyName == exec->propertyNames().arguments)
384 return false;
385
386 return JSVariableObject::deleteProperty(exec, propertyName);
387}
388
389void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value, int attr)
390{
391 if (symbolTablePut(propertyName, value, attr))
392 return;
393
394 // We don't call through to JSObject because __proto__ and getter/setter
395 // properties are non-standard extensions that other implementations do not
396 // expose in the activation object.
397 ASSERT(!_prop.hasGetterSetterProperties());
398 _prop.put(propertyName, value, attr, (attr == None || attr == DontDelete));
399}
400
401void ActivationImp::mark()
402{
403 JSVariableObject::mark();
404
405 if (!d()->function->marked())
406 d()->function->mark();
407
408 if (d()->argumentsObject && !d()->argumentsObject->marked())
409 d()->argumentsObject->mark();
410}
411
412void ActivationImp::createArgumentsObject(ExecState* exec)
413{
414 // Since "arguments" is only accessible while a function is being called,
415 // we can retrieve our argument list from the ExecState for our function
416 // call instead of storing the list ourselves.
417 d()->argumentsObject = new Arguments(exec, d()->exec->function(), *d()->exec->arguments(), this);
418}
419
420// ------------------------------ GlobalFunc -----------------------------------
421
422
423GlobalFuncImp::GlobalFuncImp(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
424 : InternalFunctionImp(funcProto, name)
425 , id(i)
426{
427 putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum);
428}
429
430static JSValue* encode(ExecState* exec, const List& args, const char* do_not_escape)
431{
432 UString r = "", s, str = args[0]->toString(exec);
433 CString cstr = str.UTF8String(true);
434 if (!cstr.c_str())
435 return throwError(exec, URIError, "String contained an illegal UTF-16 sequence.");
436 const char* p = cstr.c_str();
437 for (size_t k = 0; k < cstr.size(); k++, p++) {
438 char c = *p;
439 if (c && strchr(do_not_escape, c)) {
440 r.append(c);
441 } else {
442 char tmp[4];
443 sprintf(tmp, "%%%02X", (unsigned char)c);
444 r += tmp;
445 }
446 }
447 return jsString(r);
448}
449
450static JSValue* decode(ExecState* exec, const List& args, const char* do_not_unescape, bool strict)
451{
452 UString s = "", str = args[0]->toString(exec);
453 int k = 0, len = str.size();
454 const UChar* d = str.data();
455 UChar u;
456 while (k < len) {
457 const UChar* p = d + k;
458 UChar c = *p;
459 if (c == '%') {
460 int charLen = 0;
461 if (k <= len - 3 && isASCIIHexDigit(p[1].uc) && isASCIIHexDigit(p[2].uc)) {
462 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
463 const int sequenceLen = UTF8SequenceLength(b0);
464 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
465 charLen = sequenceLen * 3;
466 char sequence[5];
467 sequence[0] = b0;
468 for (int i = 1; i < sequenceLen; ++i) {
469 const UChar* q = p + i * 3;
470 if (q[0] == '%' && isASCIIHexDigit(q[1].uc) && isASCIIHexDigit(q[2].uc))
471 sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
472 else {
473 charLen = 0;
474 break;
475 }
476 }
477 if (charLen != 0) {
478 sequence[sequenceLen] = 0;
479 const int character = decodeUTF8Sequence(sequence);
480 if (character < 0 || character >= 0x110000) {
481 charLen = 0;
482 } else if (character >= 0x10000) {
483 // Convert to surrogate pair.
484 s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
485 u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
486 } else {
487 u = static_cast<unsigned short>(character);
488 }
489 }
490 }
491 }
492 if (charLen == 0) {
493 if (strict)
494 return throwError(exec, URIError);
495 // The only case where we don't use "strict" mode is the "unescape" function.
496 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
497 if (k <= len - 6 && p[1] == 'u'
498 && isASCIIHexDigit(p[2].uc) && isASCIIHexDigit(p[3].uc)
499 && isASCIIHexDigit(p[4].uc) && isASCIIHexDigit(p[5].uc)) {
500 charLen = 6;
501 u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
502 }
503 }
504 if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
505 c = u;
506 k += charLen - 1;
507 }
508 }
509 k++;
510 s.append(c);
511 }
512 return jsString(s);
513}
514
515static bool isStrWhiteSpace(unsigned short c)
516{
517 switch (c) {
518 case 0x0009:
519 case 0x000A:
520 case 0x000B:
521 case 0x000C:
522 case 0x000D:
523 case 0x0020:
524 case 0x00A0:
525 case 0x2028:
526 case 0x2029:
527 return true;
528 default:
529 return isSeparatorSpace(c);
530 }
531}
532
533static int parseDigit(unsigned short c, int radix)
534{
535 int digit = -1;
536
537 if (c >= '0' && c <= '9') {
538 digit = c - '0';
539 } else if (c >= 'A' && c <= 'Z') {
540 digit = c - 'A' + 10;
541 } else if (c >= 'a' && c <= 'z') {
542 digit = c - 'a' + 10;
543 }
544
545 if (digit >= radix)
546 return -1;
547 return digit;
548}
549
550double parseIntOverflow(const char* s, int length, int radix)
551{
552 double number = 0.0;
553 double radixMultiplier = 1.0;
554
555 for (const char* p = s + length - 1; p >= s; p--) {
556 if (radixMultiplier == Inf) {
557 if (*p != '0') {
558 number = Inf;
559 break;
560 }
561 } else {
562 int digit = parseDigit(*p, radix);
563 number += digit * radixMultiplier;
564 }
565
566 radixMultiplier *= radix;
567 }
568
569 return number;
570}
571
572static double parseInt(const UString& s, int radix)
573{
574 int length = s.size();
575 int p = 0;
576
577 while (p < length && isStrWhiteSpace(s[p].uc)) {
578 ++p;
579 }
580
581 double sign = 1;
582 if (p < length) {
583 if (s[p] == '+') {
584 ++p;
585 } else if (s[p] == '-') {
586 sign = -1;
587 ++p;
588 }
589 }
590
591 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
592 radix = 16;
593 p += 2;
594 } else if (radix == 0) {
595 if (p < length && s[p] == '0')
596 radix = 8;
597 else
598 radix = 10;
599 }
600
601 if (radix < 2 || radix > 36)
602 return NaN;
603
604 int firstDigitPosition = p;
605 bool sawDigit = false;
606 double number = 0;
607 while (p < length) {
608 int digit = parseDigit(s[p].uc, radix);
609 if (digit == -1)
610 break;
611 sawDigit = true;
612 number *= radix;
613 number += digit;
614 ++p;
615 }
616
617 if (number >= mantissaOverflowLowerBound) {
618 if (radix == 10)
619 number = kjs_strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
620 else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
621 number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
622 }
623
624 if (!sawDigit)
625 return NaN;
626
627 return sign * number;
628}
629
630static double parseFloat(const UString& s)
631{
632 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
633 // Need to skip any whitespace and then one + or - sign.
634 int length = s.size();
635 int p = 0;
636 while (p < length && isStrWhiteSpace(s[p].uc)) {
637 ++p;
638 }
639 if (p < length && (s[p] == '+' || s[p] == '-')) {
640 ++p;
641 }
642 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
643 return 0;
644 }
645
646 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
647}
648
649JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
650{
651 JSValue* res = jsUndefined();
652
653 static const char do_not_escape[] =
654 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
655 "abcdefghijklmnopqrstuvwxyz"
656 "0123456789"
657 "*+-./@_";
658
659 static const char do_not_escape_when_encoding_URI_component[] =
660 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
661 "abcdefghijklmnopqrstuvwxyz"
662 "0123456789"
663 "!'()*-._~";
664 static const char do_not_escape_when_encoding_URI[] =
665 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
666 "abcdefghijklmnopqrstuvwxyz"
667 "0123456789"
668 "!#$&'()*+,-./:;=?@_~";
669 static const char do_not_unescape_when_decoding_URI[] =
670 "#$&+,/:;=?@";
671
672 switch (id) {
673 case Eval: { // eval()
674 JSValue* x = args[0];
675 if (!x->isString())
676 return x;
677 else {
678 UString s = x->toString(exec);
679
680 int sourceId;
681 int errLine;
682 UString errMsg;
683 RefPtr<EvalNode> evalNode = parser().parse<EvalNode>(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg);
684
685 Debugger* dbg = exec->dynamicGlobalObject()->debugger();
686 if (dbg) {
687 bool cont = dbg->sourceParsed(exec, sourceId, UString(), s, 0, errLine, errMsg);
688 if (!cont)
689 return jsUndefined();
690 }
691
692 // no program node means a syntax occurred
693 if (!evalNode)
694 return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
695
696 bool switchGlobal = thisObj && thisObj != exec->dynamicGlobalObject() && thisObj->isGlobalObject();
697
698 // enter a new execution context
699 JSGlobalObject* globalObject = switchGlobal ? static_cast<JSGlobalObject*>(thisObj) : exec->dynamicGlobalObject();
700 ExecState newExec(globalObject, evalNode.get(), exec);
701
702 if (switchGlobal) {
703 newExec.pushScope(thisObj);
704 newExec.setVariableObject(static_cast<JSGlobalObject*>(thisObj));
705 }
706 JSValue* value = evalNode->execute(&newExec);
707 if (switchGlobal)
708 newExec.popScope();
709
710 if (newExec.completionType() == Throw) {
711 exec->setException(value);
712 return value;
713 }
714 return value ? value : jsUndefined();
715 }
716 }
717 case ParseInt:
718 res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
719 break;
720 case ParseFloat:
721 res = jsNumber(parseFloat(args[0]->toString(exec)));
722 break;
723 case IsNaN:
724 res = jsBoolean(isnan(args[0]->toNumber(exec)));
725 break;
726 case IsFinite: {
727 double n = args[0]->toNumber(exec);
728 res = jsBoolean(!isnan(n) && !isinf(n));
729 break;
730 }
731 case DecodeURI:
732 res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
733 break;
734 case DecodeURIComponent:
735 res = decode(exec, args, "", true);
736 break;
737 case EncodeURI:
738 res = encode(exec, args, do_not_escape_when_encoding_URI);
739 break;
740 case EncodeURIComponent:
741 res = encode(exec, args, do_not_escape_when_encoding_URI_component);
742 break;
743 case Escape:
744 {
745 UString r = "", s, str = args[0]->toString(exec);
746 const UChar* c = str.data();
747 for (int k = 0; k < str.size(); k++, c++) {
748 int u = c->uc;
749 if (u > 255) {
750 char tmp[7];
751 sprintf(tmp, "%%u%04X", u);
752 s = UString(tmp);
753 } else if (u != 0 && strchr(do_not_escape, (char)u)) {
754 s = UString(c, 1);
755 } else {
756 char tmp[4];
757 sprintf(tmp, "%%%02X", u);
758 s = UString(tmp);
759 }
760 r += s;
761 }
762 res = jsString(r);
763 break;
764 }
765 case UnEscape:
766 {
767 UString s = "", str = args[0]->toString(exec);
768 int k = 0, len = str.size();
769 while (k < len) {
770 const UChar* c = str.data() + k;
771 UChar u;
772 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
773 if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
774 Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
775 u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
776 (c+4)->uc, (c+5)->uc);
777 c = &u;
778 k += 5;
779 }
780 } else if (*c == UChar('%') && k <= len - 3 &&
781 Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
782 u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
783 c = &u;
784 k += 2;
785 }
786 k++;
787 s += UString(c, 1);
788 }
789 res = jsString(s);
790 break;
791 }
792#ifndef NDEBUG
793 case KJSPrint:
794 puts(args[0]->toString(exec).ascii());
795 break;
796#endif
797 }
798
799 return res;
800}
801
802} // namespace
Note: See TracBrowser for help on using the repository browser.