From 70d179084673fa317c4380529c9ab58f42de19e8 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 19 May 2014 09:58:34 +0200 Subject: Move class BType to separate component, include it by composition. Rewire BType in its new location with the rest of the backend. Remove the now-empty BCodeGlue. --- .../tools/nsc/backend/jvm/BCodeBodyBuilder.scala | 1 + .../scala/tools/nsc/backend/jvm/BCodeGlue.scala | 742 --------------------- .../scala/tools/nsc/backend/jvm/BCodeHelpers.scala | 2 +- .../tools/nsc/backend/jvm/BCodeIdiomatic.scala | 10 +- .../tools/nsc/backend/jvm/BCodeSkelBuilder.scala | 1 + .../tools/nsc/backend/jvm/BCodeSyncAndTry.scala | 1 + .../scala/tools/nsc/backend/jvm/BCodeTypes.scala | 2 +- .../scala/tools/nsc/backend/jvm/BTypes.scala | 741 ++++++++++++++++++++ 8 files changed, 755 insertions(+), 745 deletions(-) delete mode 100644 src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala create mode 100644 src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 3d1b646069..4af1b55316 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -24,6 +24,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { import global._ import definitions._ import bCodeICodeCommon._ + import bTypes._ /* * Functionality to build the body of ASM MethodNode, except for `synchronized` and `try` expressions. diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala deleted file mode 100644 index 4eecb48473..0000000000 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala +++ /dev/null @@ -1,742 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2012 LAMP/EPFL - * @author Martin Odersky - */ - -package scala -package tools.nsc -package backend.jvm - -import scala.tools.asm -import scala.annotation.switch -import scala.collection.{ immutable, mutable } - -/* - * Immutable representations of bytecode-level types. - * - * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded - * @version 1.0 - * - */ -abstract class BCodeGlue extends SubComponent { - - import global._ - - protected val bCodeICodeCommon: BCodeICodeCommon[global.type] = new BCodeICodeCommon(global) - - object BType { - - import global.chrs - - // ------------- sorts ------------- - - final val VOID = asm.Type.VOID - final val BOOLEAN = asm.Type.BOOLEAN - final val CHAR = asm.Type.CHAR - final val BYTE = asm.Type.BYTE - final val SHORT = asm.Type.SHORT - final val INT = asm.Type.INT - final val FLOAT = asm.Type.FLOAT - final val LONG = asm.Type.LONG - final val DOUBLE = asm.Type.DOUBLE - final val ARRAY = asm.Type.ARRAY - final val OBJECT = asm.Type.OBJECT - final val METHOD = asm.Type.METHOD - - // ------------- primitive types ------------- - - // magic shifted numbers: see comment on class BType. This has been copied 1:1 from asm.Type. - - val VOID_TYPE = new BType(VOID, ('V' << 24) | (5 << 16) | (0 << 8) | 0, 1) - val BOOLEAN_TYPE = new BType(BOOLEAN, ('Z' << 24) | (0 << 16) | (5 << 8) | 1, 1) - val CHAR_TYPE = new BType(CHAR, ('C' << 24) | (0 << 16) | (6 << 8) | 1, 1) - val BYTE_TYPE = new BType(BYTE, ('B' << 24) | (0 << 16) | (5 << 8) | 1, 1) - val SHORT_TYPE = new BType(SHORT, ('S' << 24) | (0 << 16) | (7 << 8) | 1, 1) - val INT_TYPE = new BType(INT, ('I' << 24) | (0 << 16) | (0 << 8) | 1, 1) - val FLOAT_TYPE = new BType(FLOAT, ('F' << 24) | (2 << 16) | (2 << 8) | 1, 1) - val LONG_TYPE = new BType(LONG, ('J' << 24) | (1 << 16) | (1 << 8) | 2, 1) - val DOUBLE_TYPE = new BType(DOUBLE, ('D' << 24) | (3 << 16) | (3 << 8) | 2, 1) - - /* - * Returns the Java type corresponding to the given type descriptor. - * - * @param off the offset of this descriptor in the chrs buffer. - * @return the Java type corresponding to the given type descriptor. - * - * can-multi-thread - */ - def getType(off: Int): BType = { - var len = 0 - chrs(off) match { - case 'V' => VOID_TYPE - case 'Z' => BOOLEAN_TYPE - case 'C' => CHAR_TYPE - case 'B' => BYTE_TYPE - case 'S' => SHORT_TYPE - case 'I' => INT_TYPE - case 'F' => FLOAT_TYPE - case 'J' => LONG_TYPE - case 'D' => DOUBLE_TYPE - case '[' => - len = 1 - while (chrs(off + len) == '[') { - len += 1 - } - if (chrs(off + len) == 'L') { - len += 1 - while (chrs(off + len) != ';') { - len += 1 - } - } - new BType(ARRAY, off, len + 1) - case 'L' => - len = 1 - while (chrs(off + len) != ';') { - len += 1 - } - new BType(OBJECT, off + 1, len - 1) - // case '(': - case _ => - assert(chrs(off) == '(') - var resPos = off + 1 - while (chrs(resPos) != ')') { resPos += 1 } - val resType = getType(resPos + 1) - val len = resPos - off + 1 + resType.len - new BType( - METHOD, - off, - if (resType.hasObjectSort) { - len + 2 // "+ 2" accounts for the "L ... ;" in a descriptor for a non-array reference. - } else { - len - } - ) - } - } - - /* Params denote an internal name. - * can-multi-thread - */ - def getObjectType(index: Int, length: Int): BType = { - val sort = if (chrs(index) == '[') ARRAY else OBJECT - new BType(sort, index, length) - } - - /* - * @param methodDescriptor a method descriptor. - * - * must-single-thread - */ - def getMethodType(methodDescriptor: String): BType = { - val n = global.newTypeName(methodDescriptor) - new BType(BType.METHOD, n.start, n.length) // TODO assert isValidMethodDescriptor - } - - /* - * Returns the Java method type corresponding to the given argument and return types. - * - * @param returnType the return type of the method. - * @param argumentTypes the argument types of the method. - * @return the Java type corresponding to the given argument and return types. - * - * must-single-thread - */ - def getMethodType(returnType: BType, argumentTypes: Array[BType]): BType = { - val n = global.newTypeName(getMethodDescriptor(returnType, argumentTypes)) - new BType(BType.METHOD, n.start, n.length) - } - - /* - * Returns the Java types corresponding to the argument types of method descriptor whose first argument starts at idx0. - * - * @param idx0 index into chrs of the first argument (after the '('). - * @return the Java types corresponding to the argument types of the given method descriptor. - * - * can-multi-thread - */ - private def getArgumentTypes(idx0: Int): Array[BType] = { - assert(chrs(idx0 - 1) == '(', "doesn't look like a method descriptor.") - val args = new Array[BType](getArgumentCount(idx0)) - var off = idx0 - var size = 0 - while (chrs(off) != ')') { - args(size) = getType(off) - off += args(size).len - if (args(size).sort == OBJECT) { off += 2 } // account for 'L' and ';' - // debug: assert("LVZBSCIJFD[)".contains(chrs(off))) - size += 1 - } - // debug: var check = 0; while (check < args.length) { assert(args(check) != null); check += 1 } - args - } - - /* - * Returns the number of argument types of this method type, whose first argument starts at idx0. - * - * @param idx0 index into chrs of the first argument. - * @return the number of argument types of this method type. - * - * can-multi-thread - */ - private def getArgumentCount(idx0: Int): Int = { - assert(chrs(idx0 - 1) == '(', "doesn't look like a method descriptor.") - var off = idx0 - var size = 0 - var keepGoing = true - while (keepGoing) { - val car = chrs(off) - off += 1 - if (car == ')') { - keepGoing = false - } else if (car == 'L') { - while (chrs(off) != ';') { off += 1 } - off += 1 - size += 1 - } else if (car != '[') { - size += 1 - } - } - - size - } - - /* - * Returns the Java type corresponding to the return type of the given - * method descriptor. - * - * @param methodDescriptor a method descriptor. - * @return the Java type corresponding to the return type of the given method descriptor. - * - * must-single-thread - */ - def getReturnType(methodDescriptor: String): BType = { - val n = global.newTypeName(methodDescriptor) - val delta = n.pos(')') // `delta` is relative to the Name's zero-based start position, not a valid index into chrs. - assert(delta < n.length, s"not a valid method descriptor: $methodDescriptor") - getType(n.start + delta + 1) - } - - /* - * Returns the descriptor corresponding to the given argument and return types. - * Note: no BType is created here for the resulting method descriptor, - * if that's desired the invoker is responsible for that. - * - * @param returnType the return type of the method. - * @param argumentTypes the argument types of the method. - * @return the descriptor corresponding to the given argument and return types. - * - * can-multi-thread - */ - def getMethodDescriptor( - returnType: BType, - argumentTypes: Array[BType]): String = - { - val buf = new StringBuffer() - buf.append('(') - var i = 0 - while (i < argumentTypes.length) { - argumentTypes(i).getDescriptor(buf) - i += 1 - } - buf.append(')') - returnType.getDescriptor(buf) - buf.toString() - } - - } // end of object BType - - /** - * Based on ASM's Type class. Namer's chrs is used in this class for the same purposes as the - * `buf` char array in asm.Type. - * - * @param sort One of BType.VOID ... BType.METHOD - * - * @param off For array, object and method types, the offset of the type description in - * global.chrs. - * For primitive types, the `off` field contains - * - at byte 0 (& 0xff): the lenght, 0 for void, 2 for long/double, 1 otherwise - * - at byte 1 (& 0xff00): the Opcode offset for the corresponding xALOAD / xSTORE - * instruction. Example: Opcodes.IALOAD is 46, to load a boolean or a byte the - * necessary Opcode.BALOAD is 51, therefore the offset value is 5. - * - at byte 2 (& 0xff0000): the Opcode offset for other instructions (xLOAD, - * xSTORE, xADD, etc). See method BType#getOpcode. - * - at byte 3 (& 0xff000000): the descriptor ('V' for void, etc) - * For array types, the description starts with one or more '[' - * - * @param len The length of the type description - * - 1 for primitive types - * - For array, object and method types, the number of characters in global.chrs. - * Note: for array and method types, '[' and '(' and ')' are stored in the array - * and included in the length, for example "[Ljava/lang/Object;" or "()L...;" - * For object types, the leading 'L' and trailing ';' are not stored in the array - * and therefore excluded from the length, for example "java/lang/Object". - * - * All methods of this classs can-multi-thread - */ - final class BType(val sort: Int, val off: Int, val len: Int) { - - import global.chrs - - /* - * can-multi-thread - */ - def toASMType: asm.Type = (sort: @switch) match { - case BType.VOID => asm.Type.VOID_TYPE - case BType.BOOLEAN => asm.Type.BOOLEAN_TYPE - case BType.CHAR => asm.Type.CHAR_TYPE - case BType.BYTE => asm.Type.BYTE_TYPE - case BType.SHORT => asm.Type.SHORT_TYPE - case BType.INT => asm.Type.INT_TYPE - case BType.FLOAT => asm.Type.FLOAT_TYPE - case BType.LONG => asm.Type.LONG_TYPE - case BType.DOUBLE => asm.Type.DOUBLE_TYPE - case BType.ARRAY | - BType.OBJECT => asm.Type.getObjectType(getInternalName) - case BType.METHOD => asm.Type.getMethodType(getDescriptor) - } - - /* - * Returns the number of dimensions of this array type. This method should - * only be used for an array type. - * - * @return the number of dimensions of this array type. - * - * can-multi-thread - */ - def getDimensions: Int = { - assert(isArray, s"getDimensions on non-array type $this") - var i = 1 - while (chrs(off + i) == '[') { - i += 1 - } - i - } - - /* - * Returns the (ultimate) element type of this array type. - * This method should only be used for an array type. - * - * @return Returns the type of the elements of this array type. - * - * can-multi-thread - */ - def getElementType: BType = { - assert(isArray, s"getElementType on non-array type $this") - BType.getType(off + getDimensions) - } - - /* - * Element vs. Component type of an array: - * Quoting from the JVMS, Sec. 2.4 "Reference Types and Values" - * - * An array type consists of a component type with a single dimension (whose - * length is not given by the type). The component type of an array type may itself be - * an array type. If, starting from any array type, one considers its component type, - * and then (if that is also an array type) the component type of that type, and so on, - * eventually one must reach a component type that is not an array type; this is called - * the element type of the array type. The element type of an array type is necessarily - * either a primitive type, or a class type, or an interface type. - * - */ - - /* The type of items this array holds. - * - * can-multi-thread - */ - def getComponentType: BType = { - assert(isArray, s"Asked for the component type of a non-array type: $this") - BType.getType(off + 1) - } - - /* - * Returns the internal name of the class corresponding to this object or - * array type. The internal name of a class is its fully qualified name (as - * returned by Class.getName(), where '.' are replaced by '/'. This method - * should only be used for an object or array type. - * - * @return the internal name of the class corresponding to this object type. - * - * can-multi-thread - */ - def getInternalName: String = { - assert(isRefOrArrayType, s"getInternalName on non-object, non-array type $this") - new String(chrs, off, len) - } - - /* - * @return the suffix of the internal name until the last '/' (if '/' present), internal name otherwise. - * - * can-multi-thread - */ - def getSimpleName: String = { - assert(hasObjectSort, s"getSimpleName on non-object $this") - val iname = getInternalName - val idx = iname.lastIndexOf('/') - if (idx == -1) iname - else iname.substring(idx + 1) - } - - /* - * Returns the argument types of methods of this type. - * This method should only be used for method types. - * - * @return the argument types of methods of this type. - * - * can-multi-thread - */ - def getArgumentTypes: Array[BType] = { - assert(sort == BType.METHOD, s"getArgumentTypes on non-method $this") - BType.getArgumentTypes(off + 1) - } - - /* - * Returns the return type of methods of this type. - * This method should only be used for method types. - * - * @return the return type of methods of this type. - * - * can-multi-thread - */ - def getReturnType: BType = { - assert(sort == BType.METHOD, s"getReturnType on non-method $this") - var resPos = off + 1 - while (chrs(resPos) != ')') { resPos += 1 } - BType.getType(resPos + 1) - } - - // ------------------------------------------------------------------------ - // Inspector methods - // ------------------------------------------------------------------------ - - def isPrimitiveOrVoid = (sort < BType.ARRAY) // can-multi-thread - def isValueType = (sort < BType.ARRAY) // can-multi-thread - def isArray = (sort == BType.ARRAY) // can-multi-thread - def isUnitType = (sort == BType.VOID) // can-multi-thread - - /* - * Unlike for ICode's REFERENCE, isBoxedType(t) implies isReferenceType(t) - * Also, `isReferenceType(RT_NOTHING) == true` , similarly for RT_NULL. - * Use isNullType() , isNothingType() to detect Nothing and Null. - * - * can-multi-thread - */ - def hasObjectSort = (sort == BType.OBJECT) - - def isRefOrArrayType = { hasObjectSort || isArray } // can-multi-thread - def isNonUnitValueType = { isValueType && !isUnitType } // can-multi-thread - - def isNonSpecial = { !isValueType && !isArray && !isPhantomType } // can-multi-thread - def isNothingType = { (this == RT_NOTHING) || (this == CT_NOTHING) } // can-multi-thread - def isNullType = { (this == RT_NULL) || (this == CT_NULL) } // can-multi-thread - def isPhantomType = { isNothingType || isNullType } // can-multi-thread - - /* - * can-multi-thread - */ - def isBoxed = { - this match { - case BOXED_UNIT | BOXED_BOOLEAN | BOXED_CHAR | - BOXED_BYTE | BOXED_SHORT | BOXED_INT | - BOXED_FLOAT | BOXED_LONG | BOXED_DOUBLE - => true - case _ - => false - } - } - - /* On the JVM, - * BOOL, BYTE, CHAR, SHORT, and INT - * are like Ints for the purpose of lub calculation. - * - * can-multi-thread - */ - def isIntSizedType = { - (sort : @switch) match { - case BType.BOOLEAN | BType.CHAR | - BType.BYTE | BType.SHORT | BType.INT - => true - case _ - => false - } - } - - /* On the JVM, similar to isIntSizedType except that BOOL isn't integral while LONG is. - * - * can-multi-thread - */ - def isIntegralType = { - (sort : @switch) match { - case BType.CHAR | - BType.BYTE | BType.SHORT | BType.INT | - BType.LONG - => true - case _ - => false - } - } - - /* On the JVM, FLOAT and DOUBLE. - * - * can-multi-thread - */ - def isRealType = { (sort == BType.FLOAT ) || (sort == BType.DOUBLE) } - - def isNumericType = (isIntegralType || isRealType) // can-multi-thread - - /* Is this type a category 2 type in JVM terms? (ie, is it LONG or DOUBLE?) - * - * can-multi-thread - */ - def isWideType = (getSize == 2) - - // ------------------------------------------------------------------------ - // Conversion to type descriptors - // ------------------------------------------------------------------------ - - /* - * @return the descriptor corresponding to this Java type. - * - * can-multi-thread - */ - def getDescriptor: String = { - val buf = new StringBuffer() - getDescriptor(buf) - buf.toString() - } - - /* - * Appends the descriptor corresponding to this Java type to the given string buffer. - * - * @param buf the string buffer to which the descriptor must be appended. - * - * can-multi-thread - */ - private def getDescriptor(buf: StringBuffer) { - if (isPrimitiveOrVoid) { - // descriptor is in byte 3 of 'off' for primitive types (buf == null) - buf.append(((off & 0xFF000000) >>> 24).asInstanceOf[Char]) - } else if (sort == BType.OBJECT) { - buf.append('L') - buf.append(chrs, off, len) - buf.append(';') - } else { // sort == ARRAY || sort == METHOD - buf.append(chrs, off, len) - } - } - - // ------------------------------------------------------------------------ - // Corresponding size and opcodes - // ------------------------------------------------------------------------ - - /* - * Returns the size of values of this type. - * This method must not be used for method types. - * - * @return the size of values of this type, i.e., 2 for long and - * double, 0 for void and 1 otherwise. - * - * can-multi-thread - */ - def getSize: Int = { - // the size is in byte 0 of 'off' for primitive types (buf == null) - if (isPrimitiveOrVoid) (off & 0xFF) else 1 - } - - /* - * Returns a JVM instruction opcode adapted to this Java type. This method - * must not be used for method types. - * - * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, - * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, - * ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. - * @return an opcode that is similar to the given opcode, but adapted to - * this Java type. For example, if this type is float and - * opcode is IRETURN, this method returns FRETURN. - * - * can-multi-thread - */ - def getOpcode(opcode: Int): Int = { - import asm.Opcodes - if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { - // the offset for IALOAD or IASTORE is in byte 1 of 'off' for - // primitive types (buf == null) - opcode + (if (isPrimitiveOrVoid) (off & 0xFF00) >> 8 else 4) - } else { - // the offset for other instructions is in byte 2 of 'off' for - // primitive types (buf == null) - opcode + (if (isPrimitiveOrVoid) (off & 0xFF0000) >> 16 else 4) - } - } - - // ------------------------------------------------------------------------ - // Equals, hashCode and toString - // ------------------------------------------------------------------------ - - /* - * Tests if the given object is equal to this type. - * - * @param o the object to be compared to this type. - * @return true if the given object is equal to this type. - * - * can-multi-thread - */ - override def equals(o: Any): Boolean = { - if (!(o.isInstanceOf[BType])) { - return false - } - val t = o.asInstanceOf[BType] - if (this eq t) { - return true - } - if (sort != t.sort) { - return false - } - if (sort >= BType.ARRAY) { - if (len != t.len) { - return false - } - // sort checked already - if (off == t.off) { - return true - } - var i = 0 - while (i < len) { - if (chrs(off + i) != chrs(t.off + i)) { - return false - } - i += 1 - } - // If we reach here, we could update the largest of (this.off, t.off) to match the other, so as to simplify future == comparisons. - // But that would require a var rather than val. - } - true - } - - /* - * @return a hash code value for this type. - * - * can-multi-thread - */ - override def hashCode(): Int = { - var hc = 13 * sort - if (sort >= BType.ARRAY) { - var i = off - val end = i + len - while (i < end) { - hc = 17 * (hc + chrs(i)) - i += 1 - } - } - hc - } - - /* - * @return the descriptor of this type. - * - * can-multi-thread - */ - override def toString: String = { getDescriptor } - - } - - /* - * Creates a TypeName and the BType token for it. - * This method does not add to `innerClassBufferASM`, use `internalName()` or `asmType()` or `toTypeKind()` for that. - * - * must-single-thread - */ - def brefType(iname: String): BType = brefType(newTypeName(iname.toCharArray(), 0, iname.length())) - - /* - * Creates a BType token for the TypeName received as argument. - * This method does not add to `innerClassBufferASM`, use `internalName()` or `asmType()` or `toTypeKind()` for that. - * - * can-multi-thread - */ - def brefType(iname: TypeName): BType = BType.getObjectType(iname.start, iname.length) - - // due to keyboard economy only - val UNIT = BType.VOID_TYPE - val BOOL = BType.BOOLEAN_TYPE - val CHAR = BType.CHAR_TYPE - val BYTE = BType.BYTE_TYPE - val SHORT = BType.SHORT_TYPE - val INT = BType.INT_TYPE - val LONG = BType.LONG_TYPE - val FLOAT = BType.FLOAT_TYPE - val DOUBLE = BType.DOUBLE_TYPE - - val BOXED_UNIT = brefType("java/lang/Void") - val BOXED_BOOLEAN = brefType("java/lang/Boolean") - val BOXED_BYTE = brefType("java/lang/Byte") - val BOXED_SHORT = brefType("java/lang/Short") - val BOXED_CHAR = brefType("java/lang/Character") - val BOXED_INT = brefType("java/lang/Integer") - val BOXED_LONG = brefType("java/lang/Long") - val BOXED_FLOAT = brefType("java/lang/Float") - val BOXED_DOUBLE = brefType("java/lang/Double") - - /* - * RT_NOTHING and RT_NULL exist at run-time only. - * They are the bytecode-level manifestation (in method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs. - * Therefore, when RT_NOTHING or RT_NULL are to be emitted, - * a mapping is needed: the internal names of NothingClass and NullClass can't be emitted as-is. - */ - val RT_NOTHING = brefType("scala/runtime/Nothing$") - val RT_NULL = brefType("scala/runtime/Null$") - val CT_NOTHING = brefType("scala/Nothing") // TODO needed? - val CT_NULL = brefType("scala/Null") // TODO needed? - - val srBooleanRef = brefType("scala/runtime/BooleanRef") - val srByteRef = brefType("scala/runtime/ByteRef") - val srCharRef = brefType("scala/runtime/CharRef") - val srIntRef = brefType("scala/runtime/IntRef") - val srLongRef = brefType("scala/runtime/LongRef") - val srFloatRef = brefType("scala/runtime/FloatRef") - val srDoubleRef = brefType("scala/runtime/DoubleRef") - - /* Map from type kinds to the Java reference types. - * Useful when pushing class literals onto the operand stack (ldc instruction taking a class literal). - * @see Predef.classOf - * @see genConstant() - */ - val classLiteral = immutable.Map[BType, BType]( - UNIT -> BOXED_UNIT, - BOOL -> BOXED_BOOLEAN, - BYTE -> BOXED_BYTE, - SHORT -> BOXED_SHORT, - CHAR -> BOXED_CHAR, - INT -> BOXED_INT, - LONG -> BOXED_LONG, - FLOAT -> BOXED_FLOAT, - DOUBLE -> BOXED_DOUBLE - ) - - case class MethodNameAndType(mname: String, mdesc: String) - - val asmBoxTo: Map[BType, MethodNameAndType] = { - Map( - BOOL -> MethodNameAndType("boxToBoolean", "(Z)Ljava/lang/Boolean;" ) , - BYTE -> MethodNameAndType("boxToByte", "(B)Ljava/lang/Byte;" ) , - CHAR -> MethodNameAndType("boxToCharacter", "(C)Ljava/lang/Character;") , - SHORT -> MethodNameAndType("boxToShort", "(S)Ljava/lang/Short;" ) , - INT -> MethodNameAndType("boxToInteger", "(I)Ljava/lang/Integer;" ) , - LONG -> MethodNameAndType("boxToLong", "(J)Ljava/lang/Long;" ) , - FLOAT -> MethodNameAndType("boxToFloat", "(F)Ljava/lang/Float;" ) , - DOUBLE -> MethodNameAndType("boxToDouble", "(D)Ljava/lang/Double;" ) - ) - } - - val asmUnboxTo: Map[BType, MethodNameAndType] = { - Map( - BOOL -> MethodNameAndType("unboxToBoolean", "(Ljava/lang/Object;)Z") , - BYTE -> MethodNameAndType("unboxToByte", "(Ljava/lang/Object;)B") , - CHAR -> MethodNameAndType("unboxToChar", "(Ljava/lang/Object;)C") , - SHORT -> MethodNameAndType("unboxToShort", "(Ljava/lang/Object;)S") , - INT -> MethodNameAndType("unboxToInt", "(Ljava/lang/Object;)I") , - LONG -> MethodNameAndType("unboxToLong", "(Ljava/lang/Object;)J") , - FLOAT -> MethodNameAndType("unboxToFloat", "(Ljava/lang/Object;)F") , - DOUBLE -> MethodNameAndType("unboxToDouble", "(Ljava/lang/Object;)D") - ) - } -} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index d1684a91e6..d812f6b58d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -20,8 +20,8 @@ import scala.tools.nsc.io.AbstractFile * */ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { - import global._ + import bTypes._ /* * must-single-thread diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index f9f6e7c3ff..e83453f51e 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -19,9 +19,17 @@ import collection.convert.Wrappers.JListWrapper * @version 1.0 * */ -abstract class BCodeIdiomatic extends BCodeGlue { +abstract class BCodeIdiomatic extends SubComponent { + protected val bCodeICodeCommon: BCodeICodeCommon[global.type] = new BCodeICodeCommon(global) + + val bTypes = new BTypes[global.type](global) { + def chrs = global.chrs + override type BTypeName = global.TypeName + override def createNewName(s: String) = global.newTypeName(s) + } import global._ + import bTypes._ val classfileVersion: Int = settings.target.value match { case "jvm-1.5" => asm.Opcodes.V1_5 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index a76fa4d7ba..a9ba1945d2 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -25,6 +25,7 @@ import java.io.PrintWriter */ abstract class BCodeSkelBuilder extends BCodeHelpers { import global._ + import bTypes._ /* * There's a dedicated PlainClassBuilder for each CompilationUnit, diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala index 9ddb7a3ce8..5801b5e09c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala @@ -22,6 +22,7 @@ import scala.tools.asm */ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { import global._ + import bTypes._ /* diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala index a158fdd570..20c2c52103 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala @@ -18,8 +18,8 @@ import scala.collection.{ immutable, mutable } * */ abstract class BCodeTypes extends BCodeIdiomatic { - import global._ + import bTypes._ // when compiling the Scala library, some assertions don't hold (e.g., scala.Boolean has null superClass although it's not an interface) val isCompilingStdLib = !(settings.sourcepath.isDefault) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala new file mode 100644 index 0000000000..0c5dcd2908 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -0,0 +1,741 @@ +package scala.tools.nsc +package backend.jvm + +import scala.collection.immutable +import scala.annotation.switch +import scala.tools.asm + +/** + * BTypes is a backend component that defines the class BType, a number of basic instances and + * some utilities. + * + * A BType is essentially an slice of the array `chrs` denoting the name of the type, and a field + * denoting the kind (object, array, method, or one of the primitive types). + * + * BTypes depends on Global just because it re-uses hash-consing of Name. It would be cleaner to + * create an interface for BTypeName and extend it in scala.reflect.internal.Names#Name, that + * would simplify testing BTypes (no Global needed). + */ +abstract class BTypes[G <: Global](val __global_dont_use: G) { + def chrs: Array[Char] + + /** + * Interface for names stored in `chrs` + */ + type BTypeName <: __global_dont_use.Name + + /** + * Create a new name in `chrs`. Names are assumed to be hash-consed. Equality on BType will use + * reference equality to compare the names. + */ + def createNewName(s: String): BTypeName + + /** + * Creates a BType for the class, interface or array descriptor `iname`. + * + * must-single-thread + */ + def brefType(iname: String): BType = brefType(createNewName(iname)) + + /** + * Creates a BType for the class, interface or array descriptor `iname`. + * + * can-multi-thread + */ + def brefType(iname: BTypeName): BType = BType.getObjectType(iname.start, iname.length) + + /** + * TODO @lry : make members private to BType, move those used outside into the BTypes component + */ + object BType { + // ------------- sorts ------------- + + final val VOID = asm.Type.VOID + final val BOOLEAN = asm.Type.BOOLEAN + final val CHAR = asm.Type.CHAR + final val BYTE = asm.Type.BYTE + final val SHORT = asm.Type.SHORT + final val INT = asm.Type.INT + final val FLOAT = asm.Type.FLOAT + final val LONG = asm.Type.LONG + final val DOUBLE = asm.Type.DOUBLE + final val ARRAY = asm.Type.ARRAY + final val OBJECT = asm.Type.OBJECT + final val METHOD = asm.Type.METHOD + + // ------------- primitive types ------------- + + // magic shifted numbers: see comment on class BType. This has been copied 1:1 from asm.Type. + + val VOID_TYPE = new BType(VOID, ('V' << 24) | (5 << 16) | (0 << 8) | 0, 1) + val BOOLEAN_TYPE = new BType(BOOLEAN, ('Z' << 24) | (0 << 16) | (5 << 8) | 1, 1) + val CHAR_TYPE = new BType(CHAR, ('C' << 24) | (0 << 16) | (6 << 8) | 1, 1) + val BYTE_TYPE = new BType(BYTE, ('B' << 24) | (0 << 16) | (5 << 8) | 1, 1) + val SHORT_TYPE = new BType(SHORT, ('S' << 24) | (0 << 16) | (7 << 8) | 1, 1) + val INT_TYPE = new BType(INT, ('I' << 24) | (0 << 16) | (0 << 8) | 1, 1) + val FLOAT_TYPE = new BType(FLOAT, ('F' << 24) | (2 << 16) | (2 << 8) | 1, 1) + val LONG_TYPE = new BType(LONG, ('J' << 24) | (1 << 16) | (1 << 8) | 2, 1) + val DOUBLE_TYPE = new BType(DOUBLE, ('D' << 24) | (3 << 16) | (3 << 8) | 2, 1) + + /* + * Returns the Java type corresponding to the given type descriptor. + * + * @param off the offset of this descriptor in the chrs buffer. + * @return the Java type corresponding to the given type descriptor. + * + * can-multi-thread + */ + def getType(off: Int): BType = { + var len = 0 + chrs(off) match { + case 'V' => VOID_TYPE + case 'Z' => BOOLEAN_TYPE + case 'C' => CHAR_TYPE + case 'B' => BYTE_TYPE + case 'S' => SHORT_TYPE + case 'I' => INT_TYPE + case 'F' => FLOAT_TYPE + case 'J' => LONG_TYPE + case 'D' => DOUBLE_TYPE + case '[' => + len = 1 + while (chrs(off + len) == '[') { + len += 1 + } + if (chrs(off + len) == 'L') { + len += 1 + while (chrs(off + len) != ';') { + len += 1 + } + } + new BType(ARRAY, off, len + 1) + case 'L' => + len = 1 + while (chrs(off + len) != ';') { + len += 1 + } + new BType(OBJECT, off + 1, len - 1) + // case '(': + case _ => + assert(chrs(off) == '(') + var resPos = off + 1 + while (chrs(resPos) != ')') { resPos += 1 } + val resType = getType(resPos + 1) + val len = resPos - off + 1 + resType.len + new BType( + METHOD, + off, + if (resType.hasObjectSort) { + len + 2 // "+ 2" accounts for the "L ... ;" in a descriptor for a non-array reference. + } else { + len + } + ) + } + } + + /* Params denote an internal name. + * can-multi-thread + */ + def getObjectType(index: Int, length: Int): BType = { + val sort = if (chrs(index) == '[') ARRAY else OBJECT + new BType(sort, index, length) + } + + /* + * @param methodDescriptor a method descriptor. + * + * must-single-thread + */ + def getMethodType(methodDescriptor: String): BType = { + val n = createNewName(methodDescriptor) + new BType(BType.METHOD, n.start, n.length) // TODO assert isValidMethodDescriptor + } + + /* + * Returns the Java method type corresponding to the given argument and return types. + * + * @param returnType the return type of the method. + * @param argumentTypes the argument types of the method. + * @return the Java type corresponding to the given argument and return types. + * + * must-single-thread + */ + def getMethodType(returnType: BType, argumentTypes: Array[BType]): BType = { + val n = createNewName(getMethodDescriptor(returnType, argumentTypes)) + new BType(BType.METHOD, n.start, n.length) + } + + /* + * Returns the Java types corresponding to the argument types of method descriptor whose first argument starts at idx0. + * + * @param idx0 index into chrs of the first argument (after the '('). + * @return the Java types corresponding to the argument types of the given method descriptor. + * + * can-multi-thread + */ + private def getArgumentTypes(idx0: Int): Array[BType] = { + assert(chrs(idx0 - 1) == '(', "doesn't look like a method descriptor.") + val args = new Array[BType](getArgumentCount(idx0)) + var off = idx0 + var size = 0 + while (chrs(off) != ')') { + args(size) = getType(off) + off += args(size).len + if (args(size).sort == OBJECT) { off += 2 } // account for 'L' and ';' + // debug: assert("LVZBSCIJFD[)".contains(chrs(off))) + size += 1 + } + // debug: var check = 0; while (check < args.length) { assert(args(check) != null); check += 1 } + args + } + + /* + * Returns the number of argument types of this method type, whose first argument starts at idx0. + * + * @param idx0 index into chrs of the first argument. + * @return the number of argument types of this method type. + * + * can-multi-thread + */ + private def getArgumentCount(idx0: Int): Int = { + assert(chrs(idx0 - 1) == '(', "doesn't look like a method descriptor.") + var off = idx0 + var size = 0 + var keepGoing = true + while (keepGoing) { + val car = chrs(off) + off += 1 + if (car == ')') { + keepGoing = false + } else if (car == 'L') { + while (chrs(off) != ';') { off += 1 } + off += 1 + size += 1 + } else if (car != '[') { + size += 1 + } + } + + size + } + + /* + * Returns the Java type corresponding to the return type of the given + * method descriptor. + * + * @param methodDescriptor a method descriptor. + * @return the Java type corresponding to the return type of the given method descriptor. + * + * must-single-thread + */ + def getReturnType(methodDescriptor: String): BType = { + val n = createNewName(methodDescriptor) + val delta = n.pos(')') // `delta` is relative to the Name's zero-based start position, not a valid index into chrs. + assert(delta < n.length, s"not a valid method descriptor: $methodDescriptor") + getType(n.start + delta + 1) + } + + /* + * Returns the descriptor corresponding to the given argument and return types. + * Note: no BType is created here for the resulting method descriptor, + * if that's desired the invoker is responsible for that. + * + * @param returnType the return type of the method. + * @param argumentTypes the argument types of the method. + * @return the descriptor corresponding to the given argument and return types. + * + * can-multi-thread + */ + def getMethodDescriptor( + returnType: BType, + argumentTypes: Array[BType]): String = + { + val buf = new StringBuffer() + buf.append('(') + var i = 0 + while (i < argumentTypes.length) { + argumentTypes(i).getDescriptor(buf) + i += 1 + } + buf.append(')') + returnType.getDescriptor(buf) + buf.toString() + } + + } // end of object BType + + /** + * Based on ASM's Type class. Namer's chrs is used in this class for the same purposes as the + * `buf` char array in asm.Type. + * + * @param sort One of BType.VOID ... BType.METHOD + * + * @param off For array, object and method types, the offset of the type description in chrs. + * For primitive types, the `off` field contains + * - at byte 0 (& 0xff): the lenght, 0 for void, 2 for long/double, 1 otherwise + * - at byte 1 (& 0xff00): the Opcode offset for the corresponding xALOAD / xSTORE + * instruction. Example: Opcodes.IALOAD is 46, to load a boolean or a byte the + * necessary Opcode.BALOAD is 51, therefore the offset value is 5. + * - at byte 2 (& 0xff0000): the Opcode offset for other instructions (xLOAD, + * xSTORE, xADD, etc). See method BType#getOpcode. + * - at byte 3 (& 0xff000000): the descriptor ('V' for void, etc) + * For array types, the description starts with one or more '[' + * + * @param len The length of the type description + * - 1 for primitive types + * - For array, object and method types, the number of characters in chrs. + * Note: for array and method types, '[' and '(' and ')' are stored in the array + * and included in the length, for example "[Ljava/lang/Object;" or "(I)L...;" + * For object types, the leading 'L' and trailing ';' are not stored in the array + * and therefore excluded from the length, for example "java/lang/Object". + * + * All methods of this classs can-multi-thread + */ + final class BType(val sort: Int, val off: Int, val len: Int) { + /* + * can-multi-thread + */ + def toASMType: asm.Type = (sort: @switch) match { + case BType.VOID => asm.Type.VOID_TYPE + case BType.BOOLEAN => asm.Type.BOOLEAN_TYPE + case BType.CHAR => asm.Type.CHAR_TYPE + case BType.BYTE => asm.Type.BYTE_TYPE + case BType.SHORT => asm.Type.SHORT_TYPE + case BType.INT => asm.Type.INT_TYPE + case BType.FLOAT => asm.Type.FLOAT_TYPE + case BType.LONG => asm.Type.LONG_TYPE + case BType.DOUBLE => asm.Type.DOUBLE_TYPE + case BType.ARRAY | + BType.OBJECT => asm.Type.getObjectType(getInternalName) + case BType.METHOD => asm.Type.getMethodType(getDescriptor) + } + + /* + * Returns the number of dimensions of this array type. This method should + * only be used for an array type. + * + * @return the number of dimensions of this array type. + * + * can-multi-thread + */ + def getDimensions: Int = { + assert(isArray, s"getDimensions on non-array type $this") + var i = 1 + while (chrs(off + i) == '[') { + i += 1 + } + i + } + + /* + * Returns the (ultimate) element type of this array type. + * This method should only be used for an array type. + * + * @return Returns the type of the elements of this array type. + * + * can-multi-thread + */ + def getElementType: BType = { + assert(isArray, s"getElementType on non-array type $this") + BType.getType(off + getDimensions) + } + + /* + * Element vs. Component type of an array: + * Quoting from the JVMS, Sec. 2.4 "Reference Types and Values" + * + * An array type consists of a component type with a single dimension (whose + * length is not given by the type). The component type of an array type may itself be + * an array type. If, starting from any array type, one considers its component type, + * and then (if that is also an array type) the component type of that type, and so on, + * eventually one must reach a component type that is not an array type; this is called + * the element type of the array type. The element type of an array type is necessarily + * either a primitive type, or a class type, or an interface type. + * + */ + + /* The type of items this array holds. + * + * can-multi-thread + */ + def getComponentType: BType = { + assert(isArray, s"Asked for the component type of a non-array type: $this") + BType.getType(off + 1) + } + + /* + * Returns the internal name of the class corresponding to this object or + * array type. The internal name of a class is its fully qualified name (as + * returned by Class.getName(), where '.' are replaced by '/'. This method + * should only be used for an object or array type. + * + * @return the internal name of the class corresponding to this object type. + * + * can-multi-thread + */ + def getInternalName: String = { + assert(isRefOrArrayType, s"getInternalName on non-object, non-array type $this") + new String(chrs, off, len) + } + + /* + * @return the suffix of the internal name until the last '/' (if '/' present), internal name otherwise. + * + * can-multi-thread + */ + def getSimpleName: String = { + assert(hasObjectSort, s"getSimpleName on non-object $this") + val iname = getInternalName + val idx = iname.lastIndexOf('/') + if (idx == -1) iname + else iname.substring(idx + 1) + } + + /* + * Returns the argument types of methods of this type. + * This method should only be used for method types. + * + * @return the argument types of methods of this type. + * + * can-multi-thread + */ + def getArgumentTypes: Array[BType] = { + assert(sort == BType.METHOD, s"getArgumentTypes on non-method $this") + BType.getArgumentTypes(off + 1) + } + + /* + * Returns the return type of methods of this type. + * This method should only be used for method types. + * + * @return the return type of methods of this type. + * + * can-multi-thread + */ + def getReturnType: BType = { + assert(sort == BType.METHOD, s"getReturnType on non-method $this") + var resPos = off + 1 + while (chrs(resPos) != ')') { resPos += 1 } + BType.getType(resPos + 1) + } + + // ------------------------------------------------------------------------ + // Inspector methods + // ------------------------------------------------------------------------ + + def isPrimitiveOrVoid = (sort < BType.ARRAY) // can-multi-thread + def isValueType = (sort < BType.ARRAY) // can-multi-thread + def isArray = (sort == BType.ARRAY) // can-multi-thread + def isUnitType = (sort == BType.VOID) // can-multi-thread + + /* + * Unlike for ICode's REFERENCE, isBoxedType(t) implies isReferenceType(t) + * Also, `isReferenceType(RT_NOTHING) == true` , similarly for RT_NULL. + * Use isNullType() , isNothingType() to detect Nothing and Null. + * + * can-multi-thread + */ + def hasObjectSort = (sort == BType.OBJECT) + + def isRefOrArrayType = { hasObjectSort || isArray } // can-multi-thread + def isNonUnitValueType = { isValueType && !isUnitType } // can-multi-thread + + def isNonSpecial = { !isValueType && !isArray && !isPhantomType } // can-multi-thread + def isNothingType = { (this == RT_NOTHING) || (this == CT_NOTHING) } // can-multi-thread + def isNullType = { (this == RT_NULL) || (this == CT_NULL) } // can-multi-thread + def isPhantomType = { isNothingType || isNullType } // can-multi-thread + + /* + * can-multi-thread + */ + def isBoxed = { + this match { + case BOXED_UNIT | BOXED_BOOLEAN | BOXED_CHAR | + BOXED_BYTE | BOXED_SHORT | BOXED_INT | + BOXED_FLOAT | BOXED_LONG | BOXED_DOUBLE + => true + case _ + => false + } + } + + /* On the JVM, + * BOOL, BYTE, CHAR, SHORT, and INT + * are like Ints for the purpose of lub calculation. + * + * can-multi-thread + */ + def isIntSizedType = { + (sort : @switch) match { + case BType.BOOLEAN | BType.CHAR | + BType.BYTE | BType.SHORT | BType.INT + => true + case _ + => false + } + } + + /* On the JVM, similar to isIntSizedType except that BOOL isn't integral while LONG is. + * + * can-multi-thread + */ + def isIntegralType = { + (sort : @switch) match { + case BType.CHAR | + BType.BYTE | BType.SHORT | BType.INT | + BType.LONG + => true + case _ + => false + } + } + + /* On the JVM, FLOAT and DOUBLE. + * + * can-multi-thread + */ + def isRealType = { (sort == BType.FLOAT ) || (sort == BType.DOUBLE) } + + def isNumericType = (isIntegralType || isRealType) // can-multi-thread + + /* Is this type a category 2 type in JVM terms? (ie, is it LONG or DOUBLE?) + * + * can-multi-thread + */ + def isWideType = (getSize == 2) + + // ------------------------------------------------------------------------ + // Conversion to type descriptors + // ------------------------------------------------------------------------ + + /* + * @return the descriptor corresponding to this Java type. + * + * can-multi-thread + */ + def getDescriptor: String = { + val buf = new StringBuffer() + getDescriptor(buf) + buf.toString() + } + + /* + * Appends the descriptor corresponding to this Java type to the given string buffer. + * + * @param buf the string buffer to which the descriptor must be appended. + * + * can-multi-thread + */ + private def getDescriptor(buf: StringBuffer) { + if (isPrimitiveOrVoid) { + // descriptor is in byte 3 of 'off' for primitive types (buf == null) + buf.append(((off & 0xFF000000) >>> 24).asInstanceOf[Char]) + } else if (sort == BType.OBJECT) { + buf.append('L') + buf.append(chrs, off, len) + buf.append(';') + } else { // sort == ARRAY || sort == METHOD + buf.append(chrs, off, len) + } + } + + // ------------------------------------------------------------------------ + // Corresponding size and opcodes + // ------------------------------------------------------------------------ + + /* + * Returns the size of values of this type. + * This method must not be used for method types. + * + * @return the size of values of this type, i.e., 2 for long and + * double, 0 for void and 1 otherwise. + * + * can-multi-thread + */ + def getSize: Int = { + // the size is in byte 0 of 'off' for primitive types (buf == null) + if (isPrimitiveOrVoid) (off & 0xFF) else 1 + } + + /* + * Returns a JVM instruction opcode adapted to this Java type. This method + * must not be used for method types. + * + * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, + * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, + * ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. + * @return an opcode that is similar to the given opcode, but adapted to + * this Java type. For example, if this type is float and + * opcode is IRETURN, this method returns FRETURN. + * + * can-multi-thread + */ + def getOpcode(opcode: Int): Int = { + import asm.Opcodes + if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { + // the offset for IALOAD or IASTORE is in byte 1 of 'off' for + // primitive types (buf == null) + opcode + (if (isPrimitiveOrVoid) (off & 0xFF00) >> 8 else 4) + } else { + // the offset for other instructions is in byte 2 of 'off' for + // primitive types (buf == null) + opcode + (if (isPrimitiveOrVoid) (off & 0xFF0000) >> 16 else 4) + } + } + + // ------------------------------------------------------------------------ + // Equals, hashCode and toString + // ------------------------------------------------------------------------ + + /* + * Tests if the given object is equal to this type. + * + * @param o the object to be compared to this type. + * @return true if the given object is equal to this type. + * + * can-multi-thread + */ + override def equals(o: Any): Boolean = { + if (!(o.isInstanceOf[BType])) { + return false + } + val t = o.asInstanceOf[BType] + if (this eq t) { + return true + } + if (sort != t.sort) { + return false + } + if (sort >= BType.ARRAY) { + if (len != t.len) { + return false + } + // sort checked already + if (off == t.off) { + return true + } + var i = 0 + while (i < len) { + if (chrs(off + i) != chrs(t.off + i)) { + return false + } + i += 1 + } + // If we reach here, we could update the largest of (this.off, t.off) to match the other, so as to simplify future == comparisons. + // But that would require a var rather than val. + } + true + } + + /* + * @return a hash code value for this type. + * + * can-multi-thread + */ + override def hashCode(): Int = { + var hc = 13 * sort + if (sort >= BType.ARRAY) { + var i = off + val end = i + len + while (i < end) { + hc = 17 * (hc + chrs(i)) + i += 1 + } + } + hc + } + + /* + * @return the descriptor of this type. + * + * can-multi-thread + */ + override def toString: String = getDescriptor + } + + val UNIT = BType.VOID_TYPE + val BOOL = BType.BOOLEAN_TYPE + val CHAR = BType.CHAR_TYPE + val BYTE = BType.BYTE_TYPE + val SHORT = BType.SHORT_TYPE + val INT = BType.INT_TYPE + val LONG = BType.LONG_TYPE + val FLOAT = BType.FLOAT_TYPE + val DOUBLE = BType.DOUBLE_TYPE + + val BOXED_UNIT = brefType("java/lang/Void") + val BOXED_BOOLEAN = brefType("java/lang/Boolean") + val BOXED_BYTE = brefType("java/lang/Byte") + val BOXED_SHORT = brefType("java/lang/Short") + val BOXED_CHAR = brefType("java/lang/Character") + val BOXED_INT = brefType("java/lang/Integer") + val BOXED_LONG = brefType("java/lang/Long") + val BOXED_FLOAT = brefType("java/lang/Float") + val BOXED_DOUBLE = brefType("java/lang/Double") + + /* + * RT_NOTHING and RT_NULL exist at run-time only. + * They are the bytecode-level manifestation (in method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs. + * Therefore, when RT_NOTHING or RT_NULL are to be emitted, + * a mapping is needed: the internal names of NothingClass and NullClass can't be emitted as-is. + */ + val RT_NOTHING = brefType("scala/runtime/Nothing$") + val RT_NULL = brefType("scala/runtime/Null$") + val CT_NOTHING = brefType("scala/Nothing") + val CT_NULL = brefType("scala/Null") + + val srBooleanRef = brefType("scala/runtime/BooleanRef") + val srByteRef = brefType("scala/runtime/ByteRef") + val srCharRef = brefType("scala/runtime/CharRef") + val srIntRef = brefType("scala/runtime/IntRef") + val srLongRef = brefType("scala/runtime/LongRef") + val srFloatRef = brefType("scala/runtime/FloatRef") + val srDoubleRef = brefType("scala/runtime/DoubleRef") + + /* Map from type kinds to the Java reference types. + * Useful when pushing class literals onto the operand stack (ldc instruction taking a class literal). + * @see Predef.classOf + * @see genConstant() + */ + val classLiteral = immutable.Map[BType, BType]( + UNIT -> BOXED_UNIT, + BOOL -> BOXED_BOOLEAN, + BYTE -> BOXED_BYTE, + SHORT -> BOXED_SHORT, + CHAR -> BOXED_CHAR, + INT -> BOXED_INT, + LONG -> BOXED_LONG, + FLOAT -> BOXED_FLOAT, + DOUBLE -> BOXED_DOUBLE + ) + + case class MethodNameAndType(mname: String, mdesc: String) + + val asmBoxTo: immutable.Map[BType, MethodNameAndType] = { + Map( + BOOL -> MethodNameAndType("boxToBoolean", "(Z)Ljava/lang/Boolean;" ) , + BYTE -> MethodNameAndType("boxToByte", "(B)Ljava/lang/Byte;" ) , + CHAR -> MethodNameAndType("boxToCharacter", "(C)Ljava/lang/Character;") , + SHORT -> MethodNameAndType("boxToShort", "(S)Ljava/lang/Short;" ) , + INT -> MethodNameAndType("boxToInteger", "(I)Ljava/lang/Integer;" ) , + LONG -> MethodNameAndType("boxToLong", "(J)Ljava/lang/Long;" ) , + FLOAT -> MethodNameAndType("boxToFloat", "(F)Ljava/lang/Float;" ) , + DOUBLE -> MethodNameAndType("boxToDouble", "(D)Ljava/lang/Double;" ) + ) + } + + val asmUnboxTo: immutable.Map[BType, MethodNameAndType] = { + Map( + BOOL -> MethodNameAndType("unboxToBoolean", "(Ljava/lang/Object;)Z") , + BYTE -> MethodNameAndType("unboxToByte", "(Ljava/lang/Object;)B") , + CHAR -> MethodNameAndType("unboxToChar", "(Ljava/lang/Object;)C") , + SHORT -> MethodNameAndType("unboxToShort", "(Ljava/lang/Object;)S") , + INT -> MethodNameAndType("unboxToInt", "(Ljava/lang/Object;)I") , + LONG -> MethodNameAndType("unboxToLong", "(Ljava/lang/Object;)J") , + FLOAT -> MethodNameAndType("unboxToFloat", "(Ljava/lang/Object;)F") , + DOUBLE -> MethodNameAndType("unboxToDouble", "(Ljava/lang/Object;)D") + ) + } +} + -- cgit v1.2.3