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

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

Comment tweak suggested by Maciej.

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