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

Last change on this file since 1824 was 1799, checked in by darin, 23 years ago

top level:

  • Tests/WebFoundation-Misc/ifnsurlextensions-test.m: (TestURLCommon): Add tests for the new WebNSURLExtras methods.
  • Tests/libiftest/IFCheckLeaks.c: (IFCheckLeaksAtExit): Remove workaround for CFPreferences race condition; it's now in WebFoundation.

JavaScriptCore:

Speed improvements. 19% faster on cvs-js-performance, 1% on cvs-static-urls.

Use global string objects for length and other common property names rather
than constantly making and destroying them. Use integer versions of get() and
other related calls rather than always making a string.

Also get rid of many unneeded constructors, destructors, copy constructors, and
assignment operators. And make some functions non-virtual.

  • kjs/internal.h:
  • kjs/internal.cpp: (NumberImp::toUInt32): Implement. (ReferenceImp::ReferenceImp): Special case for numeric property names. (ReferenceImp::getPropertyName): Moved guts here from ValueImp. Handle numeric case. (ReferenceImp::getValue): Moved guts here from ValueImp. Handle numeric case. (ReferenceImp::putValue): Moved guts here from ValueImp. Handle numeric case. (ReferenceImp::deleteValue): Added. Handle numeric case.
  • kjs/array_object.h:
  • kjs/array_object.cpp: All-new array implementation that stores the elements in a C++ array rather than in a property map. (ArrayInstanceImp::ArrayInstanceImp): Allocate the C++ array. (ArrayInstanceImp::~ArrayInstanceImp): Delete the C++ array. (ArrayInstanceImp::get): Implement both the old version and the new overload that takes an unsigned index for speed. (ArrayInstanceImp::put): Implement both the old version and the new overload that takes an unsigned index for speed. (ArrayInstanceImp::hasProperty): Implement both the old version and the new overload that takes an unsigned index for speed. (ArrayInstanceImp::deleteProperty): Implement both the old version and the new overload that takes an unsigned index for speed. (ArrayInstanceImp::setLength): Added. Used by the above to resize the array. (ArrayInstanceImp::mark): Mark the elements of the array too. (ArrayPrototypeImp::ArrayPrototypeImp): Pass the length to the array instance constructor.
  • kjs/bool_object.cpp:
  • kjs/date_object.cpp:
  • kjs/error_object.cpp:
  • kjs/function.cpp:
  • kjs/function_object.cpp:
  • kjs/math_object.cpp:
  • kjs/nodes.cpp:
  • kjs/nodes.h:
  • kjs/number_object.cpp:
  • kjs/object_object.cpp:
  • kjs/regexp_object.cpp:
  • kjs/string_object.cpp:
  • kjs/nodes2string.cpp: (SourceStream::operator<<): Add a special case for char now that you can't create a UString from a char implicitly.
  • kjs/object.h:
  • kjs/object.cpp: (ObjectImp::get): Call through to the string version if the numeric version is not implemented. (ObjectImp::put): Call through to the string version if the numeric version is not implemented. (ObjectImp::hasProperty): Call through to the string version if the numeric version is not implemented. (ObjectImp::deleteProperty): Call through to the string version if the numeric version is not implemented.
  • kjs/types.h:
  • kjs/types.cpp: (Reference::Reference): Added constructors for the numeric property name case.
  • kjs/ustring.h: Made the constructor that turns a character into a string be explicit so we don't get numbers that turn themselves into strings.
  • kjs/ustring.cpp: (UString::UString): Detect the empty string case, and use a shared empty string. (UString::find): Add an overload for single character finds. (UString::rfind): Add an overload for single character finds. (KJS::operator==): Fix bug where it would call strlen(0) if the first string was not null. Also handle non-ASCII characters consistently with the rest of the code by casting to unsigned char just in case.
  • kjs/value.h: Make ValueImp and all subclasses non-copyable and non-assignable.
  • kjs/value.cpp: (ValueImp::toUInt32): New interface, mainly useful so we can detect array indices and not turn them into strings and back. (ValueImp::toInteger): Use the new toUInt32. Probably can use more improvement. (ValueImp::toInt32): Use the new toUInt32. Probably can use more improvement. (ValueImp::toUInt16): Use the new toUInt32. Probably can use more improvement. (ValueImp::getBase): Remove handling of the Reference case. That's in ReferenceImp now. (ValueImp::getPropertyName): Remove handling of the Reference case. That's in ReferenceImp now. (ValueImp::getValue): Remove handling of the Reference case. That's in ReferenceImp now. (ValueImp::putValue): Remove handling of the Reference case. That's in ReferenceImp now. (ValueImp::deleteValue): Added. Used so we can do delete the same way we do put.

WebFoundation:

  • CacheLoader.subproj/WebHTTPResourceLoader.m: (-[WebHTTPProtocolHandler createWFLoadRequest]): Fix handling of paths with queries and some other subtle path and port number handling issues by using _web_hostWithPort and _web_pathWithQuery.
  • Misc.subproj/WebNSURLExtras.h:
  • Misc.subproj/WebNSURLExtras.m: (-[NSURL _web_hostWithPort]): Added. (-[NSURL _web_pathWithQuery]): Added.
  • CacheLoader.subproj/WebResourceLoad.m: (initLoader): Get some random preference before creating threads. This makes it impossible to run into the CFPreferences race condition.

WebCore:

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