summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala
blob: edcdd8f4342faaaa6cb495bb970edf7e1d0f243e (plain) (blame)
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
/* NSC -- new Scala compiler
 * Copyright 2005-2012 LAMP/EPFL
 * @author  Iulian Dragos
 */

package scala.tools.nsc
package backend.jvm

import scala.collection.{ mutable, immutable }
import ch.epfl.lamp.fjbg._

trait GenJVMUtil {
  self: GenJVM =>

  import global._
  import icodes._
  import icodes.opcodes._
  import definitions._

  /** Map from type kinds to the Java reference types. It is used for
   *  loading class constants. @see Predef.classOf.
   */
  val classLiteral = immutable.Map[TypeKind, JObjectType](
    UNIT   -> new JObjectType("java.lang.Void"),
    BOOL   -> new JObjectType("java.lang.Boolean"),
    BYTE   -> new JObjectType("java.lang.Byte"),
    SHORT  -> new JObjectType("java.lang.Short"),
    CHAR   -> new JObjectType("java.lang.Character"),
    INT    -> new JObjectType("java.lang.Integer"),
    LONG   -> new JObjectType("java.lang.Long"),
    FLOAT  -> new JObjectType("java.lang.Float"),
    DOUBLE -> new JObjectType("java.lang.Double")
  )

  // Don't put this in per run caches.
  private val javaNameCache = new mutable.WeakHashMap[Symbol, Name]() ++= List(
    NothingClass        -> binarynme.RuntimeNothing,
    RuntimeNothingClass -> binarynme.RuntimeNothing,
    NullClass           -> binarynme.RuntimeNull,
    RuntimeNullClass    -> binarynme.RuntimeNull
  )

  /** This trait may be used by tools who need access to
   *  utility methods like javaName and javaType. (for instance,
   *  the Eclipse plugin uses it).
   */
  trait BytecodeUtil {

    val conds = immutable.Map[TestOp, Int](
      EQ -> JExtendedCode.COND_EQ,
      NE -> JExtendedCode.COND_NE,
      LT -> JExtendedCode.COND_LT,
      GT -> JExtendedCode.COND_GT,
      LE -> JExtendedCode.COND_LE,
      GE -> JExtendedCode.COND_GE
    )

    /** Specialized array conversion to prevent calling
     *  java.lang.reflect.Array.newInstance via TraversableOnce.toArray
     */

    def mkArray(xs: Traversable[JType]): Array[JType] = { val a = new Array[JType](xs.size); xs.copyToArray(a); a }
    def mkArray(xs: Traversable[String]): Array[String] = { val a = new Array[String](xs.size); xs.copyToArray(a); a }

    /** Return the a name of this symbol that can be used on the Java
     *  platform.  It removes spaces from names.
     *
     *  Special handling:
     *    scala.Nothing erases to scala.runtime.Nothing$
     *       scala.Null erases to scala.runtime.Null$
     *
     *  This is needed because they are not real classes, and they mean
     *  'abrupt termination upon evaluation of that expression' or null respectively.
     *  This handling is done already in GenICode, but here we need to remove
     *  references from method signatures to these types, because such classes can
     *  not exist in the classpath: the type checker will be very confused.
     */
    def javaName(sym: Symbol): String =
      javaNameCache.getOrElseUpdate(sym, {
        if (sym.isClass || (sym.isModule && !sym.isMethod))
          sym.javaBinaryName
        else
          sym.javaSimpleName
      }).toString

    def javaType(t: TypeKind): JType = (t: @unchecked) match {
      case UNIT            => JType.VOID
      case BOOL            => JType.BOOLEAN
      case BYTE            => JType.BYTE
      case SHORT           => JType.SHORT
      case CHAR            => JType.CHAR
      case INT             => JType.INT
      case LONG            => JType.LONG
      case FLOAT           => JType.FLOAT
      case DOUBLE          => JType.DOUBLE
      case REFERENCE(cls)  => new JObjectType(javaName(cls))
      case ARRAY(elem)     => new JArrayType(javaType(elem))
    }

    def javaType(t: Type): JType = javaType(toTypeKind(t))

    def javaType(s: Symbol): JType =
      if (s.isMethod)
        new JMethodType(
          if (s.isClassConstructor) JType.VOID else javaType(s.tpe.resultType),
          mkArray(s.tpe.paramTypes map javaType)
        )
      else
        javaType(s.tpe)

    protected def genConstant(jcode: JExtendedCode, const: Constant) {
      const.tag match {
        case UnitTag    => ()
        case BooleanTag => jcode emitPUSH const.booleanValue
        case ByteTag    => jcode emitPUSH const.byteValue
        case ShortTag   => jcode emitPUSH const.shortValue
        case CharTag    => jcode emitPUSH const.charValue
        case IntTag     => jcode emitPUSH const.intValue
        case LongTag    => jcode emitPUSH const.longValue
        case FloatTag   => jcode emitPUSH const.floatValue
        case DoubleTag  => jcode emitPUSH const.doubleValue
        case StringTag  => jcode emitPUSH const.stringValue
        case NullTag    => jcode.emitACONST_NULL()
        case ClazzTag   =>
          val kind = toTypeKind(const.typeValue)
          val toPush =
            if (kind.isValueType) classLiteral(kind)
            else javaType(kind).asInstanceOf[JReferenceType]

          jcode emitPUSH toPush

        case EnumTag   =>
          val sym = const.symbolValue
          jcode.emitGETSTATIC(javaName(sym.owner),
                              javaName(sym),
                              javaType(sym.tpe.underlying))
        case _         =>
          abort("Unknown constant value: " + const)
      }
    }
  }
}