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

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

Reviewed by John.

  • fixed <rdar://problem/4232452> many many leaks in kjsyyparse on some well-formed JavaScript (can repro on sony.com, webkit tests)

Fixed by changing the refcounting scheme for nodes. Instead of each node implementing a custom ref and
deref for all its children (and being responsible for deleting them), nodes use a smart pointer to
hold their children, and smart pointers are used outside the node tree as well. This change mostly
removes code.

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