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

Last change on this file since 2772 was 2772, checked in by darin, 23 years ago
  • a few more globals for often-used property names
  • conversion to Identifier from UString must now be explicit
  • kjs/error_object.cpp:
  • kjs/function.cpp:
  • kjs/function_object.cpp:
  • kjs/identifier.cpp:
  • kjs/identifier.h:
  • kjs/lexer.cpp:
  • kjs/nodes.cpp:
  • kjs/number_object.cpp:
  • kjs/object.cpp:
  • kjs/object.h:
  • kjs/string_object.cpp:
  • kjs/testkjs.cpp:
  • kjs/ustring.cpp:
  • kjs/ustring.h:
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.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
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 Value protect(this);
61 //fprintf(stderr,"FunctionImp::FunctionImp this=%p\n");
62 put(exec,argumentsPropertyName,Null(),ReadOnly|DontDelete|DontEnum);
63}
64
65FunctionImp::~FunctionImp()
66{
67 delete param;
68}
69
70void FunctionImp::mark()
71{
72 InternalFunctionImp::mark();
73}
74
75bool FunctionImp::implementsCall() const
76{
77 return true;
78}
79
80Value FunctionImp::call(ExecState *exec, Object &thisObj, const List &args)
81{
82 Object &globalObj = exec->interpreter()->globalObject();
83
84 Debugger *dbg = exec->interpreter()->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 Object func(this);
94 bool cont = dbg->callEvent(exec,sid,lineno,func,args);
95 if (!cont) {
96 dbg->imp()->abort();
97 return Undefined();
98 }
99 }
100
101 // enter a new execution context
102 ContextImp ctx(globalObj, exec, thisObj, codeType(),
103 exec->context().imp(), this, args);
104 ExecState newExec(exec->interpreter(), &ctx);
105 newExec.setException(exec->exception()); // could be null
106
107 // In order to maintain our "arguments" property, we save the old
108 // value from a possible earlier call. Upon return, we restore the
109 // previous arguments object.
110 // Note: this does not appear to be part of the spec
111 Value oldArgs = get(&newExec, argumentsPropertyName);
112
113 if (codeType() == FunctionCode) {
114 assert(ctx.activationObject().inherits(&ActivationImp::info));
115 Object argsObj = static_cast<ActivationImp*>(ctx.activationObject().imp())->argumentsObject();
116 put(&newExec, argumentsPropertyName, argsObj, DontDelete|DontEnum|ReadOnly);
117 }
118
119 // assign user supplied arguments to parameters
120 processParameters(&newExec, args);
121 // add variable declarations (initialized to undefined)
122 processVarDecls(&newExec);
123
124 Completion comp = execute(&newExec);
125
126 // if an exception occured, propogate it back to the previous execution object
127 if (newExec.hadException())
128 exec->setException(newExec.exception());
129 if (codeType() == FunctionCode)
130 put(&newExec, argumentsPropertyName, oldArgs, DontDelete|DontEnum|ReadOnly);
131
132#ifdef KJS_VERBOSE
133 if (comp.complType() == Throw)
134 printInfo(exec,"throwing", comp.value());
135 else if (comp.complType() == ReturnValue)
136 printInfo(exec,"returning", comp.value());
137 else
138 fprintf(stderr, "returning: undefined\n");
139#endif
140
141 if (dbg) {
142 Object func(this);
143 int cont = dbg->returnEvent(exec,sid,lineno,func);
144 if (!cont) {
145 dbg->imp()->abort();
146 return Undefined();
147 }
148 }
149
150 if (comp.complType() == Throw) {
151 exec->setException(comp.value());
152 return comp.value();
153 }
154 else if (comp.complType() == ReturnValue)
155 return comp.value();
156 else
157 return Undefined();
158}
159
160void FunctionImp::addParameter(const Identifier &n)
161{
162 Parameter **p = &param;
163 while (*p)
164 p = &(*p)->next;
165
166 *p = new Parameter(n);
167}
168
169UString FunctionImp::parameterString() const
170{
171 UString s;
172 const Parameter * const *p = &param;
173 while (*p) {
174 if (!s.isEmpty())
175 s += ", ";
176 s += (*p)->name.ustring();
177 p = &(*p)->next;
178 }
179
180 return s;
181}
182
183
184// ECMA 10.1.3q
185void FunctionImp::processParameters(ExecState *exec, const List &args)
186{
187 Object variable = exec->context().imp()->variableObject();
188
189#ifdef KJS_VERBOSE
190 fprintf(stderr, "---------------------------------------------------\n"
191 "processing parameters for %s call\n",
192 name().isEmpty() ? "(internal)" : name().ascii());
193#endif
194
195 if (param) {
196 ListIterator it = args.begin();
197 Parameter **p = &param;
198 while (*p) {
199 if (it != args.end()) {
200#ifdef KJS_VERBOSE
201 fprintf(stderr, "setting parameter %s ", (*p)->name.ascii());
202 printInfo(exec,"to", *it);
203#endif
204 variable.put(exec,(*p)->name, *it);
205 it++;
206 } else
207 variable.put(exec,(*p)->name, Undefined());
208 p = &(*p)->next;
209 }
210 }
211#ifdef KJS_VERBOSE
212 else {
213 for (int i = 0; i < args.size(); i++)
214 printInfo(exec,"setting argument", args[i]);
215 }
216#endif
217}
218
219void FunctionImp::processVarDecls(ExecState */*exec*/)
220{
221}
222
223// ------------------------------ DeclaredFunctionImp --------------------------
224
225// ### is "Function" correct here?
226const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
227
228DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const Identifier &n,
229 FunctionBodyNode *b, const List &sc)
230 : FunctionImp(exec,n), body(b)
231{
232 Value protect(this);
233 body->ref();
234 setScope(sc.copy());
235}
236
237DeclaredFunctionImp::~DeclaredFunctionImp()
238{
239 if ( body->deref() )
240 delete body;
241}
242
243bool DeclaredFunctionImp::implementsConstruct() const
244{
245 return true;
246}
247
248// ECMA 13.2.2 [[Construct]]
249Object DeclaredFunctionImp::construct(ExecState *exec, const List &args)
250{
251 Object proto;
252 Value p = get(exec,prototypePropertyName);
253 if (p.type() == ObjectType)
254 proto = Object(static_cast<ObjectImp*>(p.imp()));
255 else
256 proto = exec->interpreter()->builtinObjectPrototype();
257
258 Object obj(new ObjectImp(proto));
259
260 Value res = call(exec,obj,args);
261
262 if (res.type() == ObjectType)
263 return Object::dynamicCast(res);
264 else
265 return obj;
266}
267
268Completion DeclaredFunctionImp::execute(ExecState *exec)
269{
270 Completion result = body->execute(exec);
271
272 if (result.complType() == Throw || result.complType() == ReturnValue)
273 return result;
274 return Completion(Normal, Undefined()); // TODO: or ReturnValue ?
275}
276
277void DeclaredFunctionImp::processVarDecls(ExecState *exec)
278{
279 body->processVarDecls(exec);
280}
281
282// ------------------------------ ArgumentsImp ---------------------------------
283
284const ClassInfo ArgumentsImp::info = {"Arguments", 0, 0, 0};
285
286// ECMA 10.1.8
287ArgumentsImp::ArgumentsImp(ExecState *exec, FunctionImp *func, const List &args)
288 : ObjectImp(exec->interpreter()->builtinObjectPrototype())
289{
290 Value protect(this);
291 put(exec,calleePropertyName, Object(func), DontEnum);
292 put(exec,lengthPropertyName, Number(args.size()), DontEnum);
293 if (!args.isEmpty()) {
294 ListIterator arg = args.begin();
295 for (int i = 0; arg != args.end(); arg++, i++) {
296 put(exec,i, *arg, DontEnum);
297 }
298 }
299}
300
301// ------------------------------ ActivationImp --------------------------------
302
303const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
304
305// ECMA 10.1.6
306ActivationImp::ActivationImp(ExecState *exec, FunctionImp *f, const List &args)
307 : ObjectImp()
308{
309 Value protect(this);
310 arguments = new ArgumentsImp(exec,f, args);
311 put(exec, argumentsPropertyName, Object(arguments), Internal|DontDelete);
312}
313
314ActivationImp::~ActivationImp()
315{
316 arguments->setGcAllowed();
317}
318
319// ------------------------------ GlobalFunc -----------------------------------
320
321
322GlobalFuncImp::GlobalFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto, int i, int len)
323 : InternalFunctionImp(funcProto), id(i)
324{
325 Value protect(this);
326 put(exec,lengthPropertyName,Number(len),DontDelete|ReadOnly|DontEnum);
327}
328
329CodeType GlobalFuncImp::codeType() const
330{
331 return id == Eval ? EvalCode : codeType();
332}
333
334bool GlobalFuncImp::implementsCall() const
335{
336 return true;
337}
338
339Value GlobalFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
340{
341 Value res;
342
343 static const char non_escape[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
344 "abcdefghijklmnopqrstuvwxyz"
345 "0123456789@*_+-./";
346
347 switch (id) {
348 case Eval: { // eval()
349 Value x = args[0];
350 if (x.type() != StringType)
351 return x;
352 else {
353 UString s = x.toString(exec);
354
355 int sid;
356 int errLine;
357 UString errMsg;
358 ProgramNode *progNode = Parser::parse(s.data(),s.size(),&sid,&errLine,&errMsg);
359
360 // no program node means a syntax occurred
361 if (!progNode) {
362 Object err = Error::create(exec,SyntaxError,errMsg.ascii(),errLine);
363 err.put(exec,"sid",Number(sid));
364 exec->setException(err);
365 return err;
366 }
367
368 progNode->ref();
369
370 // enter a new execution context
371 Object thisVal(Object::dynamicCast(exec->context().thisValue()));
372 ContextImp *ctx = new ContextImp(exec->interpreter()->globalObject(),
373 exec,
374 thisVal,
375 EvalCode,
376 exec->context().imp());
377
378 ExecState *newExec = new ExecState(exec->interpreter(),ctx);
379 newExec->setException(exec->exception()); // could be null
380
381 // execute the code
382 Completion c = progNode->execute(newExec);
383
384 // if an exception occured, propogate it back to the previous execution object
385 if (newExec->hadException())
386 exec->setException(newExec->exception());
387 delete newExec;
388 delete ctx;
389
390 if ( progNode->deref() )
391 delete progNode;
392 if (c.complType() == ReturnValue)
393 return c.value();
394 // ### setException() on throw?
395 else if (c.complType() == Normal) {
396 if (c.isValueCompletion())
397 return c.value();
398 else
399 return Undefined();
400 } else {
401 return Undefined();
402 }
403 }
404 break;
405 }
406 case ParseInt: {
407 CString cstr = args[0].toString(exec).cstring();
408 int radix = args[1].toInt32(exec);
409
410 char* endptr;
411 errno = 0;
412#ifdef HAVE_FUNC_STRTOLL
413 long long llValue = strtoll(cstr.c_str(), &endptr, radix);
414 double value = llValue;
415#else
416 long value = strtoll(cstr.c_str(), &endptr, radix);
417#endif
418 if (errno != 0 || endptr == cstr.c_str())
419 res = Number(NaN);
420 else
421 res = Number(value);
422 break;
423 }
424 case ParseFloat:
425 res = Number(args[0].toString(exec).toDouble( true /*tolerant*/ ));
426 break;
427 case IsNaN:
428 res = Boolean(isNaN(args[0].toNumber(exec)));
429 break;
430 case IsFinite: {
431 double n = args[0].toNumber(exec);
432 res = Boolean(!isNaN(n) && !isInf(n));
433 break;
434 }
435 case Escape: {
436 UString r = "", s, str = args[0].toString(exec);
437 const UChar *c = str.data();
438 for (int k = 0; k < str.size(); k++, c++) {
439 int u = c->unicode();
440 if (u > 255) {
441 char tmp[7];
442 sprintf(tmp, "%%u%04X", u);
443 s = UString(tmp);
444 } else if (strchr(non_escape, (char)u)) {
445 s = UString(c, 1);
446 } else {
447 char tmp[4];
448 sprintf(tmp, "%%%02X", u);
449 s = UString(tmp);
450 }
451 r += s;
452 }
453 res = String(r);
454 break;
455 }
456 case UnEscape: {
457 UString s, str = args[0].toString(exec);
458 int k = 0, len = str.size();
459 UChar u;
460 while (k < len) {
461 const UChar *c = str.data() + k;
462 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
463 u = Lexer::convertUnicode((c+2)->unicode(), (c+3)->unicode(),
464 (c+4)->unicode(), (c+5)->unicode());
465 c = &u;
466 k += 5;
467 } else if (*c == UChar('%') && k <= len - 3) {
468 u = UChar(Lexer::convertHex((c+1)->unicode(), (c+2)->unicode()));
469 c = &u;
470 k += 2;
471 }
472 k++;
473 s += UString(c, 1);
474 }
475 res = String(s);
476 break;
477 }
478 }
479
480 return res;
481}
Note: See TracBrowser for help on using the repository browser.