diff --git a/JavaScriptCore/AllInOneFile.cpp b/JavaScriptCore/AllInOneFile.cpp index 19b4bed..660652a 100644 --- a/JavaScriptCore/AllInOneFile.cpp +++ b/JavaScriptCore/AllInOneFile.cpp @@ -103,3 +103,4 @@ #include "wtf/TCSystemAlloc.cpp" #include "bytecompiler/CodeGenerator.cpp" #include "VM/RegisterFile.cpp" +#include "runtime/HeapGraph.cpp" diff --git a/JavaScriptCore/JavaScriptCore.exp b/JavaScriptCore/JavaScriptCore.exp index baa19a7..501104f 100644 --- a/JavaScriptCore/JavaScriptCore.exp +++ b/JavaScriptCore/JavaScriptCore.exp @@ -99,6 +99,7 @@ __ZN3JSC10JSFunction4infoE __ZN3JSC10throwErrorEPNS_9ExecStateENS_9ErrorTypeE __ZN3JSC10throwErrorEPNS_9ExecStateENS_9ErrorTypeEPKc __ZN3JSC10throwErrorEPNS_9ExecStateENS_9ErrorTypeERKNS_7UStringE +__ZN3JSC11addHeapEdgeEPNS_7JSValueES1_Ss __ZN3JSC11Interpreter11checkSyntaxEPNS_9ExecStateERKNS_10SourceCodeE __ZN3JSC11Interpreter8evaluateEPNS_9ExecStateERNS_10ScopeChainERKNS_10SourceCodeEPNS_7JSValueE __ZN3JSC11JSImmediate12nonInlineNaNEv @@ -135,6 +136,7 @@ __ZN3JSC12StringObject3putEPNS_9ExecStateERKNS_10IdentifierEPNS_7JSValueERNS_15P __ZN3JSC12StringObject4infoE __ZN3JSC12StringObjectC2EPNS_9ExecStateEN3WTF10PassRefPtrINS_11StructureIDEEERKNS_7UStringE __ZN3JSC12jsNumberCellEPNS_9ExecStateEd +__ZN3JSC13addHeapObjectEPNS_7JSValueERKSs __ZN3JSC13CodeGenerator21setDumpsGeneratedCodeEb __ZN3JSC13StatementNode6setLocEii __ZN3JSC13jsOwnedStringEPNS_12JSGlobalDataERKNS_7UStringE @@ -162,6 +164,7 @@ __ZN3JSC16JSVariableObject14deletePropertyEPNS_9ExecStateERKNS_10IdentifierE __ZN3JSC16JSVariableObject16getPropertyNamesEPNS_9ExecStateERNS_17PropertyNameArrayE __ZN3JSC16ParserRefCounted3refEv __ZN3JSC16ParserRefCounted5derefEv +__ZN3JSC16removeHeapObjectEPNS_7JSValueE __ZN3JSC17PropertyNameArray3addEPNS_7UString3RepE __ZN3JSC17PrototypeFunctionC1EPNS_9ExecStateEN3WTF10PassRefPtrINS_11StructureIDEEEiRKNS_10IdentifierEPFPNS_7JSValueES2_PNS_8JSObjectESB_RKNS_7ArgListEE __ZN3JSC17PrototypeFunctionC1EPNS_9ExecStateEiRKNS_10IdentifierEPFPNS_7JSValueES2_PNS_8JSObjectES7_RKNS_7ArgListEE @@ -169,8 +172,11 @@ __ZN3JSC17constructFunctionEPNS_9ExecStateERKNS_7ArgListERKNS_10IdentifierERKNS_ __ZN3JSC19constructEmptyArrayEPNS_9ExecStateE __ZN3JSC19initializeThreadingEv __ZN3JSC20constructEmptyObjectEPNS_9ExecStateE +__ZN3JSC21addHeapVariableObjectEPNS_16JSVariableObjectE __ZN3JSC23objectProtoFuncToStringEPNS_9ExecStateEPNS_8JSObjectEPNS_7JSValueERKNS_7ArgListE __ZN3JSC23setUpStaticFunctionSlotEPNS_9ExecStateEPKNS_9HashEntryEPNS_8JSObjectERKNS_10IdentifierERNS_12PropertySlotE +__ZN3JSC24removeHeapVariableObjectEPNS_16JSVariableObjectE +__ZN3JSC29retraceSecurityContextThroughEPNS_7JSValueES1_ __ZN3JSC4Heap14allocateNumberEm __ZN3JSC4Heap14primaryHeapEndEv __ZN3JSC4Heap15recordExtraCostEm @@ -362,3 +368,4 @@ __ZTVN3JSC8JSObjectE __ZTVN3JSC8JSStringE _jscore_fastmalloc_introspection _kJSClassDefinitionEmpty +__ZN3JSC8dumpHeapEPNS_9ExecStateEPKc diff --git a/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj index 8924db2..82699cf 100644 --- a/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj +++ b/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj @@ -123,6 +123,7 @@ 95FDFA140E22998F0006FB00 /* HeavyProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95FDFA130E22998F0006FB00 /* HeavyProfile.cpp */; }; 95FDFA160E2299980006FB00 /* HeavyProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 95FDFA150E2299980006FB00 /* HeavyProfile.h */; }; 960097A60EBABB58007A7297 /* LabelScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 960097A50EBABB58007A7297 /* LabelScope.h */; }; + 978F23460EEF0A6C00E2E4B9 /* HeapGraph.h in Headers */ = {isa = PBXBuildFile; fileRef = 934C1BD10ECB5D5400F98EDD /* HeapGraph.h */; settings = {ATTRIBUTES = (Private, ); }; }; 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 */; }; @@ -554,6 +555,8 @@ 933A3499038AE7C6008635CE /* Grammar.y */ = {isa = PBXFileReference; explicitFileType = sourcecode.yacc; fileEncoding = 4; indentWidth = 4; path = Grammar.y; sourceTree = ""; tabWidth = 8; }; 933A349A038AE7C6008635CE /* Identifier.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = Identifier.h; sourceTree = ""; tabWidth = 8; }; 933A349D038AE80F008635CE /* Identifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Identifier.cpp; sourceTree = ""; tabWidth = 8; }; + 934C1BD10ECB5D5400F98EDD /* HeapGraph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapGraph.h; sourceTree = ""; }; + 934C1BD20ECB5D5400F98EDD /* HeapGraph.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapGraph.cpp; sourceTree = ""; }; 935AF46909E9D9DB00ACD1D8 /* Forward.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Forward.h; sourceTree = ""; }; 935AF46B09E9D9DB00ACD1D8 /* UnusedParam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnusedParam.h; sourceTree = ""; }; 937013470CA97E0E00FA14D3 /* pcre_ucp_searchfuncs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pcre_ucp_searchfuncs.cpp; sourceTree = ""; }; @@ -1280,6 +1283,8 @@ 6507D2970E871E4A00D7D896 /* TypeInfo.h */, F692A8850255597D01FF60F7 /* UString.cpp */, F692A8860255597D01FF60F7 /* UString.h */, + 934C1BD10ECB5D5400F98EDD /* HeapGraph.h */, + 934C1BD20ECB5D5400F98EDD /* HeapGraph.cpp */, ); path = runtime; sourceTree = ""; @@ -1378,6 +1383,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 978F23460EEF0A6C00E2E4B9 /* HeapGraph.h in Headers */, BC18C3E40E16F5CD00B34460 /* AlwaysInline.h in Headers */, BC18C3E50E16F5CD00B34460 /* APICast.h in Headers */, BC18C3E60E16F5CD00B34460 /* ArrayConstructor.h in Headers */, @@ -2068,6 +2074,7 @@ buildSettings = { DEAD_CODE_STRIPPING = "$(DEAD_CODE_STRIPPING_debug)"; DEBUG_DEFINES = "$(DEBUG_DEFINES_debug)"; + GCC_ENABLE_CPP_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = "$(GCC_OPTIMIZATION_LEVEL_debug)"; STRIP_INSTALLED_PRODUCT = "$(STRIP_INSTALLED_PRODUCT_debug)"; }; diff --git a/JavaScriptCore/runtime/HeapGraph.cpp b/JavaScriptCore/runtime/HeapGraph.cpp new file mode 100644 index 0000000..7dc6e64 --- /dev/null +++ b/JavaScriptCore/runtime/HeapGraph.cpp @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2009 Joel Weinberger (jww@cs.berkeley.edu) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "HeapGraph.h" + +#include +#include + +#include +#include +#include +#include + +#include "JSValue.h" +#include "JSVariableObject.h" +#include "SymbolTable.h" +#include "ExecState.h" +#include "JSGlobalObject.h" + +using namespace std; + +namespace JSC { + +struct secctx_struct { + void *sec_origin; + JSGlobalObject *global; +}; + +static const char *__heapgraphfilename = "/Users/jww/heapgraph.dot"; +static set *__roots = NULL; +static set *__windowshells = NULL; +static map > *__edges = NULL; +/* + * There can be multiple edges from different objects with the same edge name, + * so we actually store the edge names and parent node in the reverse order. + * Think about it this way: __redges is a map of which objects point to a given + * object. There's no reason multiple objects cannot have properties with the + * same name pointing to the different objects. + */ +static map > *__redges = NULL; +static map *__labels = NULL; +static set *__varobjs = NULL; +static map *__secctxs = NULL; +static map *__objsecctxs = NULL; +//This is a hack for storing objects that we "know" aren't malicious. +//This is mainly used for running layout tests +static set *__ignoreobjs = NULL; + +static const char* __ignoreedges[] = { + "GCController", + "layoutTestController", + "notifyDone", + NULL +}; + +static void heapinit() +{ + if (__roots == NULL) { + __roots = new set; + __windowshells = new set; + __edges = new map >; + __redges = new map >; + __labels = new map; + __varobjs = new set; + __secctxs = new map; + __objsecctxs = new map; + __ignoreobjs = new set; + } +} + +/* + * Return the security context of a given node or NULL if it is untracable + */ +static JSValue * traceSecCtx(JSValue *val) +{ + if (0) + return NULL; + + if (!val->isObject()) + return NULL; + + JSObject *obj = val->getObject(); + + if (obj == NULL) + return NULL; + + JSValue *proto; + + while (((proto = obj->prototype()) != NULL) && !proto->isNull()) { + obj = proto->getObject(); + } + + if ((*__objsecctxs).count(obj) > 0) { + return (*__objsecctxs)[obj]; + } else if ((*__secctxs).count(obj) > 0) { + return obj; + } + + return NULL; +} + +void retraceSecurityContext(JSValue *val) +{ + retraceSecurityContextThrough(val, val); +} + +void retraceSecurityContextThrough(JSValue *val, JSValue *through) +{ + JSValue *ctx = traceSecCtx(through); + if (ctx != NULL) { + (*__objsecctxs)[val] = ctx; + } +} + +void copySecurityContext(JSValue *val, JSValue *src) +{ + (*__objsecctxs)[val] = (*__objsecctxs)[src]; +} + +void addHeapGlobalObject(JSValue *obj, const string& label) +{ + addHeapObject(obj, label); + + (*__roots).insert(obj); +} + +void addHeapObject(JSValue *obj, const string& label) +{ + heapinit(); + + if (obj->isObject() == false) + return; + + (*__edges)[obj]; + + (*__labels)[obj] = label; + + retraceSecurityContext(obj); +} + +void addHeapVariableObject(JSVariableObject *obj) +{ + heapinit(); + + (*__varobjs).insert(obj); + retraceSecurityContext((JSValue *) obj); +} + +/* + * Adds an edge to the graph, from the parent to the child labeled with the + * given name. Will remove any edges from that parent labeled with the given + * name first. + */ +void addHeapEdge(JSValue *parent, JSValue *child, string name) +{ + removeHeapEdge(parent, name); + + /* + * If it's not a String or an Object, no need to track it. There is no need + * to check the parent because it can only be called from the context of an + * Object or String. + */ + if (child->isGetterSetter() == true || + child->isObject() == false) + return; + + (*__edges)[parent][name] = child; + (*__redges)[child][parent] = name; + + /* + * After intersting the edge and reverse edge, make sure the child exists as + * a node and the parent exists as a 'reverse-edge' node. + */ + (*__edges)[child]; + (*__redges)[parent]; + + /* + * Any edges on the ignore list imply that the child object should be + * ignored. + */ + int i; + for (i = 0; __ignoreedges[i] != NULL; i++) { + if (name.compare(__ignoreedges[i]) == 0) + (*__ignoreobjs).insert(child); + } +} + +void removeHeapGlobalObject(JSValue *obj) +{ + (*__roots).erase(obj); + + removeHeapObject(obj); +} + +/* + * Removes the given heap object from the heap graph. If the object is no longer + * in the heap, does not remove it. + */ +void removeHeapObject(JSValue *obj) +{ + map::iterator it; + map::iterator it2; + + /* + * The object doesn't exist, so do nothing. + */ + if ((*__edges).count(obj) == 0) + return; + + for (it = (*__edges)[obj].begin(); it != (*__edges)[obj].end(); it++) + (*__redges)[(*it).second].erase(obj); + + for (it2 = (*__redges)[obj].begin(); it2 != (*__redges)[obj].end(); it2++) + (*__edges)[(*it2).first].erase((*it2).second); + + (*__labels).erase(obj); + (*__edges).erase(obj); + (*__redges).erase(obj); + (*__secctxs).erase(obj); + (*__objsecctxs).erase(obj); +} + +void removeHeapVariableObject(JSVariableObject *obj) +{ + (*__varobjs).erase(obj); + removeHeapObject(obj); +} + +void removeHeapEdge(JSValue *parent, string name) +{ + if ((*__edges).count(parent) == 0 || (*__edges)[parent].count(name) == 0) + return; + + JSValue *child = (*__edges)[parent][name]; + + (*__edges)[parent].erase(name); + (*__redges)[child].erase(parent); +} + +void setHeapLabel(JSValue *obj, string label) +{ + (*__labels)[obj] = label; +} + +void setHeapSecurityContext(JSValue *obj, ExecState *exec) +{ + JSGlobalObject *global = exec->lexicalGlobalObject(); + secctx_struct secctx = { + NULL, + global + }; + (*__secctxs)[obj] = secctx; +} + +void setHeapWindowShell(JSValue *obj) +{ + (*__windowshells).insert(obj); +} + +// first ctx is src, second is dst +static int chooseEdgeColor(JSValue *src, JSValue *dst) +{ + int color = 0; + JSValue *secctx1 = (*__objsecctxs)[src]; + JSValue *secctx2 = (*__objsecctxs)[dst]; + + // If dst is the windows shell, ignore the edge + if ((*__windowshells).count(dst) > 0 || + (*__ignoreobjs).count(src) > 0) + goto done; + + if (secctx1 != NULL && secctx2 != NULL && secctx1 != secctx2) { + secctx_struct active = (*__secctxs)[secctx1]; + secctx_struct target = (*__secctxs)[secctx2]; + + //TODO THINK ABOUT THIS. We probably should be getting the security + //token earlier. + active.sec_origin = active.global->getSecurityToken(); + target.sec_origin = target.global->getSecurityToken(); + + if (!active.global->canAccessOrigin(active.sec_origin, + target.sec_origin)) + color = 0x00ff0000; + } + +done: + return color; +} + +void dumpHeap(ExecState *exec, const char *path) +{ + FILE *file; + bool onlyviolations = false; + + if (path == NULL) + path = __heapgraphfilename; + + if ((file = fopen(path, "w+")) == NULL) + return; + + map >::iterator mit; + + int num_edges = 0; + printf("nodes - %d\n", (int) __edges->size()); + for (mit = (*__edges).begin(); mit != (*__edges).end(); mit++) { + num_edges += mit->second.size(); + } + printf("edges - %d\n", (int) num_edges); + + /* + * GC whatever we can just to make it as up to date as possible + */ + if (exec != NULL) + exec->heap()->collect(); + + /* + * The number of global objects is the number of security contexts + */ + JSValue *sec_ctx = NULL; + + int num_sec_ctx = 0; + map sec_ctx_to_index; + map::iterator scit; + for (scit = (*__secctxs).begin(); scit != (*__secctxs).end(); scit++) { + sec_ctx_to_index[scit->first] = num_sec_ctx; + ++num_sec_ctx; + } + int base_color = 0x00ffff00; + + fprintf(file, "digraph NFA {\n"); + + /* + * Go through heap objects + */ + char const *attrsformat = "%sstyle = \"filled\", fillcolor = \"#%x\", " + "fontcolor = \"#%x\""; + /* + * We need to add extra characters to account for the extra length of the + * numbers being added in later (a maximum of 6 characters each). For + * attrs2, we also need to add 2 characters for the ", " that it gets at the + * beginning. + */ + char attrs1[7 + strlen(attrsformat)]; + char attrs2[9 + strlen(attrsformat)]; + + for (mit = (*__edges).begin(); mit != (*__edges).end(); mit++) { + map::iterator mit2; + + sec_ctx = (*__objsecctxs)[mit->first]; + + if (sec_ctx != NULL) { + int color = (base_color * sec_ctx_to_index[sec_ctx]) / num_sec_ctx; + int fontcolor = ((~color) << 8) >> 8; + sprintf(attrs1, attrsformat, "", color, fontcolor); + sprintf(attrs2, attrsformat, ", ", color, fontcolor); + } else { + sprintf(attrs1, attrsformat, "", 0x00ffffff, 0x00000000); + sprintf(attrs2, attrsformat, ", ", 0x00ffffff, 0x00000000); + } + + if ((*__labels).count(mit->first) == 0) { + fprintf(file, "\"%x\" [ %s ];\n", (unsigned int) (mit->first), + attrs1); + } else { + fprintf(file, "\"%x\" [ label = \"%s - %x\"%s ];\n", (unsigned int) + mit->first, (*__labels)[mit->first].c_str(), (unsigned int) + mit->first, attrs2); + } + + for (mit2 = mit->second.begin(); mit2 != mit->second.end(); mit2++) { + int color = chooseEdgeColor(mit->first, mit2->second); + + if (!onlyviolations || color != 0) { + fprintf(file, "\"%x\" -> \"%x\" [ color = \"#%x\", label = " + "\"%s\", labelfontcolor = \"#%x\" ];\n", + (unsigned int) (mit->first), (unsigned int) mit2->second, color, + mit2->first.c_str(), color); + } + } + } + + /* + * Go through the variable objects, getting the local variables in each one + */ + set::iterator sit; + for (sit = (*__varobjs).begin(); sit != (*__varobjs).end(); sit++) { + sec_ctx = (*__objsecctxs)[*sit]; + + if (sec_ctx != NULL) { + int color = (base_color * sec_ctx_to_index[sec_ctx]) / num_sec_ctx; + int fontcolor = ((~color) << 8) >> 8; + sprintf(attrs2, attrsformat, ", ", color, fontcolor); + } else { + sprintf(attrs2, attrsformat, ", ", 0x00ffffff, 0x00000000); + } + + fprintf(file, "\"%x\" [ label = \"%s - %x\" %s];\n", + (unsigned int) (*sit), "JSVariableObject", (unsigned int) (*sit), + attrs2); + + SymbolTable& st = (*sit)->symbolTable(); + + for (SymbolTable::iterator stit = st.begin(); stit != st.end(); + ++stit) { + UString us(stit->first); + SymbolTableEntry& entry = stit->second; + int index = entry.getIndex(); + Register& reg = (*sit)->registerAt(index); + JSValue *val = reg.getJSValue(); + + int color = chooseEdgeColor(*sit, val); + if (!onlyviolations || color != 0) { + fprintf(file, "\"%x\" -> \"%x\" [ color = \"#%x\", " + "label = \"%s\" ];\n", + (unsigned int) (*sit), (unsigned int) val, color, + us.ascii()); + } + } + } + + /* + * Do the same as above, but also to the global objects + */ + set::iterator sit2; + for (sit2 = (*__roots).begin(); sit2 != (*__roots).end(); sit2++) { + JSVariableObject *vo = (JSVariableObject *) (*sit2); + fprintf(file, "\"%x\" [ label = \"%s - %x\" ];\n", + (unsigned int) (*sit2), "Global Object", (unsigned int) vo); + + SymbolTable& st = vo->symbolTable(); + + sec_ctx = (*__objsecctxs)[vo]; + for (SymbolTable::iterator stit = st.begin(); stit != st.end(); + ++stit) { + UString us(stit->first); + SymbolTableEntry& entry = stit->second; + int index = entry.getIndex(); + Register& reg = vo->registerAt(index); + JSValue *val = reg.getJSValue(); + + int color = chooseEdgeColor(vo, val); + if (!onlyviolations || color != 0) { + fprintf(file, "\"%x\" -> \"%x\" [ color = \"#%x\", " + "label = \"%s\" ];\n", (unsigned int) vo, + (unsigned int) val, color, us.ascii()); + } + } + + } + + fprintf(file, "}\n"); + + fflush(file); + + fclose(file); +} + +} //namespace JSC diff --git a/JavaScriptCore/runtime/HeapGraph.h b/JavaScriptCore/runtime/HeapGraph.h new file mode 100644 index 0000000..c36c743 --- /dev/null +++ b/JavaScriptCore/runtime/HeapGraph.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 Joel Weinberger (jww@cs.berkeley.edu) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef HeapGraph_h +#define HeapGraph_h + +#include "ExecState.h" +#include + +namespace JSC { + class JSValue; + class JSVariableObject; + + void addHeapGlobalObject(JSValue *, const std::string& ); + void addHeapObject(JSValue *, const std::string& ); + void addHeapVariableObject(JSVariableObject *); + + void removeHeapGlobalObject(JSValue *); + void removeHeapObject(JSValue *); + void removeHeapVariableObject(JSVariableObject *); + + void addHeapEdge(JSValue *, JSValue *, std::string); + + void removeHeapEdge(JSValue *, std::string); + + void setHeapLabel(JSValue *, std::string); + void setHeapSecurityContext(JSValue *, ExecState *); + void setHeapWindowShell(JSValue *); + + void retraceSecurityContext(JSValue *); + void retraceSecurityContextThrough(JSValue *, JSValue *); + void copySecurityContext(JSValue *, JSValue *); + + void dumpHeap(ExecState *, const char *); +} //namespace JSC + +#endif //HeapGraph_h diff --git a/JavaScriptCore/runtime/JSArray.cpp b/JavaScriptCore/runtime/JSArray.cpp index 8d526ea..ef0bb05 100644 --- a/JavaScriptCore/runtime/JSArray.cpp +++ b/JavaScriptCore/runtime/JSArray.cpp @@ -29,6 +29,8 @@ #include #include +#include "HeapGraph.h" + #define CHECK_ARRAY_CONSISTENCY 0 using namespace std; @@ -137,6 +139,10 @@ JSArray::JSArray(PassRefPtr structureID) m_storage->m_length = 0; checkConsistency(); + + //HeapGraph + std::string str("array"); + addHeapObject(this, str); } JSArray::JSArray(PassRefPtr structure, unsigned initialLength) @@ -152,6 +158,10 @@ JSArray::JSArray(PassRefPtr structure, unsigned initialLength) Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue*)); checkConsistency(); + + //HeapGraph + std::string str("array"); + addHeapObject(this, str); } JSArray::JSArray(ExecState* exec, PassRefPtr structure, const ArgList& list) @@ -179,12 +189,19 @@ JSArray::JSArray(ExecState* exec, PassRefPtr structure, const ArgLi // a property map. Therefore don't report extra memory cost. checkConsistency(); + + //HeapGraph + std::string str("array"); + addHeapObject(this, str); } JSArray::~JSArray() { checkConsistency(DestructorConsistencyCheck); + //HeapGraph + removeHeapObject(this); + delete m_storage->m_sparseValueMap; fastFree(m_storage); } @@ -268,6 +285,14 @@ void JSArray::put(ExecState* exec, unsigned i, JSValue* value) if (i < m_storage->m_vectorLength) { JSValue*& valueSlot = m_storage->m_vector[i]; + //HeapGraph + /* + * The following two cases can only succeed, so add heap reference here + */ + char iStr[100]; + sprintf(iStr, "%d", i); + addHeapEdge(this, value, std::string(iStr)); + if (valueSlot) { valueSlot = value; checkConsistency(); @@ -281,6 +306,14 @@ void JSArray::put(ExecState* exec, unsigned i, JSValue* value) } putSlowCase(exec, i, value); + //HeapGraph + /* + * In all the putSlowCases results, if it returns, it has been successfully + * added. + */ + char iStr[100]; + sprintf(iStr, "%d", i); + addHeapEdge(this, value, std::string(iStr)); } NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue* value) diff --git a/JavaScriptCore/runtime/JSArray.h b/JavaScriptCore/runtime/JSArray.h index 165fafd..e41b738 100644 --- a/JavaScriptCore/runtime/JSArray.h +++ b/JavaScriptCore/runtime/JSArray.h @@ -23,6 +23,9 @@ #include "JSObject.h" +#include "HeapGraph.h" +#include + namespace JSC { typedef HashMap SparseArrayValueMap; @@ -71,6 +74,10 @@ namespace JSC { JSValue* setIndex(unsigned i, JSValue* v) { ASSERT(canSetIndex(i)); + //HeapGraph + char iStr[100]; + sprintf(iStr, "%d", i); + addHeapEdge(this, v, std::string(iStr)); return m_storage->m_vector[i] = v; } diff --git a/JavaScriptCore/runtime/JSFunction.cpp b/JavaScriptCore/runtime/JSFunction.cpp index 2973d2d..a57aa77 100644 --- a/JavaScriptCore/runtime/JSFunction.cpp +++ b/JavaScriptCore/runtime/JSFunction.cpp @@ -50,6 +50,8 @@ JSFunction::JSFunction(ExecState* exec, const Identifier& name, FunctionBodyNode , m_body(body) , m_scopeChain(scopeChainNode) { + addHeapEdge((JSValue *) this, (JSValue *) scopeChainNode->object, + std::string("@SCOPECHAIN FUNC")); } JSFunction::~JSFunction() diff --git a/JavaScriptCore/runtime/JSGlobalObject.cpp b/JavaScriptCore/runtime/JSGlobalObject.cpp index 89c32fd..d2dbad9 100644 --- a/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -68,6 +68,9 @@ #include "StringPrototype.h" #include "Debugger.h" +#include "HeapGraph.h" +#include + namespace JSC { ASSERT_CLASS_FITS_IN_CELL(JSGlobalObject); @@ -94,6 +97,9 @@ JSGlobalObject::~JSGlobalObject() { ASSERT(JSLock::currentThreadIsHoldingLock()); + //HeapGraph + removeHeapGlobalObject(this); + if (d()->debugger) d()->debugger->detach(this); @@ -126,6 +132,13 @@ void JSGlobalObject::init(JSObject* thisValue) { ASSERT(JSLock::currentThreadIsHoldingLock()); + //HeapGraph + std::string str("global"); + addHeapGlobalObject(this, str); + std::string str2("Window Shell"); + addHeapObject(thisValue, str2); + setHeapWindowShell(thisValue); + d()->globalData = Heap::heap(this)->globalData(); d()->globalScopeChain = ScopeChain(this, d()->globalData.get(), thisValue); @@ -145,6 +158,8 @@ void JSGlobalObject::init(JSObject* thisValue) d()->profileGroup = 0; reset(prototype()); + //HeapGraph + retraceSecurityContextThrough(thisValue, this); } void JSGlobalObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot) @@ -199,12 +214,26 @@ void JSGlobalObject::reset(JSValue* prototype) ExecState* exec = JSGlobalObject::globalExec(); // Prototypes + // HeapGraph + // Every prototype creates a new JS Object that gets added to the heap, but + // we want a more specific HeapGraph label d()->functionPrototype = new (exec) FunctionPrototype(exec, FunctionPrototype::createStructureID(jsNull())); // The real prototype will be set once ObjectPrototype is created. + setHeapLabel(d()->functionPrototype, "function prototype"); d()->prototypeFunctionStructure = PrototypeFunction::createStructureID(d()->functionPrototype); - d()->functionPrototype->addFunctionProperties(exec, d()->prototypeFunctionStructure.get()); + d()->objectPrototype = new (exec) ObjectPrototype(exec, ObjectPrototype::createStructureID(jsNull()), d()->prototypeFunctionStructure.get()); + setHeapLabel(d()->objectPrototype, "object prototype"); d()->functionPrototype->structureID()->setPrototypeWithoutTransition(d()->objectPrototype); + // Because the function prototype is constructed before the object + // prototype, we need to retrace its security context here. We also move the + // addFunctionProperties call here so that they can trace back to the object + // prototype as well. + retraceSecurityContext(d()->functionPrototype); + retraceSecurityContext(d()->objectPrototype); + //copySecurityContext((JSValue *) d()->globalData.get(), (JSValue *) d()->objectPrototype); + d()->objectPrototype->addObjectProperties(exec, d()->prototypeFunctionStructure.get()); + d()->functionPrototype->addFunctionProperties(exec, d()->prototypeFunctionStructure.get()); d()->emptyObjectStructure = d()->objectPrototype->inheritorID(); @@ -215,26 +244,33 @@ void JSGlobalObject::reset(JSValue* prototype) d()->callbackObjectStructure = JSCallbackObject::createStructureID(d()->objectPrototype); d()->arrayPrototype = new (exec) ArrayPrototype(ArrayPrototype::createStructureID(d()->objectPrototype)); + setHeapLabel(d()->arrayPrototype, "array prototype"); d()->arrayStructure = JSArray::createStructureID(d()->arrayPrototype); d()->regExpMatchesArrayStructure = RegExpMatchesArray::createStructureID(d()->arrayPrototype); d()->stringPrototype = new (exec) StringPrototype(exec, StringPrototype::createStructureID(d()->objectPrototype)); + setHeapLabel(d()->stringPrototype, "string prototype"); d()->stringObjectStructure = StringObject::createStructureID(d()->stringPrototype); d()->booleanPrototype = new (exec) BooleanPrototype(exec, BooleanPrototype::createStructureID(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + setHeapLabel(d()->booleanPrototype, "boolean prototype"); d()->booleanObjectStructure = BooleanObject::createStructureID(d()->booleanPrototype); d()->numberPrototype = new (exec) NumberPrototype(exec, NumberPrototype::createStructureID(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + setHeapLabel(d()->numberPrototype, "number prototype"); d()->numberObjectStructure = NumberObject::createStructureID(d()->numberPrototype); d()->datePrototype = new (exec) DatePrototype(exec, DatePrototype::createStructureID(d()->objectPrototype)); + setHeapLabel(d()->datePrototype, "date prototype"); d()->dateStructure = DateInstance::createStructureID(d()->datePrototype); d()->regExpPrototype = new (exec) RegExpPrototype(exec, RegExpPrototype::createStructureID(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + setHeapLabel(d()->regExpPrototype, "regExp prototype"); d()->regExpStructure = RegExpObject::createStructureID(d()->regExpPrototype); ErrorPrototype* errorPrototype = new (exec) ErrorPrototype(exec, ErrorPrototype::createStructureID(d()->objectPrototype), d()->prototypeFunctionStructure.get()); d()->errorStructure = ErrorInstance::createStructureID(errorPrototype); + setHeapLabel(errorPrototype, "error prototype"); RefPtr nativeErrorPrototypeStructure = NativeErrorPrototype::createStructureID(errorPrototype); @@ -329,11 +365,14 @@ void JSGlobalObject::reset(JSValue* prototype) putDirectFunctionWithoutTransition(exec, new (exec) PrototypeFunction(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "decodeURIComponent"), globalFuncDecodeURIComponent), DontEnum); putDirectFunctionWithoutTransition(exec, new (exec) PrototypeFunction(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "encodeURI"), globalFuncEncodeURI), DontEnum); putDirectFunctionWithoutTransition(exec, new (exec) PrototypeFunction(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "encodeURIComponent"), globalFuncEncodeURIComponent), DontEnum); + putDirectFunction(exec, new (exec) PrototypeFunction(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "dumpHeap"), globalFuncDumpHeap), DontEnum); #ifndef NDEBUG putDirectFunctionWithoutTransition(exec, new (exec) PrototypeFunction(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "kjsprint"), globalFuncKJSPrint), DontEnum); #endif resetPrototype(prototype); + // HeapGraph + retraceSecurityContext(this); } // Set prototype, and also insert the object prototype at the end of the chain. @@ -440,6 +479,11 @@ void JSGlobalObject::copyGlobalsFrom(RegisterFile& registerFile) Register* registerArray = copyRegisterArray(registerFile.lastGlobal(), numGlobals); setRegisters(registerArray + numGlobals, registerArray, numGlobals); + + //HeapGraph + for (int i = 0; i < numGlobals; i++) { + addHeapEdge(this, registerArray[i].getJSValue(), "GLOBAL LOCAL"); + } } void JSGlobalObject::copyGlobalsTo(RegisterFile& registerFile) diff --git a/JavaScriptCore/runtime/JSGlobalObject.h b/JavaScriptCore/runtime/JSGlobalObject.h index d8a072a..f03e95e 100644 --- a/JavaScriptCore/runtime/JSGlobalObject.h +++ b/JavaScriptCore/runtime/JSGlobalObject.h @@ -160,6 +160,10 @@ namespace JSC { virtual void mark(); virtual void markCrossHeapDependentObjects(); + //HeapGraph + virtual void *getSecurityToken() { return NULL; } + virtual bool canAccessOrigin(void *, void *) { return false; } + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&, bool& slotIsWriteable); virtual void put(ExecState*, const Identifier&, JSValue*, PutPropertySlot&); diff --git a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index 0bc8f28..a3fd1d5 100644 --- a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -362,6 +362,28 @@ JSValue* globalFuncEncodeURIComponent(ExecState* exec, JSObject*, JSValue*, cons return encode(exec, args, do_not_escape_when_encoding_URI_component); } +/* + * The first argument can either be undefined or a path to a file + */ +JSValue* globalFuncDumpHeap(ExecState* exec, JSObject*, JSValue*, const ArgList& args) +{ + JSValue* value = args.at(exec, 0); + + if (!JSImmediate::isUndefinedOrNull(value)) { + UString str = value->toString(exec); + CString cstr = str.UTF8String(true); + if (!cstr.c_str()) + return throwError(exec, URIError, "String contained an illegal " + "UTF-16 sequence."); + + dumpHeap(exec, cstr.c_str()); + } else { + dumpHeap(exec, NULL); + } + + return JSImmediate::undefinedImmediate(); +} + JSValue* globalFuncEscape(ExecState* exec, JSObject*, JSValue*, const ArgList& args) { static const char do_not_escape[] = diff --git a/JavaScriptCore/runtime/JSObject.h b/JavaScriptCore/runtime/JSObject.h index d280b64..2510906 100644 --- a/JavaScriptCore/runtime/JSObject.h +++ b/JavaScriptCore/runtime/JSObject.h @@ -23,6 +23,7 @@ #ifndef JSObject_h #define JSObject_h + #include "ArgList.h" #include "ClassInfo.h" #include "CommonIdentifiers.h" @@ -33,6 +34,9 @@ #include "ScopeChain.h" #include "StructureID.h" +#include "HeapGraph.h" +#include + namespace JSC { class InternalFunction; @@ -223,11 +227,20 @@ inline JSObject::JSObject(PassRefPtr structureID) ASSERT(m_structureID->propertyStorageCapacity() == inlineStorageCapacity); ASSERT(m_structureID->isEmpty()); ASSERT(prototype()->isNull() || Heap::heap(this) == Heap::heap(prototype())); + + //HeapGraph + std::string str("object"); + addHeapObject(this, str); + addHeapEdge(this, prototype(), std::string("__proto__")); } inline JSObject::~JSObject() { ASSERT(m_structureID); + + //HeapGraph + removeHeapObject(this); + if (m_propertyStorage != m_inlineStorage) delete [] m_propertyStorage; m_structureID->deref(); @@ -243,6 +256,9 @@ inline void JSObject::setPrototype(JSValue* prototype) ASSERT(prototype); RefPtr newStructureID = StructureID::changePrototypeTransition(m_structureID, prototype); setStructureID(newStructureID.release()); + + //HeapGraph + addHeapEdge(this, prototype, std::string("__proto__")); } inline void JSObject::setStructureID(PassRefPtr structureID) @@ -387,6 +403,9 @@ inline void JSObject::putDirect(const Identifier& propertyName, JSValue* value, { ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + //HeapGraph + addHeapEdge(this, value, std::string(propertyName.ascii())); + if (m_structureID->isDictionary()) { unsigned currentAttributes; size_t offset = m_structureID->get(propertyName, currentAttributes); @@ -435,6 +454,10 @@ inline void JSObject::putDirectWithoutTransition(const Identifier& propertyName, { size_t currentCapacity = m_structureID->propertyStorageCapacity(); size_t offset = m_structureID->addPropertyWithoutTransition(propertyName, attributes); + + //HeapGraph + addHeapEdge(this, value, std::string(propertyName.ascii())); + if (currentCapacity != m_structureID->propertyStorageCapacity()) allocatePropertyStorage(currentCapacity, m_structureID->propertyStorageCapacity()); m_propertyStorage[offset] = value; diff --git a/JavaScriptCore/runtime/JSStaticScopeObject.h b/JavaScriptCore/runtime/JSStaticScopeObject.h index b152862..01fa235 100644 --- a/JavaScriptCore/runtime/JSStaticScopeObject.h +++ b/JavaScriptCore/runtime/JSStaticScopeObject.h @@ -28,6 +28,9 @@ #include "JSVariableObject.h" +#include "HeapGraph.h" +#include + namespace JSC{ class JSStaticScopeObject : public JSVariableObject { @@ -47,6 +50,10 @@ namespace JSC{ : JSVariableObject(exec->globalData().staticScopeStructureID, new JSStaticScopeObjectData()) { d()->registerStore = value; + //HeapGraph + std::string str("local - "); + str.append(ident.ascii()); + addHeapEdge((JSValue *) this, value, str); symbolTable().add(ident.ustring().rep(), SymbolTableEntry(-1, attributes)); } virtual ~JSStaticScopeObject(); diff --git a/JavaScriptCore/runtime/JSString.h b/JavaScriptCore/runtime/JSString.h index b053258..7828fa4 100644 --- a/JavaScriptCore/runtime/JSString.h +++ b/JavaScriptCore/runtime/JSString.h @@ -29,6 +29,8 @@ #include "JSNumberCell.h" #include "PropertySlot.h" +#include "HeapGraph.h" + namespace JSC { class JSString; @@ -68,6 +70,8 @@ namespace JSC { , m_value(value) { Heap::heap(this)->reportExtraMemoryCost(value.cost()); + //HeapGraph + addHeapObject(this, std::string("string")); } enum HasOtherOwnerType { HasOtherOwner }; @@ -75,12 +79,22 @@ namespace JSC { : JSCell(globalData->stringStructureID.get()) , m_value(value) { + //HeapGraph + addHeapObject(this, std::string("string")); } JSString(JSGlobalData* globalData, PassRefPtr value, HasOtherOwnerType) : JSCell(globalData->stringStructureID.get()) , m_value(value) { + //HeapGraph + addHeapObject(this, std::string("string")); } + + //HeapGraph + ~JSString() + { + removeHeapObject(this); + } const UString& value() const { return m_value; } @@ -97,6 +111,13 @@ namespace JSC { JSString(VPtrStealingHackType) : JSCell(0) { + //HeapGraph + /* + * Note that we do not instrument here because this JSString is not + * a real object. It is used in some inscrutable way by the VM for + * something insane. Instrumenting it just leads to pain and + * suffering. + */ } virtual JSValue* toPrimitive(ExecState*, PreferredPrimitiveType) const; diff --git a/JavaScriptCore/runtime/JSVariableObject.h b/JavaScriptCore/runtime/JSVariableObject.h index 1194517..b3a2a1f 100644 --- a/JavaScriptCore/runtime/JSVariableObject.h +++ b/JavaScriptCore/runtime/JSVariableObject.h @@ -36,6 +36,9 @@ #include #include +#include "HeapGraph.h" +#include + namespace JSC { class Register; @@ -86,8 +89,16 @@ namespace JSC { : JSObject(structureID) , d(data) // Subclass owns this pointer. { + //HeapGraph + addHeapVariableObject(this); } + ~JSVariableObject() + { + //HeapGraph + removeHeapVariableObject(this); + } + Register* copyRegisterArray(Register* src, size_t count); void setRegisters(Register* r, Register* registerArray); @@ -131,10 +142,20 @@ namespace JSC { return false; } + static std::string makeLabel(const Identifier &pn) + { + std::string str("local - "); + str.append(pn.ascii()); + str.substr(0, 30); + return str; + } + inline bool JSVariableObject::symbolTablePut(const Identifier& propertyName, JSValue* value) { ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + addHeapEdge((JSValue *) this, value, makeLabel(propertyName)); + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.ustring().rep()); if (entry.isNull()) return false; @@ -148,6 +169,8 @@ namespace JSC { { ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + addHeapEdge((JSValue *) this, value, makeLabel(propertyName)); + SymbolTable::iterator iter = symbolTable().find(propertyName.ustring().rep()); if (iter == symbolTable().end()) return false; diff --git a/JavaScriptCore/runtime/ObjectPrototype.cpp b/JavaScriptCore/runtime/ObjectPrototype.cpp index 42a8063..d8b6205 100644 --- a/JavaScriptCore/runtime/ObjectPrototype.cpp +++ b/JavaScriptCore/runtime/ObjectPrototype.cpp @@ -25,6 +25,8 @@ #include "JSString.h" #include "PrototypeFunction.h" +#include "HeapGraph.h" + namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ObjectPrototype); @@ -39,9 +41,17 @@ static JSValue* objectProtoFuncLookupSetter(ExecState*, JSObject*, JSValue*, con static JSValue* objectProtoFuncPropertyIsEnumerable(ExecState*, JSObject*, JSValue*, const ArgList&); static JSValue* objectProtoFuncToLocaleString(ExecState*, JSObject*, JSValue*, const ArgList&); -ObjectPrototype::ObjectPrototype(ExecState* exec, PassRefPtr stucture, StructureID* prototypeFunctionStructure) +ObjectPrototype::ObjectPrototype(ExecState* exec, PassRefPtr stucture, StructureID*) : JSObject(stucture) { + // HeapGraph + // For now, we are treating the Object prototype as the base of our Heap + // security contexts + setHeapSecurityContext(this, exec); +} + +void ObjectPrototype::addObjectProperties(ExecState* exec, StructureID* prototypeFunctionStructure) +{ putDirectFunctionWithoutTransition(exec, new (exec) PrototypeFunction(exec, prototypeFunctionStructure, 0, exec->propertyNames().toString, objectProtoFuncToString), DontEnum); putDirectFunctionWithoutTransition(exec, new (exec) PrototypeFunction(exec, prototypeFunctionStructure, 0, exec->propertyNames().toLocaleString, objectProtoFuncToLocaleString), DontEnum); putDirectFunctionWithoutTransition(exec, new (exec) PrototypeFunction(exec, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, objectProtoFuncValueOf), DontEnum); diff --git a/JavaScriptCore/runtime/ObjectPrototype.h b/JavaScriptCore/runtime/ObjectPrototype.h index aefdf95..36c50e3 100644 --- a/JavaScriptCore/runtime/ObjectPrototype.h +++ b/JavaScriptCore/runtime/ObjectPrototype.h @@ -28,6 +28,8 @@ namespace JSC { class ObjectPrototype : public JSObject { public: ObjectPrototype(ExecState*, PassRefPtr, StructureID* prototypeFunctionStructure); + + void addObjectProperties(ExecState*, StructureID* prototypeFunctionStructure); }; JSValue* objectProtoFuncToString(ExecState*, JSObject*, JSValue*, const ArgList&); diff --git a/JavaScriptCore/runtime/ScopeChain.h b/JavaScriptCore/runtime/ScopeChain.h index 834217c..a80433c 100644 --- a/JavaScriptCore/runtime/ScopeChain.h +++ b/JavaScriptCore/runtime/ScopeChain.h @@ -23,6 +23,9 @@ #include +#include "HeapGraph.h" +#include + namespace JSC { class JSGlobalData; @@ -40,6 +43,17 @@ namespace JSC { , refCount(1) { ASSERT(globalData); + //HeapGraph + if (next != NULL) { + addHeapEdge((JSValue *) object, (JSValue *) next->object, + std::string("@SCOPECHAIN NEXT")); + retraceSecurityContextThrough((JSValue *) object, (JSValue *) next->object); + } + + if (globalThis != NULL) { + addHeapEdge((JSValue *) object, (JSValue *) globalThis, + std::string("@SCOPECHAIN GLOBAL")); + } } ScopeChainNode* next; diff --git a/JavaScriptCore/runtime/runtime-1.moved-aside/HeapGraph.h b/JavaScriptCore/runtime/runtime-1.moved-aside/HeapGraph.h new file mode 100644 index 0000000..6d01f49 --- /dev/null +++ b/JavaScriptCore/runtime/runtime-1.moved-aside/HeapGraph.h @@ -0,0 +1,9 @@ +/* + * HeapGraph.h + * JavaScriptCore + * + * Created by Joel Weinberger on 11/12/08. + * Copyright 2008 __MyCompanyName__. All rights reserved. + * + */ + diff --git a/WebCore/ForwardingHeaders/runtime/HeapGraph.h b/WebCore/ForwardingHeaders/runtime/HeapGraph.h new file mode 100644 index 0000000..fceb130 --- /dev/null +++ b/WebCore/ForwardingHeaders/runtime/HeapGraph.h @@ -0,0 +1 @@ +#include diff --git a/WebCore/ForwardingHeaders/runtime/SecurityToken.h b/WebCore/ForwardingHeaders/runtime/SecurityToken.h new file mode 100644 index 0000000..cd3cd78 --- /dev/null +++ b/WebCore/ForwardingHeaders/runtime/SecurityToken.h @@ -0,0 +1 @@ +#include diff --git a/WebCore/bindings/js/JSDOMWindowBase.h b/WebCore/bindings/js/JSDOMWindowBase.h index 62ecdb5..61b1af9 100644 --- a/WebCore/bindings/js/JSDOMWindowBase.h +++ b/WebCore/bindings/js/JSDOMWindowBase.h @@ -62,6 +62,10 @@ namespace WebCore { void disconnectFrame(); + //HeapGraph + virtual void *getSecurityToken(); + virtual bool canAccessOrigin(void *, void *); + virtual void markCrossHeapDependentObjects(); virtual bool getOwnPropertySlot(JSC::ExecState*, const JSC::Identifier&, JSC::PropertySlot&); diff --git a/WebCore/bindings/js/JSDOMWindowCustom.cpp b/WebCore/bindings/js/JSDOMWindowCustom.cpp index 4b5f386..8a51e6c 100644 --- a/WebCore/bindings/js/JSDOMWindowCustom.cpp +++ b/WebCore/bindings/js/JSDOMWindowCustom.cpp @@ -322,4 +322,19 @@ JSValue* nonCachingStaticPostMessageFunctionGetter(ExecState* exec, const Identi return new (exec) PrototypeFunction(exec, 2, propertyName, jsDOMWindowPrototypeFunctionPostMessage); } +//HeapGraph +void *JSDOMWindowBase::getSecurityToken() +{ + return impl()->securityOrigin(); +} + +//HeapGraph +bool JSDOMWindowBase::canAccessOrigin(void *activeVoid, void *targetVoid) +{ + SecurityOrigin *active = reinterpret_cast(activeVoid); + SecurityOrigin *target = reinterpret_cast(targetVoid); + + return active->canAccess(target); +} + } // namespace WebCore diff --git a/WebCore/bindings/js/JSDOMWindowCustom.h b/WebCore/bindings/js/JSDOMWindowCustom.h index c8ce088..1e142f4 100644 --- a/WebCore/bindings/js/JSDOMWindowCustom.h +++ b/WebCore/bindings/js/JSDOMWindowCustom.h @@ -152,7 +152,8 @@ inline bool JSDOMWindowBase::allowsAccessFrom(const JSGlobalObject* other) const { if (allowsAccessFromPrivate(other)) return true; - printErrorMessage(crossDomainAccessErrorMessage(other)); + //HeapGraph + //printErrorMessage(crossDomainAccessErrorMessage(other)); return false; } diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm index 0b7d798..ab25c91 100644 --- a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm @@ -77,6 +77,12 @@ #import #import +//HeapGraph +namespace JSC { + class ExecState; + void dumpHeap(ExecState *, const char *); +} + using namespace std; @interface DumpRenderTreeEvent : NSEvent @@ -871,10 +877,31 @@ static void invalidateAnyPreviousWaitToDumpWatchdog() } } +std::string findAndReplace(std::string str, std::string find, + std::string replace) +{ + string::size_type pos = 0; + while ( (pos = str.find(find, pos)) != string::npos ) { + str.replace( pos, find.size(), replace ); + pos++; + } + + return str; +} + void dump() { invalidateAnyPreviousWaitToDumpWatchdog(); + //HeapGraph + std::string dir("/Users/jww/Desktop/layout test heaps/"); + std::string path = findAndReplace(gLayoutTestController->testPathOrURL(), + "/", "|"); + path = findAndReplace(path, ":", "|"); + dir.append(path); + dir.append(".dot"); + JSC::dumpHeap(NULL, dir.c_str()); + bool dumpAsText = gLayoutTestController->dumpAsText(); if (dumpTree) { NSString *resultString = nil;