Changeset 20004 in webkit


Ignore:
Timestamp:
Mar 6, 2007 8:25:49 PM (17 years ago)
Author:
ggaren
Message:

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.

Location:
trunk
Files:
33 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/API/JSObjectRef.h

    r19059 r20004  
    9191/*!
    9292@typedef JSObjectFinalizeCallback
    93 @abstract The callback invoked when an object is finalized (prepared for garbage collection).
     93@abstract The callback invoked when an object is finalized (prepared for garbage collection). An object may be finalized on any thread.
    9494@param object The JSObject being finalized.
    9595@discussion If you named your function Finalize, you would declare it like this:
  • trunk/JavaScriptCore/ChangeLog

    r19994 r20004  
     12007-03-06  Geoffrey Garen  <ggaren@apple.com>
     2
     3        Reviewed by Maciej Stachowiak.
     4       
     5        Fixed all known crashers exposed by run-webkit-tests --threaded. This covers:
     6
     7        <rdar://problem/4565394> | http://bugs.webkit.org/show_bug.cgi?id=12585
     8            PAC file: after closing a window that contains macworld.com, new window
     9            crashes (KJS::PropertyMap::mark()) (12585)
     10        <rdar://problem/4571215> | http://bugs.webkit.org/show_bug.cgi?id=9211
     11            PAC file: Crash occurs when clicking on the navigation tabs at http://www.businessweek.com/ (9211)
     12        <rdar://problem/4557926>
     13            PAC file: Crash occurs when attempting to view image in slideshow mode
     14            at http://d.smugmug.com/gallery/581716 ( KJS::IfNode::execute (KJS::
     15            ExecState*) + 312) if you use a PAC file
     16
     17        (1) Added some missing JSLocks, along with related ASSERTs.
     18       
     19        (2) Fully implemented support for objects that can only be garbage collected
     20        on the main thread. So far, only WebCore uses this. We can add it to API
     21        later if we learn that it's needed.
     22       
     23        The implementation uses a "main thread only" flag inside each object. When
     24        collecting on a secondary thread, the Collector does an extra pass through
     25        the heap to mark all flagged objects before sweeping. This solution makes
     26        the common case -- flag lots of objects, but never collect on a secondary
     27        thread -- very fast, even though the uncommon case of garbage collecting
     28        on a secondary thread isn't as fast as it could be. I left some notes
     29        about how to speed it up, if we ever care.
     30       
     31        For posterity, here are some things I learned about GC while investigating:
     32       
     33        * Each collect must either mark or delete every heap object. "Zombie"
     34        objects, which are neither marked nor deleted, raise these issues:
     35
     36            * On the next pass, the conservative marking algorithm might mark a
     37            zombie, causing it to mark freed objects.
     38
     39            * The client might try to use a zombie, which would seem live because
     40            its finalizer had not yet run.
     41
     42        * A collect on the main thread is free to delete any object. Presumably,
     43        objects allocated on secondary threads have thread-safe finalizers.
     44
     45        * A collect on a secondary thread must not delete thread-unsafe objects.
     46
     47        * The mark function must be thread-safe.
     48       
     49        Line by line comments:
     50
     51        * API/JSObjectRef.h: Added comment specifying that the finalize callback
     52        may run on any thread.
     53
     54        * JavaScriptCore.exp: Nothing to see here.
     55
     56        * bindings/npruntime.cpp:
     57        (_NPN_GetStringIdentifier): Added JSLock.
     58
     59        * bindings/objc/objc_instance.h:
     60        * bindings/objc/objc_instance.mm:
     61        (ObjcInstance::~ObjcInstance): Use an autorelease pool. The other callers
     62        to CFRelease needed one, too, but they were dead code, so I removed them
     63        instead. (This fixes a leak seen while running run-webkit-tests --threaded,
     64        although I don't think it's specifically a threading issue.)
     65       
     66        * kjs/collector.cpp:
     67        (KJS::Collector::collectOnMainThreadOnly): New function. Tells the collector
     68        to collect a value only if it's collecting on the main thread.
     69        (KJS::Collector::markMainThreadOnlyObjects): New function. Scans the heap
     70        for "main thread only" objects and marks them.
     71
     72        * kjs/date_object.cpp:
     73        (KJS::DateObjectImp::DateObjectImp): To make the new ASSERTs happy, allocate
     74        our globals on the heap, avoiding a seemingly unsafe destructor call at
     75        program exit time.
     76        * kjs/function_object.cpp:
     77        (FunctionPrototype::FunctionPrototype): ditto
     78
     79        * kjs/interpreter.cpp:
     80        (KJS::Interpreter::mark): Removed boolean parameter, which was an incomplete
     81        and arguably hackish way to implement markMainThreadOnlyObjects() inside WebCore.
     82        * kjs/interpreter.h:
     83
     84        * kjs/identifier.cpp:
     85        (KJS::identifierTable): Added some ASSERTs to check for thread safety
     86        problems.
     87
     88        * kjs/list.cpp: Added some ASSERTs to check for thread safety problems.
     89        (KJS::allocateListImp):
     90        (KJS::List::release):
     91        (KJS::List::append):
     92        (KJS::List::empty): Make the new ASSERTs happy.
     93
     94        * kjs/object.h:
     95        (KJS::JSObject::JSObject): "m_destructorIsThreadSafe" => "m_collectOnMainThreadOnly".
     96        I removed the constructor parameter because m_collectOnMainThreadOnly,
     97        like m_marked, is a Collector bit, so only the Collector should set or get it.
     98
     99        * kjs/object_object.cpp:
     100        (ObjectPrototype::ObjectPrototype): Make the ASSERTs happy.
     101        * kjs/regexp_object.cpp:
     102        (RegExpPrototype::RegExpPrototype): ditto
     103
     104        * kjs/ustring.cpp: Added some ASSERTs to check for thread safety problems.
     105        (KJS::UCharReference::ref):
     106        (KJS::UString::Rep::createCopying):
     107        (KJS::UString::Rep::create):
     108        (KJS::UString::Rep::destroy):
     109        (KJS::UString::null): Make the new ASSERTs happy.
     110        * kjs/ustring.h:
     111        (KJS::UString::Rep::ref): Added some ASSERTs to check for thread safety problems.
     112        (KJS::UString::Rep::deref):
     113
     114        * kjs/value.h:
     115        (KJS::JSCell::JSCell):
     116
    11172007-03-06  Geoffrey Garen  <ggaren@apple.com>
    2118
  • trunk/JavaScriptCore/JavaScriptCore.exp

    r19894 r20004  
    118118__ZN3KJS11Interpreter21shouldPrintExceptionsEv
    119119__ZN3KJS11Interpreter24setShouldPrintExceptionsEb
    120 __ZN3KJS11Interpreter4markEb
     120__ZN3KJS11Interpreter4markEv
    121121__ZN3KJS11Interpreter6s_hookE
    122122__ZN3KJS11Interpreter8evaluateERKNS_7UStringEiPKNS_5UCharEiPNS_7JSValueE
     
    167167__ZN3KJS6JSLock4lockEv
    168168__ZN3KJS6JSLock6unlockEv
     169__ZN3KJS6JSLock9lockCountEv
    169170__ZN3KJS6Lookup9findEntryEPKNS_9HashTableERKNS_10IdentifierE
    170171__ZN3KJS6Parser11prettyPrintERKNS_7UStringEPiPS1_
     
    215216__ZN3KJS9Collector19numProtectedObjectsEv
    216217__ZN3KJS9Collector20rootObjectTypeCountsEv
     218__ZN3KJS9Collector23collectOnMainThreadOnlyEPNS_7JSValueE
    217219__ZN3KJS9Collector4sizeEv
    218220__ZN3KJS9Collector7collectEv
     
    274276__ZTVN3KJS15JSWrapperObjectE
    275277__ZTVN3KJS19InternalFunctionImpE
     278__ZTVN3KJS6JSCellE
    276279__ZTVN3KJS8JSObjectE
    277280_kJSClassDefinitionEmpty
  • trunk/JavaScriptCore/bindings/npruntime.cpp

    r18424 r20004  
    2929#include "npruntime_priv.h"
    3030
     31#include "JSLock.h"
    3132#include "c_utility.h"
    3233#include "identifier.h"
     
    6263    if (name) {
    6364        PrivateIdentifier* identifier = 0;
     65       
     66        KJS::JSLock lock;
    6467       
    6568        identifier = getStringIdentifierMap()->get(identifierFromNPIdentifier(name).ustring().rep());
  • trunk/JavaScriptCore/bindings/objc/objc_instance.h

    r15696 r20004  
    4444    virtual Class *getClass() const;
    4545   
    46     ObjcInstance(const ObjcInstance &other);
    47 
    48     ObjcInstance &operator=(const ObjcInstance &other);
    49    
    5046    virtual void begin();
    5147    virtual void end();
     
    7369   
    7470private:
     71    ObjcInstance(const ObjcInstance& other);
     72    ObjcInstance& operator=(const ObjcInstance& other);
     73   
    7574    ObjectStructPtr _instance;
    7675    mutable ObjcClass *_class;
  • trunk/JavaScriptCore/bindings/objc/objc_instance.mm

    r16310 r20004  
    5353ObjcInstance::~ObjcInstance()
    5454{
     55    begin(); // -finalizeForWebScript and -dealloc/-finalize may require autorelease pools.
    5556    if ([_instance respondsToSelector:@selector(finalizeForWebScript)])
    5657        [_instance performSelector:@selector(finalizeForWebScript)];
    5758    if (_instance)
    5859        CFRelease(_instance);
    59 }
    60 
    61 ObjcInstance::ObjcInstance(const ObjcInstance &other) : Instance()
    62 {
    63     _instance = other._instance;
    64     if (_instance)
    65         CFRetain(_instance);
    66     _class = other._class;
    67     _pool = 0;
    68     _beginCount = 0;
    69 }
    70 
    71 ObjcInstance &ObjcInstance::operator=(const ObjcInstance &other)
    72 {
    73     ObjectStructPtr _oldInstance = _instance;
    74     _instance = other._instance;
    75     if (_instance)
    76         CFRetain(_instance);
    77     if (_oldInstance)
    78         CFRelease(_oldInstance);
    79     // Classes are kept around forever.
    80     _class = other._class;
    81     return* this;
     60    end();
    8261}
    8362
  • trunk/JavaScriptCore/kjs/collector.cpp

    r19994 r20004  
    109109static CollectorHeap heap = {NULL, 0, 0, 0, NULL, 0, 0, 0, 0};
    110110
     111size_t Collector::mainThreadOnlyObjectCount = 0;
    111112bool Collector::memoryFull = false;
    112113
     
    474475}
    475476
     477void Collector::collectOnMainThreadOnly(JSValue* value)
     478{
     479    ASSERT(value);
     480    ASSERT(JSLock::lockCount() > 0);
     481
     482    if (JSImmediate::isImmediate(value))
     483      return;
     484
     485    JSCell* cell = value->downcast();
     486    cell->m_collectOnMainThreadOnly = true;
     487    ++mainThreadOnlyObjectCount;
     488}
     489
    476490void Collector::markProtectedObjects()
    477491{
     
    485499}
    486500
     501void Collector::markMainThreadOnlyObjects()
     502{
     503    ASSERT(!pthread_main_np());
     504
     505    // Optimization for clients that never register "main thread only" objects.
     506    if (!mainThreadOnlyObjectCount)
     507        return;
     508
     509    // FIXME: We can optimize this marking algorithm by keeping an exact set of
     510    // "main thread only" objects when the "main thread only" object count is
     511    // small. We don't want to keep an exact set all the time, because WebCore
     512    // tends to create lots of "main thread only" objects, and all that set
     513    // thrashing can be expensive.
     514   
     515    size_t count = 0;
     516   
     517    for (size_t block = 0; block < heap.usedBlocks; block++) {
     518        ASSERT(count < mainThreadOnlyObjectCount);
     519       
     520        CollectorBlock* curBlock = heap.blocks[block];
     521        size_t minimumCellsToProcess = curBlock->usedCells;
     522        for (size_t i = 0; (i < minimumCellsToProcess) & (i < CELLS_PER_BLOCK); i++) {
     523            CollectorCell* cell = curBlock->cells + i;
     524            if (cell->u.freeCell.zeroIfFree == 0)
     525                ++minimumCellsToProcess;
     526            else {
     527                JSCell* imp = reinterpret_cast<JSCell*>(cell);
     528                if (imp->m_collectOnMainThreadOnly) {
     529                    if(!imp->marked())
     530                        imp->mark();
     531                    if (++count == mainThreadOnlyObjectCount)
     532                        return;
     533                }
     534            }
     535        }
     536    }
     537
     538    for (size_t cell = 0; cell < heap.usedOversizeCells; cell++) {
     539        ASSERT(count < mainThreadOnlyObjectCount);
     540
     541        JSCell* imp = reinterpret_cast<JSCell*>(heap.oversizeCells[cell]);
     542        if (imp->m_collectOnMainThreadOnly) {
     543            if (!imp->marked())
     544                imp->mark();
     545            if (++count == mainThreadOnlyObjectCount)
     546                return;
     547        }
     548    }
     549}
     550
    487551bool Collector::collect()
    488552{
     
    502566    Interpreter* scr = Interpreter::s_hook;
    503567    do {
    504       scr->mark(currentThreadIsMainThread);
     568      scr->mark();
    505569      scr = scr->next;
    506570    } while (scr != Interpreter::s_hook);
     
    512576  markProtectedObjects();
    513577  List::markProtectedLists();
     578  if (!currentThreadIsMainThread)
     579    markMainThreadOnlyObjects();
    514580
    515581  // SWEEP: delete everything with a zero refcount (garbage) and unmark everything else
     
    531597        if (imp->m_marked) {
    532598          imp->m_marked = false;
    533         } else if (currentThreadIsMainThread || imp->m_destructorIsThreadSafe) {
     599        } else {
    534600          // special case for allocated but uninitialized object
    535           // (We don't need this check earlier because nothing prior this point assumes the object has a valid vptr.)
     601          // (We don't need this check earlier because nothing prior this point
     602          // assumes the object has a valid vptr.)
    536603          if (cell->u.freeCell.zeroIfFree == 0)
    537604            continue;
    538605
     606          ASSERT(currentThreadIsMainThread || !imp->m_collectOnMainThreadOnly);
     607          if (imp->m_collectOnMainThreadOnly)
     608            --mainThreadOnlyObjectCount;
    539609          imp->~JSCell();
    540610          --usedCells;
     
    557627          if (imp->m_marked) {
    558628            imp->m_marked = false;
    559           } else if (currentThreadIsMainThread || imp->m_destructorIsThreadSafe) {
     629          } else {
     630            ASSERT(currentThreadIsMainThread || !imp->m_collectOnMainThreadOnly);
     631            if (imp->m_collectOnMainThreadOnly)
     632              --mainThreadOnlyObjectCount;
    560633            imp->~JSCell();
    561634            --usedCells;
     
    600673    JSCell *imp = (JSCell *)heap.oversizeCells[cell];
    601674   
    602     if (!imp->m_marked && (currentThreadIsMainThread || imp->m_destructorIsThreadSafe)) {
     675    if (imp->m_marked) {
     676      imp->m_marked = false;
     677      cell++;
     678    } else {
     679      ASSERT(currentThreadIsMainThread || !imp->m_collectOnMainThreadOnly);
     680      if (imp->m_collectOnMainThreadOnly)
     681        --mainThreadOnlyObjectCount;
    603682      imp->~JSCell();
    604683#if DEBUG_COLLECTOR
     
    618697        heap.oversizeCells = (CollectorCell **)fastRealloc(heap.oversizeCells, heap.numOversizeCells * sizeof(CollectorCell *));
    619698      }
    620     } else {
    621       imp->m_marked = false;
    622       cell++;
    623699    }
    624700  }
  • trunk/JavaScriptCore/kjs/collector.h

    r15696 r20004  
    3232namespace KJS {
    3333
    34   /**
    35    * @short Garbage collector.
    36    */
    3734  class Collector {
    38     // disallow direct construction/destruction
    39     Collector();
    4035  public:
    4136    static void* allocate(size_t s);
     
    5449    static void protect(JSValue*);
    5550    static void unprotect(JSValue*);
     51   
     52    static void collectOnMainThreadOnly(JSValue*);
    5653
    5754    static size_t numInterpreters();
     
    6158    class Thread;
    6259    static void registerThread();
    63 
     60   
    6461  private:
     62    Collector();
    6563
    6664    static void markProtectedObjects();
     65    static void markMainThreadOnlyObjects();
    6766    static void markCurrentThreadConservatively();
    6867    static void markOtherThreadConservatively(Thread* thread);
     
    7069    static void markStackObjectsConservatively(void* start, void* end);
    7170
     71    static size_t mainThreadOnlyObjectCount;
    7272    static bool memoryFull;
    7373  };
  • trunk/JavaScriptCore/kjs/date_object.cpp

    r19945 r20004  
    597597  : InternalFunctionImp(funcProto)
    598598{
    599   // ECMA 15.9.4.1 Date.prototype
     599  static const Identifier* parsePropertyName = new Identifier("parse");
     600  static const Identifier* UTCPropertyName = new Identifier("UTC");
     601
    600602  putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
    601 
    602   static const Identifier parsePropertyName("parse");
    603   putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::Parse, 1, parsePropertyName), DontEnum);
    604   static const Identifier UTCPropertyName("UTC");
    605   putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::UTC, 7, UTCPropertyName), DontEnum);
    606 
    607   // no. of arguments for constructor
     603  putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::Parse, 1, *parsePropertyName), DontEnum);
     604  putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::UTC, 7, *UTCPropertyName), DontEnum);
    608605  putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
    609606}
  • trunk/JavaScriptCore/kjs/function_object.cpp

    r19203 r20004  
    4141FunctionPrototype::FunctionPrototype(ExecState *exec)
    4242{
     43  static const Identifier* applyPropertyName = new Identifier("apply");
     44  static const Identifier* callPropertyName = new Identifier("call");
     45
    4346  putDirect(lengthPropertyName, jsNumber(0), DontDelete|ReadOnly|DontEnum);
    4447  putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::ToString, 0, toStringPropertyName), DontEnum);
    45   static const Identifier applyPropertyName("apply");
    46   putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Apply, 2, applyPropertyName), DontEnum);
    47   static const Identifier callPropertyName("call");
    48   putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Call, 1, callPropertyName), DontEnum);
     48  putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Apply, 2, *applyPropertyName), DontEnum);
     49  putDirectFunction(new FunctionProtoFunc(exec, this, FunctionProtoFunc::Call, 1, *callPropertyName), DontEnum);
    4950}
    5051
  • trunk/JavaScriptCore/kjs/identifier.cpp

    r17405 r20004  
    3939#include "identifier.h"
    4040
     41#include "JSLock.h"
     42#include <new> // for placement new
     43#include <string.h> // for strlen
     44#include <wtf/Assertions.h>
    4145#include <wtf/FastMalloc.h>
    4246#include <wtf/HashSet.h>
    43 #include <string.h> // for strlen
    44 #include <new> // for placement new
    4547
    4648namespace WTF {
     
    6769static inline IdentifierTable& identifierTable()
    6870{
     71    ASSERT(JSLock::lockCount() > 0);
     72
    6973    if (!table)
    7074        table = new IdentifierTable;
  • trunk/JavaScriptCore/kjs/interpreter.cpp

    r19901 r20004  
    538538}
    539539
    540 void Interpreter::mark(bool)
     540void Interpreter::mark()
    541541{
    542542    if (m_context)
  • trunk/JavaScriptCore/kjs/interpreter.h

    r19901 r20004  
    287287     * implementing custom mark methods must make sure to chain to this one.
    288288     */
    289     virtual void mark(bool currentThreadIsMainThread);
     289    virtual void mark();
    290290
    291291#ifdef KJS_DEBUG_MEM
  • trunk/JavaScriptCore/kjs/list.cpp

    r15698 r20004  
    142142static inline ListImp *allocateListImp()
    143143{
     144    ASSERT(JSLock::lockCount() > 0);
     145   
    144146    // Find a free one in the pool.
    145147    if (poolUsed < poolSize) {
     
    202204
    203205void List::release()
    204 {
     206{   
     207    ASSERT(JSLock::lockCount() > 0);
     208   
    205209    ListImp *imp = static_cast<ListImp *>(_impBase);
    206210   
     
    222226        poolUsed--;
    223227    } else {
    224         assert(imp->state == usedOnHeap);
     228        ASSERT(imp->state == usedOnHeap);
    225229        HeapListImp *list = static_cast<HeapListImp *>(imp);
    226230
     
    259263void List::append(JSValue *v)
    260264{
     265    ASSERT(JSLock::lockCount() > 0);
     266   
    261267    ListImp *imp = static_cast<ListImp *>(_impBase);
    262268
     
    332338}
    333339
    334 const List &List::empty()
    335 {
    336     static List emptyList;
    337     return emptyList;
     340const List& List::empty()
     341{
     342    static List* staticEmptyList = new List;
     343    return *staticEmptyList;
    338344}
    339345
  • trunk/JavaScriptCore/kjs/object.h

    r17372 r20004  
    107107     * @param proto The prototype
    108108     */
    109     JSObject(JSValue* proto, bool destructorIsThreadSafe = true);
     109    JSObject(JSValue* proto);
    110110
    111111    /**
     
    113113     * (that is, the ECMAScript "null" value, not a null object pointer).
    114114     */
    115     explicit JSObject(bool destructorIsThreadSafe = true);
     115    JSObject();
    116116
    117117    virtual void mark();
     
    503503JSObject *throwError(ExecState *, ErrorType);
    504504
    505 inline JSObject::JSObject(JSValue* proto, bool destructorIsThreadSafe)
    506     : JSCell(destructorIsThreadSafe)
    507     , _proto(proto)
     505inline JSObject::JSObject(JSValue* proto)
     506    : _proto(proto)
    508507{
    509508    assert(proto);
    510509}
    511510
    512 inline JSObject::JSObject(bool destructorIsThreadSafe)
    513     : JSCell(destructorIsThreadSafe)
    514     , _proto(jsNull())
     511inline JSObject::JSObject()
     512    : _proto(jsNull())
    515513{
    516514}
  • trunk/JavaScriptCore/kjs/object_object.cpp

    r13833 r20004  
    3434  : JSObject() // [[Prototype]] is null
    3535{
     36    static const Identifier* hasOwnPropertyPropertyName = new Identifier("hasOwnProperty");
     37    static const Identifier* propertyIsEnumerablePropertyName = new Identifier("propertyIsEnumerable");
     38    static const Identifier* isPrototypeOfPropertyName = new Identifier("isPrototypeOf");
     39    static const Identifier* defineGetterPropertyName = new Identifier("__defineGetter__");
     40    static const Identifier* defineSetterPropertyName = new Identifier("__defineSetter__");
     41    static const Identifier* lookupGetterPropertyName = new Identifier("__lookupGetter__");
     42    static const Identifier* lookupSetterPropertyName = new Identifier("__lookupSetter__");
     43
    3644    putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::ToString, 0, toStringPropertyName), DontEnum);
    3745    putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::ToLocaleString, 0, toLocaleStringPropertyName), DontEnum);
    3846    putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::ValueOf, 0, valueOfPropertyName), DontEnum);
    39     static Identifier hasOwnPropertyPropertyName("hasOwnProperty");
    40     static Identifier propertyIsEnumerablePropertyName("propertyIsEnumerable");
    41     static Identifier isPrototypeOfPropertyName("isPrototypeOf");
    42     putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::HasOwnProperty, 1, hasOwnPropertyPropertyName), DontEnum);
    43     putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::PropertyIsEnumerable, 1, propertyIsEnumerablePropertyName), DontEnum);
    44     putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::IsPrototypeOf, 1, isPrototypeOfPropertyName), DontEnum);
     47    putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::HasOwnProperty, 1, *hasOwnPropertyPropertyName), DontEnum);
     48    putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::PropertyIsEnumerable, 1, *propertyIsEnumerablePropertyName), DontEnum);
     49    putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::IsPrototypeOf, 1, *isPrototypeOfPropertyName), DontEnum);
    4550
    4651    // Mozilla extensions
    47     static const Identifier defineGetterPropertyName("__defineGetter__");
    48     static const Identifier defineSetterPropertyName("__defineSetter__");
    49     static const Identifier lookupGetterPropertyName("__lookupGetter__");
    50     static const Identifier lookupSetterPropertyName("__lookupSetter__");
    51     putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::DefineGetter, 2, defineGetterPropertyName), DontEnum);
    52     putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::DefineSetter, 2, defineSetterPropertyName), DontEnum);
    53     putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::LookupGetter, 1, lookupGetterPropertyName), DontEnum);
    54     putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::LookupSetter, 1, lookupSetterPropertyName), DontEnum);
     52    putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::DefineGetter, 2, *defineGetterPropertyName), DontEnum);
     53    putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::DefineSetter, 2, *defineSetterPropertyName), DontEnum);
     54    putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::LookupGetter, 1, *lookupGetterPropertyName), DontEnum);
     55    putDirectFunction(new ObjectProtoFunc(exec, funcProto, ObjectProtoFunc::LookupSetter, 1, *lookupSetterPropertyName), DontEnum);
    5556}
    5657
  • trunk/JavaScriptCore/kjs/regexp_object.cpp

    r18256 r20004  
    4949  : JSObject(objProto)
    5050{
    51   // The constructor will be added later in RegExpObject's constructor (?)
    52 
    53   static const Identifier execPropertyName("exec");
    54   static const Identifier testPropertyName("test");
    55   putDirectFunction(new RegExpProtoFunc(exec, funcProto, RegExpProtoFunc::Exec, 0, execPropertyName), DontEnum);
    56   putDirectFunction(new RegExpProtoFunc(exec, funcProto, RegExpProtoFunc::Test, 0, testPropertyName), DontEnum);
     51  static const Identifier* execPropertyName = new Identifier("exec");
     52  static const Identifier* testPropertyName = new Identifier("test");
     53
     54  putDirectFunction(new RegExpProtoFunc(exec, funcProto, RegExpProtoFunc::Exec, 0, *execPropertyName), DontEnum);
     55  putDirectFunction(new RegExpProtoFunc(exec, funcProto, RegExpProtoFunc::Test, 0, *testPropertyName), DontEnum);
    5756  putDirectFunction(new RegExpProtoFunc(exec, funcProto, RegExpProtoFunc::ToString, 0, toStringPropertyName), DontEnum);
    5857}
  • trunk/JavaScriptCore/kjs/ustring.cpp

    r19679 r20004  
    2525#include "ustring.h"
    2626
     27#include "JSLock.h"
     28#include "dtoa.h"
     29#include "identifier.h"
     30#include "operations.h"
    2731#include <assert.h>
     32#include <ctype.h>
     33#include <float.h>
     34#include <math.h>
     35#include <stdio.h>
    2836#include <stdlib.h>
    29 #include <stdio.h>
    30 #include <ctype.h>
     37#include <wtf/Vector.h>
     38
    3139#if HAVE(STRING_H)
    3240#include <string.h>
     
    3543#include <strings.h>
    3644#endif
    37 
    38 #include "dtoa.h"
    39 #include "identifier.h"
    40 #include "operations.h"
    41 #include <float.h>
    42 #include <math.h>
    43 #include <wtf/Vector.h>
    4445
    4546using std::max;
     
    155156UChar& UCharReference::ref() const
    156157{
     158  ASSERT(JSLock::lockCount() > 0);
     159
    157160  if (offset < str->rep()->len)
    158161    return *(str->rep()->data() + offset);
     
    165168PassRefPtr<UString::Rep> UString::Rep::createCopying(const UChar *d, int l)
    166169{
     170  ASSERT(JSLock::lockCount() > 0);
     171
    167172  int sizeInBytes = l * sizeof(UChar);
    168173  UChar *copyD = static_cast<UChar *>(fastMalloc(sizeInBytes));
     
    174179PassRefPtr<UString::Rep> UString::Rep::create(UChar *d, int l)
    175180{
     181  ASSERT(JSLock::lockCount() > 0);
     182
    176183  Rep *r = new Rep;
    177184  r->offset = 0;
     
    193200PassRefPtr<UString::Rep> UString::Rep::create(PassRefPtr<Rep> base, int offset, int length)
    194201{
    195   assert(base);
     202  ASSERT(JSLock::lockCount() > 0);
     203  ASSERT(base);
    196204
    197205  int baseOffset = base->offset;
     
    223231void UString::Rep::destroy()
    224232{
     233  ASSERT(JSLock::lockCount() > 0);
     234
    225235  if (isIdentifier)
    226236    Identifier::remove(this);
     
    467477}
    468478
    469 const UString &UString::null()
    470 {
    471   static UString n;
    472   return n;
     479const UString& UString::null()
     480{
     481  static UString* n = new UString;
     482  return *n;
    473483}
    474484
  • trunk/JavaScriptCore/kjs/ustring.h

    r19679 r20004  
    2525#define _KJS_USTRING_H_
    2626
     27#include "JSLock.h"
     28#include <stdint.h>
     29#include <wtf/Assertions.h>
    2730#include <wtf/FastMalloc.h>
     31#include <wtf/PassRefPtr.h>
    2832#include <wtf/RefPtr.h>
    29 #include <wtf/PassRefPtr.h>
    30 
    31 #include <stdint.h>
    3233
    3334/* On ARM some versions of GCC don't pack structures by default so sizeof(UChar)
     
    200201      static unsigned computeHash(const char *);
    201202
    202       Rep* ref() { ++rc; return this; }
    203       void deref() { if (--rc == 0) destroy(); }
     203      Rep* ref() { ASSERT(JSLock::lockCount() > 0); ++rc; return this; }
     204      void deref() { ASSERT(JSLock::lockCount() > 0); if (--rc == 0) destroy(); }
    204205
    205206      // unshared data
  • trunk/JavaScriptCore/kjs/value.h

    r18715 r20004  
    123123    friend class GetterSetterImp;
    124124private:
    125     explicit JSCell(bool destructorIsThreadSafe = true);
     125    JSCell();
    126126    virtual ~JSCell();
    127127public:
     
    157157
    158158private:
    159     bool m_destructorIsThreadSafe : 1;
     159    bool m_collectOnMainThreadOnly : 1;
    160160    bool m_marked : 1;
    161161};
     
    204204}
    205205
    206 inline JSCell::JSCell(bool destructorIsThreadSafe)
    207     : m_destructorIsThreadSafe(destructorIsThreadSafe)
     206inline JSCell::JSCell()
     207    : m_collectOnMainThreadOnly(false)
    208208    , m_marked(false)
    209209{
  • trunk/JavaScriptGlue/ChangeLog

    r19805 r20004  
     12007-03-06  Geoffrey Garen  <ggaren@apple.com>
     2
     3        Reviewed by Maciej Stachowiak.
     4
     5        Fixed all known crashers exposed by run-webkit-tests --threaded while using
     6        a PAC file (for maximum carnage). See JavaScriptCore ChangeLog for
     7        more details.
     8
     9        * JSBase.cpp:
     10        (JSBase::Release): Lock when deleting, because we may be deleting an object
     11        (like a JSRun) that holds thread-unsafe data.
     12
     13        * JSUtils.cpp:
     14        (CFStringToUString): Don't lock, because our caller locks. Also, locking
     15        inside a function that returns thread-unsafe data by copy will only mask
     16        threading problems.
     17
     18        * JavaScriptGlue.cpp:
     19        (JSRunEvaluate): Added missing JSLock.
     20        (JSRunCheckSyntax): Converted to JSLock.
     21        * JavaScriptGlue.xcodeproj/project.pbxproj:
     22
    1232007-02-22  Geoffrey Garen  <ggaren@apple.com>
    224
  • trunk/JavaScriptGlue/JSBase.cpp

    r13089 r20004  
    4747    if (--fRetainCount == 0)
    4848    {
     49        JSLock lock;
    4950        delete this;
    5051    }
  • trunk/JavaScriptGlue/JSUtils.cpp

    r19666 r20004  
    5252UString CFStringToUString(CFStringRef inCFString)
    5353{
    54     JSLock lock;
    55 
    5654    UString result;
    5755    if (inCFString) {
  • trunk/JavaScriptGlue/JavaScriptGlue.cpp

    r13089 r20004  
    288288    if (ptr)
    289289    {
     290        JSLock lock;
    290291        Completion completion = ptr->Evaluate();
    291292        if (completion.isValueCompletion())
     
    321322    if (ptr)
    322323    {
    323             JSLockInterpreter();
     324            JSLock lock;
    324325            result = ptr->CheckSyntax();
    325             JSUnlockInterpreter();
    326326    }
    327327    return result;
  • trunk/JavaScriptGlue/JavaScriptGlue.xcodeproj/project.pbxproj

    r19725 r20004  
    271271                        isa = PBXProject;
    272272                        buildConfigurationList = 14AC662B08CE7791006915A8 /* Build configuration list for PBXProject "JavaScriptGlue" */;
    273                         compatibilityVersion = "Xcode 2.4";
    274273                        hasScannedForEncodings = 1;
    275274                        mainGroup = 0867D691FE84028FC02AAC07 /* JavaScriptGlue */;
     
    277276                        projectDirPath = "";
    278277                        projectRoot = "";
    279                         shouldCheckCompatibility = 1;
    280278                        targets = (
    281279                                DD66F3B908F73ED700C75FD7 /* JavaScriptGlue */,
  • trunk/WebCore/ChangeLog

    r20001 r20004  
     12007-03-06  Geoffrey Garen  <ggaren@apple.com>
     2
     3        Reviewed by Maciej Stachowiak.
     4
     5        Fixed all known crashers exposed by run-webkit-tests --threaded [*]. See
     6        JavaScriptCore ChangeLog for more details.
     7
     8        * bindings/js/kjs_binding.cpp:
     9        (KJS::domNodesPerDocument): Added thread safety ASSERT.
     10        (KJS::ScriptInterpreter::mark): Removed obsolete logic for marking unsafe
     11        objects when collecting on a secondary thread. The Collector takes care
     12        of this now.
     13
     14        * bindings/js/kjs_binding.h:
     15        (KJS::DOMObject::DOMObject): Used new API for specifying that WebCore
     16        objects should be garbage collected on the main thread only.
     17
     18        * bindings/js/kjs_window.cpp:
     19        (KJS::ScheduledAction::execute): Moved JSLock to cover implementedsCall() call,
     20        which, for some subclasses, ends up allocating garbage collected objects.
     21        (This fix was speculative. I didn't actually see a crash from this.)
     22        (KJS::Window::timerFired): Added JSLock around ScheduleAction destruction,
     23        since it destroys a KJS::List.
     24
     25        * bindings/objc/WebScriptObject.mm:
     26        (-[WebScriptObject setException:]): Added JSLock. (This fix was speculative.
     27        I didn't actually see a crash from this.)
     28
     29        * bridge/mac/WebCoreScriptDebugger.mm:
     30        (-[WebCoreScriptCallFrame evaluateWebScript:]): Added JSLock. (This fix
     31        was speculative. I didn't actually see a crash from this.)
     32
     33        * dom/Document.cpp:
     34        (WebCore::Document::~Document): Added JSLock around modification to
     35        domNodesPerDocument(), which can be accessed concurrently during garbage
     36        collection.
     37        * dom/Node.cpp:
     38        (WebCore::Node::setDocument): ditto.
     39       
     40        [*] fast/js/toString-stack-overflow.html is an exception. --threaded mode
     41        crashes this test because it causes the garbage collector to run frequently,
     42        and this test crashes if you happen to garbage collect while it's running.
     43        This is a known issue with stack overflow during the mark phase. It's
     44        not related to threading.
     45
    1462007-03-06  Mark Rowe  <mrowe@apple.com>
    247
  • trunk/WebCore/bindings/js/kjs_binding.cpp

    r19855 r20004  
    120120static NodePerDocMap& domNodesPerDocument()
    121121{
     122    // domNodesPerDocument() callers must synchronize using the JSLock because
     123    // domNodesPerDocument() is called during garbage collection, which can happen
     124    // on a secondary thread.
     125    ASSERT(JSLock::lockCount());
     126
    122127    static NodePerDocMap staticDOMNodesPerDocument;
    123128    return staticDOMNodesPerDocument;
     
    215220        }
    216221    }
    217 }
    218 
    219 void ScriptInterpreter::mark(bool currentThreadIsMainThread)
    220 {
    221     if (!currentThreadIsMainThread) {
    222         // On alternate threads, DOMObjects remain in the cache because they're not collected.
    223         // So, they need an opportunity to mark their children.
    224         DOMObjectMap::iterator objectEnd = domObjects().end();
    225         for (DOMObjectMap::iterator objectIt = domObjects().begin(); objectIt != objectEnd; ++objectIt) {
    226             DOMObject* object = objectIt->second;
    227             if (!object->marked())
    228                 object->mark();
    229         }
    230     }
    231 
    232     Interpreter::mark(currentThreadIsMainThread);
    233222}
    234223
  • trunk/WebCore/bindings/js/kjs_binding.h

    r19013 r20004  
    4747    class DOMObject : public JSObject {
    4848    protected:
    49         // DOMObject Destruction is not thread-safe because JS DOM objects
    50         // wrap unsafe WebCore DOM data structures
    51         DOMObject() : JSObject(false) {}
     49        DOMObject()
     50        {
     51            // DOMObject destruction is not thread-safe because DOMObjects wrap
     52            // unsafe WebCore DOM data structures.
     53            Collector::collectOnMainThreadOnly(this);
     54        }
    5255#ifndef NDEBUG
    5356        virtual ~DOMObject();
     
    9396        bool wasRunByUserGesture() const;
    9497
    95         virtual void mark(bool currentThreadIsMainThread);
    9698        virtual ExecState* globalExec();
    9799
  • trunk/WebCore/bindings/js/kjs_window.cpp

    r19995 r20004  
    8484    DOMWindowTimer(int timeoutId, int nestingLevel, Window* o, ScheduledAction* a)
    8585        : m_timeoutId(timeoutId), m_nestingLevel(nestingLevel), m_object(o), m_action(a) { }
    86     virtual ~DOMWindowTimer() { delete m_action; }
     86    virtual ~DOMWindowTimer()
     87    {
     88        JSLock lock;
     89        delete m_action;
     90    }
    8791
    8892    int timeoutId() const { return m_timeoutId; }
     
    18491853
    18501854    if (JSValue* func = m_func.get()) {
     1855        JSLock lock;
    18511856        if (func->isObject() && static_cast<JSObject*>(func)->implementsCall()) {
    18521857            ExecState* exec = interpreter->globalExec();
    18531858            ASSERT(window == interpreter->globalObject());
    1854             JSLock lock;
    18551859            interpreter->startTimeoutCheck();
    18561860            static_cast<JSObject*>(func)->call(exec, window, m_args);
     
    19911995    delete timer;
    19921996    action->execute(this);
     1997   
     1998    JSLock lock;
    19931999    delete action;
    19942000}
  • trunk/WebCore/bindings/objc/WebScriptObject.mm

    r19747 r20004  
    418418        return;
    419419
     420    JSLock lock;
     421   
    420422    if ([self _rootObject]->interpreter()->context()) {
    421423        ExecState *exec = [self _rootObject]->interpreter()->context()->execState();
  • trunk/WebCore/bridge/mac/WebCoreScriptDebugger.mm

    r19747 r20004  
    337337- (id)evaluateWebScript:(NSString *)script
    338338{
     339    JSLock lock;
     340
    339341    UString code = String(script);
    340342
     
    360362    JSValue *result;
    361363    if (eval) {
    362         JSLock lock;
    363364        List args;
    364365        args.append(jsString(code));
  • trunk/WebCore/dom/Document.cpp

    r19952 r20004  
    393393
    394394    XMLHttpRequest::detachRequests(this);
    395     KJS::ScriptInterpreter::forgetAllDOMNodesForDocument(this);
     395    {
     396        KJS::JSLock lock;
     397        KJS::ScriptInterpreter::forgetAllDOMNodesForDocument(this);
     398    }
    396399
    397400    if (m_docChanged && changedDocuments)
  • trunk/WebCore/dom/Node.cpp

    r19855 r20004  
    170170        return;
    171171
    172     KJS::ScriptInterpreter::updateDOMNodeDocument(this, m_document.get(), doc);
     172    {
     173        KJS::JSLock lock;
     174        KJS::ScriptInterpreter::updateDOMNodeDocument(this, m_document.get(), doc);
     175    }
    173176    m_document = doc;
    174177}
Note: See TracChangeset for help on using the changeset viewer.