-
Notifications
You must be signed in to change notification settings - Fork 3.1k
/
elidable.scala
133 lines (127 loc) · 4.63 KB
/
elidable.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
/*
* 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.annotation
/** An annotation for methods whose bodies may be excluded
* from compiler-generated bytecode.
*
* Behavior is influenced by passing `-Xelide-below <arg>` to `scalac`.
* Calls to methods marked elidable (as well as the method body) will
* be omitted from generated code if the priority given the annotation
* is lower than that given on the command line.
*
* {{{
* @elidable(123) // annotation priority
* scalac -Xelide-below 456 // command line priority
* }}}
*
* The method call will be replaced with an expression which depends on
* the type of the elided expression. In decreasing order of precedence:
*
* {{{
* Unit ()
* Boolean false
* T <: AnyVal 0
* T >: Null null
* T >: Nothing Predef.???
* }}}
*
* Complete example:
* {{{
* import scala.annotation._, elidable._
* object Test extends App {
* def expensiveComputation(): Int = { Thread.sleep(1000) ; 172 }
*
* @elidable(WARNING) def warning(msg: String) = println(msg)
* @elidable(FINE) def debug(msg: String) = println(msg)
* @elidable(FINE) def computedValue = expensiveComputation()
*
* warning("Warning! Danger! Warning!")
* debug("Debug! Danger! Debug!")
* println("I computed a value: " + computedValue)
* }
* % scalac example.scala && scala Test
* Warning! Danger! Warning!
* Debug! Danger! Debug!
* I computed a value: 172
*
* // INFO lies between WARNING and FINE
* % scalac -Xelide-below INFO example.scala && scala Test
* Warning! Danger! Warning!
* I computed a value: 0
* }}}
*
* Note that only concrete methods can be marked `@elidable`. A non-annotated method
* is not elided, even if it overrides / implements a method that has the annotation.
*
* Also note that the static type determines which annotations are considered:
*
* {{{
* import scala.annotation._, elidable._
* class C { @elidable(0) def f(): Unit = ??? }
* object O extends C { override def f(): Unit = println("O.f") }
* object Test extends App {
* O.f() // not elided
* (O: C).f() // elided if compiled with `-Xelide-below 1`
* }
* }}}
*/
final class elidable(final val level: Int) extends scala.annotation.ConstantAnnotation
/** This useless appearing code was necessary to allow people to use
* named constants for the elidable annotation. This is what it takes
* to convince the compiler to fold the constants: otherwise when it's
* time to check an elision level it's staring at a tree like
* {{{
* (Select(Level, Select(FINEST, Apply(intValue, Nil))))
* }}}
* instead of the number `300`.
*/
object elidable {
/** The levels `ALL` and `OFF` are confusing in this context because
* the sentiment being expressed when using the annotation is at cross
* purposes with the one being expressed via `-Xelide-below`. This
* confusion reaches its zenith at level `OFF`, where the annotation means
* ''never elide this method'' but `-Xelide-below OFF` is how you would
* say ''elide everything possible''.
*
* With no simple remedy at hand, the issue is now at least documented,
* and aliases `MAXIMUM` and `MINIMUM` are offered.
*/
final val ALL = Int.MinValue // Level.ALL.intValue()
final val FINEST = 300 // Level.FINEST.intValue()
final val FINER = 400 // Level.FINER.intValue()
final val FINE = 500 // Level.FINE.intValue()
final val CONFIG = 700 // Level.CONFIG.intValue()
final val INFO = 800 // Level.INFO.intValue()
final val WARNING = 900 // Level.WARNING.intValue()
final val SEVERE = 1000 // Level.SEVERE.intValue()
final val OFF = Int.MaxValue // Level.OFF.intValue()
// a couple aliases for the confusing ALL and OFF
final val MAXIMUM = OFF
final val MINIMUM = ALL
// and we can add a few of our own
final val ASSERTION = 2000 // we should make this more granular
// for command line parsing so we can use names or ints
val byName: Map[String, Int] = Map(
"FINEST" -> FINEST,
"FINER" -> FINER,
"FINE" -> FINE,
"CONFIG" -> CONFIG,
"INFO" -> INFO,
"WARNING" -> WARNING,
"SEVERE" -> SEVERE,
"ASSERTION" -> ASSERTION,
"ALL" -> ALL,
"OFF" -> OFF,
"MAXIMUM" -> MAXIMUM,
"MINIMUM" -> MINIMUM
)
}