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

Last change on this file since 3009 was 3009, checked in by mjs, 22 years ago

Reviewed by Don.

  • Add kjsprint global function in Development build for ease of debugging.
  • Print uncaught JavaScript exceptions to the console in Development.
  • Improve wording of exception error messages.
  • kjs/function.cpp: (GlobalFuncImp::call):
  • kjs/function.h:
  • kjs/internal.cpp: (InterpreterImp::initGlobalObject):
  • kjs/interpreter.cpp: (Interpreter::evaluate):
  • kjs/nodes.cpp: (NewExprNode::evaluate): (FunctionCallNode::evaluate): (RelationalNode::evaluate):
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.5 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#include "context.h"
33
34#include <stdio.h>
35#include <errno.h>
36#include <stdlib.h>
37#include <assert.h>
38#include <string.h>
39
40using namespace KJS;
41
42// ----------------------------- FunctionImp ----------------------------------
43
44const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
45
46namespace KJS {
47 class Parameter {
48 public:
49 Parameter(const Identifier &n) : name(n), next(0L) { }
50 ~Parameter() { delete next; }
51 Identifier name;
52 Parameter *next;
53 };
54};
55
56FunctionImp::FunctionImp(ExecState *exec, const Identifier &n)
57 : InternalFunctionImp(
58 static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
59 ), param(0L), ident(n)
60{
61 //fprintf(stderr,"FunctionImp::FunctionImp this=%p\n");
62}
63
64FunctionImp::~FunctionImp()
65{
66 delete param;
67}
68
69bool FunctionImp::implementsCall() const
70{
71 return true;
72}
73
74Value FunctionImp::call(ExecState *exec, Object &thisObj, const List &args)
75{
76 Object &globalObj = exec->interpreter()->globalObject();
77
78 Debugger *dbg = exec->interpreter()->imp()->debugger();
79 int sid = -1;
80 int lineno = -1;
81 if (dbg) {
82 if (inherits(&DeclaredFunctionImp::info)) {
83 sid = static_cast<DeclaredFunctionImp*>(this)->body->sourceId();
84 lineno = static_cast<DeclaredFunctionImp*>(this)->body->firstLine();
85 }
86
87 Object func(this);
88 bool cont = dbg->callEvent(exec,sid,lineno,func,args);
89 if (!cont) {
90 dbg->imp()->abort();
91 return Undefined();
92 }
93 }
94
95 // enter a new execution context
96 ContextImp ctx(globalObj, exec->interpreter()->imp(), thisObj, codeType(),
97 exec->context().imp(), this, &args);
98 ExecState newExec(exec->interpreter(), &ctx);
99 newExec.setException(exec->exception()); // could be null
100
101 // assign user supplied arguments to parameters
102 processParameters(&newExec, args);
103 // add variable declarations (initialized to undefined)
104 processVarDecls(&newExec);
105
106 Completion comp = execute(&newExec);
107
108 // if an exception occured, propogate it back to the previous execution object
109 if (newExec.hadException())
110 exec->setException(newExec.exception());
111
112#ifdef KJS_VERBOSE
113 if (comp.complType() == Throw)
114 printInfo(exec,"throwing", comp.value());
115 else if (comp.complType() == ReturnValue)
116 printInfo(exec,"returning", comp.value());
117 else
118 fprintf(stderr, "returning: undefined\n");
119#endif
120
121 if (dbg) {
122 Object func(this);
123 int cont = dbg->returnEvent(exec,sid,lineno,func);
124 if (!cont) {
125 dbg->imp()->abort();
126 return Undefined();
127 }
128 }
129
130 if (comp.complType() == Throw) {
131 exec->setException(comp.value());
132 return comp.value();
133 }
134 else if (comp.complType() == ReturnValue)
135 return comp.value();
136 else
137 return Undefined();
138}
139
140void FunctionImp::addParameter(const Identifier &n)
141{
142 Parameter **p = &param;
143 while (*p)
144 p = &(*p)->next;
145
146 *p = new Parameter(n);
147}
148
149UString FunctionImp::parameterString() const
150{
151 UString s;
152 const Parameter *p = param;
153 while (p) {
154 if (!s.isEmpty())
155 s += ", ";
156 s += p->name.ustring();
157 p = p->next;
158 }
159
160 return s;
161}
162
163
164// ECMA 10.1.3q
165void FunctionImp::processParameters(ExecState *exec, const List &args)
166{
167 Object variable = exec->context().imp()->variableObject();
168
169#ifdef KJS_VERBOSE
170 fprintf(stderr, "---------------------------------------------------\n"
171 "processing parameters for %s call\n",
172 name().isEmpty() ? "(internal)" : name().ascii());
173#endif
174
175 if (param) {
176 ListIterator it = args.begin();
177 Parameter *p = param;
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, *it);
185 it++;
186 } else
187 variable.put(exec, p->name, Undefined());
188 p = p->next;
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
203Value FunctionImp::get(ExecState *exec, const Identifier &propertyName) const
204{
205 // Find the arguments from the closest context.
206 if (propertyName == argumentsPropertyName) {
207 ContextImp *context = exec->_context;
208 while (context) {
209 if (context->function() == this)
210 return static_cast<ActivationImp *>
211 (context->activationObject())->get(exec, propertyName);
212 context = context->callingContext();
213 }
214 return Undefined();
215 }
216
217 // Compute length of parameters.
218 if (propertyName == lengthPropertyName) {
219 const Parameter * p = param;
220 int count = 0;
221 while (p) {
222 ++count;
223 p = p->next;
224 }
225 return Number(count);
226 }
227
228 return InternalFunctionImp::get(exec, propertyName);
229}
230
231void FunctionImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
232{
233 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
234 return;
235 InternalFunctionImp::put(exec, propertyName, value, attr);
236}
237
238bool FunctionImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
239{
240 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
241 return true;
242 return InternalFunctionImp::hasProperty(exec, propertyName);
243}
244
245bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
246{
247 if (propertyName == argumentsPropertyName || propertyName == lengthPropertyName)
248 return false;
249 return InternalFunctionImp::deleteProperty(exec, propertyName);
250}
251
252// ------------------------------ DeclaredFunctionImp --------------------------
253
254// ### is "Function" correct here?
255const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
256
257DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const Identifier &n,
258 FunctionBodyNode *b, const ScopeChain &sc)
259 : FunctionImp(exec,n), body(b)
260{
261 Value protect(this);
262 body->ref();
263 setScope(sc);
264}
265
266DeclaredFunctionImp::~DeclaredFunctionImp()
267{
268 if ( body->deref() )
269 delete body;
270}
271
272bool DeclaredFunctionImp::implementsConstruct() const
273{
274 return true;
275}
276
277// ECMA 13.2.2 [[Construct]]
278Object DeclaredFunctionImp::construct(ExecState *exec, const List &args)
279{
280 Object proto;
281 Value p = get(exec,prototypePropertyName);
282 if (p.type() == ObjectType)
283 proto = Object(static_cast<ObjectImp*>(p.imp()));
284 else
285 proto = exec->interpreter()->builtinObjectPrototype();
286
287 Object obj(new ObjectImp(proto));
288
289 Value res = call(exec,obj,args);
290
291 if (res.type() == ObjectType)
292 return Object::dynamicCast(res);
293 else
294 return obj;
295}
296
297Completion DeclaredFunctionImp::execute(ExecState *exec)
298{
299 Completion result = body->execute(exec);
300
301 if (result.complType() == Throw || result.complType() == ReturnValue)
302 return result;
303 return Completion(Normal, Undefined()); // TODO: or ReturnValue ?
304}
305
306void DeclaredFunctionImp::processVarDecls(ExecState *exec)
307{
308 body->processVarDecls(exec);
309}
310
311// ------------------------------ ArgumentsImp ---------------------------------
312
313const ClassInfo ArgumentsImp::info = {"Arguments", 0, 0, 0};
314
315// ECMA 10.1.8
316ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func)
317 : ArrayInstanceImp(exec->interpreter()->builtinObjectPrototype().imp(), 0)
318{
319 Value protect(this);
320 putDirect(calleePropertyName, func, DontEnum);
321}
322
323ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func, const List &args)
324 : ArrayInstanceImp(exec->interpreter()->builtinObjectPrototype().imp(), args)
325{
326 Value protect(this);
327 putDirect(calleePropertyName, func, DontEnum);
328}
329
330// ------------------------------ ActivationImp --------------------------------
331
332const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
333
334// ECMA 10.1.6
335ActivationImp::ActivationImp(FunctionImp *function, const List &arguments)
336 : _function(function), _arguments(true), _argumentsObject(0)
337{
338 _arguments = arguments.copy();
339 // FIXME: Do we need to support enumerating the arguments property?
340}
341
342Value ActivationImp::get(ExecState *exec, const Identifier &propertyName) const
343{
344 if (propertyName == argumentsPropertyName) {
345 if (!_argumentsObject)
346 createArgumentsObject(exec);
347 return Value(_argumentsObject);
348 }
349 return ObjectImp::get(exec, propertyName);
350}
351
352void ActivationImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr)
353{
354 if (propertyName == argumentsPropertyName) {
355 // FIXME: Do we need to allow overwriting this?
356 return;
357 }
358 ObjectImp::put(exec, propertyName, value, attr);
359}
360
361bool ActivationImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
362{
363 if (propertyName == argumentsPropertyName)
364 return true;
365 return ObjectImp::hasProperty(exec, propertyName);
366}
367
368bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
369{
370 if (propertyName == argumentsPropertyName)
371 return false;
372 return ObjectImp::deleteProperty(exec, propertyName);
373}
374
375void ActivationImp::mark()
376{
377 if (_function && !_function->marked())
378 _function->mark();
379 _arguments.mark();
380 if (_argumentsObject && !_argumentsObject->marked())
381 _argumentsObject->mark();
382 ObjectImp::mark();
383}
384
385void ActivationImp::createArgumentsObject(ExecState *exec) const
386{
387 _argumentsObject = new ArgumentsImp(exec, _function, _arguments);
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->interpreter()->imp(),
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#if !NDEBUG
548 case KJSPrint: {
549 UString str = args[0].toString(exec);
550 puts(str.ascii());
551 }
552#endif
553 }
554
555 return res;
556}
Note: See TracBrowser for help on using the repository browser.