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

Last change on this file since 11527 was 11527, checked in by darin, 19 years ago

JavaScriptCore:

Rubber stamped by Maciej.

  • did long-promised KJS renaming:

ValueImp -> JSValue
ObjectImp -> JSObject
AllocatedValueImp -> JSCell

A renaming to get a class out of the way

KJS::Bindings::JSObject -> JavaJSObject

and some other "imp-reduction" renaming

*InstanceImp -> *Instance
*ProtoFuncImp -> *ProtoFunc
*PrototypeImp -> *Prototype
ArgumentsImp -> Arguments
RuntimeArrayImp -> RuntimeArray
RuntimeMethodImp -> RuntimeMethod

  • most files and functions

WebCore:

Rubber stamped by Maciej.

  • updated for KJS class renaming
  • many files and functions
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.7 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, 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, 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, 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, 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 if (JSObject::getOwnPropertySlot(exec, propertyName, slot))
512 return true;
513
514 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier()) {
515 slot.setCustom(this, getArgumentsGetter());
516 return true;
517 }
518
519 return false;
520}
521
522bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
523{
524 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier())
525 return false;
526 return JSObject::deleteProperty(exec, propertyName);
527}
528
529void ActivationImp::mark()
530{
531 if (_function && !_function->marked())
532 _function->mark();
533 _arguments.mark();
534 if (_argumentsObject && !_argumentsObject->marked())
535 _argumentsObject->mark();
536 JSObject::mark();
537}
538
539void ActivationImp::createArgumentsObject(ExecState *exec) const
540{
541 _argumentsObject = new Arguments(exec, _function, _arguments, const_cast<ActivationImp *>(this));
542}
543
544// ------------------------------ GlobalFunc -----------------------------------
545
546
547GlobalFuncImp::GlobalFuncImp(ExecState *exec, FunctionPrototype *funcProto, int i, int len)
548 : InternalFunctionImp(funcProto), id(i)
549{
550 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
551}
552
553CodeType GlobalFuncImp::codeType() const
554{
555 return id == Eval ? EvalCode : codeType();
556}
557
558bool GlobalFuncImp::implementsCall() const
559{
560 return true;
561}
562
563static JSValue *encode(ExecState *exec, const List &args, const char *do_not_escape)
564{
565 UString r = "", s, str = args[0]->toString(exec);
566 CString cstr = str.UTF8String();
567 const char *p = cstr.c_str();
568 for (int k = 0; k < cstr.size(); k++, p++) {
569 char c = *p;
570 if (c && strchr(do_not_escape, c)) {
571 r.append(c);
572 } else {
573 char tmp[4];
574 sprintf(tmp, "%%%02X", (unsigned char)c);
575 r += tmp;
576 }
577 }
578 return jsString(r);
579}
580
581static JSValue *decode(ExecState *exec, const List &args, const char *do_not_unescape, bool strict)
582{
583 UString s = "", str = args[0]->toString(exec);
584 int k = 0, len = str.size();
585 const UChar *d = str.data();
586 UChar u;
587 while (k < len) {
588 const UChar *p = d + k;
589 UChar c = *p;
590 if (c == '%') {
591 int charLen = 0;
592 if (k <= len - 3 && isxdigit(p[1].uc) && isxdigit(p[2].uc)) {
593 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
594 const int sequenceLen = UTF8SequenceLength(b0);
595 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
596 charLen = sequenceLen * 3;
597 char sequence[5];
598 sequence[0] = b0;
599 for (int i = 1; i < sequenceLen; ++i) {
600 const UChar *q = p + i * 3;
601 if (q[0] == '%' && isxdigit(q[1].uc) && isxdigit(q[2].uc))
602 sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
603 else {
604 charLen = 0;
605 break;
606 }
607 }
608 if (charLen != 0) {
609 sequence[sequenceLen] = 0;
610 const int character = decodeUTF8Sequence(sequence);
611 if (character < 0 || character >= 0x110000) {
612 charLen = 0;
613 } else if (character >= 0x10000) {
614 // Convert to surrogate pair.
615 s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
616 u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
617 } else {
618 u = static_cast<unsigned short>(character);
619 }
620 }
621 }
622 }
623 if (charLen == 0) {
624 if (strict)
625 return throwError(exec, URIError);
626 // The only case where we don't use "strict" mode is the "unescape" function.
627 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
628 if (k <= len - 6 && p[1] == 'u'
629 && isxdigit(p[2].uc) && isxdigit(p[3].uc)
630 && isxdigit(p[4].uc) && isxdigit(p[5].uc)) {
631 charLen = 6;
632 u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
633 }
634 }
635 if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
636 c = u;
637 k += charLen - 1;
638 }
639 }
640 k++;
641 s.append(c);
642 }
643 return jsString(s);
644}
645
646static bool isStrWhiteSpace(unsigned short c)
647{
648 switch (c) {
649 case 0x0009:
650 case 0x000A:
651 case 0x000B:
652 case 0x000C:
653 case 0x000D:
654 case 0x0020:
655 case 0x00A0:
656 case 0x2028:
657 case 0x2029:
658 return true;
659 default:
660 return u_charType(c) == U_SPACE_SEPARATOR;
661 }
662}
663
664static int parseDigit(unsigned short c, int radix)
665{
666 int digit = -1;
667
668 if (c >= '0' && c <= '9') {
669 digit = c - '0';
670 } else if (c >= 'A' && c <= 'Z') {
671 digit = c - 'A' + 10;
672 } else if (c >= 'a' && c <= 'z') {
673 digit = c - 'a' + 10;
674 }
675
676 if (digit >= radix)
677 return -1;
678 return digit;
679}
680
681static double parseInt(const UString &s, int radix)
682{
683 int length = s.size();
684 int p = 0;
685
686 while (p < length && isStrWhiteSpace(s[p].uc)) {
687 ++p;
688 }
689
690 double sign = 1;
691 if (p < length) {
692 if (s[p] == '+') {
693 ++p;
694 } else if (s[p] == '-') {
695 sign = -1;
696 ++p;
697 }
698 }
699
700 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
701 radix = 16;
702 p += 2;
703 } else if (radix == 0) {
704 if (p < length && s[p] == '0')
705 radix = 8;
706 else
707 radix = 10;
708 }
709
710 if (radix < 2 || radix > 36)
711 return NaN;
712
713 bool sawDigit = false;
714 double number = 0;
715 while (p < length) {
716 int digit = parseDigit(s[p].uc, radix);
717 if (digit == -1)
718 break;
719 sawDigit = true;
720 number *= radix;
721 number += digit;
722 ++p;
723 }
724
725 if (!sawDigit)
726 return NaN;
727
728 return sign * number;
729}
730
731static double parseFloat(const UString &s)
732{
733 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
734 // Need to skip any whitespace and then one + or - sign.
735 int length = s.size();
736 int p = 0;
737 while (p < length && isStrWhiteSpace(s[p].uc)) {
738 ++p;
739 }
740 if (p < length && (s[p] == '+' || s[p] == '-')) {
741 ++p;
742 }
743 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
744 return 0;
745 }
746
747 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
748}
749
750JSValue *GlobalFuncImp::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
751{
752 JSValue *res = jsUndefined();
753
754 static const char do_not_escape[] =
755 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
756 "abcdefghijklmnopqrstuvwxyz"
757 "0123456789"
758 "*+-./@_";
759
760 static const char do_not_escape_when_encoding_URI_component[] =
761 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
762 "abcdefghijklmnopqrstuvwxyz"
763 "0123456789"
764 "!'()*-._~";
765 static const char do_not_escape_when_encoding_URI[] =
766 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
767 "abcdefghijklmnopqrstuvwxyz"
768 "0123456789"
769 "!#$&'()*+,-./:;=?@_~";
770 static const char do_not_unescape_when_decoding_URI[] =
771 "#$&+,/:;=?@";
772
773 switch (id) {
774 case Eval: { // eval()
775 JSValue *x = args[0];
776 if (!x->isString())
777 return x;
778 else {
779 UString s = x->toString(exec);
780
781 int sid;
782 int errLine;
783 UString errMsg;
784 RefPtr<ProgramNode> progNode(Parser::parse(UString(), 0, s.data(),s.size(),&sid,&errLine,&errMsg));
785
786 Debugger *dbg = exec->dynamicInterpreter()->imp()->debugger();
787 if (dbg) {
788 bool cont = dbg->sourceParsed(exec, sid, UString(), s, errLine);
789 if (!cont)
790 return jsUndefined();
791 }
792
793 // no program node means a syntax occurred
794 if (!progNode) {
795 return throwError(exec, SyntaxError, errMsg, errLine, sid, NULL);
796 }
797
798 // enter a new execution context
799 JSObject *thisVal = static_cast<JSObject *>(exec->context().thisValue());
800 ContextImp ctx(exec->dynamicInterpreter()->globalObject(),
801 exec->dynamicInterpreter()->imp(),
802 thisVal,
803 EvalCode,
804 exec->context().imp());
805
806 ExecState newExec(exec->dynamicInterpreter(), &ctx);
807 newExec.setException(exec->exception()); // could be null
808
809 // execute the code
810 progNode->processVarDecls(&newExec);
811 Completion c = progNode->execute(&newExec);
812
813 // if an exception occured, propogate it back to the previous execution object
814 if (newExec.hadException())
815 exec->setException(newExec.exception());
816
817 res = jsUndefined();
818 if (c.complType() == Throw)
819 exec->setException(c.value());
820 else if (c.isValueCompletion())
821 res = c.value();
822 }
823 break;
824 }
825 case ParseInt:
826 res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
827 break;
828 case ParseFloat:
829 res = jsNumber(parseFloat(args[0]->toString(exec)));
830 break;
831 case IsNaN:
832 res = jsBoolean(isNaN(args[0]->toNumber(exec)));
833 break;
834 case IsFinite: {
835 double n = args[0]->toNumber(exec);
836 res = jsBoolean(!isNaN(n) && !isInf(n));
837 break;
838 }
839 case DecodeURI:
840 res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
841 break;
842 case DecodeURIComponent:
843 res = decode(exec, args, "", true);
844 break;
845 case EncodeURI:
846 res = encode(exec, args, do_not_escape_when_encoding_URI);
847 break;
848 case EncodeURIComponent:
849 res = encode(exec, args, do_not_escape_when_encoding_URI_component);
850 break;
851 case Escape:
852 {
853 UString r = "", s, str = args[0]->toString(exec);
854 const UChar *c = str.data();
855 for (int k = 0; k < str.size(); k++, c++) {
856 int u = c->uc;
857 if (u > 255) {
858 char tmp[7];
859 sprintf(tmp, "%%u%04X", u);
860 s = UString(tmp);
861 } else if (u != 0 && strchr(do_not_escape, (char)u)) {
862 s = UString(c, 1);
863 } else {
864 char tmp[4];
865 sprintf(tmp, "%%%02X", u);
866 s = UString(tmp);
867 }
868 r += s;
869 }
870 res = jsString(r);
871 break;
872 }
873 case UnEscape:
874 {
875 UString s = "", str = args[0]->toString(exec);
876 int k = 0, len = str.size();
877 while (k < len) {
878 const UChar *c = str.data() + k;
879 UChar u;
880 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
881 if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
882 Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
883 u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
884 (c+4)->uc, (c+5)->uc);
885 c = &u;
886 k += 5;
887 }
888 } else if (*c == UChar('%') && k <= len - 3 &&
889 Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
890 u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
891 c = &u;
892 k += 2;
893 }
894 k++;
895 s += UString(c, 1);
896 }
897 res = jsString(s);
898 break;
899 }
900#ifndef NDEBUG
901 case KJSPrint:
902 puts(args[0]->toString(exec).ascii());
903 break;
904#endif
905 }
906
907 return res;
908}
909
910} // namespace
Note: See TracBrowser for help on using the repository browser.