summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/Settings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala13
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala326
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala2
-rw-r--r--src/library/scala/runtime/BoxesRunTime.java45
-rw-r--r--src/library/scala/runtime/Equality.java51
-rw-r--r--src/library/scala/xml/Utility.scala8
-rw-r--r--src/library/scala/xml/parsing/MarkupParser.scala36
10 files changed, 256 insertions, 230 deletions
diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala
index 027c25cd0c..4a5afb7baf 100644
--- a/src/compiler/scala/tools/nsc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/Settings.scala
@@ -819,7 +819,7 @@ trait ScalacSettings {
val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics")
val stop = PhasesSetting ("-Ystop", "Stop after phase")
val logEquality = BooleanSetting ("-Ylog-equality", "Log all noteworthy equality tests (hardcoded to /tmp/scala-equality-log.txt)") .
- withPostSetHook(() => scala.runtime.BoxesRunTime.setEqEqLogging(true))
+ withPostSetHook(() => scala.runtime.Equality.logEverything = true)
val refinementMethodDispatch =
ChoiceSetting ("-Ystruct-dispatch", "Selects dispatch method for structural refinement method calls",
List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") .
diff --git a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala
index d205ab1098..290d90b3e9 100644
--- a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala
+++ b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala
@@ -508,6 +508,8 @@ abstract class ScalaPrimitives {
case _ => false
}
+ def isUniversalEqualityOp(code: Int): Boolean = (code == EQ) || (code == NE)
+ def isReferenceEqualityOp(code: Int): Boolean = (code == ID) || (code == NI)
def isArithmeticOp(code: Int): Boolean = code match {
case POS | NEG | NOT => true; // unary
diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
index 27c71e60a3..def5f1060d 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
@@ -340,6 +340,19 @@ trait BasicBlocks {
instrs foreach (i => emit(i, i.pos))
}
+ /** The semantics of this are a little odd but it's designed to work
+ * seamlessly with the existing code. It emits each supplied instruction,
+ * then closes the block. The odd part is that if the instruction has
+ * pos == NoPosition, it calls the 1-arg emit, but otherwise it calls
+ * the 2-arg emit. This way I could retain existing behavior exactly by
+ * calling setPos on any instruction using the two arg version which
+ * I wanted to include in a call to emitOnly.
+ */
+ def emitOnly(instrs: Instruction*) {
+ instrs foreach (i => if (i.pos == NoPosition) emit(i) else emit(i, i.pos))
+ this.close
+ }
+
/** Close the block */
def close {
assert(instructionList.length > 0, "Empty block.")
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 5919351614..da067ae1d9 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -12,7 +12,7 @@ package icode
import scala.collection.mutable.{Map, HashMap, ListBuffer, Buffer, HashSet}
import scala.tools.nsc.symtab._
import scala.tools.nsc.util.Position
-
+import PartialFunction._
/** This class ...
*
@@ -25,6 +25,14 @@ abstract class GenICode extends SubComponent {
import global._
import icodes._
import icodes.opcodes._
+ import definitions.{
+ ArrayClass, ObjectClass, ThrowableClass,
+ Object_equals
+ }
+ import scalaPrimitives.{
+ isArrayOp, isComparisonOp, isLogicalOp,
+ isUniversalEqualityOp, isReferenceEqualityOp
+ }
val phaseName = "icode"
@@ -40,11 +48,11 @@ abstract class GenICode extends SubComponent {
val STRING = REFERENCE(definitions.StringClass)
// this depends on the backend! should be changed.
- val ANY_REF_CLASS = REFERENCE(definitions.ObjectClass)
+ val ANY_REF_CLASS = REFERENCE(ObjectClass)
val SCALA_ALL = REFERENCE(definitions.NothingClass)
val SCALA_ALLREF = REFERENCE(definitions.NullClass)
- val THROWABLE = REFERENCE(definitions.ThrowableClass)
+ val THROWABLE = REFERENCE(ThrowableClass)
val BoxesRunTime_equals =
if (!forMSIL)
@@ -526,9 +534,9 @@ abstract class GenICode extends SubComponent {
genLoad(duplicateFinalizer, ctx1, UNIT);
})
- case Ident(nme.WILDCARD) => (definitions.ThrowableClass, kind, {
+ case Ident(nme.WILDCARD) => (ThrowableClass, kind, {
ctx: Context =>
- ctx.bb.emit(DROP(REFERENCE(definitions.ThrowableClass)))
+ ctx.bb.emit(DROP(REFERENCE(ThrowableClass)))
val ctx1 = genLoad(body, ctx, kind)
if (guardResult) {
ctx1.bb.emit(STORE_LOCAL(tmp))
@@ -755,10 +763,9 @@ abstract class GenICode extends SubComponent {
} else if (code == scalaPrimitives.CONCAT) {
ctx1 = genStringConcat(tree, ctx1)
generatedType = STRING
- } else if (scalaPrimitives.isArrayOp(code)) {
+ } else if (isArrayOp(code)) {
ctx1 = genArrayOp(tree, ctx1, code)
- } else if (scalaPrimitives.isLogicalOp(code) ||
- scalaPrimitives.isComparisonOp(code)) {
+ } else if (isLogicalOp(code) || isComparisonOp(code)) {
val trueCtx = ctx1.newBlock
val falseCtx = ctx1.newBlock
@@ -773,7 +780,7 @@ abstract class GenICode extends SubComponent {
generatedType = BOOL
ctx1 = afterCtx
} else if (code == scalaPrimitives.SYNCHRONIZED) {
- val monitor = ctx.makeLocal(tree.pos, definitions.ObjectClass.tpe, "monitor")
+ val monitor = ctx.makeLocal(tree.pos, ObjectClass.tpe, "monitor")
ctx1 = genLoadQualifier(fun, ctx1)
ctx1.bb.emit(DUP(ANY_REF_CLASS))
ctx1.bb.emit(STORE_LOCAL(monitor))
@@ -829,7 +836,7 @@ abstract class GenICode extends SubComponent {
val hostClass = fun match {
case Select(qualifier, _)
- if (qualifier.tpe.typeSymbol != definitions.ArrayClass) =>
+ if (qualifier.tpe.typeSymbol != ArrayClass) =>
qualifier.tpe.typeSymbol
case _ => sym.owner
}
@@ -864,8 +871,8 @@ abstract class GenICode extends SubComponent {
generatedType = REFERENCE(tree.symbol)
} else {
ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos)
- if (tree.symbol == definitions.ArrayClass)
- generatedType = REFERENCE(definitions.ObjectClass)
+ if (tree.symbol == ArrayClass)
+ generatedType = REFERENCE(ObjectClass)
else
generatedType = REFERENCE(ctx.clazz.symbol)
}
@@ -1272,6 +1279,12 @@ abstract class GenICode extends SubComponent {
List(tree)
}
+ /** Some useful equality helpers.
+ */
+ def isNull(t: Tree) = cond(t) { case Literal(Constant(null)) => true }
+
+ /* If l or r is constant null, returns the other ; otherwise null */
+ def ifOneIsNull(l: Tree, r: Tree) = if (isNull(l)) r else if (isNull(r)) l else null
/**
* Traverse the tree and store label stubs in the context. This is
@@ -1313,23 +1326,8 @@ abstract class GenICode extends SubComponent {
elseCtx: Context): Unit =
{
def genComparisonOp(l: Tree, r: Tree, code: Int) {
- // special-case reference (in)equality test for null
- if (code == scalaPrimitives.ID || code == scalaPrimitives.NI) {
- val expr: Tree = (l, r) match {
- case (Literal(Constant(null)), expr) => expr
- case (expr, Literal(Constant(null))) => expr
- case _ => null
- }
- if (expr ne null) {
- val ctx1 = genLoad(expr, ctx, ANY_REF_CLASS)
- if (code == scalaPrimitives.ID)
- ctx1.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS))
- else
- ctx1.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, ANY_REF_CLASS))
- ctx1.bb.close
- return
- }
- }
+ if (settings.logEquality.value && isUniversalEqualityOp(code))
+ logEqEq(tree, l, r, code)
val op: TestOp = code match {
case scalaPrimitives.LT => LT
@@ -1342,100 +1340,74 @@ abstract class GenICode extends SubComponent {
case _ => abort("Unknown comparison primitive: " + code)
}
- val kind = getMaxType(l.tpe :: r.tpe :: Nil)
- var ctx1 = genLoad(l, ctx, kind);
- ctx1 = genLoad(r, ctx1, kind);
- ctx1.bb.emit(CJUMP(thenCtx.bb, elseCtx.bb, op, kind), r.pos)
- ctx1.bb.close
- }
-
- /** Log equality tests to file if they are playing with typefire */
- def logEqEq(l: Tree, r: Tree, op: String) {
- def mayBeNumericComparison: Boolean = {
- def isPossiblyBoxed(sym: Symbol): Boolean = {
- import definitions._
-
- // good enough for now
- (sym == ObjectClass) ||
- (sym isNonBottomSubClass BoxedNumberClass) ||
- (sym isNonBottomSubClass BoxedCharacterClass)
- }
-
- val lsym = l.tpe.typeSymbol
- val rsym = r.tpe.typeSymbol
-
- (lsym != rsym) && (isPossiblyBoxed(lsym) && isPossiblyBoxed(rsym))
+ // special-case reference (in)equality test for null (null eq x, x eq null)
+ lazy val nonNullSide = ifOneIsNull(l, r)
+ if (isReferenceEqualityOp(code) && nonNullSide != null) {
+ val ctx1 = genLoad(nonNullSide, ctx, ANY_REF_CLASS)
+ ctx1.bb.emitOnly(
+ CZJUMP(thenCtx.bb, elseCtx.bb, op, ANY_REF_CLASS)
+ )
+ }
+ else {
+ val kind = getMaxType(l.tpe :: r.tpe :: Nil)
+ var ctx1 = genLoad(l, ctx, kind)
+ ctx1 = genLoad(r, ctx1, kind)
+
+ ctx1.bb.emitOnly(
+ CJUMP(thenCtx.bb, elseCtx.bb, op, kind) setPos r.pos
+ )
}
-
- val tkl = toTypeKind(l.tpe)
- val tkr = toTypeKind(r.tpe)
- lazy val whereAreWe = tree.pos.source + ":" + tree.pos.line
- def logit(preface: String) =
- runtime.BoxesRunTime.log("[%s] %s %s %s (%s)".format(preface, l.tpe, op, r.tpe, whereAreWe))
-
- if (tkl.isNumericType && tkr.isNumericType && tkl != tkr)
- logit(" KNOWN ")
- else if (mayBeNumericComparison)
- logit("UNKNOWN")
}
if (settings.debug.value)
log("Entering genCond with tree: " + tree);
+ // the default emission
+ def default = {
+ val ctx1 = genLoad(tree, ctx, BOOL)
+ ctx1.bb.emitOnly(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL) setPos tree.pos)
+ }
+
tree match {
- case Apply(fun, args)
- if isPrimitive(fun.symbol) =>
- val code = scalaPrimitives.getPrimitive(fun.symbol)
+ // The comparison symbol is in ScalaPrimitives's "primitives" map
+ case Apply(fun, args) if isPrimitive(fun.symbol) =>
+ import scalaPrimitives.{ ZNOT, ZAND, ZOR, EQ, getPrimitive }
- if (code == scalaPrimitives.ZNOT) {
- val Select(leftArg, _) = fun
- genCond(leftArg, ctx, elseCtx, thenCtx)
- }
- else if ((code == scalaPrimitives.EQ || code == scalaPrimitives.NE)) {
- val Select(leftArg, _) = fun;
- if (settings.logEquality.value)
- logEqEq(leftArg, args.head, (if (code == scalaPrimitives.EQ) "==" else "!="))
-
- if (toTypeKind(leftArg.tpe).isReferenceType) {
- if (code == scalaPrimitives.EQ)
- genEqEqPrimitive(leftArg, args.head, ctx, thenCtx, elseCtx)
- else
- genEqEqPrimitive(leftArg, args.head, ctx, elseCtx, thenCtx)
- }
- else
- genComparisonOp(leftArg, args.head, code);
- }
- else if (scalaPrimitives.isComparisonOp(code)) {
- val Select(leftArg, _) = fun
- genComparisonOp(leftArg, args.head, code)
- }
- else {
- code match {
- case scalaPrimitives.ZAND =>
- val Select(leftArg, _) = fun
+ // lhs and rhs of test
+ lazy val Select(lhs, _) = fun
+ lazy val rhs = args.head
- val ctxInterm = ctx.newBlock
- genCond(leftArg, ctx, ctxInterm, elseCtx)
- genCond(args.head, ctxInterm, thenCtx, elseCtx)
+ def genZandOrZor(and: Boolean) = {
+ val ctxInterm = ctx.newBlock
- case scalaPrimitives.ZOR =>
- val Select(leftArg, _) = fun
+ if (and) genCond(lhs, ctx, ctxInterm, elseCtx)
+ else genCond(lhs, ctx, thenCtx, ctxInterm)
- val ctxInterm = ctx.newBlock
- genCond(leftArg, ctx, thenCtx, ctxInterm)
- genCond(args.head, ctxInterm, thenCtx, elseCtx)
+ genCond(rhs, ctxInterm, thenCtx, elseCtx)
+ }
+ def genRefEq(isEq: Boolean) = {
+ val f = genEqEqPrimitive(lhs, rhs, ctx) _
+ if (isEq) f(thenCtx, elseCtx)
+ else f(elseCtx, thenCtx)
+ }
- case _ =>
- var ctx1 = genLoad(tree, ctx, BOOL)
- ctx1.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL), tree.pos)
- ctx1.bb.close
+ getPrimitive(fun.symbol) match {
+ case ZNOT => genCond(lhs, ctx, elseCtx, thenCtx)
+ case ZAND => genZandOrZor(and = true)
+ case ZOR => genZandOrZor(and = false)
+ case code =>
+ // x == y where LHS is reference type
+ if (isUniversalEqualityOp(code) && toTypeKind(lhs.tpe).isReferenceType) {
+ if (code == EQ) genRefEq(isEq = true)
+ else genRefEq(isEq = false)
}
- }
+ else if (isComparisonOp(code))
+ genComparisonOp(lhs, rhs, code)
+ else
+ default
+ }
- case _ =>
- var ctx1 = genLoad(tree, ctx, BOOL)
- ctx1.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL), tree.pos)
- ctx1.bb.close
+ case _ => default
}
}
@@ -1449,9 +1421,7 @@ abstract class GenICode extends SubComponent {
* @param thenCtx target context if the comparison yields true
* @param elseCtx target context if the comparison yields false
*/
- def genEqEqPrimitive(l: Tree, r: Tree, ctx: Context,
- thenCtx: Context, elseCtx: Context): Unit =
- {
+ def genEqEqPrimitive(l: Tree, r: Tree, ctx: Context)(thenCtx: Context, elseCtx: Context): Unit = {
def eqEqTempName: Name = "eqEqTemp$"
@@ -1479,8 +1449,8 @@ abstract class GenICode extends SubComponent {
val lsym = l.tpe.typeSymbol
val rsym = r.tpe.typeSymbol
- (lsym == definitions.ObjectClass) ||
- (rsym == definitions.ObjectClass) ||
+ (lsym == ObjectClass) ||
+ (rsym == ObjectClass) ||
(lsym != rsym) && (isBoxed(lsym) || isBoxed(rsym))
}
@@ -1488,57 +1458,58 @@ abstract class GenICode extends SubComponent {
val ctx1 = genLoad(l, ctx, ANY_REF_CLASS)
val ctx2 = genLoad(r, ctx1, ANY_REF_CLASS)
- ctx2.bb.emit(CALL_METHOD(BoxesRunTime_equals, Static(false)))
- ctx2.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL))
- ctx2.bb.close
-
+ ctx2.bb.emitOnly(
+ CALL_METHOD(BoxesRunTime_equals, Static(false)),
+ CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL)
+ )
}
else {
- (l, r) match {
+ if (isNull(l))
// null == expr -> expr eq null
- case (Literal(Constant(null)), expr) =>
- val ctx1 = genLoad(expr, ctx, ANY_REF_CLASS)
- ctx1.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS))
- ctx1.bb.close
-
- // expr == null -> if(expr eq null) true else expr.equals(null)
- case (expr, Literal(Constant(null))) =>
- val eqEqTempLocal = getTempLocal
- var ctx1 = genLoad(expr, ctx, ANY_REF_CLASS)
- ctx1.bb.emit(DUP(ANY_REF_CLASS))
- ctx1.bb.emit(STORE_LOCAL(eqEqTempLocal), l.pos)
- val nonNullCtx = ctx1.newBlock
- ctx1.bb.emit(CZJUMP(thenCtx.bb, nonNullCtx.bb, EQ, ANY_REF_CLASS))
- ctx1.bb.close
-
- nonNullCtx.bb.emit(LOAD_LOCAL(eqEqTempLocal), l.pos)
- nonNullCtx.bb.emit(CONSTANT(Constant(null)), r.pos)
- nonNullCtx.bb.emit(CALL_METHOD(definitions.Object_equals, Dynamic))
- nonNullCtx.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL))
- nonNullCtx.bb.close
-
- // l == r -> if (l eq null) r eq null else l.equals(r)
- case _ =>
- val eqEqTempLocal = getTempLocal
- var ctx1 = genLoad(l, ctx, ANY_REF_CLASS)
+ genLoad(r, ctx, ANY_REF_CLASS).bb emitOnly CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS)
+ else {
+ val eqEqTempLocal = getTempLocal
+ var ctx1 = genLoad(l, ctx, ANY_REF_CLASS)
+
+ // dicey refactor section
+ lazy val nonNullCtx = ctx1.newBlock
+
+ if (isNull(r)) {
+ // expr == null -> if (l eq null) true else l.equals(null)
+ ctx1.bb.emitOnly(
+ DUP(ANY_REF_CLASS),
+ STORE_LOCAL(eqEqTempLocal) setPos l.pos,
+ CZJUMP(thenCtx.bb, nonNullCtx.bb, EQ, ANY_REF_CLASS)
+ )
+ nonNullCtx.bb.emitOnly(
+ LOAD_LOCAL(eqEqTempLocal) setPos l.pos,
+ CONSTANT(Constant(null)) setPos r.pos,
+ CALL_METHOD(Object_equals, Dynamic),
+ CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL)
+ )
+ }
+ else {
+ // l == r -> if (l eq null) r eq null else l.equals(r)
ctx1 = genLoad(r, ctx1, ANY_REF_CLASS)
val nullCtx = ctx1.newBlock
- val nonNullCtx = ctx1.newBlock
- ctx1.bb.emit(STORE_LOCAL(eqEqTempLocal), l.pos)
- ctx1.bb.emit(DUP(ANY_REF_CLASS))
- ctx1.bb.emit(CZJUMP(nullCtx.bb, nonNullCtx.bb, EQ, ANY_REF_CLASS))
- ctx1.bb.close
- nullCtx.bb.emit(DROP(ANY_REF_CLASS), l.pos) // type of AnyRef
- nullCtx.bb.emit(LOAD_LOCAL(eqEqTempLocal))
- nullCtx.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS))
- nullCtx.bb.close
-
- nonNullCtx.bb.emit(LOAD_LOCAL(eqEqTempLocal), l.pos)
- nonNullCtx.bb.emit(CALL_METHOD(definitions.Object_equals, Dynamic))
- nonNullCtx.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL))
- nonNullCtx.bb.close
+ ctx1.bb.emitOnly(
+ STORE_LOCAL(eqEqTempLocal) setPos l.pos,
+ DUP(ANY_REF_CLASS),
+ CZJUMP(nullCtx.bb, nonNullCtx.bb, EQ, ANY_REF_CLASS)
+ )
+ nullCtx.bb.emitOnly(
+ DROP(ANY_REF_CLASS) setPos l.pos, // type of AnyRef
+ LOAD_LOCAL(eqEqTempLocal),
+ CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS)
+ )
+ nonNullCtx.bb.emitOnly(
+ LOAD_LOCAL(eqEqTempLocal) setPos l.pos,
+ CALL_METHOD(Object_equals, Dynamic),
+ CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL)
+ )
+ }
}
}
}
@@ -1984,7 +1955,7 @@ abstract class GenICode extends SubComponent {
*
* <code> ctx.Try( ctx => {
* ctx.bb.emit(...) // protected block
- * }, (definitions.ThrowableClass,
+ * }, (ThrowableClass,
* ctx => {
* ctx.bb.emit(...); // exception handler
* }), (AnotherExceptionClass,
@@ -2002,7 +1973,7 @@ abstract class GenICode extends SubComponent {
val exh = outerCtx.newHandler(NoSymbol, toTypeKind(finalizer.tpe)) // finalizer covers exception handlers
this.addActiveHandler(exh) // .. and body aswell
val ctx = finalizerCtx.enterHandler(exh)
- val exception = ctx.makeLocal(finalizer.pos, definitions.ThrowableClass.tpe, "exc")
+ val exception = ctx.makeLocal(finalizer.pos, ThrowableClass.tpe, "exc")
if (settings.Xdce.value) ctx.bb.emit(LOAD_EXCEPTION())
ctx.bb.emit(STORE_LOCAL(exception));
val ctx1 = genLoad(finalizer, ctx, UNIT);
@@ -2189,4 +2160,39 @@ abstract class GenICode extends SubComponent {
override def varsInScope: Buffer[Local] = new ListBuffer
}
+ /** Log equality tests to file if they are playing with typefire */
+ def logEqEq(tree: Tree, l: Tree, r: Tree, code: Int) {
+ import definitions._
+ val op = if (code == scalaPrimitives.EQ) "==" else if (code == scalaPrimitives.NE) "!=" else "??"
+
+ def mayBeNumericComparison: Boolean = {
+ def isPossiblyBoxed(sym: Symbol): Boolean = {
+ import definitions._
+ // classes as which a boxed primitive may statically appear
+ val possibleBoxes = List(BoxedNumberClass, BoxedCharacterClass, SerializableClass, ComparableClass)
+
+ (sym == ObjectClass) || (possibleBoxes exists (sym isNonBottomSubClass _))
+ }
+
+ val lsym = l.tpe.typeSymbol
+ val rsym = r.tpe.typeSymbol
+
+ def isSameBox = {
+ def isFinalBox(s: Symbol) = (s isNonBottomSubClass BoxedNumberClass) && s.isFinal
+ isFinalBox(lsym) && isFinalBox(rsym) && lsym == rsym
+ }
+ isPossiblyBoxed(lsym) && isPossiblyBoxed(rsym) && !isSameBox
+ }
+
+ val tkl = toTypeKind(l.tpe)
+ val tkr = toTypeKind(r.tpe)
+ lazy val whereAreWe = tree.pos.source + ":" + tree.pos.line
+ def logit(preface: String) =
+ runtime.Equality.log("[%s] %s %s %s (%s)".format(preface, l.tpe, op, r.tpe, whereAreWe))
+
+ if (tkl.isNumericType && tkr.isNumericType && tkl != tkr)
+ logit(" KNOWN ")
+ else if (mayBeNumericComparison)
+ logit("UNKNOWN")
+ }
}
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index 5c7d8229e3..404ffbafc1 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -127,6 +127,7 @@ trait Definitions {
lazy val TypeConstraintClass = getClass("scala.TypeConstraint")
lazy val SingletonClass = newClass(ScalaPackageClass, nme.Singleton, anyparam) setFlag (ABSTRACT | TRAIT | FINAL)
lazy val SerializableClass = getClass(sn.Serializable)
+ lazy val ComparableClass = getClass("java.lang.Comparable")
lazy val RepeatedParamClass = newCovariantPolyClass(
ScalaPackageClass,
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index 25330bd641..904983a80f 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -811,7 +811,7 @@ abstract class ClassfileParser {
}
def parseAnnotArg: Option[ClassfileAnnotArg] = {
- val tag = in.nextByte
+ val tag = in.nextByte.toChar
val index = in.nextChar
tag match {
case STRING_TAG =>
diff --git a/src/library/scala/runtime/BoxesRunTime.java b/src/library/scala/runtime/BoxesRunTime.java
index 17a1729467..2e4af2d13a 100644
--- a/src/library/scala/runtime/BoxesRunTime.java
+++ b/src/library/scala/runtime/BoxesRunTime.java
@@ -29,43 +29,6 @@ import java.io.*;
* @version 2.0 */
public class BoxesRunTime
{
- /**** Temporary code to support logging all equality comparisons. ****/
- private static boolean eqeqLogging = false;
- private static String eqeqLogName = "/tmp/trunk-eqeq.log";
- private static FileWriter eqeqLog;
- public static void setEqEqLogging(boolean state) {
- eqeqLogging = state;
- if (state) {
- try { eqeqLog = new FileWriter(eqeqLogName, true); }
- catch (IOException e) { eqeqLog = null; }
-
- log("Started eqeq log at " + (new java.util.Date()));
- }
- }
- private static String obToString(Object o) {
- String s = o.toString() + " (" + o.getClass().getSimpleName() + ")";
- return s.replaceAll("\\n", " ");
- }
- private static void logInternal(String msg, Object a, Object b, String where) {
- log(msg + obToString(a) + " == " + obToString(b) + " " + where);
- }
-
- public static String whereAreWe() {
- StackTraceElement e = Thread.currentThread().getStackTrace()[3];
- return"(" + e.getClassName() + "." + e.getMethodName() + e.getFileName() + ":" + e.getLineNumber() + ")";
- }
- public static void log(String msg) {
- if (eqeqLogging && eqeqLog != null) {
- try {
- eqeqLog.write(msg + "\n");
- eqeqLog.flush();
- }
- catch (IOException e) { }
- }
- }
-
- /**** End temporary logging section. ****/
-
private static final int CHAR = 0, BYTE = 1, SHORT = 2, INT = 3, LONG = 4, FLOAT = 5, DOUBLE = 6, OTHER = 7;
private static int typeCode(Object a) {
@@ -92,8 +55,8 @@ public class BoxesRunTime
// foo(-100)
// and the -100 will get to Character, which will duly crash.
// The bug was masked before because the Characters were created
- // with "new Character(c)" and the constructor avenue must have
- // some check against negative values, whereas the static method doesn't.
+ // with "new Character(c)", but now the static method uses the argument
+ // as an index into a cache array, which can't be negative.
//
// It appears to be Short-specific; I can't get anything similar
// out of Byte or Int.
@@ -369,7 +332,7 @@ public class BoxesRunTime
public static boolean equals(Object a, Object b) {
if ((a instanceof Number || a instanceof Character) && (b instanceof Number || b instanceof Character)) {
if (a.getClass() != b.getClass()) {
- logInternal("[ BOXED ] Comparing: ", a, b, whereAreWe());
+ Equality.logInternal("[ BOXED ] Comparing: ", a, b, Equality.whereAreWe());
}
}
@@ -407,7 +370,7 @@ public class BoxesRunTime
String msg;
if (res) msg = "[ BOXED ] Overriding equals between different types: ";
else msg = "[ BOXED ] Overriding equals because b.equals(a): ";
- logInternal(msg, a, b, whereAreWe());
+ Equality.logInternal(msg, a, b, Equality.whereAreWe());
return true;
}
return false;
diff --git a/src/library/scala/runtime/Equality.java b/src/library/scala/runtime/Equality.java
new file mode 100644
index 0000000000..3ec89360b8
--- /dev/null
+++ b/src/library/scala/runtime/Equality.java
@@ -0,0 +1,51 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2006-2009, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+// $Id$
+
+
+package scala.runtime;
+
+import java.io.*;
+
+/** An object (static class) encapsulating the variations on equality
+ * presently under consideration. It's written in java so it can easily
+ * be made available to BoxesRunTime as well as scala code.
+ */
+public class Equality
+{
+ public static boolean disallowNullEquals = true;
+ public static boolean disallowPrimitiveEquals = true;
+ public static boolean warnOnEqualsAny = true;
+ public static boolean callCanEqualIfAvailable = true;
+ public static boolean logEverything = false;
+
+ public static String obToString(Object o) {
+ String s = o.toString() + " (" + o.getClass().getSimpleName() + ")";
+ return s.replaceAll("\\n", " ");
+ }
+
+ public static void logInternal(String msg, Object a, Object b, String where) {
+ log(String.format("%s %s == %s at %s", msg, obToString(a), obToString(b), where));
+ }
+
+ public static String whereAreWe() {
+ StackTraceElement[] es = Thread.currentThread().getStackTrace();
+ if (es.length < 4)
+ return "<unknown>";
+
+ StackTraceElement e = es[3];
+ String clazz = e.getClassName().replaceAll("\\$.*$", "\\$...");
+ return String.format("%s.%s(%s.%d)", clazz, e.getMethodName(), e.getFileName(), e.getLineNumber());
+ }
+
+ public static void log(String msg) {
+ if (logEverything)
+ System.out.println(msg);
+ }
+}
diff --git a/src/library/scala/xml/Utility.scala b/src/library/scala/xml/Utility.scala
index 302f77d07c..f4452fd064 100644
--- a/src/library/scala/xml/Utility.scala
+++ b/src/library/scala/xml/Utility.scala
@@ -87,7 +87,7 @@ object Utility extends AnyRef with parsing.TokenTests
object Escapes {
/** For reasons unclear escape and unescape are a long ways from
being logical inverses. */
- private val pairs = List(
+ val pairs = Map(
"lt" -> '<',
"gt" -> '>',
"amp" -> '&',
@@ -96,10 +96,10 @@ object Utility extends AnyRef with parsing.TokenTests
// is valid xhtml but not html, and IE doesn't know it, says jweb
// "apos" -> '\''
)
- val escMap = Map((for ((s, c) <- pairs) yield (c, "&%s;" format s)) : _*)
- val unescMap = Map(("apos" -> '\'') :: pairs : _*)
+ val escMap = pairs map { case (s, c) => c-> ("&%s;" format s) }
+ val unescMap = pairs ++ Map("apos" -> '\'')
}
- import Escapes._
+ import Escapes.{ escMap, unescMap }
/**
* Appends escaped string to <code>s</code>.
diff --git a/src/library/scala/xml/parsing/MarkupParser.scala b/src/library/scala/xml/parsing/MarkupParser.scala
index fa60329456..a4af954e5a 100644
--- a/src/library/scala/xml/parsing/MarkupParser.scala
+++ b/src/library/scala/xml/parsing/MarkupParser.scala
@@ -14,6 +14,7 @@ package parsing
import scala.io.Source
import scala.xml.dtd._
+import Utility.Escapes.{ pairs => unescape }
/**
* An XML parser.
@@ -515,32 +516,21 @@ trait MarkupParser extends AnyRef with TokenTests { self: MarkupParser with Mar
// postcond: xEmbeddedBlock == false!
case '&' => // EntityRef or CharRef
nextch;
- ch match {
- case '#' => // CharacterRef
- nextch;
- val theChar = handle.text( tmppos,
- xCharRef ({ ()=> ch },{ () => nextch }) );
- xToken(';');
- ts &+ theChar ;
- case _ => // EntityRef
- val n = xName
- xToken(';')
- n match {
- case "lt" => ts &+ '<'
- case "gt" => ts &+ '>'
- case "amp" => ts &+ '&'
- case "quot" => ts &+ '"'
- case _ =>
- /*
- ts + handle.entityRef( tmppos, n ) ;
- */
- push(n)
- }
+ if (ch == '#') { // CharacterRef
+ nextch
+ val theChar = handle.text(tmppos, xCharRef(() => ch, () => nextch))
+ xToken(';');
+ ts &+ theChar
+ }
+ else { // EntityRef
+ val n = xName
+ xToken(';')
+
+ if (unescape contains n) ts &+ unescape(n)
+ else push(n)
}
case _ => // text content
- //Console.println("text content?? pos = "+pos);
appendText(tmppos, ts, xText);
- // here xEmbeddedBlock might be true
}
/*}*/
}