source: webkit/trunk/JavaScriptCore/kjs/object.cpp@ 8453

Last change on this file since 8453 was 8453, checked in by rjw, 20 years ago

JavaScriptCore:

Fixed <rdar://problem/3972522> (179-180) 40% slowdown on iBench JavaScript test

I added a member variable to ObjectImp. This changed it's size and consequently
hampered the optimizations built into the garbage collector. Objects no longer
fit within the allocators cell size, and thus allocation fell back to a slower
allocator.

As a result of this fix I also dramatically cleaned up how runtime objects are
accessed. The path mostly *removes* code.

Reviewed by Chris.

  • bindings/runtime_method.cpp: (RuntimeMethodImp::call):
  • bindings/runtime_object.cpp: (RuntimeObjectImp::get): (RuntimeObjectImp::put): (RuntimeObjectImp::canPut): (RuntimeObjectImp::hasProperty): (RuntimeObjectImp::defaultValue):
  • bindings/runtime_object.h:
  • kjs/object.cpp: (KJS::ObjectImp::ObjectImp):
  • kjs/object.h:

WebCore:

Fixed <rdar://problem/3972522> (179-180) 40% slowdown on iBench JavaScript test

I added a member variable to ObjectImp. This changed it's size and consequently
hampered the optimizations built into the garbage collector. Objects no longer
fit within the allocators cell size, and thus allocation fell back to a slower
allocator.

As a result of this fix I also dramatically cleaned up how runtime objects are
accessed. The path mostly *removes* code.

Reviewed by Chris.

  • khtml/ecma/kjs_dom.cpp: (DOMDocumentProtoFunc::tryCall): (DOMElementProtoFunc::tryCall): (KJS::getRuntimeObject):
  • khtml/ecma/kjs_dom.h:
  • khtml/ecma/kjs_html.cpp: (KJS::HTMLDocument::tryGet): (KJS::HTMLElement::tryGet): (KJS::HTMLElement::implementsCall): (KJS::HTMLElement::call): (KJS::HTMLElement::tryPut): (KJS::HTMLCollection::tryGet): (KJS::HTMLCollection::getNamedItems):
  • khtml/ecma/kjs_html.h:
  • khtml/ecma/kjs_window.cpp: (Window::get):
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2001 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., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 *
23 */
24
25#include "value.h"
26#include "object.h"
27#include "types.h"
28#include "interpreter.h"
29#include "lookup.h"
30#include "reference_list.h"
31
32#include <assert.h>
33#include <math.h>
34#include <stdio.h>
35
36#include "internal.h"
37#include "collector.h"
38#include "operations.h"
39#include "error_object.h"
40#include "nodes.h"
41
42#ifndef NDEBUG
43#define JAVASCRIPT_CALL_TRACING Yes
44#endif
45
46#ifdef JAVASCRIPT_CALL_TRACING
47static bool _traceJavaScript = false;
48
49extern "C" {
50 void setTraceJavaScript(bool f)
51 {
52 _traceJavaScript = f;
53 }
54
55 static bool traceJavaScript()
56 {
57 return _traceJavaScript;
58 }
59}
60#endif
61
62namespace KJS {
63
64// ------------------------------ Object ---------------------------------------
65
66Object Object::dynamicCast(const Value &v)
67{
68 if (v.isNull() || v.type() != ObjectType)
69 return Object(0);
70
71 return Object(static_cast<ObjectImp*>(v.imp()));
72}
73
74
75Value Object::call(ExecState *exec, Object &thisObj, const List &args)
76{
77#if KJS_MAX_STACK > 0
78 static int depth = 0; // sum of all concurrent interpreters
79
80#ifdef JAVASCRIPT_CALL_TRACING
81 static bool tracing = false;
82 if (traceJavaScript() && !tracing) {
83 tracing = true;
84 for (int i = 0; i < depth; i++)
85 putchar (' ');
86 printf ("*** calling: %s\n", toString(exec).ascii());
87 for (int j = 0; j < args.size(); j++) {
88 for (int i = 0; i < depth; i++)
89 putchar (' ');
90 printf ("*** arg[%d] = %s\n", j, args[j].toString(exec).ascii());
91 }
92 tracing = false;
93 }
94#endif
95
96 if (++depth > KJS_MAX_STACK) {
97 --depth;
98 Object err = Error::create(exec, RangeError,
99 "Maximum call stack size exceeded.");
100 exec->setException(err);
101 return err;
102 }
103#endif
104
105 Value ret = imp()->call(exec,thisObj,args);
106
107#if KJS_MAX_STACK > 0
108 --depth;
109#endif
110
111#ifdef JAVASCRIPT_CALL_TRACING
112 if (traceJavaScript() && !tracing) {
113 tracing = true;
114 for (int i = 0; i < depth; i++)
115 putchar (' ');
116 printf ("*** returning: %s\n", ret.toString(exec).ascii());
117 tracing = false;
118 }
119#endif
120
121 return ret;
122}
123
124// ------------------------------ ObjectImp ------------------------------------
125
126ObjectImp::ObjectImp(const Object &proto)
127 : _proto(static_cast<ObjectImp*>(proto.imp())), _internalValue(0L)
128{
129 //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
130}
131
132ObjectImp::ObjectImp(ObjectImp *proto)
133 : _proto(proto), _internalValue(0L)
134{
135 //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
136}
137
138ObjectImp::ObjectImp()
139{
140 //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
141 _proto = NullImp::staticNull;
142 _internalValue = 0L;
143}
144
145ObjectImp::~ObjectImp()
146{
147 //fprintf(stderr,"ObjectImp::~ObjectImp %p\n",(void*)this);
148}
149
150void ObjectImp::mark()
151{
152 //fprintf(stderr,"ObjectImp::mark() %p\n",(void*)this);
153 ValueImp::mark();
154
155 if (_proto && !_proto->marked())
156 _proto->mark();
157
158 _prop.mark();
159
160 if (_internalValue && !_internalValue->marked())
161 _internalValue->mark();
162
163 _scope.mark();
164}
165
166const ClassInfo *ObjectImp::classInfo() const
167{
168 return 0;
169}
170
171bool ObjectImp::inherits(const ClassInfo *info) const
172{
173 if (!info)
174 return false;
175
176 const ClassInfo *ci = classInfo();
177 if (!ci)
178 return false;
179
180 while (ci && ci != info)
181 ci = ci->parentClass;
182
183 return (ci == info);
184}
185
186Type ObjectImp::type() const
187{
188 return ObjectType;
189}
190
191Value ObjectImp::prototype() const
192{
193 return Value(_proto);
194}
195
196void ObjectImp::setPrototype(const Value &proto)
197{
198 _proto = proto.imp();
199}
200
201UString ObjectImp::className() const
202{
203 const ClassInfo *ci = classInfo();
204 if ( ci )
205 return ci->className;
206 return "Object";
207}
208
209Value ObjectImp::get(ExecState *exec, const Identifier &propertyName) const
210{
211 ValueImp *imp = getDirect(propertyName);
212 if (imp)
213 return Value(imp);
214
215 // non-standard netscape extension
216 if (propertyName == specialPrototypePropertyName)
217 return Value(_proto);
218
219 if (_proto->dispatchType() != ObjectType) {
220 return Undefined();
221 }
222
223 return static_cast<ObjectImp *>(_proto)->get(exec, propertyName);
224}
225
226Value ObjectImp::get(ExecState *exec, unsigned propertyName) const
227{
228 return get(exec, Identifier::from(propertyName));
229}
230
231// ECMA 8.6.2.2
232void ObjectImp::put(ExecState *exec, const Identifier &propertyName,
233 const Value &value, int attr)
234{
235 assert(!value.isNull());
236
237 // non-standard netscape extension
238 if (propertyName == specialPrototypePropertyName) {
239 setPrototype(value);
240 return;
241 }
242
243 /* TODO: check for write permissions directly w/o this call */
244 /* Doesn't look very easy with the PropertyMap API - David */
245 // putValue() is used for JS assignemnts. It passes no attribute.
246 // Assume that a C++ implementation knows what it is doing
247 // and let it override the canPut() check.
248 if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
249#ifdef KJS_VERBOSE
250 fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
251#endif
252 return;
253 }
254
255 _prop.put(propertyName,value.imp(),attr);
256}
257
258void ObjectImp::put(ExecState *exec, unsigned propertyName,
259 const Value &value, int attr)
260{
261 put(exec, Identifier::from(propertyName), value, attr);
262}
263
264// ECMA 8.6.2.3
265bool ObjectImp::canPut(ExecState *, const Identifier &propertyName) const
266{
267 int attributes;
268 ValueImp *v = _prop.get(propertyName, attributes);
269 if (v)
270 return!(attributes & ReadOnly);
271
272 // Look in the static hashtable of properties
273 const HashEntry* e = findPropertyHashEntry(propertyName);
274 if (e)
275 return !(e->attr & ReadOnly);
276
277 // Don't look in the prototype here. We can always put an override
278 // in the object, even if the prototype has a ReadOnly property.
279 return true;
280}
281
282// ECMA 8.6.2.4
283bool ObjectImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
284{
285 if (_prop.get(propertyName))
286 return true;
287
288 // Look in the static hashtable of properties
289 if (findPropertyHashEntry(propertyName))
290 return true;
291
292 // non-standard netscape extension
293 if (propertyName == specialPrototypePropertyName)
294 return true;
295
296 if (_proto->dispatchType() != ObjectType) {
297 return false;
298 }
299
300 // Look in the prototype
301 return static_cast<ObjectImp *>(_proto)->hasProperty(exec, propertyName);
302}
303
304bool ObjectImp::hasProperty(ExecState *exec, unsigned propertyName) const
305{
306 return hasProperty(exec, Identifier::from(propertyName));
307}
308
309// ECMA 8.6.2.5
310bool ObjectImp::deleteProperty(ExecState */*exec*/, const Identifier &propertyName)
311{
312 int attributes;
313 ValueImp *v = _prop.get(propertyName, attributes);
314 if (v) {
315 if ((attributes & DontDelete))
316 return false;
317 _prop.remove(propertyName);
318 return true;
319 }
320
321 // Look in the static hashtable of properties
322 const HashEntry* entry = findPropertyHashEntry(propertyName);
323 if (entry && entry->attr & DontDelete)
324 return false; // this builtin property can't be deleted
325 return true;
326}
327
328bool ObjectImp::deleteProperty(ExecState *exec, unsigned propertyName)
329{
330 return deleteProperty(exec, Identifier::from(propertyName));
331}
332
333void ObjectImp::deleteAllProperties( ExecState * )
334{
335 _prop.clear();
336}
337
338// ECMA 8.6.2.6
339Value ObjectImp::defaultValue(ExecState *exec, Type hint) const
340{
341 if (hint != StringType && hint != NumberType) {
342 /* Prefer String for Date objects */
343 if (_proto == exec->lexicalInterpreter()->builtinDatePrototype().imp())
344 hint = StringType;
345 else
346 hint = NumberType;
347 }
348
349 Value v;
350 if (hint == StringType)
351 v = get(exec,toStringPropertyName);
352 else
353 v = get(exec,valueOfPropertyName);
354
355 if (v.type() == ObjectType) {
356 Object o = Object(static_cast<ObjectImp*>(v.imp()));
357 if (o.implementsCall()) { // spec says "not primitive type" but ...
358 Object thisObj = Object(const_cast<ObjectImp*>(this));
359 Value def = o.call(exec,thisObj,List::empty());
360 Type defType = def.type();
361 if (defType == UnspecifiedType || defType == UndefinedType ||
362 defType == NullType || defType == BooleanType ||
363 defType == StringType || defType == NumberType) {
364 return def;
365 }
366 }
367 }
368
369 if (hint == StringType)
370 v = get(exec,valueOfPropertyName);
371 else
372 v = get(exec,toStringPropertyName);
373
374 if (v.type() == ObjectType) {
375 Object o = Object(static_cast<ObjectImp*>(v.imp()));
376 if (o.implementsCall()) { // spec says "not primitive type" but ...
377 Object thisObj = Object(const_cast<ObjectImp*>(this));
378 Value def = o.call(exec,thisObj,List::empty());
379 Type defType = def.type();
380 if (defType == UnspecifiedType || defType == UndefinedType ||
381 defType == NullType || defType == BooleanType ||
382 defType == StringType || defType == NumberType) {
383 return def;
384 }
385 }
386 }
387
388 Object err = Error::create(exec, TypeError, I18N_NOOP("No default value"));
389 exec->setException(err);
390 return err;
391}
392
393const HashEntry* ObjectImp::findPropertyHashEntry( const Identifier& propertyName ) const
394{
395 const ClassInfo *info = classInfo();
396 while (info) {
397 if (info->propHashTable) {
398 const HashEntry *e = Lookup::findEntry(info->propHashTable, propertyName);
399 if (e)
400 return e;
401 }
402 info = info->parentClass;
403 }
404 return 0L;
405}
406
407bool ObjectImp::implementsConstruct() const
408{
409 return false;
410}
411
412Object ObjectImp::construct(ExecState */*exec*/, const List &/*args*/)
413{
414 assert(false);
415 return Object(0);
416}
417
418Object ObjectImp::construct(ExecState *exec, const List &args, const UString &/*sourceURL*/, int /*lineNumber*/)
419{
420 return construct(exec, args);
421}
422
423bool ObjectImp::implementsCall() const
424{
425 return false;
426}
427
428Value ObjectImp::call(ExecState */*exec*/, Object &/*thisObj*/, const List &/*args*/)
429{
430 assert(false);
431 return Object(0);
432}
433
434bool ObjectImp::implementsHasInstance() const
435{
436 return false;
437}
438
439Boolean ObjectImp::hasInstance(ExecState */*exec*/, const Value &/*value*/)
440{
441 assert(false);
442 return Boolean(false);
443}
444
445ReferenceList ObjectImp::propList(ExecState *exec, bool recursive)
446{
447 ReferenceList list;
448 if (_proto && _proto->dispatchType() == ObjectType && recursive)
449 list = static_cast<ObjectImp*>(_proto)->propList(exec,recursive);
450
451 _prop.addEnumerablesToReferenceList(list, Object(this));
452
453 // Add properties from the static hashtable of properties
454 const ClassInfo *info = classInfo();
455 while (info) {
456 if (info->propHashTable) {
457 int size = info->propHashTable->size;
458 const HashEntry *e = info->propHashTable->entries;
459 for (int i = 0; i < size; ++i, ++e) {
460 if ( e->s && !(e->attr & DontEnum) )
461 list.append(Reference(this, e->s)); /// ######### check for duplicates with the propertymap
462 }
463 }
464 info = info->parentClass;
465 }
466
467 return list;
468}
469
470Value ObjectImp::internalValue() const
471{
472 return Value(_internalValue);
473}
474
475void ObjectImp::setInternalValue(const Value &v)
476{
477 _internalValue = v.imp();
478}
479
480void ObjectImp::setInternalValue(ValueImp *v)
481{
482#if !USE_CONSERVATIVE_GC
483 v->setGcAllowed();
484#endif
485 _internalValue = v;
486}
487
488Value ObjectImp::toPrimitive(ExecState *exec, Type preferredType) const
489{
490 return defaultValue(exec,preferredType);
491}
492
493bool ObjectImp::toBoolean(ExecState */*exec*/) const
494{
495 return true;
496}
497
498double ObjectImp::toNumber(ExecState *exec) const
499{
500 Value prim = toPrimitive(exec,NumberType);
501 if (exec->hadException()) // should be picked up soon in nodes.cpp
502 return 0.0;
503 return prim.toNumber(exec);
504}
505
506UString ObjectImp::toString(ExecState *exec) const
507{
508 Value prim = toPrimitive(exec,StringType);
509 if (exec->hadException()) // should be picked up soon in nodes.cpp
510 return "";
511 return prim.toString(exec);
512}
513
514Object ObjectImp::toObject(ExecState */*exec*/) const
515{
516 return Object(const_cast<ObjectImp*>(this));
517}
518
519void ObjectImp::putDirect(const Identifier &propertyName, ValueImp *value, int attr)
520{
521#if !USE_CONSERVATIVE_GC
522 value->setGcAllowed();
523#endif
524 _prop.put(propertyName, value, attr);
525}
526
527void ObjectImp::putDirect(const Identifier &propertyName, int value, int attr)
528{
529 _prop.put(propertyName, NumberImp::create(value), attr);
530}
531
532// ------------------------------ Error ----------------------------------------
533
534const char * const errorNamesArr[] = {
535 I18N_NOOP("Error"), // GeneralError
536 I18N_NOOP("Evaluation error"), // EvalError
537 I18N_NOOP("Range error"), // RangeError
538 I18N_NOOP("Reference error"), // ReferenceError
539 I18N_NOOP("Syntax error"), // SyntaxError
540 I18N_NOOP("Type error"), // TypeError
541 I18N_NOOP("URI error"), // URIError
542};
543
544const char * const * const Error::errorNames = errorNamesArr;
545
546Object Error::create(ExecState *exec, ErrorType errtype, const char *message,
547 int lineno, int sourceId, const UString *sourceURL)
548{
549 Object cons;
550 switch (errtype) {
551 case EvalError:
552 cons = exec->lexicalInterpreter()->builtinEvalError();
553 break;
554 case RangeError:
555 cons = exec->lexicalInterpreter()->builtinRangeError();
556 break;
557 case ReferenceError:
558 cons = exec->lexicalInterpreter()->builtinReferenceError();
559 break;
560 case SyntaxError:
561 cons = exec->lexicalInterpreter()->builtinSyntaxError();
562 break;
563 case TypeError:
564 cons = exec->lexicalInterpreter()->builtinTypeError();
565 break;
566 case URIError:
567 cons = exec->lexicalInterpreter()->builtinURIError();
568 break;
569 default:
570 cons = exec->lexicalInterpreter()->builtinError();
571 break;
572 }
573
574 if (!message)
575 message = errorNames[errtype];
576 List args;
577 args.append(String(message));
578 Object err = Object::dynamicCast(cons.construct(exec,args));
579
580 if (lineno != -1)
581 err.put(exec, "line", Number(lineno));
582 if (sourceId != -1)
583 err.put(exec, "sourceId", Number(sourceId));
584
585 if(sourceURL)
586 err.put(exec,"sourceURL", String(*sourceURL));
587
588 return err;
589
590/*
591#ifndef NDEBUG
592 const char *msg = err.get("message").toString().value().ascii();
593 if (l >= 0)
594 fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
595 else
596 fprintf(stderr, "KJS: %s. %s\n", estr, msg);
597#endif
598
599 return err;
600*/
601}
602
603} // namespace KJS
Note: See TracBrowser for help on using the repository browser.