scala.reflect.api.Constants.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-reflect Show documentation
Show all versions of scala-reflect Show documentation
Reflection Library for the Scala Programming Language
/*
* 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
package reflect
package api
/**
* EXPERIMENTAL
*
* According to the section 6.24 "Constant Expressions" of the Scala language specification,
* certain expressions (dubbed ''constant expressions'') can be evaluated by the Scala compiler at compile-time.
*
* [[scala.reflect.api.Constants#Constant]] instances represent certain kinds of these expressions
* (with values stored in the `value` field and its strongly-typed views named `booleanValue`, `intValue` etc.), namely:
* 1. Literals of primitive value classes (bytes, shorts, ints, longs, floats, doubles, chars, booleans and voids).
* 1. String literals.
* 1. References to classes (typically constructed with [[scala.Predef#classOf]]).
* 1. References to enumeration values.
*
* Such constants are used to represent literals in abstract syntax trees (the [[scala.reflect.api.Trees#Literal]] node)
* and literal arguments for Java class file annotations (the [[scala.reflect.api.Annotations#LiteralArgument]] class).
*
* === Example ===
*
* The `value` field deserves some explanation. Primitive and string values are represented as themselves, whereas
* references to classes and enums are a bit roundabout.
*
* Class references are represented as instances of [[scala.reflect.api.Types#Type]]
* (because when the Scala compiler processes a class reference, the underlying runtime class might not yet have been compiled).
* To convert such a reference to a runtime class, one should use the `runtimeClass` method of a mirror such as [[scala.reflect.api.Mirrors#RuntimeMirror]]
* (the simplest way to get such a mirror is using [[scala.reflect.runtime.package#currentMirror]]).
*
* Enumeration value references are represented as instances of [[scala.reflect.api.Symbols#Symbol]], which on JVM point to methods
* that return underlying enum values. To inspect an underlying enumeration or to get runtime value of a reference to an enum,
* one should use a [[scala.reflect.api.Mirrors#RuntimeMirror]] (the simplest way to get such a mirror is again [[scala.reflect.runtime.package#currentMirror]]).
* {{{
* enum JavaSimpleEnumeration { FOO, BAR }
*
* import java.lang.annotation.*;
* @Retention(RetentionPolicy.RUNTIME)
* @Target({ElementType.TYPE})
* public @interface JavaSimpleAnnotation {
* Class> classRef();
* JavaSimpleEnumeration enumRef();
* }
*
* @JavaSimpleAnnotation(
* classRef = JavaAnnottee.class,
* enumRef = JavaSimpleEnumeration.BAR
* )
* public class JavaAnnottee {}
* }}}
* {{{
* import scala.reflect.runtime.universe._
* import scala.reflect.runtime.{currentMirror => cm}
*
* object Test extends App {
* val jann = typeOf[JavaAnnottee].typeSymbol.annotations(0).javaArgs
* def jarg(name: String) = jann(TermName(name)).asInstanceOf[LiteralArgument].value
*
* val classRef = jarg("classRef").typeValue
* println(showRaw(classRef)) // TypeRef(ThisType(), JavaAnnottee, List())
* println(cm.runtimeClass(classRef)) // class JavaAnnottee
*
* val enumRef = jarg("enumRef").symbolValue
* println(enumRef) // value BAR
*
* val siblings = enumRef.owner.info.decls
* val enumValues = siblings.filter(sym => sym.isVal && sym.isPublic)
* println(enumValues) // Scope{
* // final val FOO: JavaSimpleEnumeration;
* // final val BAR: JavaSimpleEnumeration
* // }
*
* // doesn't work because of https://github.com/scala/bug/issues/6459
* // val enumValue = mirror.reflectField(enumRef.asTerm).get
* val enumClass = cm.runtimeClass(enumRef.owner.asClass)
* val enumValue = enumClass.getDeclaredField(enumRef.name.toString).get(null)
* println(enumValue) // BAR
* }
* }}}
*
* @contentDiagram hideNodes "*Api"
* @group ReflectionAPI
*/
trait Constants {
self: Universe =>
/**
* This "virtual" case class represents the reflection interface for literal expressions which can not be further
* broken down or evaluated, such as "true", "0", "classOf[List]". Such values become parts of the Scala abstract
* syntax tree representing the program. The constants
* correspond to section 6.24 "Constant Expressions" of the
* [[http://www.scala-lang.org/files/archive/spec/2.12/ Scala Language Specification]].
*
* Such constants are used to represent literals in abstract syntax trees (the [[scala.reflect.api.Trees#Literal]] node)
* and literal arguments for Java class file annotations (the [[scala.reflect.api.Annotations#LiteralArgument]] class).
*
* Constants can be matched against and can be constructed directly, as if they were case classes:
* {{{
* assert(Constant(true).value == true)
* Constant(true) match {
* case Constant(s: String) => println("A string: " + s)
* case Constant(b: Boolean) => println("A boolean value: " + b)
* case Constant(x) => println("Something else: " + x)
* }
* }}}
*
* `Constant` instances can wrap certain kinds of these expressions:
* 1. Literals of primitive value classes ([[scala.Byte `Byte`]], [[scala.Short `Short`]], [[scala.Int `Int`]], [[scala.Long `Long`]], [[scala.Float `Float`]], [[scala.Double `Double`]], [[scala.Char `Char`]], [[scala.Boolean `Boolean`]] and [[scala.Unit `Unit`]]) - represented directly as the corresponding type
* 1. String literals - represented as instances of the `String`.
* 1. References to classes, typically constructed with [[scala.Predef#classOf]] - represented as [[scala.reflect.api.Types#Type types]].
* 1. References to enumeration values - represented as [[scala.reflect.api.Symbols#Symbol symbols]].
*
* Class references are represented as instances of [[scala.reflect.api.Types#Type]]
* (because when the Scala compiler processes a class reference, the underlying runtime class might not yet have
* been compiled). To convert such a reference to a runtime class, one should use the [[scala.reflect.api.Mirrors#RuntimeMirror#runtimeClass `runtimeClass`]] method of a
* mirror such as [[scala.reflect.api.Mirrors#RuntimeMirror `RuntimeMirror`]] (the simplest way to get such a mirror is using
* [[scala.reflect.runtime#currentMirror `scala.reflect.runtime.currentMirror`]]).
*
* Enumeration value references are represented as instances of [[scala.reflect.api.Symbols#Symbol]], which on JVM point to methods
* that return underlying enum values. To inspect an underlying enumeration or to get runtime value of a reference to an enum,
* one should use a [[scala.reflect.api.Mirrors#RuntimeMirror]] (the simplest way to get such a mirror is again [[scala.reflect.runtime.package#currentMirror]]).
*
* Usage example:
* {{{
* enum JavaSimpleEnumeration { FOO, BAR }
*
* import java.lang.annotation.*;
* @Retention(RetentionPolicy.RUNTIME)
* @Target({ElementType.TYPE})
* public @interface JavaSimpleAnnotation {
* Class> classRef();
* JavaSimpleEnumeration enumRef();
* }
*
* @JavaSimpleAnnotation(
* classRef = JavaAnnottee.class,
* enumRef = JavaSimpleEnumeration.BAR
* )
* public class JavaAnnottee {}
* }}}
* {{{
* import scala.reflect.runtime.universe._
* import scala.reflect.runtime.{currentMirror => cm}
*
* object Test extends App {
* val jann = typeOf[JavaAnnottee].typeSymbol.annotations(0).javaArgs
* def jarg(name: String) = jann(TermName(name)) match {
* // Constant is always wrapped into a Literal or LiteralArgument tree node
* case LiteralArgument(ct: Constant) => value
* case _ => sys.error("Not a constant")
* }
*
* val classRef = jarg("classRef").value.asInstanceOf[Type]
* // ideally one should match instead of casting
* println(showRaw(classRef)) // TypeRef(ThisType(), JavaAnnottee, List())
* println(cm.runtimeClass(classRef)) // class JavaAnnottee
*
* val enumRef = jarg("enumRef").value.asInstanceOf[Symbol]
* // ideally one should match instead of casting
* println(enumRef) // value BAR
*
* val siblings = enumRef.owner.info.decls
* val enumValues = siblings.filter(sym => sym.isVal && sym.isPublic)
* println(enumValues) // Scope{
* // final val FOO: JavaSimpleEnumeration;
* // final val BAR: JavaSimpleEnumeration
* // }
*
* // doesn't work because of https://github.com/scala/bug/issues/6459
* // val enumValue = mirror.reflectField(enumRef.asTerm).get
* val enumClass = cm.runtimeClass(enumRef.owner.asClass)
* val enumValue = enumClass.getDeclaredField(enumRef.name.toString).get(null)
* println(enumValue) // BAR
* }
* }}}
* @template
* @group Constants
*/
type Constant >: Null <: AnyRef with ConstantApi
/** The constructor/extractor for `Constant` instances.
* @group Extractors
*/
val Constant: ConstantExtractor
/** An extractor class to create and pattern match with syntax `Constant(value)`
* where `value` is the Scala value of the constant.
* @group Extractors
*/
abstract class ConstantExtractor {
/** A factory method that produces [[Constant `Constant`]] instances.
*
* Notice that not any value can be passed to a constant: it must be either a primitive, a `String`, a
* [[scala.reflect.api.Types#Type type]] or a [[scala.reflect.api.Symbols#Symbol symbol]].
* See [[Constant the `Constant` class]] for more information.
*/
def apply(value: Any): Constant
/** An extractor that enables writing pattern matches against the [[Constant `Constant`]] class. */
def unapply(arg: Constant): Option[Any]
}
/** The API of [[Constant]] instances.
* @group API
*/
abstract class ConstantApi {
/** Payload of the constant, that can be accessed directly or pattern matched against. */
val value: Any
/** Scala type that describes the constant. It is generated automatically based on the type of the value. */
def tpe: Type
}
}