Changeset 47267 in webkit


Ignore:
Timestamp:
Aug 13, 2009 10:35:33 PM (15 years ago)
Author:
oliver@apple.com
Message:

Devirtualise marking
https://bugs.webkit.org/show_bug.cgi?id=28294

Reviewed by Maciej Stachowiak.

Add a bit to TypeInfo to indicate that an object uses the standard
JSObject::markChildren method. This allows us to devirtualise marking
of most objects (though a branch is still needed). We also add a branch
to identify arrays thus devirtualising marking in that case as well.

In order to make the best use of this devirtualisation I've also reworked
the MarkStack::drain() logic to make the iteration more efficient.

Location:
trunk
Files:
31 edited

Legend:

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

    r43122 r47267  
    4242    static PassRefPtr<Structure> createStructure(JSValue proto)
    4343    {
    44         return Structure::create(proto, TypeInfo(ObjectType, ImplementsHasInstance | HasStandardGetOwnPropertySlot));
     44        return Structure::create(proto, TypeInfo(ObjectType, ImplementsHasInstance | HasStandardGetOwnPropertySlot | HasDefaultMark));
    4545    }
    4646
  • trunk/JavaScriptCore/API/JSCallbackFunction.h

    r43372 r47267  
    4242    static PassRefPtr<Structure> createStructure(JSValue proto)
    4343    {
    44         return Structure::create(proto, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot));
     44        return Structure::create(proto, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultMark));
    4545    }
    4646
  • trunk/JavaScriptCore/ChangeLog

    r47265 r47267  
     12009-08-13  Oliver Hunt  <oliver@apple.com>
     2
     3        Reviewed by Maciej Stachowiak.
     4
     5        Devirtualise marking
     6        https://bugs.webkit.org/show_bug.cgi?id=28294
     7
     8        Add a bit to TypeInfo to indicate that an object uses the standard
     9        JSObject::markChildren method.  This allows us to devirtualise marking
     10        of most objects (though a branch is still needed).  We also add a branch
     11        to identify arrays thus devirtualising marking in that case as well.
     12
     13        In order to make the best use of this devirtualisation I've also reworked
     14        the MarkStack::drain() logic to make the iteration more efficient.
     15
     16        * API/JSCallbackConstructor.h:
     17        (JSC::JSCallbackConstructor::createStructure):
     18        * API/JSCallbackFunction.h:
     19        (JSC::JSCallbackFunction::createStructure):
     20        * JavaScriptCore.exp:
     21        * runtime/BooleanObject.h:
     22        (JSC::BooleanObject::createStructure):
     23        * runtime/FunctionPrototype.h:
     24        (JSC::FunctionPrototype::createStructure):
     25        * runtime/InternalFunction.h:
     26        (JSC::InternalFunction::createStructure):
     27        * runtime/JSAPIValueWrapper.h:
     28        (JSC::JSAPIValueWrapper::JSAPIValueWrapper):
     29        * runtime/JSArray.cpp:
     30        (JSC::JSArray::markChildren):
     31        * runtime/JSArray.h:
     32        (JSC::JSArray::markChildrenDirect):
     33        (JSC::MarkStack::drain):
     34        * runtime/JSByteArray.cpp:
     35        (JSC::JSByteArray::createStructure):
     36        * runtime/JSCell.h:
     37        (JSC::MarkStack::append):
     38        * runtime/JSGlobalData.cpp:
     39        (JSC::JSGlobalData::JSGlobalData):
     40        * runtime/JSNumberCell.h:
     41        (JSC::JSNumberCell::createStructure):
     42        * runtime/JSONObject.h:
     43        (JSC::JSONObject::createStructure):
     44        * runtime/JSObject.cpp:
     45        (JSC::JSObject::markChildren):
     46        * runtime/JSObject.h:
     47        (JSC::JSObject::markChildrenDirect):
     48        (JSC::JSObject::createStructure):
     49        * runtime/JSString.h:
     50        (JSC::JSString::createStructure):
     51        * runtime/JSType.h:
     52        (JSC::):
     53        * runtime/MarkStack.h:
     54        (JSC::MarkStack::MarkStack):
     55        (JSC::MarkStack::MarkSet::MarkSet):
     56        (JSC::MarkStack::MarkStackArray::last):
     57        * runtime/MathObject.h:
     58        (JSC::MathObject::createStructure):
     59        * runtime/NumberConstructor.h:
     60        (JSC::NumberConstructor::createStructure):
     61        * runtime/NumberObject.h:
     62        (JSC::NumberObject::createStructure):
     63        * runtime/RegExpConstructor.h:
     64        (JSC::RegExpConstructor::createStructure):
     65        * runtime/RegExpObject.h:
     66        (JSC::RegExpObject::createStructure):
     67        * runtime/StringObjectThatMasqueradesAsUndefined.h:
     68        (JSC::StringObjectThatMasqueradesAsUndefined::createStructure):
     69        * runtime/TypeInfo.h:
     70        (JSC::TypeInfo::hasDefaultMark):
     71
    1722009-08-13  Darin Adler  <darin@apple.com>
    273
  • trunk/JavaScriptCore/JavaScriptCore.exp

    r47236 r47267  
    263263__ZN3JSC9MarkStack13allocateStackEm
    264264__ZN3JSC9MarkStack18initializePagesizeEv
     265__ZN3JSC9MarkStack5drainEv
    265266__ZN3JSC9Structure17stopIgnoringLeaksEv
    266267__ZN3JSC9Structure18startIgnoringLeaksEv
  • trunk/JavaScriptCore/runtime/BooleanObject.h

    r43122 r47267  
    3232        virtual const ClassInfo* classInfo() const { return &info; }
    3333        static const ClassInfo info;
     34       
     35        static PassRefPtr<Structure> createStructure(JSValue prototype)
     36        {
     37            return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultMark));
     38        }
    3439    };
    3540
  • trunk/JavaScriptCore/runtime/FunctionPrototype.h

    r43225 r47267  
    3535        static PassRefPtr<Structure> createStructure(JSValue proto)
    3636        {
    37             return Structure::create(proto, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot));
     37            return Structure::create(proto, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultMark));
    3838        }
    3939
  • trunk/JavaScriptCore/runtime/InternalFunction.h

    r43122 r47267  
    4343        static PassRefPtr<Structure> createStructure(JSValue proto)
    4444        {
    45             return Structure::create(proto, TypeInfo(ObjectType, ImplementsHasInstance | HasStandardGetOwnPropertySlot));
     45            return Structure::create(proto, TypeInfo(ObjectType, ImplementsHasInstance | HasStandardGetOwnPropertySlot | HasDefaultMark));
    4646        }
    4747
  • trunk/JavaScriptCore/runtime/JSAPIValueWrapper.h

    r47022 r47267  
    5555            , m_value(value)
    5656        {
     57            ASSERT(!value.isCell());
    5758        }
    5859
  • trunk/JavaScriptCore/runtime/JSArray.cpp

    r47239 r47267  
    603603void JSArray::markChildren(MarkStack& markStack)
    604604{
    605     JSObject::markChildren(markStack);
    606 
    607     ArrayStorage* storage = m_storage;
    608 
    609     unsigned usedVectorLength = min(storage->m_length, storage->m_vectorLength);
    610     markStack.appendValues(storage->m_vector, usedVectorLength, MayContainNullValues);
    611 
    612     if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
    613         SparseArrayValueMap::iterator end = map->end();
    614         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
    615             markStack.append(it->second);
    616     }
     605    markChildrenDirect(markStack);
    617606}
    618607
  • trunk/JavaScriptCore/runtime/JSArray.h

    r47022 r47267  
    8383            return Structure::create(prototype, TypeInfo(ObjectType));
    8484        }
     85       
     86        inline void markChildrenDirect(MarkStack& markStack);
    8587
    8688    protected:
     
    126128    inline bool isJSArray(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsArrayVPtr; }
    127129
     130    void JSArray::markChildrenDirect(MarkStack& markStack) {
     131        JSObject::markChildrenDirect(markStack);
     132       
     133        ArrayStorage* storage = m_storage;
     134       
     135        unsigned usedVectorLength = std::min(storage->m_length, storage->m_vectorLength);
     136        markStack.appendValues(storage->m_vector, usedVectorLength, MayContainNullValues);
     137       
     138        if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
     139            SparseArrayValueMap::iterator end = map->end();
     140            for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
     141                markStack.append(it->second);
     142        }
     143    }
     144
     145    inline void MarkStack::drain()
     146    {
     147        while (!m_markSets.isEmpty() || !m_values.isEmpty()) {
     148            while (!m_markSets.isEmpty() && m_values.size() < 50) {
     149                ASSERT(!m_markSets.isEmpty());
     150                MarkSet& current = m_markSets.last();
     151                ASSERT(current.m_values);
     152                JSValue* end = current.m_end;
     153                ASSERT(current.m_values);
     154                ASSERT(current.m_values != end);
     155            findNextUnmarkedNullValue:
     156                ASSERT(current.m_values != end);
     157                JSValue v = *current.m_values;
     158                current.m_values++;
     159               
     160                if (!v || v.marked()) {
     161                    if (current.m_values == end) {
     162                        m_markSets.removeLast();
     163                        continue;
     164                    }
     165                    goto findNextUnmarkedNullValue;
     166                }
     167               
     168                JSCell* currentCell = v.asCell();
     169                currentCell->markCellDirect();
     170                if (currentCell->structure()->typeInfo().type() < CompoundType) {
     171                    if (current.m_values == end) {
     172                        m_markSets.removeLast();
     173                        continue;
     174                    }
     175                    goto findNextUnmarkedNullValue;
     176                }
     177               
     178                if (current.m_values == end)
     179                    m_markSets.removeLast();
     180
     181                if (currentCell->structure()->typeInfo().hasDefaultMark())
     182                    static_cast<JSObject*>(currentCell)->markChildrenDirect(*this);
     183                else if (currentCell->vptr() == m_jsArrayVPtr)
     184                    static_cast<JSArray*>(currentCell)->markChildrenDirect(*this);
     185                else
     186                    currentCell->markChildren(*this);
     187            }
     188            while (!m_values.isEmpty()) {
     189                JSCell* current = m_values.removeLast();
     190                ASSERT(current->marked());
     191                if (current->structure()->typeInfo().hasDefaultMark())
     192                    static_cast<JSObject*>(current)->markChildrenDirect(*this);
     193                else if (current->vptr() == m_jsArrayVPtr)
     194                    static_cast<JSArray*>(current)->markChildrenDirect(*this);
     195                else
     196                    current->markChildren(*this);
     197            }
     198        }
     199       // printf("virtual: %d nonvirtual: %d\n", virtualMark, nonVirtualMark);
     200    }
     201   
    128202} // namespace JSC
    129203
  • trunk/JavaScriptCore/runtime/JSByteArray.cpp

    r43122 r47267  
    4646PassRefPtr<Structure> JSByteArray::createStructure(JSValue prototype)
    4747{
    48     PassRefPtr<Structure> result = Structure::create(prototype, TypeInfo(ObjectType));
     48    PassRefPtr<Structure> result = Structure::create(prototype, TypeInfo(ObjectType, HasDefaultMark));
    4949    return result;
    5050}
  • trunk/JavaScriptCore/runtime/JSCell.h

    r47022 r47267  
    380380            m_values.append(cell);
    381381    }
    382 
    383     inline void MarkStack::drain() {
    384         while (!m_markSets.isEmpty() || !m_values.isEmpty()) {
    385             while ((!m_markSets.isEmpty()) && m_values.size() < 50) {
    386                 const MarkSet& current = m_markSets.removeLast();
    387                 JSValue* ptr = current.m_values;
    388                 JSValue* end = current.m_end;
    389                 if (current.m_properties == NoNullValues) {
    390                     while (ptr != end)
    391                         append(*ptr++);
    392                 } else {
    393                     while (ptr != end) {
    394                         if (JSValue value = *ptr++)
    395                             append(value);
    396                     }
    397                 }
    398             }
    399             while (!m_values.isEmpty()) {
    400                 JSCell* current = m_values.removeLast();
    401                 ASSERT(current->marked());
    402                 current->markChildren(*this);
    403             }
    404         }
    405     }
    406382} // namespace JSC
    407383
  • trunk/JavaScriptCore/runtime/JSGlobalData.cpp

    r47022 r47267  
    147147    , scopeNodeBeingReparsed(0)
    148148    , firstStringifierToMark(0)
     149    , markStack(vptrSet.jsArrayVPtr)
    149150{
    150151#if PLATFORM(MAC)
  • trunk/JavaScriptCore/runtime/JSNumberCell.h

    r46598 r47267  
    8585        }
    8686
    87         static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(NumberType, NeedsThisConversion)); }
     87        static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(NumberType, NeedsThisConversion | HasDefaultMark)); }
    8888
    8989    private:
  • trunk/JavaScriptCore/runtime/JSONObject.h

    r47022 r47267  
    4242        static PassRefPtr<Structure> createStructure(JSValue prototype)
    4343        {
    44             return Structure::create(prototype, TypeInfo(ObjectType));
     44            return Structure::create(prototype, TypeInfo(ObjectType, HasDefaultMark));
    4545        }
    4646
  • trunk/JavaScriptCore/runtime/JSObject.cpp

    r47022 r47267  
    3838#include <wtf/Assertions.h>
    3939
    40 #define JSOBJECT_MARK_TRACING 0
    41 
    42 #if JSOBJECT_MARK_TRACING
    43 
    44 #define JSOBJECT_MARK_BEGIN() \
    45     static int markStackDepth = 0; \
    46     for (int i = 0; i < markStackDepth; i++) \
    47         putchar('-'); \
    48     printf("%s (%p)\n", className().UTF8String().c_str(), this); \
    49     markStackDepth++; \
    50 
    51 #define JSOBJECT_MARK_END() \
    52     markStackDepth--;
    53 
    54 #else // JSOBJECT_MARK_TRACING
    55 
    56 #define JSOBJECT_MARK_BEGIN()
    57 #define JSOBJECT_MARK_END()
    58 
    59 #endif // JSOBJECT_MARK_TRACING
    6040
    6141namespace JSC {
     
    6545void JSObject::markChildren(MarkStack& markStack)
    6646{
    67     JSOBJECT_MARK_BEGIN();
    68 
    69     JSCell::markChildren(markStack);
    70     m_structure->markAggregate(markStack);
    71 
    72     PropertyStorage storage = propertyStorage();
    73     size_t storageSize = m_structure->propertyStorageSize();
    74     markStack.appendValues(reinterpret_cast<JSValue*>(storage), storageSize);
    75 
    76     JSOBJECT_MARK_END();
     47    markChildrenDirect(markStack);
    7748}
    7849
  • trunk/JavaScriptCore/runtime/JSObject.h

    r47022 r47267  
    2828#include "CommonIdentifiers.h"
    2929#include "CallFrame.h"
     30#include "JSCell.h"
    3031#include "JSNumberCell.h"
     32#include "MarkStack.h"
    3133#include "PropertySlot.h"
    3234#include "PutPropertySlot.h"
     
    7577
    7678        virtual void markChildren(MarkStack&);
     79        ALWAYS_INLINE void markChildrenDirect(MarkStack& markStack);
    7780
    7881        // The inline virtual destructor cannot be the first virtual function declared
     
    202205        static PassRefPtr<Structure> createStructure(JSValue prototype)
    203206        {
    204             return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot));
     207            return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultMark));
    205208        }
    206209
     
    628631}
    629632
     633ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack)
     634{
     635    JSCell::markChildren(markStack);
     636    m_structure->markAggregate(markStack);
     637   
     638    PropertyStorage storage = propertyStorage();
     639    size_t storageSize = m_structure->propertyStorageSize();
     640    markStack.appendValues(reinterpret_cast<JSValue*>(storage), storageSize);
     641}
     642
    630643} // namespace JSC
    631644
  • trunk/JavaScriptCore/runtime/JSString.h

    r46598 r47267  
    9191        JSString* getIndex(JSGlobalData*, unsigned);
    9292
    93         static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, NeedsThisConversion)); }
     93        static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, NeedsThisConversion | HasDefaultMark)); }
    9494
    9595    private:
  • trunk/JavaScriptCore/runtime/JSType.h

    r47022 r47267  
    3434        NullType          = 4,
    3535        StringType        = 5,
    36        
    3736        // The CompoundType value must come before any JSType that may have children
    3837        CompoundType      = 6,
  • trunk/JavaScriptCore/runtime/MarkStack.h

    r47159 r47267  
    3232
    3333namespace JSC {
     34    class JSGlobalData;
    3435    class Register;
    3536   
     
    3839    class MarkStack : Noncopyable {
    3940    public:
    40         MarkStack()
    41             : m_markSets()
    42             , m_values()
     41        MarkStack(void* jsArrayVPtr)
     42            : m_jsArrayVPtr(jsArrayVPtr)
    4343        {
    4444        }
     
    8383                , m_properties(properties)
    8484            {
     85                ASSERT(values);
    8586            }
    8687            JSValue* m_values;
     
    137138                return m_data[--m_top];
    138139            }
     140           
     141            inline T& last()
     142            {
     143                ASSERT(m_top);
     144                return m_data[m_top - 1];
     145            }
    139146
    140147            inline bool isEmpty()
     
    170177        };
    171178
     179        void* m_jsArrayVPtr;
    172180        MarkStackArray<MarkSet> m_markSets;
    173181        MarkStackArray<JSCell*> m_values;
  • trunk/JavaScriptCore/runtime/MathObject.h

    r43122 r47267  
    3737        static PassRefPtr<Structure> createStructure(JSValue prototype)
    3838        {
    39             return Structure::create(prototype, TypeInfo(ObjectType));
     39            return Structure::create(prototype, TypeInfo(ObjectType, HasDefaultMark));
    4040        }
    4141    };
  • trunk/JavaScriptCore/runtime/NumberConstructor.h

    r43122 r47267  
    3939        static PassRefPtr<Structure> createStructure(JSValue proto)
    4040        {
    41             return Structure::create(proto, TypeInfo(ObjectType, ImplementsHasInstance));
     41            return Structure::create(proto, TypeInfo(ObjectType, ImplementsHasInstance | HasDefaultMark));
    4242        }
    4343
  • trunk/JavaScriptCore/runtime/NumberObject.h

    r43122 r47267  
    3131
    3232        static const ClassInfo info;
     33       
     34        static PassRefPtr<Structure> createStructure(JSValue prototype)
     35        {
     36            return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot | HasDefaultMark));
     37        }
    3338
    3439    private:
  • trunk/JavaScriptCore/runtime/RegExpConstructor.h

    r44224 r47267  
    3737        static PassRefPtr<Structure> createStructure(JSValue prototype)
    3838        {
    39             return Structure::create(prototype, TypeInfo(ObjectType, ImplementsHasInstance));
     39            return Structure::create(prototype, TypeInfo(ObjectType, ImplementsHasInstance | HasDefaultMark));
    4040        }
    4141
  • trunk/JavaScriptCore/runtime/RegExpObject.h

    r46907 r47267  
    4949        static PassRefPtr<Structure> createStructure(JSValue prototype)
    5050        {
    51             return Structure::create(prototype, TypeInfo(ObjectType));
     51            return Structure::create(prototype, TypeInfo(ObjectType, HasDefaultMark));
    5252        }
    5353
  • trunk/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h

    r43122 r47267  
    4545        static PassRefPtr<Structure> createStructure(JSValue proto)
    4646        {
    47             return Structure::create(proto, TypeInfo(ObjectType, MasqueradesAsUndefined));
     47            return Structure::create(proto, TypeInfo(ObjectType, MasqueradesAsUndefined | HasDefaultMark));
    4848        }
    4949
  • trunk/JavaScriptCore/runtime/TypeInfo.h

    r44224 r47267  
    3939    static const unsigned NeedsThisConversion = 1 << 4;
    4040    static const unsigned HasStandardGetOwnPropertySlot = 1 << 5;
     41    static const unsigned HasDefaultMark = 1 << 6;
    4142
    4243    class TypeInfo {
     
    6061        bool needsThisConversion() const { return m_flags & NeedsThisConversion; }
    6162        bool hasStandardGetOwnPropertySlot() const { return m_flags & HasStandardGetOwnPropertySlot; }
    62 
     63        bool hasDefaultMark() const { return m_flags & HasDefaultMark; }
    6364        unsigned flags() const { return m_flags; }
    6465
  • trunk/JavaScriptGlue/ChangeLog

    r47022 r47267  
     12009-08-13  Oliver Hunt  <oliver@apple.com>
     2
     3        Reviewed by Maciej Stachowiak.
     4
     5        Devirtualise marking
     6        https://bugs.webkit.org/show_bug.cgi?id=28294
     7
     8        Continue to jump through hoops to deal with the exposed marking routines
     9        in JavaScriptGlue.
     10
     11        * JSValueWrapper.cpp:
     12        (JSValueWrapper::JSObjectMark):
     13
    1142009-08-07  Oliver Hunt  <oliver@apple.com>
    215
  • trunk/JavaScriptGlue/JSValueWrapper.cpp

    r47022 r47267  
    3030#include "JSValueWrapper.h"
    3131#include "JSRun.h"
     32#include <JavaScriptCore/JSArray.h>
    3233#include <JavaScriptCore/PropertyNameArray.h>
    3334#include <pthread.h>
     
    198199    {
    199200        // This results in recursive marking but will be otherwise safe and correct.
    200         MarkStack markStack;
     201        // We claim the array vptr is 0 because we don't have access to it here, and
     202        // claiming 0 is functionally harmless -- it merely means that we can't
     203        // devirtualise marking of arrays when recursing from this point.
     204        MarkStack markStack(0);
    201205        markStack.append(ptr->fValue.get());
    202206        markStack.drain();
  • trunk/WebCore/ChangeLog

    r47266 r47267  
     12009-08-13  Oliver Hunt  <oliver@apple.com>
     2
     3        Reviewed by Maciej Stachowiak.
     4
     5        Devirtualise marking
     6        https://bugs.webkit.org/show_bug.cgi?id=28294
     7
     8        Make sure we override the JSObject createStructure method on those
     9        objects that have custom marking routines.
     10
     11        * bindings/scripts/CodeGeneratorJS.pm:
     12
    1132009-08-13  Darin Adler  <darin@apple.com>
    214
  • trunk/WebCore/bindings/scripts/CodeGeneratorJS.pm

    r47022 r47267  
    665665        push(@headerContent, "    bool getOwnPropertySlotDelegate(JSC::ExecState*, const JSC::Identifier&, JSC::PropertySlot&);\n") if $dataNode->extendedAttributes->{"DelegatingPrototypeGetOwnPropertySlot"};
    666666
     667        push(@headerContent,
     668            "    static PassRefPtr<JSC::Structure> createStructure(JSC::JSValue prototype)\n" .
     669            "    {\n" .
     670            "        return JSC::Structure::create(prototype, JSC::TypeInfo(JSC::ObjectType" . ($dataNode->extendedAttributes->{"CustomMarkFunction"} ? "" : ", JSC::HasDefaultMark") . "));\n" .
     671            "    }\n");
     672    } elsif ($dataNode->extendedAttributes->{"CustomMarkFunction"}) {
    667673        push(@headerContent,
    668674            "    static PassRefPtr<JSC::Structure> createStructure(JSC::JSValue prototype)\n" .
Note: See TracChangeset for help on using the changeset viewer.