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

Last change on this file since 9445 was 9445, checked in by mjs, 20 years ago

JavaScriptCore:

Reviewed by Darin(first pass) and Hyatt.

I started with the KDE implementation of const but I ended up changing it a bit
to avoid the use of a global variable. Now instead of the global variable it distinguishes
const and var at the grammar level so the appropriate node can know the right kind of
declaration.

Test cases:

  • tests/mozilla/expected.html: Updated for one new test that is failing - we used to bail on it entirely because it checks for const support before starting.
  • see also test cases added in WebCore
  • kjs/grammar.y: Add rules for const declarations.
  • kjs/keywords.table: Add const keyword.
  • kjs/nodes.cpp: (VarDeclNode::VarDeclNode): Add parameter. (VarDeclNode::evaluate): Add const support. (VarDeclNode::processVarDecls): Add const support. (VarStatementNode::execute): Irrelevant change. (ForInNode::ForInNode): Tell our variable node that it's a variable.
  • kjs/nodes.h: (KJS::VarDeclNode::): Add declaration of type enum, extra constructor parameter. (KJS::VarStatementNode::VarStatementNode): Irrelevant change.
  • kjs/function.cpp: (KJS::GlobalFuncImp::call): Process var decls before evaluating.

WebCore:

Reviewed by Darin(first pass) and Hyatt.

Test cases only, fix is in JavaScriptCore

Test cases added:

  • layout-tests/fast/js/const-expected.txt: Added.
  • layout-tests/fast/js/const.html: Added.
  • layout-tests/fast/js/eval-var-decl-expected.txt: Added.
  • layout-tests/fast/js/eval-var-decl.html: Added.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.5 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., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 *
23 */
24
25#include "function.h"
26
27#include "internal.h"
28#include "function_object.h"
29#include "lexer.h"
30#include "nodes.h"
31#include "operations.h"
32#include "debugger.h"
33#include "context.h"
34
35#include <stdio.h>
36#include <errno.h>
37#include <stdlib.h>
38#include <assert.h>
39#include <string.h>
40
41#if APPLE_CHANGES
42#include <unicode/uchar.h>
43#endif
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<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
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
76Value FunctionImp::call(ExecState *exec, Object &thisObj, const List &args)
77{
78 Object &globalObj = exec->dynamicInterpreter()->globalObject();
79
80 Debugger *dbg = exec->dynamicInterpreter()->imp()->debugger();
81 int sid = -1;
82 int lineno = -1;
83 if (dbg) {
84 if (inherits(&DeclaredFunctionImp::info)) {
85 sid = static_cast<DeclaredFunctionImp*>(this)->body->sourceId();
86 lineno = static_cast<DeclaredFunctionImp*>(this)->body->firstLine();
87 }
88
89 Object func(this);
90 bool cont = dbg->callEvent(exec,sid,lineno,func,args);
91 if (!cont) {
92 dbg->imp()->abort();
93 return Undefined();
94 }
95 }
96
97 // enter a new execution context
98 ContextImp ctx(globalObj, exec->dynamicInterpreter()->imp(), thisObj, codeType(),
99 exec->context().imp(), this, &args);
100 ExecState newExec(exec->dynamicInterpreter(), &ctx);
101 newExec.setException(exec->exception()); // could be null
102
103 // assign user supplied arguments to parameters
104 processParameters(&newExec, args);
105 // add variable declarations (initialized to undefined)
106 processVarDecls(&newExec);
107
108 Completion comp = execute(&newExec);
109
110 // if an exception occured, propogate it back to the previous execution object
111 if (newExec.hadException())
112 exec->setException(newExec.exception());
113
114#ifdef KJS_VERBOSE
115 if (comp.complType() == Throw)
116 printInfo(exec,"throwing", comp.value());
117 else if (comp.complType() == ReturnValue)
118 printInfo(exec,"returning", comp.value());
119 else
120 fprintf(stderr, "returning: undefined\n");
121#endif
122
123 if (dbg) {
124 Object func(this);
125 int cont = dbg->returnEvent(exec,sid,lineno,func);
126 if (!cont) {
127 dbg->imp()->abort();
128 return Undefined();
129 }
130 }
131
132 if (comp.complType() == Throw) {
133 exec->setException(comp.value());
134 return comp.value();
135 }
136 else if (comp.complType() == ReturnValue)
137 return comp.value();
138 else
139 return Undefined();
140}
141
142void FunctionImp::addParameter(const Identifier &n)
143{
144 Parameter **p = &param;
145 while (*p)
146 p = &(*p)->next;
147
148 *p = new Parameter(n);
149}
150
151UString FunctionImp::parameterString() const
152{
153 UString s;
154 const Parameter *p = param;
155 while (p) {
156 if (!s.isEmpty())
157 s += ", ";
158 s += p->name.ustring();
159 p = p->next;
160 }
161
162 return s;
163}
164
165
166// ECMA 10.1.3q
167void FunctionImp::processParameters(ExecState *exec, const List &args)
168{
169 Object variable = exec->context().imp()->variableObject();
170
171#ifdef KJS_VERBOSE
172 fprintf(stderr, "---------------------------------------------------\n"
173 "processing parameters for %s call\n",
174 name().isEmpty() ? "(internal)" : name().ascii());
175#endif
176
177 if (param) {
178 ListIterator it = args.begin();
179 Parameter *p = param;
180 while (p) {
181 if (it != args.end()) {
182#ifdef KJS_VERBOSE
183 fprintf(stderr, "setting parameter %s ", p->name.ascii());
184 printInfo(exec,"to", *it);
185#endif
186 variable.put(exec, p->name, *it);
187 it++;
188 } else
189 variable.put(exec, p->name, Undefined());
190 p = p->next;
191 }
192 }
193#ifdef KJS_VERBOSE
194 else {
195 for (int i = 0; i < args.size(); i++)
196 printInfo(exec,"setting argument", args[i]);
197 }
198#endif
199}
200
201void FunctionImp::processVarDecls(ExecState */*exec*/)
202{
203}
204
205Value FunctionImp::get(ExecState *exec, const Identifier &propertyName) const
206{
207 // Find the arguments from the closest context.
208 if (propertyName == argumentsPropertyName) {
209 ContextImp *context = exec->_context;
210 while (context) {
211 if (context->function() == this)
212 return static_cast<ActivationImp *>
213 (context->activationObject())->get(exec, propertyName);
214 context = context->callingContext();
215 }
216 return Null();
217 }
218
219 // Compute length of parameters.
220 if (propertyName == lengthPropertyName) {
221 const Parameter * p = param;
222 int count = 0;
223 while (p) {
224 ++count;
225 p = p->next;
226 }
227 return Number(count);
228 }
229
230 return InternalFunctionImp::get(exec, propertyName);
231}
232
233void FunctionImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
234{
235 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
236 return;
237 InternalFunctionImp::put(exec, propertyName, value, attr);
238}
239
240bool FunctionImp::hasOwnProperty(ExecState *exec, const Identifier &propertyName) const
241{
242 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
243 return true;
244 return InternalFunctionImp::hasOwnProperty(exec, propertyName);
245}
246
247bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
248{
249 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
250 return false;
251 return InternalFunctionImp::deleteProperty(exec, propertyName);
252}
253
254// ------------------------------ DeclaredFunctionImp --------------------------
255
256// ### is "Function" correct here?
257const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
258
259DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const Identifier &n,
260 FunctionBodyNode *b, const ScopeChain &sc)
261 : FunctionImp(exec,n), body(b)
262{
263 Value protect(this);
264 body->ref();
265 setScope(sc);
266}
267
268DeclaredFunctionImp::~DeclaredFunctionImp()
269{
270 if ( body->deref() )
271 delete body;
272}
273
274bool DeclaredFunctionImp::implementsConstruct() const
275{
276 return true;
277}
278
279// ECMA 13.2.2 [[Construct]]
280Object DeclaredFunctionImp::construct(ExecState *exec, const List &args)
281{
282 Object proto;
283 Value p = get(exec,prototypePropertyName);
284 if (p.type() == ObjectType)
285 proto = Object(static_cast<ObjectImp*>(p.imp()));
286 else
287 proto = exec->lexicalInterpreter()->builtinObjectPrototype();
288
289 Object obj(new ObjectImp(proto));
290
291 Value res = call(exec,obj,args);
292
293 if (res.type() == ObjectType)
294 return Object::dynamicCast(res);
295 else
296 return obj;
297}
298
299Completion DeclaredFunctionImp::execute(ExecState *exec)
300{
301 Completion result = body->execute(exec);
302
303 if (result.complType() == Throw || result.complType() == ReturnValue)
304 return result;
305 return Completion(Normal, Undefined()); // TODO: or ReturnValue ?
306}
307
308void DeclaredFunctionImp::processVarDecls(ExecState *exec)
309{
310 body->processVarDecls(exec);
311}
312
313// ------------------------------ ArgumentsImp ---------------------------------
314
315const ClassInfo ArgumentsImp::info = {"Arguments", 0, 0, 0};
316
317// ECMA 10.1.8
318ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func)
319 : ArrayInstanceImp(exec->lexicalInterpreter()->builtinObjectPrototype().imp(), 0)
320{
321 Value protect(this);
322 putDirect(calleePropertyName, func, DontEnum);
323}
324
325ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func, const List &args)
326 : ArrayInstanceImp(exec->lexicalInterpreter()->builtinObjectPrototype().imp(), args)
327{
328 Value protect(this);
329 putDirect(calleePropertyName, func, DontEnum);
330}
331
332// ------------------------------ ActivationImp --------------------------------
333
334const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
335
336// ECMA 10.1.6
337ActivationImp::ActivationImp(FunctionImp *function, const List &arguments)
338 : _function(function), _arguments(true), _argumentsObject(0)
339{
340 _arguments = arguments.copy();
341 // FIXME: Do we need to support enumerating the arguments property?
342}
343
344Value ActivationImp::get(ExecState *exec, const Identifier &propertyName) const
345{
346 if (propertyName == argumentsPropertyName) {
347 // check for locally declared arguments property
348 ValueImp *v = getDirect(propertyName);
349 if (v)
350 return Value(v);
351
352 // default: return builtin arguments array
353 if (!_argumentsObject)
354 createArgumentsObject(exec);
355 return Value(_argumentsObject);
356 }
357 return ObjectImp::get(exec, propertyName);
358}
359
360bool ActivationImp::hasOwnProperty(ExecState *exec, const Identifier &propertyName) const
361{
362 if (propertyName == argumentsPropertyName)
363 return true;
364 return ObjectImp::hasOwnProperty(exec, propertyName);
365}
366
367bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
368{
369 if (propertyName == argumentsPropertyName)
370 return false;
371 return ObjectImp::deleteProperty(exec, propertyName);
372}
373
374void ActivationImp::mark()
375{
376 if (_function && !_function->marked())
377 _function->mark();
378 _arguments.mark();
379 if (_argumentsObject && !_argumentsObject->marked())
380 _argumentsObject->mark();
381 ObjectImp::mark();
382}
383
384void ActivationImp::createArgumentsObject(ExecState *exec) const
385{
386 _argumentsObject = new ArgumentsImp(exec, _function, _arguments);
387}
388
389// ------------------------------ GlobalFunc -----------------------------------
390
391
392GlobalFuncImp::GlobalFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto, int i, int len)
393 : InternalFunctionImp(funcProto), id(i)
394{
395 Value protect(this);
396 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
397}
398
399CodeType GlobalFuncImp::codeType() const
400{
401 return id == Eval ? EvalCode : codeType();
402}
403
404bool GlobalFuncImp::implementsCall() const
405{
406 return true;
407}
408
409static Value encode(ExecState *exec, const List &args, const char *do_not_escape)
410{
411 UString r = "", s, str = args[0].toString(exec);
412 CString cstr = str.UTF8String();
413 const char *p = cstr.c_str();
414 for (int k = 0; k < cstr.size(); k++, p++) {
415 char c = *p;
416 if (c && strchr(do_not_escape, c)) {
417 r.append(c);
418 } else {
419 char tmp[4];
420 sprintf(tmp, "%%%02X", (unsigned char)c);
421 r += tmp;
422 }
423 }
424 return String(r);
425}
426
427static Value decode(ExecState *exec, const List &args, const char *do_not_unescape, bool strict)
428{
429 UString s = "", str = args[0].toString(exec);
430 int k = 0, len = str.size();
431 const UChar *d = str.data();
432 UChar u;
433 while (k < len) {
434 const UChar *p = d + k;
435 UChar c = *p;
436 if (c == '%') {
437 int charLen = 0;
438 if (k <= len - 3 && isxdigit(p[1].uc) && isxdigit(p[2].uc)) {
439 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
440 const int sequenceLen = UTF8SequenceLength(b0);
441 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
442 charLen = sequenceLen * 3;
443 char sequence[5];
444 sequence[0] = b0;
445 for (int i = 1; i < sequenceLen; ++i) {
446 const UChar *q = p + i * 3;
447 if (q[0] == '%' && isxdigit(q[1].uc) && isxdigit(q[2].uc))
448 sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
449 else {
450 charLen = 0;
451 break;
452 }
453 }
454 if (charLen != 0) {
455 sequence[sequenceLen] = 0;
456 const int character = decodeUTF8Sequence(sequence);
457 if (character < 0 || character >= 0x110000) {
458 charLen = 0;
459 } else if (character >= 0x10000) {
460 // Convert to surrogate pair.
461 s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
462 u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
463 } else {
464 u = static_cast<unsigned short>(character);
465 }
466 }
467 }
468 }
469 if (charLen == 0) {
470 if (strict) {
471 Object error = Error::create(exec, URIError);
472 exec->setException(error);
473 return error;
474 }
475 // The only case where we don't use "strict" mode is the "unescape" function.
476 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
477 if (k <= len - 6 && p[1] == 'u'
478 && isxdigit(p[2].uc) && isxdigit(p[3].uc)
479 && isxdigit(p[4].uc) && isxdigit(p[5].uc)) {
480 charLen = 6;
481 u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
482 }
483 }
484 if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
485 c = u;
486 k += charLen - 1;
487 }
488 }
489 k++;
490 s.append(c);
491 }
492 return String(s);
493}
494
495static bool isStrWhiteSpace(unsigned short c)
496{
497 switch (c) {
498 case 0x0009:
499 case 0x000A:
500 case 0x000B:
501 case 0x000C:
502 case 0x000D:
503 case 0x0020:
504 case 0x00A0:
505 case 0x2028:
506 case 0x2029:
507 return true;
508 default:
509#if APPLE_CHANGES
510 return u_charType(c) == U_SPACE_SEPARATOR;
511#else
512 // ### properly support other Unicode Zs characters
513 return false;
514#endif
515 }
516}
517
518static int parseDigit(unsigned short c, int radix)
519{
520 int digit = -1;
521
522 if (c >= '0' && c <= '9') {
523 digit = c - '0';
524 } else if (c >= 'A' && c <= 'Z') {
525 digit = c - 'A' + 10;
526 } else if (c >= 'a' && c <= 'z') {
527 digit = c - 'a' + 10;
528 }
529
530 if (digit >= radix)
531 return -1;
532 return digit;
533}
534
535static double parseInt(const UString &s, int radix)
536{
537 int length = s.size();
538 int p = 0;
539
540 while (p < length && isStrWhiteSpace(s[p].uc)) {
541 ++p;
542 }
543
544 double sign = 1;
545 if (p < length) {
546 if (s[p] == '+') {
547 ++p;
548 } else if (s[p] == '-') {
549 sign = -1;
550 ++p;
551 }
552 }
553
554 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
555 radix = 16;
556 p += 2;
557 } else if (radix == 0) {
558 if (p < length && s[p] == '0')
559 radix = 8;
560 else
561 radix = 10;
562 }
563
564 if (radix < 2 || radix > 36)
565 return NaN;
566
567 bool sawDigit = false;
568 double number = 0;
569 while (p < length) {
570 int digit = parseDigit(s[p].uc, radix);
571 if (digit == -1)
572 break;
573 sawDigit = true;
574 number *= radix;
575 number += digit;
576 ++p;
577 }
578
579 if (!sawDigit)
580 return NaN;
581
582 return sign * number;
583}
584
585static double parseFloat(const UString &s)
586{
587 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
588 // Need to skip any whitespace and then one + or - sign.
589 int length = s.size();
590 int p = 0;
591 while (p < length && isStrWhiteSpace(s[p].uc)) {
592 ++p;
593 }
594 if (p < length && (s[p] == '+' || s[p] == '-')) {
595 ++p;
596 }
597 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
598 return 0;
599 }
600
601 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
602}
603
604Value GlobalFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
605{
606 Value res;
607
608 static const char do_not_escape[] =
609 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
610 "abcdefghijklmnopqrstuvwxyz"
611 "0123456789"
612 "*+-./@_";
613
614 static const char do_not_escape_when_encoding_URI_component[] =
615 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
616 "abcdefghijklmnopqrstuvwxyz"
617 "0123456789"
618 "!'()*-._~";
619 static const char do_not_escape_when_encoding_URI[] =
620 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
621 "abcdefghijklmnopqrstuvwxyz"
622 "0123456789"
623 "!#$&'()*+,-./:;=?@_~";
624 static const char do_not_unescape_when_decoding_URI[] =
625 "#$&+,/:;=?@";
626
627 switch (id) {
628 case Eval: { // eval()
629 Value x = args[0];
630 if (x.type() != StringType)
631 return x;
632 else {
633 UString s = x.toString(exec);
634
635 int sid;
636 int errLine;
637 UString errMsg;
638 ProgramNode *progNode = Parser::parse(UString(), 0, s.data(),s.size(),&sid,&errLine,&errMsg);
639
640 // no program node means a syntax occurred
641 if (!progNode) {
642 Object err = Error::create(exec,SyntaxError,errMsg.ascii(),errLine);
643 err.put(exec,"sid",Number(sid));
644 exec->setException(err);
645 return err;
646 }
647
648 progNode->ref();
649
650 // enter a new execution context
651 Object thisVal(Object::dynamicCast(exec->context().thisValue()));
652 ContextImp ctx(exec->dynamicInterpreter()->globalObject(),
653 exec->dynamicInterpreter()->imp(),
654 thisVal,
655 EvalCode,
656 exec->context().imp());
657
658 ExecState newExec(exec->dynamicInterpreter(), &ctx);
659 newExec.setException(exec->exception()); // could be null
660
661 // execute the code
662 progNode->processVarDecls(&newExec);
663 Completion c = progNode->execute(&newExec);
664
665 // if an exception occured, propogate it back to the previous execution object
666 if (newExec.hadException())
667 exec->setException(newExec.exception());
668
669 if ( progNode->deref() )
670 delete progNode;
671 if (c.complType() == ReturnValue)
672 return c.value();
673 // ### setException() on throw?
674 else if (c.complType() == Normal) {
675 if (c.isValueCompletion())
676 return c.value();
677 else
678 return Undefined();
679 } else {
680 return Undefined();
681 }
682 }
683 break;
684 }
685 case ParseInt:
686 res = Number(parseInt(args[0].toString(exec), args[1].toInt32(exec)));
687 break;
688 case ParseFloat:
689 res = Number(parseFloat(args[0].toString(exec)));
690 break;
691 case IsNaN:
692 res = Boolean(isNaN(args[0].toNumber(exec)));
693 break;
694 case IsFinite: {
695 double n = args[0].toNumber(exec);
696 res = Boolean(!isNaN(n) && !isInf(n));
697 break;
698 }
699 case DecodeURI:
700 res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
701 break;
702 case DecodeURIComponent:
703 res = decode(exec, args, "", true);
704 break;
705 case EncodeURI:
706 res = encode(exec, args, do_not_escape_when_encoding_URI);
707 break;
708 case EncodeURIComponent:
709 res = encode(exec, args, do_not_escape_when_encoding_URI_component);
710 break;
711 case Escape:
712 {
713 UString r = "", s, str = args[0].toString(exec);
714 const UChar *c = str.data();
715 for (int k = 0; k < str.size(); k++, c++) {
716 int u = c->uc;
717 if (u > 255) {
718 char tmp[7];
719 sprintf(tmp, "%%u%04X", u);
720 s = UString(tmp);
721 } else if (u != 0 && strchr(do_not_escape, (char)u)) {
722 s = UString(c, 1);
723 } else {
724 char tmp[4];
725 sprintf(tmp, "%%%02X", u);
726 s = UString(tmp);
727 }
728 r += s;
729 }
730 res = String(r);
731 break;
732 }
733 case UnEscape:
734 {
735 UString s = "", str = args[0].toString(exec);
736 int k = 0, len = str.size();
737 while (k < len) {
738 const UChar *c = str.data() + k;
739 UChar u;
740 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
741 if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
742 Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
743 u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
744 (c+4)->uc, (c+5)->uc);
745 c = &u;
746 k += 5;
747 }
748 } else if (*c == UChar('%') && k <= len - 3 &&
749 Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
750 u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
751 c = &u;
752 k += 2;
753 }
754 k++;
755 s += UString(c, 1);
756 }
757 res = String(s);
758 break;
759 }
760#ifndef NDEBUG
761 case KJSPrint:
762 puts(args[0].toString(exec).ascii());
763 break;
764#endif
765 }
766
767 return res;
768}
769
770} // namespace
Note: See TracBrowser for help on using the repository browser.