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

Last change on this file since 11910 was 11910, checked in by ap, 19 years ago

Same as the previous checkin, but should be for real now.

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