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

Last change on this file since 4837 was 4837, checked in by darin, 22 years ago

Reviewed by Maciej.

  • fixed 3247528 -- encodeURI missing from JavaScriptCore (needed by Crystal Reports)
  • fixed 3381297 -- escape method does not escape the null character
  • fixed 3381299 -- escape method produces incorrect escape sequences ala WinIE, rather than correct ala Gecko
  • fixed 3381303 -- unescape method treats escape sequences as Latin-1 ala WinIE rather than as UTF-8 ala Gecko
  • fixed 3381304 -- unescape method garbles strings with bad escape sequences in them
  • kjs/function.h: Added constants for decodeURI, decodeURIComponent, encodeURI, and encodeURIComponent.
  • kjs/function.cpp: (encode): Added. New helper function for escape, encodeURI, and encodeURIComponent. (decode): Added. New helper function for unescape, decodeURI, and decodeURIComponent. (GlobalFuncImp::call): Added decodeURI, decodeURIComponent, encodeURI, and encodeURIComponent implementations. Changed escape and unescape to use new helper functions, which fixes the four problems above.
  • kjs/internal.cpp: (InterpreterImp::initGlobalObject): Add decodeURI, decodeURIComponent, encodeURI, and encodeURIComponent to the global object.
  • kjs/ustring.h: Added a length to the CString class so it can hold strings with null characters in them, not just null-terminated strings. This allows a null character from a UString to survive the process of UTF-16 to UTF-8 decoding. Added overloads to UString::append, UString::UTF8String, UTF8SequenceLength, decodeUTF8Sequence, convertUTF16OffsetsToUTF8Offsets, and convertUTF8OffsetsToUTF16Offsets.
  • kjs/ustring.cpp: (CString::CString): Set up the length properly in all the constructors. Also add a new constructor that takes a length. (CString::append): Use and set the length properly. (CString::operator=): Use and set the length properly. (operator==): Use and the length and memcmp instead of strcmp. (UString::append): Added new overloads for const char * and for a single string to make it more efficient to build up a UString from pieces. The old way, a UString was created and destroyed each time you appended. (UTF8SequenceLength): New. Helper for decoding UTF-8. (decodeUTF8Sequence): New. Helper for decoding UTF-8. (UString::UTF8String): New. Decodes from UTF-16 to UTF-8. Same as the function that was in regexp.cpp, except has proper handling for UTF-16 surrogates. (compareStringOffsets): Moved from regexp.cpp. (createSortedOffsetsArray): Moved from regexp.cpp. (convertUTF16OffsetsToUTF8Offsets): New. Converts UTF-16 offsets to UTF-8 offsets, given a UTF-8 string. Same as the function that was in regexp.cpp, except has proper handling for UTF-16 surrogates. (convertUTF8OffsetsToUTF16Offsets): New. Converts UTF-8 offsets to UTF-16 offsets, given a UTF-8 string. Same as the function that was in regexp.cpp, except has proper handling for UTF-16 surrogates.
  • fixed 3381296 -- regular expression matches with UTF-16 surrogates will treat sequences as two characters
  • kjs/regexp.cpp: (RegExp::RegExp): Use the new UString::UTF8String function instead a function in this file. (RegExp::match): Use the new convertUTF16OffsetsToUTF8Offsets (and the corresponding reverse) instead of convertCharacterOffsetsToUTF8ByteOffsets in this file.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.2 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
41using namespace KJS;
42
43// ----------------------------- FunctionImp ----------------------------------
44
45const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
46
47namespace KJS {
48 class Parameter {
49 public:
50 Parameter(const Identifier &n) : name(n), next(0L) { }
51 ~Parameter() { delete next; }
52 Identifier name;
53 Parameter *next;
54 };
55};
56
57FunctionImp::FunctionImp(ExecState *exec, const Identifier &n)
58 : InternalFunctionImp(
59 static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
60 ), param(0L), ident(n)
61{
62 //fprintf(stderr,"FunctionImp::FunctionImp this=%p\n");
63}
64
65FunctionImp::~FunctionImp()
66{
67 delete param;
68}
69
70bool FunctionImp::implementsCall() const
71{
72 return true;
73}
74
75Value FunctionImp::call(ExecState *exec, Object &thisObj, const List &args)
76{
77 Object &globalObj = exec->interpreter()->globalObject();
78
79 Debugger *dbg = exec->interpreter()->imp()->debugger();
80 int sid = -1;
81 int lineno = -1;
82 if (dbg) {
83 if (inherits(&DeclaredFunctionImp::info)) {
84 sid = static_cast<DeclaredFunctionImp*>(this)->body->sourceId();
85 lineno = static_cast<DeclaredFunctionImp*>(this)->body->firstLine();
86 }
87
88 Object func(this);
89 bool cont = dbg->callEvent(exec,sid,lineno,func,args);
90 if (!cont) {
91 dbg->imp()->abort();
92 return Undefined();
93 }
94 }
95
96 // enter a new execution context
97 ContextImp ctx(globalObj, exec->interpreter()->imp(), thisObj, codeType(),
98 exec->context().imp(), this, &args);
99 ExecState newExec(exec->interpreter(), &ctx);
100 newExec.setException(exec->exception()); // could be null
101
102 // assign user supplied arguments to parameters
103 processParameters(&newExec, args);
104 // add variable declarations (initialized to undefined)
105 processVarDecls(&newExec);
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 exec->setException(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 Object func(this);
124 int cont = dbg->returnEvent(exec,sid,lineno,func);
125 if (!cont) {
126 dbg->imp()->abort();
127 return Undefined();
128 }
129 }
130
131 if (comp.complType() == Throw) {
132 exec->setException(comp.value());
133 return comp.value();
134 }
135 else if (comp.complType() == ReturnValue)
136 return comp.value();
137 else
138 return Undefined();
139}
140
141void FunctionImp::addParameter(const Identifier &n)
142{
143 Parameter **p = &param;
144 while (*p)
145 p = &(*p)->next;
146
147 *p = new Parameter(n);
148}
149
150UString FunctionImp::parameterString() const
151{
152 UString s;
153 const Parameter *p = param;
154 while (p) {
155 if (!s.isEmpty())
156 s += ", ";
157 s += p->name.ustring();
158 p = p->next;
159 }
160
161 return s;
162}
163
164
165// ECMA 10.1.3q
166void FunctionImp::processParameters(ExecState *exec, const List &args)
167{
168 Object variable = exec->context().imp()->variableObject();
169
170#ifdef KJS_VERBOSE
171 fprintf(stderr, "---------------------------------------------------\n"
172 "processing parameters for %s call\n",
173 name().isEmpty() ? "(internal)" : name().ascii());
174#endif
175
176 if (param) {
177 ListIterator it = args.begin();
178 Parameter *p = param;
179 while (p) {
180 if (it != args.end()) {
181#ifdef KJS_VERBOSE
182 fprintf(stderr, "setting parameter %s ", p->name.ascii());
183 printInfo(exec,"to", *it);
184#endif
185 variable.put(exec, p->name, *it);
186 it++;
187 } else
188 variable.put(exec, p->name, Undefined());
189 p = p->next;
190 }
191 }
192#ifdef KJS_VERBOSE
193 else {
194 for (int i = 0; i < args.size(); i++)
195 printInfo(exec,"setting argument", args[i]);
196 }
197#endif
198}
199
200void FunctionImp::processVarDecls(ExecState */*exec*/)
201{
202}
203
204Value FunctionImp::get(ExecState *exec, const Identifier &propertyName) const
205{
206 // Find the arguments from the closest context.
207 if (propertyName == argumentsPropertyName) {
208 ContextImp *context = exec->_context;
209 while (context) {
210 if (context->function() == this)
211 return static_cast<ActivationImp *>
212 (context->activationObject())->get(exec, propertyName);
213 context = context->callingContext();
214 }
215 return Undefined();
216 }
217
218 // Compute length of parameters.
219 if (propertyName == lengthPropertyName) {
220 const Parameter * p = param;
221 int count = 0;
222 while (p) {
223 ++count;
224 p = p->next;
225 }
226 return Number(count);
227 }
228
229 return InternalFunctionImp::get(exec, propertyName);
230}
231
232void FunctionImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
233{
234 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
235 return;
236 InternalFunctionImp::put(exec, propertyName, value, attr);
237}
238
239bool FunctionImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
240{
241 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
242 return true;
243 return InternalFunctionImp::hasProperty(exec, propertyName);
244}
245
246bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
247{
248 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
249 return false;
250 return InternalFunctionImp::deleteProperty(exec, propertyName);
251}
252
253// ------------------------------ DeclaredFunctionImp --------------------------
254
255// ### is "Function" correct here?
256const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
257
258DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const Identifier &n,
259 FunctionBodyNode *b, const ScopeChain &sc)
260 : FunctionImp(exec,n), body(b)
261{
262 Value protect(this);
263 body->ref();
264 setScope(sc);
265}
266
267DeclaredFunctionImp::~DeclaredFunctionImp()
268{
269 if ( body->deref() )
270 delete body;
271}
272
273bool DeclaredFunctionImp::implementsConstruct() const
274{
275 return true;
276}
277
278// ECMA 13.2.2 [[Construct]]
279Object DeclaredFunctionImp::construct(ExecState *exec, const List &args)
280{
281 Object proto;
282 Value p = get(exec,prototypePropertyName);
283 if (p.type() == ObjectType)
284 proto = Object(static_cast<ObjectImp*>(p.imp()));
285 else
286 proto = exec->interpreter()->builtinObjectPrototype();
287
288 Object obj(new ObjectImp(proto));
289
290 Value res = call(exec,obj,args);
291
292 if (res.type() == ObjectType)
293 return Object::dynamicCast(res);
294 else
295 return obj;
296}
297
298Completion DeclaredFunctionImp::execute(ExecState *exec)
299{
300 Completion result = body->execute(exec);
301
302 if (result.complType() == Throw || result.complType() == ReturnValue)
303 return result;
304 return Completion(Normal, Undefined()); // TODO: or ReturnValue ?
305}
306
307void DeclaredFunctionImp::processVarDecls(ExecState *exec)
308{
309 body->processVarDecls(exec);
310}
311
312// ------------------------------ ArgumentsImp ---------------------------------
313
314const ClassInfo ArgumentsImp::info = {"Arguments", 0, 0, 0};
315
316// ECMA 10.1.8
317ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func)
318 : ArrayInstanceImp(exec->interpreter()->builtinObjectPrototype().imp(), 0)
319{
320 Value protect(this);
321 putDirect(calleePropertyName, func, DontEnum);
322}
323
324ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func, const List &args)
325 : ArrayInstanceImp(exec->interpreter()->builtinObjectPrototype().imp(), args)
326{
327 Value protect(this);
328 putDirect(calleePropertyName, func, DontEnum);
329}
330
331// ------------------------------ ActivationImp --------------------------------
332
333const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
334
335// ECMA 10.1.6
336ActivationImp::ActivationImp(FunctionImp *function, const List &arguments)
337 : _function(function), _arguments(true), _argumentsObject(0)
338{
339 _arguments = arguments.copy();
340 // FIXME: Do we need to support enumerating the arguments property?
341}
342
343Value ActivationImp::get(ExecState *exec, const Identifier &propertyName) const
344{
345 if (propertyName == argumentsPropertyName) {
346 if (!_argumentsObject)
347 createArgumentsObject(exec);
348 return Value(_argumentsObject);
349 }
350 return ObjectImp::get(exec, propertyName);
351}
352
353void ActivationImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
354{
355 if (propertyName == argumentsPropertyName) {
356 // FIXME: Do we need to allow overwriting this?
357 return;
358 }
359 ObjectImp::put(exec, propertyName, value, attr);
360}
361
362bool ActivationImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
363{
364 if (propertyName == argumentsPropertyName)
365 return true;
366 return ObjectImp::hasProperty(exec, propertyName);
367}
368
369bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
370{
371 if (propertyName == argumentsPropertyName)
372 return false;
373 return ObjectImp::deleteProperty(exec, propertyName);
374}
375
376void ActivationImp::mark()
377{
378 if (_function && !_function->marked())
379 _function->mark();
380 _arguments.mark();
381 if (_argumentsObject && !_argumentsObject->marked())
382 _argumentsObject->mark();
383 ObjectImp::mark();
384}
385
386void ActivationImp::createArgumentsObject(ExecState *exec) const
387{
388 _argumentsObject = new ArgumentsImp(exec, _function, _arguments);
389}
390
391// ------------------------------ GlobalFunc -----------------------------------
392
393
394GlobalFuncImp::GlobalFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto, int i, int len)
395 : InternalFunctionImp(funcProto), id(i)
396{
397 Value protect(this);
398 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
399}
400
401CodeType GlobalFuncImp::codeType() const
402{
403 return id == Eval ? EvalCode : codeType();
404}
405
406bool GlobalFuncImp::implementsCall() const
407{
408 return true;
409}
410
411static Value encode(ExecState *exec, const List &args, const char *do_not_escape)
412{
413 UString r = "", s, str = args[0].toString(exec);
414 CString cstr = str.UTF8String();
415 const char *p = cstr.c_str();
416 for (int k = 0; k < cstr.size(); k++, p++) {
417 char c = *p;
418 if (c && strchr(do_not_escape, c)) {
419 r.append(c);
420 } else {
421 char tmp[4];
422 sprintf(tmp, "%%%02X", (unsigned char)c);
423 r += tmp;
424 }
425 }
426 return String(r);
427}
428
429static Value decode(ExecState *exec, const List &args, const char *do_not_unescape, bool strict)
430{
431 UString s = "", str = args[0].toString(exec);
432 int k = 0, len = str.size();
433 const UChar *d = str.data();
434 UChar u;
435 while (k < len) {
436 const UChar *p = d + k;
437 UChar c = *p;
438 if (c == '%') {
439 int charLen = 0;
440 if (k <= len - 3 && isxdigit(p[1].uc) && isxdigit(p[2].uc)) {
441 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
442 const int sequenceLen = UTF8SequenceLength(b0);
443 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
444 charLen = sequenceLen * 3;
445 char sequence[5];
446 sequence[0] = b0;
447 for (int i = 1; i < sequenceLen; ++i) {
448 const UChar *q = p + i * 3;
449 if (q[0] == '%' && isxdigit(q[1].uc) && isxdigit(q[2].uc))
450 sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
451 else {
452 charLen = 0;
453 break;
454 }
455 }
456 if (charLen != 0) {
457 sequence[sequenceLen] = 0;
458 const int character = decodeUTF8Sequence(sequence);
459 if (character < 0 || character >= 0x110000) {
460 charLen = 0;
461 } else if (character >= 0x10000) {
462 // Convert to surrogate pair.
463 s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
464 u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
465 } else {
466 u = static_cast<unsigned short>(character);
467 }
468 }
469 }
470 }
471 if (charLen == 0) {
472 if (strict) {
473 Object error = Error::create(exec, URIError);
474 exec->setException(error);
475 return error;
476 }
477 // The only case where we don't use "strict" mode is the "unescape" function.
478 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
479 if (k <= len - 6 && p[1] == 'u'
480 && isxdigit(p[2].uc) && isxdigit(p[3].uc)
481 && isxdigit(p[4].uc) && isxdigit(p[5].uc)) {
482 charLen = 6;
483 u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
484 }
485 }
486 if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
487 c = u;
488 k += charLen - 1;
489 }
490 }
491 k++;
492 s.append(c);
493 }
494 return String(s);
495}
496
497Value GlobalFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
498{
499 Value res;
500
501 static const char do_not_escape[] =
502 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
503 "abcdefghijklmnopqrstuvwxyz"
504 "0123456789"
505 "*+-./@_";
506 static const char do_not_escape_when_encoding_URI_component[] =
507 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
508 "abcdefghijklmnopqrstuvwxyz"
509 "0123456789"
510 "!'()*-._~";
511 static const char do_not_escape_when_encoding_URI[] =
512 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
513 "abcdefghijklmnopqrstuvwxyz"
514 "0123456789"
515 "!#$&'()*+,-./:;=?@_~";
516 static const char do_not_unescape_when_decoding_URI[] =
517 "#$&+,/:;=?@";
518
519 switch (id) {
520 case Eval: { // eval()
521 Value x = args[0];
522 if (x.type() != StringType)
523 return x;
524 else {
525 UString s = x.toString(exec);
526
527 int sid;
528 int errLine;
529 UString errMsg;
530 ProgramNode *progNode = Parser::parse(s.data(),s.size(),&sid,&errLine,&errMsg);
531
532 // no program node means a syntax occurred
533 if (!progNode) {
534 Object err = Error::create(exec,SyntaxError,errMsg.ascii(),errLine);
535 err.put(exec,"sid",Number(sid));
536 exec->setException(err);
537 return err;
538 }
539
540 progNode->ref();
541
542 // enter a new execution context
543 Object thisVal(Object::dynamicCast(exec->context().thisValue()));
544 ContextImp ctx(exec->interpreter()->globalObject(),
545 exec->interpreter()->imp(),
546 thisVal,
547 EvalCode,
548 exec->context().imp());
549
550 ExecState newExec(exec->interpreter(), &ctx);
551 newExec.setException(exec->exception()); // could be null
552
553 // execute the code
554 Completion c = progNode->execute(&newExec);
555
556 // if an exception occured, propogate it back to the previous execution object
557 if (newExec.hadException())
558 exec->setException(newExec.exception());
559
560 if ( progNode->deref() )
561 delete progNode;
562 if (c.complType() == ReturnValue)
563 return c.value();
564 // ### setException() on throw?
565 else if (c.complType() == Normal) {
566 if (c.isValueCompletion())
567 return c.value();
568 else
569 return Undefined();
570 } else {
571 return Undefined();
572 }
573 }
574 break;
575 }
576 case ParseInt: {
577 CString cstr = args[0].toString(exec).cstring();
578 int radix = args[1].toInt32(exec);
579
580 char* endptr;
581 errno = 0;
582#ifdef HAVE_FUNC_STRTOLL
583 long long llValue = strtoll(cstr.c_str(), &endptr, radix);
584 double value = llValue;
585#else
586 long value = strtol(cstr.c_str(), &endptr, radix);
587#endif
588 if (errno != 0 || endptr == cstr.c_str())
589 res = Number(NaN);
590 else
591 res = Number(value);
592 break;
593 }
594 case ParseFloat:
595 res = Number(args[0].toString(exec).toDouble( true /*tolerant*/ ));
596 break;
597 case IsNaN:
598 res = Boolean(isNaN(args[0].toNumber(exec)));
599 break;
600 case IsFinite: {
601 double n = args[0].toNumber(exec);
602 res = Boolean(!isNaN(n) && !isInf(n));
603 break;
604 }
605 case DecodeURI:
606 res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
607 break;
608 case DecodeURIComponent:
609 res = decode(exec, args, "", true);
610 break;
611 case EncodeURI:
612 res = encode(exec, args, do_not_escape_when_encoding_URI);
613 break;
614 case EncodeURIComponent:
615 res = encode(exec, args, do_not_escape_when_encoding_URI_component);
616 break;
617 case Escape:
618 res = encode(exec, args, do_not_escape);
619 break;
620 case UnEscape:
621 res = decode(exec, args, "", false);
622 break;
623#ifndef NDEBUG
624 case KJSPrint:
625 puts(args[0].toString(exec).ascii());
626 break;
627#endif
628 }
629
630 return res;
631}
Note: See TracBrowser for help on using the repository browser.