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

Last change on this file since 13597 was 13304, checked in by mjs, 19 years ago

Reviewed by Anders.


The memory usage of Node was reduced by 2 machine words per node:

  • sourceURL was removed and only kept on FunctionBodyNode. The source URL can only be distinct per function or top-level program node, and you always have one.


  • refcount was removed and kept in a separate hashtable when greater than 1. newNodes set represents floating nodes with refcount of 0. This helps because almost all nodes have a refcount of 1 for almost all of their lifetime.


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