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

Last change on this file since 2800 was 2800, checked in by darin, 23 years ago
  • add self-check to property map in hopes of finding the cnet.com bug
  • kjs/property_map.h: Add check() function.
  • kjs/property_map.cpp: Add the checking, controlled by DO_CONSISTENCY_CHECK.
  • fixed UChar interface so it's not so slow in debug builds
  • kjs/ustring.h: Nothing in UChar needs to be private.
  • kjs/function.cpp: (GlobalFuncImp::call):
  • kjs/function_object.cpp: (FunctionObjectImp::construct):
  • kjs/identifier.cpp:
  • kjs/lexer.cpp: (Lexer::setCode), (Lexer::shift):
  • kjs/lookup.cpp: (keysMatch):
  • kjs/ustring.cpp: (UString::Rep::computeHash), (KJS::compare): Use the "uc" field instead of the "unicode()" inline function.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.4 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 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 */
23
24#include "function.h"
25
26#include "internal.h"
27#include "function_object.h"
28#include "lexer.h"
29#include "nodes.h"
30#include "operations.h"
31#include "debugger.h"
32
33#include <stdio.h>
34#include <errno.h>
35#include <stdlib.h>
36#include <assert.h>
37#include <string.h>
38
39using namespace KJS;
40
41// ----------------------------- FunctionImp ----------------------------------
42
43const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
44
45namespace KJS {
46 class Parameter {
47 public:
48 Parameter(const Identifier &n) : name(n), next(0L) { }
49 ~Parameter() { delete next; }
50 Identifier name;
51 Parameter *next;
52 };
53};
54
55FunctionImp::FunctionImp(ExecState *exec, const Identifier &n)
56 : InternalFunctionImp(
57 static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
58 ), param(0L), ident(n)
59{
60 //fprintf(stderr,"FunctionImp::FunctionImp this=%p\n");
61}
62
63FunctionImp::~FunctionImp()
64{
65 delete param;
66}
67
68bool FunctionImp::implementsCall() const
69{
70 return true;
71}
72
73Value FunctionImp::call(ExecState *exec, Object &thisObj, const List &args)
74{
75 Object &globalObj = exec->interpreter()->globalObject();
76
77 Debugger *dbg = exec->interpreter()->imp()->debugger();
78 int sid = -1;
79 int lineno = -1;
80 if (dbg) {
81 if (inherits(&DeclaredFunctionImp::info)) {
82 sid = static_cast<DeclaredFunctionImp*>(this)->body->sourceId();
83 lineno = static_cast<DeclaredFunctionImp*>(this)->body->firstLine();
84 }
85
86 Object func(this);
87 bool cont = dbg->callEvent(exec,sid,lineno,func,args);
88 if (!cont) {
89 dbg->imp()->abort();
90 return Undefined();
91 }
92 }
93
94 // enter a new execution context
95 ContextImp ctx(globalObj, exec, thisObj, codeType(),
96 exec->context().imp(), this, &args);
97 ExecState newExec(exec->interpreter(), &ctx);
98 newExec.setException(exec->exception()); // could be null
99
100 // assign user supplied arguments to parameters
101 processParameters(&newExec, args);
102 // add variable declarations (initialized to undefined)
103 processVarDecls(&newExec);
104
105 Completion comp = execute(&newExec);
106
107 // if an exception occured, propogate it back to the previous execution object
108 if (newExec.hadException())
109 exec->setException(newExec.exception());
110
111#ifdef KJS_VERBOSE
112 if (comp.complType() == Throw)
113 printInfo(exec,"throwing", comp.value());
114 else if (comp.complType() == ReturnValue)
115 printInfo(exec,"returning", comp.value());
116 else
117 fprintf(stderr, "returning: undefined\n");
118#endif
119
120 if (dbg) {
121 Object func(this);
122 int cont = dbg->returnEvent(exec,sid,lineno,func);
123 if (!cont) {
124 dbg->imp()->abort();
125 return Undefined();
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 Undefined();
137}
138
139void FunctionImp::addParameter(const Identifier &n)
140{
141 Parameter **p = &param;
142 while (*p)
143 p = &(*p)->next;
144
145 *p = new Parameter(n);
146}
147
148UString FunctionImp::parameterString() const
149{
150 UString s;
151 const Parameter *p = param;
152 while (p) {
153 if (!s.isEmpty())
154 s += ", ";
155 s += p->name.ustring();
156 p = p->next;
157 }
158
159 return s;
160}
161
162
163// ECMA 10.1.3q
164void FunctionImp::processParameters(ExecState *exec, const List &args)
165{
166 Object 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;
177 while (p) {
178 if (it != args.end()) {
179#ifdef KJS_VERBOSE
180 fprintf(stderr, "setting parameter %s ", p->name.ascii());
181 printInfo(exec,"to", *it);
182#endif
183 variable.put(exec, p->name, *it);
184 it++;
185 } else
186 variable.put(exec, p->name, Undefined());
187 p = p->next;
188 }
189 }
190#ifdef KJS_VERBOSE
191 else {
192 for (int i = 0; i < args.size(); i++)
193 printInfo(exec,"setting argument", args[i]);
194 }
195#endif
196}
197
198void FunctionImp::processVarDecls(ExecState */*exec*/)
199{
200}
201
202Value FunctionImp::get(ExecState *exec, const Identifier &propertyName) const
203{
204 // Find the arguments from the closest context.
205 if (propertyName == argumentsPropertyName) {
206 ContextImp *context = exec->_context;
207 while (context) {
208 if (context->function() == this)
209 return static_cast<ActivationImp *>
210 (context->activationObject())->get(exec, propertyName);
211 context = context->callingContext();
212 }
213 return Undefined();
214 }
215
216 // Compute length of parameters.
217 if (propertyName == lengthPropertyName) {
218 const Parameter * p = param;
219 int count = 0;
220 while (p) {
221 ++count;
222 p = p->next;
223 }
224 return Number(count);
225 }
226
227 return InternalFunctionImp::get(exec, propertyName);
228}
229
230void FunctionImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
231{
232 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
233 return;
234 InternalFunctionImp::put(exec, propertyName, value, attr);
235}
236
237bool FunctionImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
238{
239 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
240 return true;
241 return InternalFunctionImp::hasProperty(exec, propertyName);
242}
243
244bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
245{
246 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
247 return false;
248 return InternalFunctionImp::deleteProperty(exec, propertyName);
249}
250
251// ------------------------------ DeclaredFunctionImp --------------------------
252
253// ### is "Function" correct here?
254const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
255
256DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const Identifier &n,
257 FunctionBodyNode *b, const List &sc)
258 : FunctionImp(exec,n), body(b)
259{
260 Value protect(this);
261 body->ref();
262 setScope(sc.copy());
263}
264
265DeclaredFunctionImp::~DeclaredFunctionImp()
266{
267 if ( body->deref() )
268 delete body;
269}
270
271bool DeclaredFunctionImp::implementsConstruct() const
272{
273 return true;
274}
275
276// ECMA 13.2.2 [[Construct]]
277Object DeclaredFunctionImp::construct(ExecState *exec, const List &args)
278{
279 Object proto;
280 Value p = get(exec,prototypePropertyName);
281 if (p.type() == ObjectType)
282 proto = Object(static_cast<ObjectImp*>(p.imp()));
283 else
284 proto = exec->interpreter()->builtinObjectPrototype();
285
286 Object obj(new ObjectImp(proto));
287
288 Value res = call(exec,obj,args);
289
290 if (res.type() == ObjectType)
291 return Object::dynamicCast(res);
292 else
293 return obj;
294}
295
296Completion DeclaredFunctionImp::execute(ExecState *exec)
297{
298 Completion result = body->execute(exec);
299
300 if (result.complType() == Throw || result.complType() == ReturnValue)
301 return result;
302 return Completion(Normal, Undefined()); // TODO: or ReturnValue ?
303}
304
305void DeclaredFunctionImp::processVarDecls(ExecState *exec)
306{
307 body->processVarDecls(exec);
308}
309
310// ------------------------------ ArgumentsImp ---------------------------------
311
312const ClassInfo ArgumentsImp::info = {"Arguments", 0, 0, 0};
313
314// ECMA 10.1.8
315ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func)
316 : ArrayInstanceImp(exec->interpreter()->builtinObjectPrototype().imp(), 0)
317{
318 Value protect(this);
319 putDirect(calleePropertyName, func, DontEnum);
320}
321
322ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func, const List &args)
323 : ArrayInstanceImp(exec->interpreter()->builtinObjectPrototype().imp(), args)
324{
325 Value protect(this);
326 putDirect(calleePropertyName, func, DontEnum);
327}
328
329// ------------------------------ ActivationImp --------------------------------
330
331const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
332
333// ECMA 10.1.6
334ActivationImp::ActivationImp(ContextImp *context)
335 : _context(context), _argumentsObject(0)
336{
337 // FIXME: Do we need to support enumerating the arguments property?
338}
339
340Value ActivationImp::get(ExecState *exec, const Identifier &propertyName) const
341{
342 if (propertyName == argumentsPropertyName) {
343 if (!_argumentsObject)
344 createArgumentsObject(exec);
345 return Value(_argumentsObject);
346 }
347 return ObjectImp::get(exec, propertyName);
348}
349
350void ActivationImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
351{
352 if (propertyName == argumentsPropertyName) {
353 // FIXME: Do we need to allow overwriting this?
354 return;
355 }
356 ObjectImp::put(exec, propertyName, value, attr);
357}
358
359bool ActivationImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
360{
361 if (propertyName == argumentsPropertyName)
362 return true;
363 return ObjectImp::hasProperty(exec, propertyName);
364}
365
366bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
367{
368 if (propertyName == argumentsPropertyName)
369 return false;
370 return ObjectImp::deleteProperty(exec, propertyName);
371}
372
373void ActivationImp::mark()
374{
375 if (_argumentsObject && !_argumentsObject->marked())
376 _argumentsObject->mark();
377 ObjectImp::mark();
378}
379
380void ActivationImp::createArgumentsObject(ExecState *exec) const
381{
382 FunctionImp *function = _context->function();
383 const ArgumentList *arguments = _context->arguments();
384 if (arguments)
385 _argumentsObject = new ArgumentsImp(exec, function, *arguments);
386 else
387 _argumentsObject = new ArgumentsImp(exec, function);
388}
389
390// ------------------------------ GlobalFunc -----------------------------------
391
392
393GlobalFuncImp::GlobalFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto, int i, int len)
394 : InternalFunctionImp(funcProto), id(i)
395{
396 Value protect(this);
397 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
398}
399
400CodeType GlobalFuncImp::codeType() const
401{
402 return id == Eval ? EvalCode : codeType();
403}
404
405bool GlobalFuncImp::implementsCall() const
406{
407 return true;
408}
409
410Value GlobalFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
411{
412 Value res;
413
414 static const char non_escape[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
415 "abcdefghijklmnopqrstuvwxyz"
416 "0123456789@*_+-./";
417
418 switch (id) {
419 case Eval: { // eval()
420 Value x = args[0];
421 if (x.type() != StringType)
422 return x;
423 else {
424 UString s = x.toString(exec);
425
426 int sid;
427 int errLine;
428 UString errMsg;
429 ProgramNode *progNode = Parser::parse(s.data(),s.size(),&sid,&errLine,&errMsg);
430
431 // no program node means a syntax occurred
432 if (!progNode) {
433 Object err = Error::create(exec,SyntaxError,errMsg.ascii(),errLine);
434 err.put(exec,"sid",Number(sid));
435 exec->setException(err);
436 return err;
437 }
438
439 progNode->ref();
440
441 // enter a new execution context
442 Object thisVal(Object::dynamicCast(exec->context().thisValue()));
443 ContextImp ctx(exec->interpreter()->globalObject(),
444 exec,
445 thisVal,
446 EvalCode,
447 exec->context().imp());
448
449 ExecState newExec(exec->interpreter(), &ctx);
450 newExec.setException(exec->exception()); // could be null
451
452 // execute the code
453 Completion c = progNode->execute(&newExec);
454
455 // if an exception occured, propogate it back to the previous execution object
456 if (newExec.hadException())
457 exec->setException(newExec.exception());
458
459 if ( progNode->deref() )
460 delete progNode;
461 if (c.complType() == ReturnValue)
462 return c.value();
463 // ### setException() on throw?
464 else if (c.complType() == Normal) {
465 if (c.isValueCompletion())
466 return c.value();
467 else
468 return Undefined();
469 } else {
470 return Undefined();
471 }
472 }
473 break;
474 }
475 case ParseInt: {
476 CString cstr = args[0].toString(exec).cstring();
477 int radix = args[1].toInt32(exec);
478
479 char* endptr;
480 errno = 0;
481#ifdef HAVE_FUNC_STRTOLL
482 long long llValue = strtoll(cstr.c_str(), &endptr, radix);
483 double value = llValue;
484#else
485 long value = strtoll(cstr.c_str(), &endptr, radix);
486#endif
487 if (errno != 0 || endptr == cstr.c_str())
488 res = Number(NaN);
489 else
490 res = Number(value);
491 break;
492 }
493 case ParseFloat:
494 res = Number(args[0].toString(exec).toDouble( true /*tolerant*/ ));
495 break;
496 case IsNaN:
497 res = Boolean(isNaN(args[0].toNumber(exec)));
498 break;
499 case IsFinite: {
500 double n = args[0].toNumber(exec);
501 res = Boolean(!isNaN(n) && !isInf(n));
502 break;
503 }
504 case Escape: {
505 UString r = "", s, str = args[0].toString(exec);
506 const UChar *c = str.data();
507 for (int k = 0; k < str.size(); k++, c++) {
508 int u = c->uc;
509 if (u > 255) {
510 char tmp[7];
511 sprintf(tmp, "%%u%04X", u);
512 s = UString(tmp);
513 } else if (strchr(non_escape, (char)u)) {
514 s = UString(c, 1);
515 } else {
516 char tmp[4];
517 sprintf(tmp, "%%%02X", u);
518 s = UString(tmp);
519 }
520 r += s;
521 }
522 res = String(r);
523 break;
524 }
525 case UnEscape: {
526 UString s, str = args[0].toString(exec);
527 int k = 0, len = str.size();
528 UChar u;
529 while (k < len) {
530 const UChar *c = str.data() + k;
531 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
532 u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
533 (c+4)->uc, (c+5)->uc);
534 c = &u;
535 k += 5;
536 } else if (*c == UChar('%') && k <= len - 3) {
537 u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
538 c = &u;
539 k += 2;
540 }
541 k++;
542 s += UString(c, 1);
543 }
544 res = String(s);
545 break;
546 }
547 }
548
549 return res;
550}
Note: See TracBrowser for help on using the repository browser.