-
Notifications
You must be signed in to change notification settings - Fork 3.1k
/
PostProcessorFrontendAccess.scala
294 lines (227 loc) · 10.6 KB
/
PostProcessorFrontendAccess.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala.tools.nsc
package backend.jvm
import scala.collection.mutable.Clearable
import scala.reflect.internal.util.{JavaClearable, Position, Statistics}
import scala.reflect.io.AbstractFile
import scala.tools.nsc.backend.jvm.BTypes.InternalName
import java.util.{Collection => JCollection, Map => JMap}
import scala.tools.nsc.Reporting.WarningCategory
/**
* Functionality needed in the post-processor whose implementation depends on the compiler
* frontend. All methods are synchronized.
*/
sealed abstract class PostProcessorFrontendAccess {
import PostProcessorFrontendAccess._
def initialize(): Unit
final val frontendLock: AnyRef = new Object()
@inline final def frontendSynch[T](x: => T): T = frontendLock.synchronized(x)
def compilerSettings: CompilerSettings
def withThreadLocalReporter[T](reporter: BackendReporting)(fn: => T): T
def backendReporting: BackendReporting
def directBackendReporting: BackendReporting
/**
* Statistics are not thread-safe, they can only be used if `compilerSettings.backendThreads == 1`
*/
def unsafeStatistics: Statistics with BackendStats
def backendClassPath: BackendClassPath
def getEntryPoints: List[String]
def javaDefinedClasses: Set[InternalName]
def recordPerRunCache[T <: Clearable](cache: T): T
def recordPerRunJavaMapCache[T <: JMap[_,_]](cache: T): T
def recordPerRunJavaCache[T <: JCollection[_]](cache: T): T
}
object PostProcessorFrontendAccess {
sealed trait CompilerSettings {
def debug: Boolean
def target: String
def outputDirectory(source: AbstractFile): AbstractFile
def optAddToBytecodeRepository: Boolean
def optBuildCallGraph: Boolean
def optUseAnalyzerCache: Boolean
def optNone: Boolean
def optUnreachableCode: Boolean
def optNullnessTracking: Boolean
def optBoxUnbox: Boolean
def optCopyPropagation: Boolean
def optRedundantCasts: Boolean
def optSimplifyJumps: Boolean
def optCompactLocals: Boolean
def optClosureInvocations: Boolean
def optAllowSkipCoreModuleInit: Boolean
def optAssumeModulesNonNull: Boolean
def optAllowSkipClassLoading: Boolean
def optInlinerEnabled: Boolean
def optInlineFrom: List[String]
def optInlineHeuristics: String
def optWarningNoInlineMixed: Boolean
def optWarningNoInlineMissingBytecode: Boolean
def optWarningNoInlineMissingScalaInlineInfoAttr: Boolean
def optWarningEmitAtInlineFailed: Boolean
def optWarningEmitAnyInlineFailed: Boolean
def optLogInline: Option[String]
def optTrace: Option[String]
}
trait BackendReporting {
def siteString(owner: InternalName, method: String): String = {
val c = owner.replace('/', '.').replaceAll("\\$+", ".").replaceAll("\\.$", "")
if (method.isEmpty) c
else s"$c.$method"
}
def optimizerWarning(pos: Position, message: String, site: String): Unit
def error(pos: Position, message: String): Unit
def warning(pos: Position, message: String): Unit
def inform(message: String): Unit
def log(message: String): Unit
}
final class BufferingBackendReporting extends BackendReporting {
// We optimise access to the buffered reports for the common case - that there are no warning/errors to report
// We could use a ListBuffer etc - but that would be extra allocation in the common case
// Note - all access is externally synchronized, as this allow the reports to be generated in on thread and
// consumed in another
private var bufferedReports = List.empty[Report]
def optimizerWarning(pos: Position, message: String, site: String): Unit =
this.synchronized(bufferedReports ::= new ReportOptimizerWarning(pos, message, site))
def error(pos: Position, message: String): Unit =
this.synchronized(bufferedReports ::= new ReportError(pos, message))
def warning(pos: Position, message: String): Unit =
this.synchronized(bufferedReports ::= new ReportWarning(pos, message))
def inform(message: String): Unit =
this.synchronized(bufferedReports ::= new ReportInform(message))
def log(message: String): Unit =
this.synchronized(bufferedReports ::= new ReportLog(message))
def relayReports(toReporting: BackendReporting): Unit = this.synchronized {
if (bufferedReports.nonEmpty) {
bufferedReports.reverse.foreach(_.relay(toReporting))
bufferedReports = Nil
}
}
private sealed trait Report {
def relay(backendReporting: BackendReporting): Unit
}
private class ReportOptimizerWarning(pos: Position, message: String, site: String) extends Report {
override def relay(reporting: BackendReporting): Unit =
reporting.optimizerWarning(pos, message, site)
}
private class ReportError(pos: Position, message: String) extends Report {
override def relay(reporting: BackendReporting): Unit =
reporting.error(pos, message)
}
private class ReportWarning(pos: Position, message: String) extends Report {
override def relay(reporting: BackendReporting): Unit =
reporting.warning(pos, message)
}
private class ReportInform(message: String) extends Report {
override def relay(reporting: BackendReporting): Unit =
reporting.inform(message)
}
private class ReportLog(message: String) extends Report {
override def relay(reporting: BackendReporting): Unit =
reporting.log(message)
}
}
sealed trait BackendClassPath {
def findClassFile(className: String): Option[AbstractFile]
}
class PostProcessorFrontendAccessImpl(val global: Global) extends PostProcessorFrontendAccess with PerRunInit {
import global._
import genBCode.bTypes.{LazyVar, perRunLazy}
private[this] lazy val _compilerSettings: LazyVar[CompilerSettings] = perRunLazy(this)(buildCompilerSettings())
def compilerSettings: CompilerSettings = _compilerSettings.get
private def buildCompilerSettings(): CompilerSettings = new CompilerSettings {
import global.{settings => s}
@inline def debug: Boolean = s.isDebug
val target: String = s.target.value
private val singleOutDir = s.outputDirs.getSingleOutput
// the call to `outputDirFor` should be frontendSynch'd, but we assume that the setting is not mutated during the backend
def outputDirectory(source: AbstractFile): AbstractFile = singleOutDir.getOrElse(s.outputDirs.outputDirFor(source))
val optAddToBytecodeRepository: Boolean = s.optAddToBytecodeRepository
val optBuildCallGraph: Boolean = s.optBuildCallGraph
val optUseAnalyzerCache: Boolean = s.optUseAnalyzerCache
val optNone: Boolean = s.optNone
val optUnreachableCode: Boolean = s.optUnreachableCode
val optNullnessTracking: Boolean = s.optNullnessTracking
val optBoxUnbox: Boolean = s.optBoxUnbox
val optCopyPropagation: Boolean = s.optCopyPropagation
val optRedundantCasts: Boolean = s.optRedundantCasts
val optSimplifyJumps: Boolean = s.optSimplifyJumps
val optCompactLocals: Boolean = s.optCompactLocals
val optClosureInvocations: Boolean = s.optClosureInvocations
val optAllowSkipCoreModuleInit: Boolean = s.optAllowSkipCoreModuleInit
val optAssumeModulesNonNull: Boolean = s.optAssumeModulesNonNull
val optAllowSkipClassLoading: Boolean = s.optAllowSkipClassLoading
val optInlinerEnabled: Boolean = s.optInlinerEnabled
val optInlineFrom: List[String] = s.optInlineFrom
val optInlineHeuristics: String = s.YoptInlineHeuristics.value
val optWarningNoInlineMixed: Boolean = s.optWarningNoInlineMixed
val optWarningNoInlineMissingBytecode: Boolean = s.optWarningNoInlineMissingBytecode
val optWarningNoInlineMissingScalaInlineInfoAttr: Boolean = s.optWarningNoInlineMissingScalaInlineInfoAttr
val optWarningEmitAtInlineFailed: Boolean = s.optWarningEmitAtInlineFailed
val optWarningEmitAnyInlineFailed: Boolean = {
val z = s // `s` is a def, but need a stable path, the argument type of `contains` is path-dependent
z.optWarnings.contains(z.optWarningsChoices.anyInlineFailed)
}
val optLogInline: Option[String] = s.YoptLogInline.valueSetByUser
val optTrace: Option[String] = s.YoptTrace.valueSetByUser
}
private lazy val localReporter = perRunLazy(this)(new ThreadLocal[BackendReporting])
override def withThreadLocalReporter[T](reporter: BackendReporting)(fn: => T): T = {
val threadLocal = localReporter.get
val old = threadLocal.get()
threadLocal.set(reporter)
try fn finally
if (old eq null) threadLocal.remove() else threadLocal.set(old)
}
override def backendReporting: BackendReporting = {
val local = localReporter.get.get()
if (local eq null) directBackendReporting else local
}
object directBackendReporting extends BackendReporting {
def optimizerWarning(pos: Position, message: String, site: String): Unit = frontendSynch {
runReporting.warning(pos, message, WarningCategory.Optimizer, site)
}
def error(pos: Position, message: String): Unit = frontendSynch {
reporter.error(pos, message)
}
def warning(pos: Position, message: String): Unit = frontendSynch {
runReporting.warning(pos, message, WarningCategory.Other, site = "")
}
def inform(message: String): Unit = frontendSynch {
global.inform(message)
}
def log(message: String): Unit = frontendSynch {
global.log(message)
}
}
def unsafeStatistics: Statistics with BackendStats = global.statistics
private lazy val cp = perRunLazy(this)(frontendSynch(optimizerClassPath(classPath)))
object backendClassPath extends BackendClassPath {
def findClassFile(className: String): Option[AbstractFile] = cp.get.findClassFile(className)
}
def getEntryPoints: List[String] = frontendSynch(cleanup.getEntryPoints)
def javaDefinedClasses: Set[InternalName] = frontendSynch {
currentRun.symSource.keys.iterator.collect{
case sym if sym.isJavaDefined => sym.javaBinaryNameString
}.toSet
}
def recordPerRunCache[T <: Clearable](cache: T): T = frontendSynch(perRunCaches.recordCache(cache))
def recordPerRunJavaMapCache[T <: JMap[_,_]](cache: T): T = {
recordPerRunCache(JavaClearable.forMap(cache))
cache
}
def recordPerRunJavaCache[T <: JCollection[_]](cache: T): T = {
recordPerRunCache(JavaClearable.forCollection(cache))
cache
}
}
}