Commit a400816a authored by kmccullough@apple.com's avatar kmccullough@apple.com

2008-07-09 Kevin McCullough <kmccullough@apple.com>

        Reviewed by Geoff and Adam.

        <rdar://problem/5951532> JSProfiler: Implement Bottom-Up view (19228)
        - This is the plumbing for bottom-up, but does not include calculating
        time, mostly because I'm still undclear about what the end result should
        look like.
        - This, obviously, does not include the UI to expose this in the
        inspector yet.

        * JavaScriptCore.xcodeproj/project.pbxproj:
        * profiler/CallIdentifier.h:
        (KJS::CallIdentifier::CallIdentifier):
        (WTF::): Added HashTraits for CallIdentifiers to be used by a HashMap.
        * profiler/HeavyProfile.cpp: Added.
        (KJS::HeavyProfile::HeavyProfile):
        (KJS::HeavyProfile::generateHeavyStructure):
        (KJS::HeavyProfile::addNode):
        (KJS::HeavyProfile::mergeProfiles):
        (KJS::HeavyProfile::addAncestorsAsChildren):
        * profiler/HeavyProfile.h: Added.
        (KJS::HeavyProfile::create):
        (KJS::HeavyProfile::heavyProfile):
        (KJS::HeavyProfile::treeProfile):
        * profiler/Profile.cpp: Removed old commented out includes.
        * profiler/Profile.h: The m_head is needed by the HeavyProfile so it
        is now protected as opposed to private.
        * profiler/ProfileNode.cpp:
        (KJS::ProfileNode::ProfileNode): Created a constructor to copy
        ProfileNodes.
        (KJS::ProfileNode::findChild): Added a null check to make HeavyProfile
        children finding easier and avoid a potential crasher.
        * profiler/ProfileNode.h: Mostly moved things around but also added some
        functionality needed by HeavyProfile.
        (KJS::ProfileNode::create):
        (KJS::ProfileNode::functionName):
        (KJS::ProfileNode::url):
        (KJS::ProfileNode::lineNumber):
        (KJS::ProfileNode::head):
        (KJS::ProfileNode::setHead):
        (KJS::ProfileNode::setNextSibling):
        (KJS::ProfileNode::actualTotalTime):
        (KJS::ProfileNode::actualSelfTime):
        * profiler/TreeProfile.cpp: Implemented the ability to get a
        HeavyProfile.
        (KJS::TreeProfile::heavyProfile):
        * profiler/TreeProfile.h:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@35077 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent d545a160
2008-07-09 Kevin McCullough <kmccullough@apple.com>
Reviewed by Geoff and Adam.
<rdar://problem/5951532> JSProfiler: Implement Bottom-Up view (19228)
- This is the plumbing for bottom-up, but does not include calculating
time, mostly because I'm still undclear about what the end result should
look like.
- This, obviously, does not include the UI to expose this in the
inspector yet.
* JavaScriptCore.xcodeproj/project.pbxproj:
* profiler/CallIdentifier.h:
(KJS::CallIdentifier::CallIdentifier):
(WTF::): Added HashTraits for CallIdentifiers to be used by a HashMap.
* profiler/HeavyProfile.cpp: Added.
(KJS::HeavyProfile::HeavyProfile):
(KJS::HeavyProfile::generateHeavyStructure):
(KJS::HeavyProfile::addNode):
(KJS::HeavyProfile::mergeProfiles):
(KJS::HeavyProfile::addAncestorsAsChildren):
* profiler/HeavyProfile.h: Added.
(KJS::HeavyProfile::create):
(KJS::HeavyProfile::heavyProfile):
(KJS::HeavyProfile::treeProfile):
* profiler/Profile.cpp: Removed old commented out includes.
* profiler/Profile.h: The m_head is needed by the HeavyProfile so it
is now protected as opposed to private.
* profiler/ProfileNode.cpp:
(KJS::ProfileNode::ProfileNode): Created a constructor to copy
ProfileNodes.
(KJS::ProfileNode::findChild): Added a null check to make HeavyProfile
children finding easier and avoid a potential crasher.
* profiler/ProfileNode.h: Mostly moved things around but also added some
functionality needed by HeavyProfile.
(KJS::ProfileNode::create):
(KJS::ProfileNode::functionName):
(KJS::ProfileNode::url):
(KJS::ProfileNode::lineNumber):
(KJS::ProfileNode::head):
(KJS::ProfileNode::setHead):
(KJS::ProfileNode::setNextSibling):
(KJS::ProfileNode::actualTotalTime):
(KJS::ProfileNode::actualSelfTime):
* profiler/TreeProfile.cpp: Implemented the ability to get a
HeavyProfile.
(KJS::TreeProfile::heavyProfile):
* profiler/TreeProfile.h:
2008-07-08 Geoffrey Garen <ggaren@apple.com>
Reviewed by Oliver Hunt.
......@@ -94,6 +94,8 @@
95CD45760E1C4FDD0085358E /* ProfileGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95CD45740E1C4FDD0085358E /* ProfileGenerator.cpp */; };
95CD45770E1C4FDD0085358E /* ProfileGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 95CD45750E1C4FDD0085358E /* ProfileGenerator.h */; settings = {ATTRIBUTES = (); }; };
95E3BC050E1AE68200B2D1C1 /* CallIdentifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 95E3BC040E1AE68200B2D1C1 /* CallIdentifier.h */; settings = {ATTRIBUTES = (Private, ); }; };
95FDFA140E22998F0006FB00 /* HeavyProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95FDFA130E22998F0006FB00 /* HeavyProfile.cpp */; };
95FDFA160E2299980006FB00 /* HeavyProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 95FDFA150E2299980006FB00 /* HeavyProfile.h */; };
A72700900DAC6BBC00E548D7 /* JSNotAnObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A72700780DAC605600E548D7 /* JSNotAnObject.cpp */; };
A72701B60DADE94900E548D7 /* ExceptionHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A72701B40DADE94900E548D7 /* ExceptionHelpers.cpp */; };
A72701B90DADE94900E548D7 /* ExceptionHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = A72701B30DADE94900E548D7 /* ExceptionHelpers.h */; };
......@@ -597,6 +599,8 @@
95CD45740E1C4FDD0085358E /* ProfileGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProfileGenerator.cpp; path = profiler/ProfileGenerator.cpp; sourceTree = "<group>"; };
95CD45750E1C4FDD0085358E /* ProfileGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProfileGenerator.h; path = profiler/ProfileGenerator.h; sourceTree = "<group>"; };
95E3BC040E1AE68200B2D1C1 /* CallIdentifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CallIdentifier.h; path = profiler/CallIdentifier.h; sourceTree = "<group>"; };
95FDFA130E22998F0006FB00 /* HeavyProfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = HeavyProfile.cpp; path = profiler/HeavyProfile.cpp; sourceTree = "<group>"; };
95FDFA150E2299980006FB00 /* HeavyProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HeavyProfile.h; path = profiler/HeavyProfile.h; sourceTree = "<group>"; };
A72700770DAC605600E548D7 /* JSNotAnObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSNotAnObject.h; sourceTree = "<group>"; };
A72700780DAC605600E548D7 /* JSNotAnObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSNotAnObject.cpp; sourceTree = "<group>"; };
A72701B30DADE94900E548D7 /* ExceptionHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExceptionHelpers.h; path = VM/ExceptionHelpers.h; sourceTree = "<group>"; };
......@@ -1260,6 +1264,8 @@
isa = PBXGroup;
children = (
95E3BC040E1AE68200B2D1C1 /* CallIdentifier.h */,
95FDFA130E22998F0006FB00 /* HeavyProfile.cpp */,
95FDFA150E2299980006FB00 /* HeavyProfile.h */,
95742F630DD11F5A000917FB /* Profile.cpp */,
95742F640DD11F5A000917FB /* Profile.h */,
95CD45740E1C4FDD0085358E /* ProfileGenerator.cpp */,
......@@ -1492,6 +1498,7 @@
BC257DF40E1F53740016B6C9 /* PrototypeFunction.h in Headers */,
BC756FC90E2031B200DE7D12 /* JSGlobalObjectFunctions.h in Headers */,
BCF605140E203EF800B9A64D /* ArgList.h in Headers */,
95FDFA160E2299980006FB00 /* HeavyProfile.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -1793,6 +1800,7 @@
E18E3A590DF9278C00D90B34 /* JSGlobalData.cpp in Sources */,
95CD41B30E1BF6560085358E /* TreeProfile.cpp in Sources */,
95CD45760E1C4FDD0085358E /* ProfileGenerator.cpp in Sources */,
95FDFA140E22998F0006FB00 /* HeavyProfile.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -50,6 +50,13 @@ namespace KJS {
{
}
CallIdentifier()
: m_name("")
, m_url("")
, m_lineNumber(0)
{
}
inline bool operator==(const CallIdentifier& ci) const { return ci.m_lineNumber == m_lineNumber && ci.m_name == m_name && ci.m_url == m_url; }
inline bool operator!=(const CallIdentifier& ci) const { return !(*this == ci); }
......@@ -61,4 +68,31 @@ namespace KJS {
} // namespace KJS
#endif // CallIdentifier_h
namespace WTF {
template<> struct IntHash<KJS::CallIdentifier> {
static unsigned hash(const KJS::CallIdentifier& key)
{
unsigned hashCodes[3] = {
key.m_name.rep()->hash(),
key.m_url.rep()->hash(),
key.m_lineNumber
};
return KJS::UString::Rep::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar));
}
static bool equal(const KJS::CallIdentifier& a, const KJS::CallIdentifier& b) { return a == b; }
static const bool safeToCompareToEmptyOrDeleted = true;
};
template<> struct DefaultHash<KJS::CallIdentifier> { typedef IntHash<KJS::CallIdentifier> Hash; };
template<> struct HashTraits<KJS::CallIdentifier> : GenericHashTraits<KJS::CallIdentifier> {
static const bool emptyValueIsZero = false;
static KJS::CallIdentifier emptyValue() { return KJS::CallIdentifier(KJS::UString(), KJS::UString(), 0); }
static const bool needsDestruction = false;
static void constructDeletedValue(KJS::CallIdentifier& slot) { new (&slot) KJS::CallIdentifier(KJS::UString(), KJS::UString(), 0); }
static bool isDeletedValue(const KJS::CallIdentifier& value) { return value.m_name.isNull() && value.m_url.isNull() && value.m_lineNumber == 0; }
};
} // namespace WTF
#endif // CallIdentifier_h
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "HeavyProfile.h"
#include "TreeProfile.h"
namespace KJS {
HeavyProfile::HeavyProfile(TreeProfile* treeProfile)
: Profile(treeProfile->title())
{
// FIXME: Find a way to calculate time for a HeavyProfile.
// We will need to copy the time information from the m_treeProfile's head to our m_head.
m_treeProfile = treeProfile;
generateHeavyStructure();
}
void HeavyProfile::generateHeavyStructure()
{
ProfileNode* treeHead = m_treeProfile->callTree();
ProfileNode* currentNode = treeHead->firstChild();
for (ProfileNode* nextNode = currentNode; nextNode; nextNode = nextNode->firstChild())
currentNode = nextNode;
// For each node
HashMap<CallIdentifier, ProfileNode*> foundChildren;
while (currentNode && currentNode != treeHead) {
ProfileNode* child = foundChildren.get(currentNode->callIdentifier());
if (child) // currentNode is in the set already
mergeProfiles(child, currentNode);
else { // currentNode is not in the set
child = addNode(currentNode);
foundChildren.set(currentNode->callIdentifier(), child);
}
currentNode = currentNode->traverseNextNodePostOrder();
}
}
ProfileNode* HeavyProfile::addNode(ProfileNode* currentNode)
{
RefPtr<ProfileNode> node = ProfileNode::create(m_head.get(), currentNode);
m_head->addChild(node);
addAncestorsAsChildren(currentNode->parent(), node.get());
return node.get();
}
void HeavyProfile::mergeProfiles(ProfileNode* heavyProfileHead, ProfileNode* treeProfileHead)
{
ASSERT_ARG(heavyProfileHead, heavyProfileHead);
ASSERT_ARG(treeProfileHead, treeProfileHead);
ProfileNode* currentHeavyNode = heavyProfileHead;
ProfileNode* currentTreeNode = treeProfileHead;
ProfileNode* previousHeavyNode;
while (currentHeavyNode) {
previousHeavyNode = currentHeavyNode;
currentTreeNode = currentTreeNode->parent();
currentHeavyNode = currentHeavyNode->findChild(currentTreeNode);
// FIXME: Find a way to calculate time for a HeavyProfile.
// We must sum self & total time, but maybe only for the leaf nodes.
}
// If currentTreeNode is null then we already have the whole tree we wanted to copy.
// If not we need to copy the subset of the tree that remains different between the two.
if (currentTreeNode)
addAncestorsAsChildren(currentTreeNode, previousHeavyNode);
}
void HeavyProfile::addAncestorsAsChildren(ProfileNode* getFrom, ProfileNode* addTo)
{
ASSERT_ARG(getFrom, getFrom);
ASSERT_ARG(addTo, addTo);
if (!getFrom->head())
return;
RefPtr<ProfileNode> currentNode = addTo;
for (ProfileNode* treeAncestor = getFrom; treeAncestor && treeAncestor != getFrom->head(); treeAncestor = treeAncestor->parent()) {
RefPtr<ProfileNode> newChild = ProfileNode::create(currentNode->head(), treeAncestor);
currentNode->addChild(newChild);
currentNode = newChild.release();
}
}
} // namespace KJS
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HeavyProfile_h
#define HeavyProfile_h
#include "Profile.h"
#include "ProfileNode.h"
#include "TreeProfile.h"
namespace KJS {
class ExecState;
class ProfilerClient;
class UString;
class HeavyProfile : public Profile {
public:
static PassRefPtr<HeavyProfile> create(TreeProfile* treeProfile)
{
return adoptRef(new HeavyProfile(treeProfile));
}
virtual Profile* heavyProfile() { return this; }
virtual Profile* treeProfile()
{
return m_treeProfile;
}
private:
HeavyProfile(TreeProfile*);
void generateHeavyStructure();
ProfileNode* addNode(ProfileNode*);
void mergeProfiles(ProfileNode* heavyProfileHead, ProfileNode* treeProfileHead);
void addAncestorsAsChildren(ProfileNode* getFrom, ProfileNode* addTo);
TreeProfile* m_treeProfile;
};
} // namespace KJS
#endif // HeavyProfile_h
......@@ -26,9 +26,6 @@
#include "config.h"
#include "Profile.h"
//#include "ExecState.h"
//#include "JSFunction.h"
//#include "JSGlobalObject.h"
#include "ProfileNode.h"
#include "TreeProfile.h"
#include <stdio.h>
......
......@@ -70,12 +70,13 @@ namespace KJS {
protected:
Profile(const UString& title);
RefPtr<ProfileNode> m_head;
private:
void removeProfileStart();
void removeProfileEnd();
UString m_title;
RefPtr<ProfileNode> m_head;
};
} // namespace KJS
......
......@@ -69,6 +69,21 @@ ProfileNode::ProfileNode(const CallIdentifier& callIdentifier, ProfileNode* head
startTimer();
}
ProfileNode::ProfileNode(ProfileNode* headNode, ProfileNode* nodeToCopy)
: m_callIdentifier(nodeToCopy->callIdentifier())
, m_head(headNode)
, m_parent(nodeToCopy->parent())
, m_nextSibling(0)
, m_startTime(0.0) // FIXME: For now we don't calculate time correctly for a HeavyProfile so just
, m_actualTotalTime(0.0) // zero it out. When we are ready to do this correctly, get the time from the
, m_visibleTotalTime(0.0) // nodeToCopy.
, m_actualSelfTime(0.0)
, m_visibleSelfTime(0.0)
, m_numberOfCalls(0)
, m_visible(nodeToCopy->visible())
{
}
ProfileNode* ProfileNode::willExecute(const CallIdentifier& callIdentifier)
{
for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) {
......@@ -102,6 +117,9 @@ void ProfileNode::addChild(PassRefPtr<ProfileNode> prpChild)
ProfileNode* ProfileNode::findChild(ProfileNode* node) const
{
if (!node)
return 0;
for (size_t i = 0; i < m_children.size(); ++i) {
if (*node == m_children[i].get())
return m_children[i].get();
......
......@@ -47,6 +47,10 @@ namespace KJS {
{
return adoptRef(new ProfileNode(callIdentifier, headNode, parentNode));
}
static PassRefPtr<ProfileNode> create(ProfileNode* headNode, ProfileNode* node)
{
return adoptRef(new ProfileNode(headNode, node));
}
bool operator==(ProfileNode* node) { return m_callIdentifier == node->callIdentifier(); }
......@@ -57,22 +61,28 @@ namespace KJS {
// CallIdentifier members
const CallIdentifier& callIdentifier() const { return m_callIdentifier; }
const UString& functionName() const { return m_callIdentifier.m_name; }
const UString& url() const { return m_callIdentifier.m_url; }
unsigned lineNumber() const { return m_callIdentifier.m_lineNumber; }
// Relationships
ProfileNode* head() const { return m_head; }
void setHead(ProfileNode* head) { m_head = head; }
ProfileNode* parent() const { return m_parent; }
void setParent(ProfileNode* parent) { m_parent = parent; }
ProfileNode* nextSibling() const { return m_nextSibling; }
void setNextSibling(ProfileNode* nextSibling) { m_nextSibling = nextSibling; }
UString functionName() const { return m_callIdentifier.m_name; }
UString url() const { return m_callIdentifier.m_url; }
unsigned lineNumber() const { return m_callIdentifier.m_lineNumber; }
// Time members
double startTime() const { return m_startTime; }
void setStartTime(double startTime) { m_startTime = startTime; }
double totalTime() const { return m_visibleTotalTime; }
double actualTotalTime() const { return m_actualTotalTime; }
void setTotalTime(double time) { m_actualTotalTime = time; m_visibleTotalTime = time; }
void setActualTotalTime(double time) { m_actualTotalTime = time; }
void setVisibleTotalTime(double time) { m_visibleTotalTime = time; }
double selfTime() const { return m_visibleSelfTime; }
double actualSelfTime() const { return m_actualSelfTime; }
void setSelfTime(double time) {m_actualSelfTime = time; m_visibleSelfTime = time; }
void setActualSelfTime(double time) { m_actualSelfTime = time; }
void setVisibleSelfTime(double time) { m_visibleSelfTime = time; }
......@@ -128,6 +138,7 @@ namespace KJS {
private:
ProfileNode(const CallIdentifier&, ProfileNode* headNode, ProfileNode* parentNode);
ProfileNode(ProfileNode* headNode, ProfileNode* nodeToCopy);
void startTimer();
void resetChildrensSiblings();
......
......@@ -26,6 +26,8 @@
#include "config.h"
#include "TreeProfile.h"
#include "HeavyProfile.h"
namespace KJS {
PassRefPtr<TreeProfile> TreeProfile::create(const UString& title)
......@@ -40,7 +42,10 @@ TreeProfile::TreeProfile(const UString& title)
Profile* TreeProfile::heavyProfile()
{
return this; // FIXME: When you've implemented heavy profiles replace this.
if (!m_heavyProfile)
m_heavyProfile = HeavyProfile::create(this);
return m_heavyProfile.get();
}
} // namespace KJS
......@@ -31,6 +31,7 @@
namespace KJS {
class ExecState;
class HeavyProfile;
class ProfilerClient;
class UString;
......@@ -43,6 +44,7 @@ namespace KJS {
private:
TreeProfile(const UString& title);
RefPtr<HeavyProfile> m_heavyProfile;
};
} // namespace KJS
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment