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 "InspectorProtocolObjects.h"
30#include "TypeLocation.h"
31#include <wtf/text/StringBuilder.h>
32
33namespace JSC {
34
35namespace TypeProfilerInternal {
36static const bool verbose = false;
37}
38
39TypeProfiler::TypeProfiler()
40 : m_nextUniqueVariableID(1)
41{
42}
43
44void TypeProfiler::logTypesForTypeLocation(TypeLocation* location, VM& vm)
45{
46 TypeProfilerSearchDescriptor descriptor = location->m_globalVariableID == TypeProfilerReturnStatement ? TypeProfilerSearchDescriptorFunctionReturn : TypeProfilerSearchDescriptorNormal;
47
48 dataLogF("[Start, End]::[%u, %u]\n", location->m_divotStart, location->m_divotEnd);
49
50 if (findLocation(location->m_divotStart, location->m_sourceID, descriptor, vm))
51 dataLog("\t\t[Entry IS in System]\n");
52 else
53 dataLog("\t\t[Entry IS NOT in system]\n");
54
55 dataLog("\t\t", location->m_globalVariableID == TypeProfilerReturnStatement ? "[Return Statement]" : "[Normal Statement]", "\n");
56
57 dataLog("\t\t#Local#\n\t\t", location->m_instructionTypeSet->dumpTypes().replace("\n", "\n\t\t"), "\n");
58 if (location->m_globalTypeSet)
59 dataLog("\t\t#Global#\n\t\t", location->m_globalTypeSet->dumpTypes().replace("\n", "\n\t\t"), "\n");
60}
61
62void TypeProfiler::insertNewLocation(TypeLocation* location)
63{
64 if (TypeProfilerInternal::verbose)
65 dataLogF("Registering location:: divotStart:%u, divotEnd:%u\n", location->m_divotStart, location->m_divotEnd);
66
67 if (!m_bucketMap.contains(location->m_sourceID)) {
68 Vector<TypeLocation*> bucket;
69 m_bucketMap.set(location->m_sourceID, bucket);
70 }
71
72 Vector<TypeLocation*>& bucket = m_bucketMap.find(location->m_sourceID)->value;
73 bucket.append(location);
74}
75
76String TypeProfiler::typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptor descriptor, unsigned offset, intptr_t sourceID, VM& vm)
77{
78 // This returns a JSON string representing an Object with the following properties:
79 // globalTypeSet: 'JSON<TypeSet> | null'
80 // instructionTypeSet: 'JSON<TypeSet>'
81
82 TypeLocation* location = findLocation(offset, sourceID, descriptor, vm);
83 ASSERT(location);
84
85 StringBuilder json;
86
87 json.append('{');
88
89 json.appendLiteral("\"globalTypeSet\":");
90 if (location->m_globalTypeSet && location->m_globalVariableID != TypeProfilerNoGlobalIDExists)
91 json.append(location->m_globalTypeSet->toJSONString());
92 else
93 json.appendLiteral("null");
94 json.append(',');
95
96 json.appendLiteral("\"instructionTypeSet\":");
97 json.append(location->m_instructionTypeSet->toJSONString());
98 json.append(',');
99
100 json.appendLiteral("\"isOverflown\":");
101 if (location->m_instructionTypeSet->isOverflown() || (location->m_globalTypeSet && location->m_globalTypeSet->isOverflown()))
102 json.appendLiteral("true");
103 else
104 json.appendLiteral("false");
105
106 json.append('}');
107
108 return json.toString();
109}
110
111TypeLocation* TypeProfiler::findLocation(unsigned divot, intptr_t sourceID, TypeProfilerSearchDescriptor descriptor, VM& vm)
112{
113 QueryKey queryKey(sourceID, divot, descriptor);
114 auto iter = m_queryCache.find(queryKey);
115 if (iter != m_queryCache.end())
116 return iter->value;
117
118 if (!vm.functionHasExecutedCache()->hasExecutedAtOffset(sourceID, divot))
119 return nullptr;
120
121 if (!m_bucketMap.contains(sourceID))
122 return nullptr;
123
124 Vector<TypeLocation*>& bucket = m_bucketMap.find(sourceID)->value;
125 TypeLocation* bestMatch = nullptr;
126 unsigned distance = UINT_MAX; // Because assignments may be nested, make sure we find the closest enclosing assignment to this character offset.
127 for (auto* location : bucket) {
128 // We found the type location that correlates to the convergence of all return statements in a function.
129 // This text offset is the offset of the opening brace in a function declaration.
130 if (descriptor == TypeProfilerSearchDescriptorFunctionReturn && location->m_globalVariableID == TypeProfilerReturnStatement && location->m_divotForFunctionOffsetIfReturnStatement == divot)
131 return location;
132
133 if (descriptor != TypeProfilerSearchDescriptorFunctionReturn && location->m_globalVariableID != TypeProfilerReturnStatement && location->m_divotStart <= divot && divot <= location->m_divotEnd && location->m_divotEnd - location->m_divotStart <= distance) {
134 distance = location->m_divotEnd - location->m_divotStart;
135 bestMatch = location;
136 }
137 }
138
139 if (bestMatch)
140 m_queryCache.set(queryKey, bestMatch);
141 // 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).
142 // https://bugs.webkit.org/show_bug.cgi?id=135184
143 return bestMatch;
144}
145
146TypeLocation* TypeProfiler::nextTypeLocation()
147{
148 return m_typeLocationInfo.add();
149}
150
151void TypeProfiler::invalidateTypeSetCache(VM& vm)
152{
153 for (Bag<TypeLocation>::iterator iter = m_typeLocationInfo.begin(); !!iter; ++iter) {
154 TypeLocation* location = *iter;
155 location->m_instructionTypeSet->invalidateCache(vm);
156 if (location->m_globalTypeSet)
157 location->m_globalTypeSet->invalidateCache(vm);
158 }
159}
160
161void TypeProfiler::dumpTypeProfilerData(VM& vm)
162{
163 for (Bag<TypeLocation>::iterator iter = m_typeLocationInfo.begin(); !!iter; ++iter) {
164 TypeLocation* location = *iter;
165 logTypesForTypeLocation(location, vm);
166 }
167}
168
169} // namespace JSC
170