diff options
author | Paul Phillips <paulp@improving.org> | 2010-03-19 21:48:42 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-03-19 21:48:42 +0000 |
commit | 6613b1cdae66ae23edfd0e20eb5d4d066018681d (patch) | |
tree | c033a214a3d7e15ca01cdf778b88ac6e026c0fb7 /src | |
parent | f2be3e6836014b3e4db9c7eca3bb4da8ab447f89 (diff) | |
download | scala-6613b1cdae66ae23edfd0e20eb5d4d066018681d.tar.gz scala-6613b1cdae66ae23edfd0e20eb5d4d066018681d.tar.bz2 scala-6613b1cdae66ae23edfd0e20eb5d4d066018681d.zip |
Returning to the thrilling world of equality an...
Returning to the thrilling world of equality and hashCodes now
that Any.## is a reality. Moved the hash functions from Predef to
ScalaRunTime, and made what appears to be an optimization to equals by
not losing the result of an instanceof test. Review by community.
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/JavaPlatform.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/icode/GenICode.scala | 30 | ||||
-rw-r--r-- | src/library/scala/Predef.scala | 16 | ||||
-rw-r--r-- | src/library/scala/runtime/BoxesRunTime.java | 103 | ||||
-rw-r--r-- | src/library/scala/runtime/ScalaRunTime.scala | 43 |
5 files changed, 123 insertions, 72 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index 129a747e64..c4365a82ac 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -26,6 +26,9 @@ trait JavaPlatform extends Platform[AbstractFile] { ) ::: depAnalysisPhase lazy val externalEquals = getMember(BoxesRunTimeClass, nme.equals_) + def externalEqualsNumNum = getMember(BoxesRunTimeClass, "equalsNumNum") + def externalEqualsNumChar = getMember(BoxesRunTimeClass, "equalsNumChar") + def externalEqualsNumObject = getMember(BoxesRunTimeClass, "equalsNumObject") def isMaybeBoxed(sym: Symbol): Boolean = { import definitions._ diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 64e6759da2..77e14139ba 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -9,7 +9,8 @@ package scala.tools.nsc package backend package icode -import scala.collection.mutable.{Map, HashMap, ListBuffer, Buffer, HashSet} +import scala.collection.{ mutable, immutable } +import scala.collection.mutable.{ HashMap, ListBuffer, Buffer, HashSet } import scala.tools.nsc.symtab._ import scala.annotation.switch import PartialFunction._ @@ -28,6 +29,7 @@ abstract class GenICode extends SubComponent { import definitions.{ ArrayClass, ObjectClass, ThrowableClass, StringClass, NothingClass, NullClass, Object_equals, Object_isInstanceOf, Object_asInstanceOf, ScalaRunTimeModule, + BoxedNumberClass, BoxedCharacterClass, getMember } import scalaPrimitives.{ @@ -1362,13 +1364,29 @@ abstract class GenICode extends SubComponent { * comparison might have a run-time type subtype of java.lang.Number or java.lang.Character. * When it is statically known that both sides are equal and subtypes of Number of Character, * not using the rich equality is possible (their own equals method will do ok.)*/ - def mustUseAnyComparator: Boolean = - isMaybeBoxed(l.tpe.typeSymbol) && isMaybeBoxed(r.tpe.typeSymbol) + def mustUseAnyComparator: Boolean = { + def areSameFinals = l.tpe.isFinalType && r.tpe.isFinalType && (l.tpe =:= r.tpe) + !areSameFinals && isMaybeBoxed(l.tpe.typeSymbol) && isMaybeBoxed(r.tpe.typeSymbol) + } if (mustUseAnyComparator) { // when -optimise is on we call the @inline-version of equals, found in ScalaRunTime val equalsMethod = - if (!settings.XO.value) platform.externalEquals + if (!settings.XO.value) { + def default = platform.externalEquals + platform match { + case x: JavaPlatform => + import x._ + if (l.tpe <:< BoxedNumberClass.tpe) { + if (r.tpe <:< BoxedNumberClass.tpe) externalEqualsNumNum + else if (r.tpe <:< BoxedCharacterClass.tpe) externalEqualsNumChar + else externalEqualsNumObject + } + else default + + case _ => default + } + } else { ctx.bb.emit(LOAD_MODULE(ScalaRunTimeModule)) getMember(ScalaRunTimeModule, nme.inlinedEquals) @@ -1603,7 +1621,7 @@ abstract class GenICode extends SubComponent { * to delay it any more: they will be used at some point. */ class DuplicateLabels(boundLabels: collection.Set[Symbol]) extends Transformer { - val labels: Map[Symbol, Symbol] = new HashMap + val labels: mutable.Map[Symbol, Symbol] = new HashMap var method: Symbol = _ var ctx: Context = _ @@ -2073,7 +2091,7 @@ abstract class GenICode extends SubComponent { * jumps to the given basic block. */ def patch(code: Code) { - def substMap: Map[Instruction, Instruction] = { + def substMap: mutable.Map[Instruction, Instruction] = { val map = new HashMap[Instruction, Instruction]() toPatch foreach (i => map += (i -> patch(i))) diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala index 760d215d3f..d022b3e4a5 100644 --- a/src/library/scala/Predef.scala +++ b/src/library/scala/Predef.scala @@ -53,22 +53,6 @@ object Predef extends LowPriorityImplicits { @inline def locally[T](x: T): T = x - // hashcode ----------------------------------------------------------- - - @inline def hash(x: Any): Int = - if (x.isInstanceOf[Number]) runtime.BoxesRunTime.hashFromNumber(x.asInstanceOf[Number]) - else x.hashCode - - @inline def hash(x: Number): Int = - runtime.BoxesRunTime.hashFromNumber(x) - - @inline def hash(x: java.lang.Long): Int = { - val iv = x.intValue - if (iv == x.longValue) iv else x.hashCode - } - - @inline def hash(x: Int): Int = x - // errors and asserts ------------------------------------------------- def error(message: String): Nothing = throw new RuntimeException(message) diff --git a/src/library/scala/runtime/BoxesRunTime.java b/src/library/scala/runtime/BoxesRunTime.java index 087331e1c5..7c27835b5a 100644 --- a/src/library/scala/runtime/BoxesRunTime.java +++ b/src/library/scala/runtime/BoxesRunTime.java @@ -28,7 +28,7 @@ import scala.math.ScalaNumber; * @author Martin Odersky * @contributor Stepan Koltsov * @version 2.0 */ -public class BoxesRunTime +public final class BoxesRunTime { private static final int CHAR = 0, BYTE = 1, SHORT = 2, INT = 3, LONG = 4, FLOAT = 5, DOUBLE = 6, OTHER = 7; @@ -136,38 +136,51 @@ public class BoxesRunTime * in any case, we dispatch to it as soon as we spot one on either side. */ public static boolean equals2(Object x, Object y) { - if (x instanceof Number) { - Number xn = (Number)x; - - if (y instanceof Number) { - Number yn = (Number)y; - int xcode = eqTypeCode(xn); - int ycode = eqTypeCode(yn); - switch (ycode > xcode ? ycode : xcode) { - case INT: - return xn.intValue() == yn.intValue(); - case LONG: - return xn.longValue() == yn.longValue(); - case FLOAT: - return xn.floatValue() == yn.floatValue(); - case DOUBLE: - return xn.doubleValue() == yn.doubleValue(); - default: - if ((yn instanceof ScalaNumber) && !(xn instanceof ScalaNumber)) - return y.equals(x); - } - } else if (y instanceof Character) - return equalsNumChar(xn, (Character)y); - } else if (x instanceof Character) { - Character xc = (Character)x; - if (y instanceof Character) - return xc.charValue() == ((Character)y).charValue(); - if (y instanceof Number) - return equalsNumChar((Number)y, xc); - } + if (x instanceof Number) + return equalsNumObject((Number)x, y); + if (x instanceof Character) + return equalsCharObject((Character)x, y); + return x.equals(y); } + public static boolean equalsNumObject(Number xn, Object y) { + if (y instanceof Number) + return equalsNumNum(xn, (Number)y); + else if (y instanceof Character) + return equalsNumChar(xn, (Character)y); + + return xn.equals(y); + } + + public static boolean equalsNumNum(Number xn, Number yn) { + int xcode = eqTypeCode(xn); + int ycode = eqTypeCode(yn); + switch (ycode > xcode ? ycode : xcode) { + case INT: + return xn.intValue() == yn.intValue(); + case LONG: + return xn.longValue() == yn.longValue(); + case FLOAT: + return xn.floatValue() == yn.floatValue(); + case DOUBLE: + return xn.doubleValue() == yn.doubleValue(); + default: + if ((yn instanceof ScalaNumber) && !(xn instanceof ScalaNumber)) + return yn.equals(xn); + } + return xn.equals(yn); + } + + public static boolean equalsCharObject(Character xc, Object y) { + if (y instanceof Character) + return xc.charValue() == ((Character)y).charValue(); + if (y instanceof Number) + return equalsNumChar((Number)y, xc); + + return xc.equals(y); + } + private static boolean equalsNumChar(Number xn, Character yc) { char ch = yc.charValue(); switch (eqTypeCode(xn)) { @@ -212,27 +225,27 @@ public class BoxesRunTime * verisons are equal. This still needs reconciliation. */ public static int hashFromLong(Long n) { - int iv = n.intValue(); - if (iv == n.longValue()) return iv; - else return n.hashCode(); + int iv = n.intValue(); + if (iv == n.longValue()) return iv; + else return n.hashCode(); } public static int hashFromDouble(Double n) { - int iv = n.intValue(); - double dv = n.doubleValue(); - if (iv == dv) return iv; + int iv = n.intValue(); + double dv = n.doubleValue(); + if (iv == dv) return iv; - long lv = n.longValue(); - if (lv == dv) return Long.valueOf(lv).hashCode(); - else return n.hashCode(); + long lv = n.longValue(); + if (lv == dv) return Long.valueOf(lv).hashCode(); + else return n.hashCode(); } public static int hashFromFloat(Float n) { - int iv = n.intValue(); - float fv = n.floatValue(); - if (iv == fv) return iv; + int iv = n.intValue(); + float fv = n.floatValue(); + if (iv == fv) return iv; - long lv = n.longValue(); - if (lv == fv) return Long.valueOf(lv).hashCode(); - else return n.hashCode(); + long lv = n.longValue(); + if (lv == fv) return Long.valueOf(lv).hashCode(); + else return n.hashCode(); } public static int hashFromNumber(Number n) { if (n instanceof Long) return hashFromLong((Long)n); diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index d04824bae1..43d60f2383 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -166,7 +166,8 @@ object ScalaRunTime { @inline def inlinedEquals(x: Object, y: Object): Boolean = if (x eq y) true else if (x eq null) false - else if (x.isInstanceOf[java.lang.Number] || x.isInstanceOf[java.lang.Character]) BoxesRunTime.equals2(x, y) + else if (x.isInstanceOf[java.lang.Number]) BoxesRunTime.equalsNumObject(x.asInstanceOf[java.lang.Number], y) + else if (x.isInstanceOf[java.lang.Character]) BoxesRunTime.equalsCharObject(x.asInstanceOf[java.lang.Character], y) else x.equals(y) def _equals(x: Product, y: Any): Boolean = y match { @@ -174,10 +175,42 @@ object ScalaRunTime { case _ => false } - /** Just a stub for now, but I think this is where the Predef.hash - * methods should be. - */ - @inline def hash(x: Any): Int = Predef.hash(x) + // hashcode ----------------------------------------------------------- + + @inline def hash(x: Any): Int = + if (x.isInstanceOf[java.lang.Number]) BoxesRunTime.hashFromNumber(x.asInstanceOf[java.lang.Number]) + else x.hashCode + + @inline def hash(dv: Double): Int = { + val iv = dv.toInt + if (iv == dv) return iv + + val lv = dv.toLong + if (lv == dv) return lv.hashCode + else dv.hashCode + } + @inline def hash(fv: Float): Int = { + val iv = fv.toInt + if (iv == fv) return iv + + val lv = fv.toLong + if (lv == fv) return lv.hashCode + else fv.hashCode + } + @inline def hash(lv: Long): Int = { + val iv = lv.toInt + if (iv == lv) iv else lv.hashCode + } + @inline def hash(x: Int): Int = x + @inline def hash(x: Short): Int = x.toInt + @inline def hash(x: Byte): Int = x.toInt + @inline def hash(x: Char): Int = x.toInt + + @inline def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x) + @inline def hash(x: java.lang.Long): Int = { + val iv = x.intValue + if (iv == x.longValue) iv else x.hashCode + } /** A helper method for constructing case class equality methods, * because existential types get in the way of a clean outcome and |