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

Last change on this file since 11566 was 11566, checked in by andersca, 19 years ago

2005-12-12 Anders Carlsson <[email protected]>

Reviewed by Darin.

  • bindings/runtime_array.cpp: (RuntimeArray::lengthGetter): (RuntimeArray::indexGetter):
  • bindings/runtime_array.h:
  • bindings/runtime_method.cpp: (RuntimeMethod::lengthGetter):
  • bindings/runtime_method.h:
  • bindings/runtime_object.cpp: (RuntimeObjectImp::fallbackObjectGetter): (RuntimeObjectImp::fieldGetter): (RuntimeObjectImp::methodGetter):
  • bindings/runtime_object.h:
  • kjs/array_instance.h:
  • kjs/array_object.cpp: (ArrayInstance::lengthGetter): (getProperty): Update for changes to PropertySlot::getValue and PropertySlot::GetValueFunc.
  • kjs/collector.cpp: (KJS::className): Handle GetterSetterType.
  • kjs/function.cpp: (KJS::FunctionImp::argumentsGetter): (KJS::FunctionImp::lengthGetter): (KJS::Arguments::mappedIndexGetter): (KJS::ActivationImp::argumentsGetter):
  • kjs/function.h: Update for changes to PropertySlot::getValue and PropertySlot::GetValueFunc.
  • kjs/grammar.y: Rework grammar parts for get set declarations directly in the object literal.
  • kjs/internal.cpp: (KJS::GetterSetterImp::mark): (KJS::GetterSetterImp::toPrimitive): (KJS::GetterSetterImp::toBoolean): (KJS::GetterSetterImp::toNumber): (KJS::GetterSetterImp::toString): (KJS::GetterSetterImp::toObject): Add type conversion functions. These aren't meant to be called.

(KJS::printInfo):
Handle GetterSetterType.

  • kjs/lookup.h: (KJS::staticFunctionGetter): (KJS::staticValueGetter): Update for changes to PropertySlot::GetValueFunc.
  • kjs/nodes.cpp: Refactor they way properties nodes are implemented. We now have a PropertyListNode which is a list of PropertyNodes. Each PropertyNode has a name (which is a PropertyNameNode) and an associated value node. PropertyNodes can be of different types. The Constant type is the old constant declaration and the Getter and Setter types are for property getters and setters. (ResolveNode::evaluate): Update for changes to PropertySlot::getValue.

(PropertyListNode::evaluate):
Go through all property nodes and set them on the newly created object. If the
property nodes are of type Getter or Setter, define getters and setters. Otherwise,
just add the properties like before.

(PropertyNode::evaluate):
This should never be called directly.

(PropertyNameNode::evaluate):
Rename from PropertyNode::evaluate.

(FunctionCallResolveNode::evaluate):
(FunctionCallBracketNode::evaluate):
(FunctionCallDotNode::evaluate):
(PostfixResolveNode::evaluate):
(PostfixBracketNode::evaluate):
(PostfixDotNode::evaluate):
(TypeOfResolveNode::evaluate):
(PrefixResolveNode::evaluate):
(PrefixBracketNode::evaluate):
(PrefixDotNode::evaluate):
(AssignResolveNode::evaluate):
(AssignDotNode::evaluate):
(AssignBracketNode::evaluate):
Update for changes to PropertySlot::getValue.

  • kjs/nodes.h: (KJS::PropertyNameNode::PropertyNameNode): Rename from PropertyNode.

(KJS::PropertyNode::):
(KJS::PropertyNode::PropertyNode):
New class, representing a single property.

(KJS::PropertyListNode::PropertyListNode):
Rename from PropertyValueNode.

(KJS::FuncExprNode::FuncExprNode):
Put ParameterNode parameter last, and make it optional.

(KJS::ObjectLiteralNode::ObjectLiteralNode):
Use a PropertyListNode here now.

  • kjs/nodes2string.cpp: (PropertyListNode::streamTo): Iterate through all property nodes.

(PropertyNode::streamTo):
Print out the name and value. Doesn't handle getters and setters currently.

(PropertyNameNode::streamTo):
Rename from PropertyNode::streamTo.

  • kjs/object.cpp: (KJS::JSObject::get): Update for changes to PropertySlot::getValue.

(KJS::JSObject::put):
If the property already exists and has a Setter, invoke
the setter function instead of setting the property directly.

(KJS::JSObject::defineGetter):
(KJS::JSObject::defineSetter):
New functions for defining property getters and setters on the object.

  • kjs/object.h: (KJS::GetterSetterImp::type): (KJS::GetterSetterImp::GetterSetterImp): (KJS::GetterSetterImp::getGetter): (KJS::GetterSetterImp::setGetter): (KJS::GetterSetterImp::getSetter): (KJS::GetterSetterImp::setSetter): New class for properties which have getters and setters defined. This class is only used internally and should never be seen from the outside.

(KJS::JSObject::getOwnPropertySlot):

If the property is a getter, call setGetterSlot on the property slot.

  • kjs/object_object.cpp: (ObjectPrototype::ObjectPrototype): Add defineGetter, defineSetter, lookupGetter, lookupSetter to prototype.

(ObjectProtoFunc::callAsFunction):
Implement handlers for new functions.

  • kjs/object_object.h: (KJS::ObjectProtoFunc::): Add ids for new functions.
  • kjs/property_slot.cpp: (KJS::PropertySlot::undefinedGetter): Update for changes to PropertySlot::GetValueFunc.

(KJS::PropertySlot::functionGetter):
Call the function getter object and return its value.

  • kjs/property_slot.h: (KJS::PropertySlot::getValue): Add a new argument which is the original object that getPropertySlot was called on.

(KJS::PropertySlot::setGetterSlot):
(KJS::PropertySlot::):
New function which sets a getter slot. When getValue is called on a
getter slot, the getter function object is invoked.

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