Web Inspector: Modify the type profiler runtime protocol to transfer some computation...
[WebKit-https.git] / Source / JavaScriptCore / runtime / TypeProfiler.cpp
1 /*
2  * Copyright (C) 2014 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "TypeProfiler.h"
28
29 #include "InspectorJSProtocolTypes.h"
30 #include "TypeLocation.h"
31
32 namespace JSC {
33
34 static const bool verbose = false;
35
36 void TypeProfiler::logTypesForTypeLocation(TypeLocation* location)
37 {
38     TypeProfilerSearchDescriptor descriptor = location->m_globalVariableID == TypeProfilerReturnStatement ? TypeProfilerSearchDescriptorFunctionReturn : TypeProfilerSearchDescriptorNormal;
39
40     dataLogF("[Start, End]::[%u, %u]\n", location->m_divotStart, location->m_divotEnd);
41
42     if (findLocation(location->m_divotStart, location->m_sourceID, descriptor))
43         dataLog("\t\t[Entry IS in System]\n");
44     else
45         dataLog("\t\t[Entry IS NOT in system]\n");
46
47     dataLog("\t\t", location->m_globalVariableID == TypeProfilerReturnStatement ? "[Return Statement]" : "[Normal Statement]", "\n");
48
49     dataLog("\t\t#Local#\n\t\t", location->m_instructionTypeSet->seenTypes().replace("\n", "\n\t\t"), "\n");
50     if (location->m_globalTypeSet)
51         dataLog("\t\t#Global#\n\t\t", location->m_globalTypeSet->seenTypes().replace("\n", "\n\t\t"), "\n");
52 }
53
54 void TypeProfiler::insertNewLocation(TypeLocation* location)
55 {
56     if (verbose)
57         dataLogF("Registering location:: divotStart:%u, divotEnd:%u\n", location->m_divotStart, location->m_divotEnd);
58
59     if (!m_bucketMap.contains(location->m_sourceID)) {
60         Vector<TypeLocation*> bucket;
61         m_bucketMap.set(location->m_sourceID, bucket);
62     }
63
64     Vector<TypeLocation*>& bucket = m_bucketMap.find(location->m_sourceID)->value;
65     bucket.append(location);
66 }
67
68 String TypeProfiler::typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptor descriptor, unsigned offset, intptr_t sourceID)
69 {
70     // This returns a JSON string representing an Object with the following properties:
71     //     globalTypeSet: 'JSON<TypeSet> | null'
72     //     instructionTypeSet: 'JSON<TypeSet>'
73
74     TypeLocation* location = findLocation(offset, sourceID, descriptor);
75     ASSERT(location);
76
77     StringBuilder json;  
78
79     json.append("{");
80
81     json.append("\"globalTypeSet\":");
82     if (location->m_globalTypeSet && location->m_globalVariableID != TypeProfilerNoGlobalIDExists)
83         json.append(location->m_globalTypeSet->toJSONString());
84     else
85         json.append("null");
86     json.append(",");
87
88     json.append("\"instructionTypeSet\":");
89     json.append(location->m_instructionTypeSet->toJSONString());
90     json.append(",");
91
92     json.append("\"isOverflown\":");
93     if (location->m_instructionTypeSet->isOverflown() || (location->m_globalTypeSet && location->m_globalTypeSet->isOverflown()))
94         json.append("true");
95     else
96         json.append("false");
97
98
99     json.append("}");
100     
101     return json.toString();
102 }
103
104 TypeLocation* TypeProfiler::findLocation(unsigned divot, intptr_t sourceID, TypeProfilerSearchDescriptor descriptor)
105 {
106     QueryKey queryKey(sourceID, divot);
107     auto iter = m_queryCache.find(queryKey);
108     if (iter != m_queryCache.end())
109         return iter->value;
110
111     if (!m_functionHasExecutedCache.hasExecutedAtOffset(sourceID, divot))
112         return nullptr;
113
114     ASSERT(m_bucketMap.contains(sourceID));
115
116     Vector<TypeLocation*>& bucket = m_bucketMap.find(sourceID)->value;
117     TypeLocation* bestMatch = nullptr;
118     unsigned distance = UINT_MAX; // Because assignments may be nested, make sure we find the closest enclosing assignment to this character offset.
119     for (size_t i = 0, size = bucket.size(); i < size; i++) {
120         TypeLocation* location = bucket.at(i);
121         // We found the type location that correlates to the convergence of all return statements in a function.
122         // This text offset is the offset of the opening brace in a function declaration.
123         if (descriptor == TypeProfilerSearchDescriptorFunctionReturn && location->m_globalVariableID == TypeProfilerReturnStatement && location->m_divotForFunctionOffsetIfReturnStatement == divot)
124             return location;
125
126         if (descriptor != TypeProfilerSearchDescriptorFunctionReturn && location->m_divotStart <= divot && divot <= location->m_divotEnd && location->m_divotEnd - location->m_divotStart <= distance) {
127             distance = location->m_divotEnd - location->m_divotStart;
128             bestMatch = location;
129         }
130     }
131
132     if (bestMatch)
133         m_queryCache.set(queryKey, bestMatch);
134     // FIXME: BestMatch should never be null past this point. This doesn't hold currently because we ignore var assignments when code contains eval/With (VarInjection). 
135     // https://bugs.webkit.org/show_bug.cgi?id=135184
136     return bestMatch;
137 }
138
139 } // namespace JSC