source: webkit/trunk/JavaScriptCore/kjs/value.h@ 20204

Last change on this file since 20204 was 20004, checked in by ggaren, 18 years ago

JavaScriptCore:

Reviewed by Maciej Stachowiak.


Fixed all known crashers exposed by run-webkit-tests --threaded. This covers:

<rdar://problem/4565394> | http://bugs.webkit.org/show_bug.cgi?id=12585

PAC file: after closing a window that contains macworld.com, new window
crashes (KJS::PropertyMap::mark()) (12585)

<rdar://problem/4571215> | http://bugs.webkit.org/show_bug.cgi?id=9211

PAC file: Crash occurs when clicking on the navigation tabs at http://www.businessweek.com/ (9211)

<rdar://problem/4557926>

PAC file: Crash occurs when attempting to view image in slideshow mode
at http://d.smugmug.com/gallery/581716 ( KJS::IfNode::execute (KJS::
ExecState*) + 312) if you use a PAC file

(1) Added some missing JSLocks, along with related ASSERTs.


(2) Fully implemented support for objects that can only be garbage collected
on the main thread. So far, only WebCore uses this. We can add it to API
later if we learn that it's needed.


The implementation uses a "main thread only" flag inside each object. When
collecting on a secondary thread, the Collector does an extra pass through
the heap to mark all flagged objects before sweeping. This solution makes
the common case -- flag lots of objects, but never collect on a secondary
thread -- very fast, even though the uncommon case of garbage collecting
on a secondary thread isn't as fast as it could be. I left some notes
about how to speed it up, if we ever care.


For posterity, here are some things I learned about GC while investigating:


  • Each collect must either mark or delete every heap object. "Zombie" objects, which are neither marked nor deleted, raise these issues:
  • On the next pass, the conservative marking algorithm might mark a zombie, causing it to mark freed objects.
  • The client might try to use a zombie, which would seem live because its finalizer had not yet run.
  • A collect on the main thread is free to delete any object. Presumably, objects allocated on secondary threads have thread-safe finalizers.
  • A collect on a secondary thread must not delete thread-unsafe objects.
  • The mark function must be thread-safe.


Line by line comments:

  • API/JSObjectRef.h: Added comment specifying that the finalize callback may run on any thread.
  • bindings/npruntime.cpp: (_NPN_GetStringIdentifier): Added JSLock.
  • bindings/objc/objc_instance.h:
  • bindings/objc/objc_instance.mm: (ObjcInstance::~ObjcInstance): Use an autorelease pool. The other callers to CFRelease needed one, too, but they were dead code, so I removed them instead. (This fixes a leak seen while running run-webkit-tests --threaded, although I don't think it's specifically a threading issue.)


  • kjs/collector.cpp: (KJS::Collector::collectOnMainThreadOnly): New function. Tells the collector to collect a value only if it's collecting on the main thread. (KJS::Collector::markMainThreadOnlyObjects): New function. Scans the heap for "main thread only" objects and marks them.
  • kjs/date_object.cpp: (KJS::DateObjectImp::DateObjectImp): To make the new ASSERTs happy, allocate our globals on the heap, avoiding a seemingly unsafe destructor call at program exit time.
  • kjs/function_object.cpp: (FunctionPrototype::FunctionPrototype): ditto
  • kjs/interpreter.cpp: (KJS::Interpreter::mark): Removed boolean parameter, which was an incomplete and arguably hackish way to implement markMainThreadOnlyObjects() inside WebCore.
  • kjs/interpreter.h:
  • kjs/identifier.cpp: (KJS::identifierTable): Added some ASSERTs to check for thread safety problems.
  • kjs/list.cpp: Added some ASSERTs to check for thread safety problems. (KJS::allocateListImp): (KJS::List::release): (KJS::List::append): (KJS::List::empty): Make the new ASSERTs happy.
  • kjs/object.h: (KJS::JSObject::JSObject): "m_destructorIsThreadSafe" => "m_collectOnMainThreadOnly". I removed the constructor parameter because m_collectOnMainThreadOnly, like m_marked, is a Collector bit, so only the Collector should set or get it.
  • kjs/object_object.cpp: (ObjectPrototype::ObjectPrototype): Make the ASSERTs happy.
  • kjs/regexp_object.cpp: (RegExpPrototype::RegExpPrototype): ditto
  • kjs/ustring.cpp: Added some ASSERTs to check for thread safety problems. (KJS::UCharReference::ref): (KJS::UString::Rep::createCopying): (KJS::UString::Rep::create): (KJS::UString::Rep::destroy): (KJS::UString::null): Make the new ASSERTs happy.
  • kjs/ustring.h: (KJS::UString::Rep::ref): Added some ASSERTs to check for thread safety problems. (KJS::UString::Rep::deref):
  • kjs/value.h: (KJS::JSCell::JSCell):

JavaScriptGlue:

Reviewed by Maciej Stachowiak.

Fixed all known crashers exposed by run-webkit-tests --threaded while using
a PAC file (for maximum carnage). See JavaScriptCore ChangeLog for
more details.

  • JSBase.cpp: (JSBase::Release): Lock when deleting, because we may be deleting an object (like a JSRun) that holds thread-unsafe data.
  • JSUtils.cpp: (CFStringToUString): Don't lock, because our caller locks. Also, locking inside a function that returns thread-unsafe data by copy will only mask threading problems.
  • JavaScriptGlue.cpp: (JSRunEvaluate): Added missing JSLock. (JSRunCheckSyntax): Converted to JSLock.
  • JavaScriptGlue.xcodeproj/project.pbxproj:

WebCore:

Reviewed by Maciej Stachowiak.

Fixed all known crashers exposed by run-webkit-tests --threaded [*]. See
JavaScriptCore ChangeLog for more details.

  • bindings/js/kjs_binding.cpp: (KJS::domNodesPerDocument): Added thread safety ASSERT. (KJS::ScriptInterpreter::mark): Removed obsolete logic for marking unsafe objects when collecting on a secondary thread. The Collector takes care of this now.
  • bindings/js/kjs_binding.h: (KJS::DOMObject::DOMObject): Used new API for specifying that WebCore objects should be garbage collected on the main thread only.
  • bindings/js/kjs_window.cpp: (KJS::ScheduledAction::execute): Moved JSLock to cover implementedsCall() call, which, for some subclasses, ends up allocating garbage collected objects. (This fix was speculative. I didn't actually see a crash from this.) (KJS::Window::timerFired): Added JSLock around ScheduleAction destruction, since it destroys a KJS::List.
  • bindings/objc/WebScriptObject.mm: (-[WebScriptObject setException:]): Added JSLock. (This fix was speculative. I didn't actually see a crash from this.)
  • bridge/mac/WebCoreScriptDebugger.mm: (-[WebCoreScriptCallFrame evaluateWebScript:]): Added JSLock. (This fix was speculative. I didn't actually see a crash from this.)
  • dom/Document.cpp: (WebCore::Document::~Document): Added JSLock around modification to domNodesPerDocument(), which can be accessed concurrently during garbage collection.
  • dom/Node.cpp: (WebCore::Node::setDocument): ditto.


[*] fast/js/toString-stack-overflow.html is an exception. --threaded mode
crashes this test because it causes the garbage collector to run frequently,
and this test crashes if you happen to garbage collect while it's running.
This is a known issue with stack overflow during the mark phase. It's
not related to threading.

  • Property svn:eol-style set to native
File size: 9.8 KB
Line 
1/*
2 * This file is part of the KDE libraries
3 * Copyright (C) 1999-2001 Harri Porten ([email protected])
4 * Copyright (C) 2001 Peter Kelly ([email protected])
5 * Copyright (C) 2003-2005 Apple Computer, Inc.
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., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24#ifndef KJS_VALUE_H
25#define KJS_VALUE_H
26
27#include "JSImmediate.h"
28#include "ustring.h"
29#include <stddef.h> // for size_t
30
31#ifndef NDEBUG // protection against problems if committing with KJS_VERBOSE on
32
33// Uncomment this to enable very verbose output from KJS
34//#define KJS_VERBOSE
35// Uncomment this to debug memory allocation and garbage collection
36//#define KJS_DEBUG_MEM
37
38#endif
39
40namespace KJS {
41
42class ExecState;
43class JSObject;
44class JSCell;
45
46struct ClassInfo;
47
48/**
49 * JSValue is the base type for all primitives (Undefined, Null, Boolean,
50 * String, Number) and objects in ECMAScript.
51 *
52 * Note: you should never inherit from JSValue as it is for primitive types
53 * only (all of which are provided internally by KJS). Instead, inherit from
54 * JSObject.
55 */
56class JSValue {
57 friend class JSCell; // so it can derive from this class
58 friend class Collector; // so it can call downcast()
59
60private:
61 JSValue();
62 virtual ~JSValue();
63
64public:
65 // Querying the type.
66 JSType type() const;
67 bool isUndefined() const;
68 bool isNull() const;
69 bool isUndefinedOrNull() const;
70 bool isBoolean() const;
71 bool isNumber() const;
72 bool isString() const;
73 bool isObject() const;
74 bool isObject(const ClassInfo *) const;
75
76 // Extracting the value.
77 bool getBoolean(bool&) const;
78 bool getBoolean() const; // false if not a boolean
79 bool getNumber(double&) const;
80 double getNumber() const; // NaN if not a number
81 bool getString(UString&) const;
82 UString getString() const; // null string if not a string
83 JSObject *getObject(); // NULL if not an object
84 const JSObject *getObject() const; // NULL if not an object
85
86 // Extracting integer values.
87 bool getUInt32(uint32_t&) const;
88
89 // Basic conversions.
90 JSValue *toPrimitive(ExecState *exec, JSType preferredType = UnspecifiedType) const;
91 bool toBoolean(ExecState *exec) const;
92 double toNumber(ExecState *exec) const;
93 UString toString(ExecState *exec) const;
94 JSObject *toObject(ExecState *exec) const;
95
96 // Integer conversions.
97 double toInteger(ExecState*) const;
98 int32_t toInt32(ExecState*) const;
99 int32_t toInt32(ExecState*, bool& ok) const;
100 uint32_t toUInt32(ExecState*) const;
101 uint32_t toUInt32(ExecState*, bool& ok) const;
102 uint16_t toUInt16(ExecState*) const;
103
104 // Garbage collection.
105 void mark();
106 bool marked() const;
107
108private:
109 // Implementation details.
110 JSCell *downcast();
111 const JSCell *downcast() const;
112
113 // Give a compile time error if we try to copy one of these.
114 JSValue(const JSValue&);
115 JSValue& operator=(const JSValue&);
116};
117
118class JSCell : public JSValue {
119 friend class Collector;
120 friend class NumberImp;
121 friend class StringImp;
122 friend class JSObject;
123 friend class GetterSetterImp;
124private:
125 JSCell();
126 virtual ~JSCell();
127public:
128 // Querying the type.
129 virtual JSType type() const = 0;
130 bool isNumber() const;
131 bool isString() const;
132 bool isObject() const;
133 bool isObject(const ClassInfo *) const;
134
135 // Extracting the value.
136 bool getNumber(double&) const;
137 double getNumber() const; // NaN if not a number
138 bool getString(UString&) const;
139 UString getString() const; // null string if not a string
140 JSObject *getObject(); // NULL if not an object
141 const JSObject *getObject() const; // NULL if not an object
142
143 // Extracting integer values.
144 virtual bool getUInt32(uint32_t&) const;
145
146 // Basic conversions.
147 virtual JSValue *toPrimitive(ExecState *exec, JSType preferredType = UnspecifiedType) const = 0;
148 virtual bool toBoolean(ExecState *exec) const = 0;
149 virtual double toNumber(ExecState *exec) const = 0;
150 virtual UString toString(ExecState *exec) const = 0;
151 virtual JSObject *toObject(ExecState *exec) const = 0;
152
153 // Garbage collection.
154 void *operator new(size_t);
155 virtual void mark();
156 bool marked() const;
157
158private:
159 bool m_collectOnMainThreadOnly : 1;
160 bool m_marked : 1;
161};
162
163JSValue *jsNumberCell(double);
164
165JSCell *jsString(const UString &); // returns empty string if passed null string
166JSCell *jsString(const char * = ""); // returns empty string if passed 0
167
168extern const double NaN;
169extern const double Inf;
170
171
172inline JSValue *jsUndefined()
173{
174 return JSImmediate::undefinedImmediate();
175}
176
177inline JSValue *jsNull()
178{
179 return JSImmediate::nullImmediate();
180}
181
182inline JSValue *jsNaN()
183{
184 return JSImmediate::NaNImmediate();
185}
186
187inline JSValue *jsBoolean(bool b)
188{
189 return b ? JSImmediate::trueImmediate() : JSImmediate::falseImmediate();
190}
191
192inline JSValue *jsNumber(double d)
193{
194 JSValue *v = JSImmediate::fromDouble(d);
195 return v ? v : jsNumberCell(d);
196}
197
198inline JSValue::JSValue()
199{
200}
201
202inline JSValue::~JSValue()
203{
204}
205
206inline JSCell::JSCell()
207 : m_collectOnMainThreadOnly(false)
208 , m_marked(false)
209{
210}
211
212inline JSCell::~JSCell()
213{
214}
215
216inline bool JSCell::isNumber() const
217{
218 return type() == NumberType;
219}
220
221inline bool JSCell::isString() const
222{
223 return type() == StringType;
224}
225
226inline bool JSCell::isObject() const
227{
228 return type() == ObjectType;
229}
230
231inline bool JSCell::marked() const
232{
233 return m_marked;
234}
235
236inline void JSCell::mark()
237{
238 m_marked = true;
239}
240
241inline JSCell *JSValue::downcast()
242{
243 ASSERT(!JSImmediate::isImmediate(this));
244 return static_cast<JSCell *>(this);
245}
246
247inline const JSCell *JSValue::downcast() const
248{
249 ASSERT(!JSImmediate::isImmediate(this));
250 return static_cast<const JSCell *>(this);
251}
252
253inline bool JSValue::isUndefined() const
254{
255 return this == jsUndefined();
256}
257
258inline bool JSValue::isNull() const
259{
260 return this == jsNull();
261}
262
263inline bool JSValue::isUndefinedOrNull() const
264{
265 return JSImmediate::isUndefinedOrNull(this);
266}
267
268inline bool JSValue::isBoolean() const
269{
270 return JSImmediate::isBoolean(this);
271}
272
273inline bool JSValue::isNumber() const
274{
275 return JSImmediate::isNumber(this) || !JSImmediate::isImmediate(this) && downcast()->isNumber();
276}
277
278inline bool JSValue::isString() const
279{
280 return !JSImmediate::isImmediate(this) && downcast()->isString();
281}
282
283inline bool JSValue::isObject() const
284{
285 return !JSImmediate::isImmediate(this) && downcast()->isObject();
286}
287
288inline bool JSValue::getBoolean(bool& v) const
289{
290 if (JSImmediate::isBoolean(this)) {
291 v = JSImmediate::toBoolean(this);
292 return true;
293 }
294
295 return false;
296}
297
298inline bool JSValue::getBoolean() const
299{
300 return JSImmediate::isBoolean(this) ? JSImmediate::toBoolean(this) : false;
301}
302
303inline bool JSValue::getNumber(double& v) const
304{
305 if (JSImmediate::isImmediate(this)) {
306 v = JSImmediate::toDouble(this);
307 return true;
308 }
309 return downcast()->getNumber(v);
310}
311
312inline double JSValue::getNumber() const
313{
314 return JSImmediate::isImmediate(this) ? JSImmediate::toDouble(this) : downcast()->getNumber();
315}
316
317inline bool JSValue::getString(UString& s) const
318{
319 return !JSImmediate::isImmediate(this) && downcast()->getString(s);
320}
321
322inline UString JSValue::getString() const
323{
324 return JSImmediate::isImmediate(this) ? UString() : downcast()->getString();
325}
326
327inline JSObject *JSValue::getObject()
328{
329 return JSImmediate::isImmediate(this) ? 0 : downcast()->getObject();
330}
331
332inline const JSObject *JSValue::getObject() const
333{
334 return JSImmediate::isImmediate(this) ? 0 : downcast()->getObject();
335}
336
337inline bool JSValue::getUInt32(uint32_t& v) const
338{
339 if (JSImmediate::isImmediate(this)) {
340 double d = JSImmediate::toDouble(this);
341 if (!(d >= 0) || d > 0xFFFFFFFFUL) // true for NaN
342 return false;
343 v = static_cast<uint32_t>(d);
344 return JSImmediate::isNumber(this);
345 }
346 return downcast()->getUInt32(v);
347}
348
349inline void JSValue::mark()
350{
351 ASSERT(!JSImmediate::isImmediate(this)); // callers should check !marked() before calling mark()
352 downcast()->mark();
353}
354
355inline bool JSValue::marked() const
356{
357 return JSImmediate::isImmediate(this) || downcast()->marked();
358}
359
360inline JSType JSValue::type() const
361{
362 return JSImmediate::isImmediate(this) ? JSImmediate::type(this) : downcast()->type();
363}
364
365inline JSValue *JSValue::toPrimitive(ExecState *exec, JSType preferredType) const
366{
367 return JSImmediate::isImmediate(this) ? const_cast<JSValue *>(this) : downcast()->toPrimitive(exec, preferredType);
368}
369
370inline bool JSValue::toBoolean(ExecState *exec) const
371{
372 return JSImmediate::isImmediate(this) ? JSImmediate::toBoolean(this) : downcast()->toBoolean(exec);
373}
374
375inline double JSValue::toNumber(ExecState *exec) const
376{
377 return JSImmediate::isImmediate(this) ? JSImmediate::toDouble(this) : downcast()->toNumber(exec);
378}
379
380inline UString JSValue::toString(ExecState *exec) const
381{
382 return JSImmediate::isImmediate(this) ? JSImmediate::toString(this) : downcast()->toString(exec);
383}
384
385inline JSObject* JSValue::toObject(ExecState* exec) const
386{
387 return JSImmediate::isImmediate(this) ? JSImmediate::toObject(this, exec) : downcast()->toObject(exec);
388}
389
390} // namespace
391
392#endif // KJS_VALUE_H
Note: See TracBrowser for help on using the repository browser.