summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2012-08-15 16:25:13 +0200
committerGrzegorz Kossakowski <grzegorz.kossakowski@gmail.com>2012-08-20 08:11:03 +0100
commit1241af326c7b202298b66884776d63d5acda51b4 (patch)
treeb1e41a4089fffe86be4c1506a973ea205e0e7d6b /src
parent3bf87369cfbfe800cd808dc427ebdccfcd896b7a (diff)
downloadscala-1241af326c7b202298b66884776d63d5acda51b4.tar.gz
scala-1241af326c7b202298b66884776d63d5acda51b4.tar.bz2
scala-1241af326c7b202298b66884776d63d5acda51b4.zip
Method splitting for erasure, genJVM, parsers, scanners, uncurry
Also, added inlined List.foreach
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala173
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala159
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala505
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala303
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala71
-rw-r--r--src/library/scala/collection/immutable/List.scala10
6 files changed, 664 insertions, 557 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index cbe9d96e4d..17bea7f796 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -1302,23 +1302,25 @@ self =>
placeholderParams = placeholderParams ::: savedPlaceholderParams
res
}
+
- def expr0(location: Int): Tree = in.token match {
+ def expr0(location: Int): Tree = (in.token: @scala.annotation.switch) match {
case IF =>
- atPos(in.skipToken()) {
+ def parseIf = atPos(in.skipToken()) {
val cond = condExpr()
newLinesOpt()
val thenp = expr()
val elsep = if (in.token == ELSE) { in.nextToken(); expr() }
- else Literal(Constant())
+ else Literal(Constant())
If(cond, thenp, elsep)
}
+ parseIf
case TRY =>
- atPos(in.skipToken()) {
+ def parseTry = atPos(in.skipToken()) {
val body = in.token match {
- case LBRACE => inBracesOrUnit(block())
- case LPAREN => inParensOrUnit(expr())
- case _ => expr()
+ case LBRACE => inBracesOrUnit(block())
+ case LPAREN => inParensOrUnit(expr())
+ case _ => expr()
}
def catchFromExpr() = List(makeCatchFromExpr(expr()))
val catches: List[CaseDef] =
@@ -1332,32 +1334,39 @@ self =>
}
}
val finalizer = in.token match {
- case FINALLY => in.nextToken() ; expr()
- case _ => EmptyTree
+ case FINALLY => in.nextToken(); expr()
+ case _ => EmptyTree
}
Try(body, catches, finalizer)
}
+ parseTry
case WHILE =>
- val start = in.offset
- atPos(in.skipToken()) {
- val lname: Name = freshTermName(nme.WHILE_PREFIX)
- val cond = condExpr()
- newLinesOpt()
- val body = expr()
- makeWhile(lname, cond, body)
+ def parseWhile = {
+ val start = in.offset
+ atPos(in.skipToken()) {
+ val lname: Name = freshTermName(nme.WHILE_PREFIX)
+ val cond = condExpr()
+ newLinesOpt()
+ val body = expr()
+ makeWhile(lname, cond, body)
+ }
}
+ parseWhile
case DO =>
- val start = in.offset
- atPos(in.skipToken()) {
- val lname: Name = freshTermName(nme.DO_WHILE_PREFIX)
- val body = expr()
- if (isStatSep) in.nextToken()
- accept(WHILE)
- val cond = condExpr()
- makeDoWhile(lname, body, cond)
+ def parseDo = {
+ val start = in.offset
+ atPos(in.skipToken()) {
+ val lname: Name = freshTermName(nme.DO_WHILE_PREFIX)
+ val body = expr()
+ if (isStatSep) in.nextToken()
+ accept(WHILE)
+ val cond = condExpr()
+ makeDoWhile(lname, body, cond)
+ }
}
+ parseDo
case FOR =>
- atPos(in.skipToken()) {
+ def parseFor = atPos(in.skipToken()) {
val enums =
if (in.token == LBRACE) inBracesOrNil(enumerators())
else inParensOrNil(enumerators())
@@ -1369,70 +1378,78 @@ self =>
makeFor(enums, expr())
}
}
+ parseFor
case RETURN =>
- atPos(in.skipToken()) {
- Return(if (isExprIntro) expr() else Literal(Constant()))
- }
+ def parseReturn =
+ atPos(in.skipToken()) {
+ Return(if (isExprIntro) expr() else Literal(Constant()))
+ }
+ parseReturn
case THROW =>
- atPos(in.skipToken()) {
- Throw(expr())
- }
+ def parseThrow =
+ atPos(in.skipToken()) {
+ Throw(expr())
+ }
+ parseThrow
case IMPLICIT =>
implicitClosure(in.skipToken(), location)
case _ =>
- var t = postfixExpr()
- if (in.token == EQUALS) {
- t match {
- case Ident(_) | Select(_, _) | Apply(_, _) =>
- t = atPos(t.pos.startOrPoint, in.skipToken()) { makeAssign(t, expr()) }
- case _ =>
- }
- } else if (in.token == COLON) {
- t = stripParens(t)
- val colonPos = in.skipToken()
- if (in.token == USCORE) {
- //todo: need to handle case where USCORE is a wildcard in a type
- val uscorePos = in.skipToken()
- if (isIdent && in.name == nme.STAR) {
- in.nextToken()
- t = atPos(t.pos.startOrPoint, colonPos) {
- Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) })
- }
- } else {
- syntaxErrorOrIncomplete("`*' expected", true)
+ def parseOther = {
+ var t = postfixExpr()
+ if (in.token == EQUALS) {
+ t match {
+ case Ident(_) | Select(_, _) | Apply(_, _) =>
+ t = atPos(t.pos.startOrPoint, in.skipToken()) { makeAssign(t, expr()) }
+ case _ =>
}
- } else if (in.token == AT) {
- t = (t /: annotations(skipNewLines = false)) (makeAnnotated)
- } else {
- t = atPos(t.pos.startOrPoint, colonPos) {
- val tpt = typeOrInfixType(location)
- if (isWildcard(t))
- (placeholderParams: @unchecked) match {
- case (vd @ ValDef(mods, name, _, _)) :: rest =>
- placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest
+ } else if (in.token == COLON) {
+ t = stripParens(t)
+ val colonPos = in.skipToken()
+ if (in.token == USCORE) {
+ //todo: need to handle case where USCORE is a wildcard in a type
+ val uscorePos = in.skipToken()
+ if (isIdent && in.name == nme.STAR) {
+ in.nextToken()
+ t = atPos(t.pos.startOrPoint, colonPos) {
+ Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) })
}
- // this does not correspond to syntax, but is necessary to
- // accept closures. We might restrict closures to be between {...} only.
- Typed(t, tpt)
+ } else {
+ syntaxErrorOrIncomplete("`*' expected", true)
+ }
+ } else if (in.token == AT) {
+ t = (t /: annotations(skipNewLines = false))(makeAnnotated)
+ } else {
+ t = atPos(t.pos.startOrPoint, colonPos) {
+ val tpt = typeOrInfixType(location)
+ if (isWildcard(t))
+ (placeholderParams: @unchecked) match {
+ case (vd @ ValDef(mods, name, _, _)) :: rest =>
+ placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest
+ }
+ // this does not correspond to syntax, but is necessary to
+ // accept closures. We might restrict closures to be between {...} only.
+ Typed(t, tpt)
+ }
}
+ } else if (in.token == MATCH) {
+ t = atPos(t.pos.startOrPoint, in.skipToken())(Match(stripParens(t), inBracesOrNil(caseClauses())))
+ }
+ // in order to allow anonymous functions as statements (as opposed to expressions) inside
+ // templates, we have to disambiguate them from self type declarations - bug #1565
+ // The case still missed is unparenthesized single argument, like "x: Int => x + 1", which
+ // may be impossible to distinguish from a self-type and so remains an error. (See #1564)
+ def lhsIsTypedParamList() = t match {
+ case Parens(xs) if xs forall (_.isInstanceOf[Typed]) => true
+ case _ => false
}
- } else if (in.token == MATCH) {
- t = atPos(t.pos.startOrPoint, in.skipToken())(Match(stripParens(t), inBracesOrNil(caseClauses())))
- }
- // in order to allow anonymous functions as statements (as opposed to expressions) inside
- // templates, we have to disambiguate them from self type declarations - bug #1565
- // The case still missed is unparenthesized single argument, like "x: Int => x + 1", which
- // may be impossible to distinguish from a self-type and so remains an error. (See #1564)
- def lhsIsTypedParamList() = t match {
- case Parens(xs) if xs forall (_.isInstanceOf[Typed]) => true
- case _ => false
- }
- if (in.token == ARROW && (location != InTemplate || lhsIsTypedParamList)) {
- t = atPos(t.pos.startOrPoint, in.skipToken()) {
- Function(convertToParams(t), if (location != InBlock) expr() else block())
+ if (in.token == ARROW && (location != InTemplate || lhsIsTypedParamList)) {
+ t = atPos(t.pos.startOrPoint, in.skipToken()) {
+ Function(convertToParams(t), if (location != InBlock) expr() else block())
+ }
}
+ stripParens(t)
}
- stripParens(t)
+ parseOther
}
/** {{{
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index f99b9a66c9..e6bf43fe93 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -360,16 +360,19 @@ trait Scanners extends ScannersCommon {
if (ch == '"' && token == IDENTIFIER)
token = INTERPOLATIONID
case '<' => // is XMLSTART?
- val last = if (charOffset >= 2) buf(charOffset - 2) else ' '
- nextChar()
- last match {
- case ' '|'\t'|'\n'|'{'|'('|'>' if isNameStart(ch) || ch == '!' || ch == '?' =>
- token = XMLSTART
- case _ =>
- // Console.println("found '<', but last is '"+in.last+"'"); // DEBUG
- putChar('<')
- getOperatorRest()
+ def fetchLT = {
+ val last = if (charOffset >= 2) buf(charOffset - 2) else ' '
+ nextChar()
+ last match {
+ case ' ' | '\t' | '\n' | '{' | '(' | '>' if isNameStart(ch) || ch == '!' || ch == '?' =>
+ token = XMLSTART
+ case _ =>
+ // Console.println("found '<', but last is '"+in.last+"'"); // DEBUG
+ putChar('<')
+ getOperatorRest()
+ }
}
+ fetchLT
case '~' | '!' | '@' | '#' | '%' |
'^' | '*' | '+' | '-' | /*'<' | */
'>' | '?' | ':' | '=' | '&' |
@@ -386,78 +389,87 @@ trait Scanners extends ScannersCommon {
getOperatorRest()
}
case '0' =>
- putChar(ch)
- nextChar()
- if (ch == 'x' || ch == 'X') {
+ def fetchZero = {
+ putChar(ch)
nextChar()
- base = 16
- }
- else {
- /** What should leading 0 be in the future? It is potentially dangerous
- * to let it be base-10 because of history. Should it be an error? Is
- * there a realistic situation where one would need it?
- */
- if (isDigit(ch)) {
- if (opt.future) syntaxError("Non-zero numbers may not have a leading zero.")
- else deprecationWarning("Treating numbers with a leading zero as octal is deprecated.")
+ if (ch == 'x' || ch == 'X') {
+ nextChar()
+ base = 16
+ } else {
+ /**
+ * What should leading 0 be in the future? It is potentially dangerous
+ * to let it be base-10 because of history. Should it be an error? Is
+ * there a realistic situation where one would need it?
+ */
+ if (isDigit(ch)) {
+ if (opt.future) syntaxError("Non-zero numbers may not have a leading zero.")
+ else deprecationWarning("Treating numbers with a leading zero as octal is deprecated.")
+ }
+ base = 8
}
- base = 8
+ getNumber()
}
- getNumber()
+ fetchZero
case '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
base = 10
getNumber()
case '`' =>
getBackquotedIdent()
case '\"' =>
- if (token == INTERPOLATIONID) {
- nextRawChar()
- if (ch == '\"') {
+ def fetchDoubleQuote = {
+ if (token == INTERPOLATIONID) {
nextRawChar()
if (ch == '\"') {
nextRawChar()
- getStringPart(multiLine = true)
- sepRegions = STRINGPART :: sepRegions // indicate string part
- sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part
+ if (ch == '\"') {
+ nextRawChar()
+ getStringPart(multiLine = true)
+ sepRegions = STRINGPART :: sepRegions // indicate string part
+ sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part
+ } else {
+ token = STRINGLIT
+ strVal = ""
+ }
} else {
- token = STRINGLIT
- strVal = ""
+ getStringPart(multiLine = false)
+ sepRegions = STRINGLIT :: sepRegions // indicate single line string part
}
} else {
- getStringPart(multiLine = false)
- sepRegions = STRINGLIT :: sepRegions // indicate single line string part
- }
- } else {
- nextChar()
- if (ch == '\"') {
nextChar()
if (ch == '\"') {
- nextRawChar()
- getRawStringLit()
+ nextChar()
+ if (ch == '\"') {
+ nextRawChar()
+ getRawStringLit()
+ } else {
+ token = STRINGLIT
+ strVal = ""
+ }
} else {
- token = STRINGLIT
- strVal = ""
+ getStringLit()
}
- } else {
- getStringLit()
}
}
+ fetchDoubleQuote
case '\'' =>
- nextChar()
- if (isIdentifierStart(ch))
- charLitOr(getIdentRest)
- else if (isOperatorPart(ch) && (ch != '\\'))
- charLitOr(getOperatorRest)
- else {
- getLitChar()
- if (ch == '\'') {
- nextChar()
- token = CHARLIT
- setStrVal()
- } else {
- syntaxError("unclosed character literal")
+ def fetchSingleQuote = {
+ nextChar()
+ if (isIdentifierStart(ch))
+ charLitOr(getIdentRest)
+ else if (isOperatorPart(ch) && (ch != '\\'))
+ charLitOr(getOperatorRest)
+ else {
+ getLitChar()
+ if (ch == '\'') {
+ nextChar()
+ token = CHARLIT
+ setStrVal()
+ } else {
+ syntaxError("unclosed character literal")
+ }
}
}
+ fetchSingleQuote
case '.' =>
nextChar()
if ('0' <= ch && ch <= '9') {
@@ -488,22 +500,25 @@ trait Scanners extends ScannersCommon {
nextChar()
}
case _ =>
- if (ch == '\u21D2') {
- nextChar(); token = ARROW
- } else if (ch == '\u2190') {
- nextChar(); token = LARROW
- } else if (Character.isUnicodeIdentifierStart(ch)) {
- putChar(ch)
- nextChar()
- getIdentRest()
- } else if (isSpecial(ch)) {
- putChar(ch)
- nextChar()
- getOperatorRest()
- } else {
- syntaxError("illegal character '" + ("" + '\\' + 'u' + "%04x".format(ch: Int)) + "'")
- nextChar()
+ def fetchOther = {
+ if (ch == '\u21D2') {
+ nextChar(); token = ARROW
+ } else if (ch == '\u2190') {
+ nextChar(); token = LARROW
+ } else if (Character.isUnicodeIdentifierStart(ch)) {
+ putChar(ch)
+ nextChar()
+ getIdentRest()
+ } else if (isSpecial(ch)) {
+ putChar(ch)
+ nextChar()
+ getOperatorRest()
+ } else {
+ syntaxError("illegal character '" + ("" + '\\' + 'u' + "%04x".format(ch: Int)) + "'")
+ nextChar()
+ }
}
+ fetchOther
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 6541a1325c..cbd61dcebb 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -2371,8 +2371,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
def genBlock(b: BasicBlock) {
jmethod.visitLabel(labels(b))
- import asm.Opcodes;
-
debuglog("Generating code for block: " + b)
// val lastInstr = b.lastInstruction
@@ -2391,287 +2389,308 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
}
}
- (instr.category: @scala.annotation.switch) match {
-
- case icodes.localsCat => (instr: @unchecked) match {
- case THIS(_) => jmethod.visitVarInsn(Opcodes.ALOAD, 0)
- case LOAD_LOCAL(local) => jcode.load(indexOf(local), local.kind)
- case STORE_LOCAL(local) => jcode.store(indexOf(local), local.kind)
- case STORE_THIS(_) =>
- // this only works for impl classes because the self parameter comes first
- // in the method signature. If that changes, this code has to be revisited.
- jmethod.visitVarInsn(Opcodes.ASTORE, 0)
-
- case SCOPE_ENTER(lv) =>
- // locals removed by closelim (via CopyPropagation) may have left behind SCOPE_ENTER, SCOPE_EXIT that are to be ignored
- val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv))
- if(relevant) { // TODO check: does GenICode emit SCOPE_ENTER, SCOPE_EXIT for synthetic vars?
- // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes)
- // similarly, these labels aren't tracked in the `labels` map.
- val start = new asm.Label
- jmethod.visitLabel(start)
- scoping.pushScope(lv, start)
- }
+ genInstr(instr, b)
- case SCOPE_EXIT(lv) =>
- val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv))
- if(relevant) {
- // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes)
- // similarly, these labels aren't tracked in the `labels` map.
- val end = new asm.Label
- jmethod.visitLabel(end)
- scoping.popScope(lv, end, instr.pos)
- }
- }
+ }
- case icodes.stackCat => (instr: @unchecked) match {
+ }
- case LOAD_MODULE(module) =>
- // assert(module.isModule, "Expected module: " + module)
- debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags));
- if (clasz.symbol == module.moduleClass && jMethodName != nme.readResolve.toString) {
- jmethod.visitVarInsn(Opcodes.ALOAD, 0)
- } else {
- jmethod.visitFieldInsn(
- Opcodes.GETSTATIC,
- javaName(module) /* + "$" */ ,
- strMODULE_INSTANCE_FIELD,
- descriptor(module)
- )
- }
+ def genInstr(instr: Instruction, b: BasicBlock) {
+ import asm.Opcodes
+ (instr.category: @scala.annotation.switch) match {
+
+ case icodes.localsCat =>
+ def genLocalInstr = (instr: @unchecked) match {
+ case THIS(_) => jmethod.visitVarInsn(Opcodes.ALOAD, 0)
+ case LOAD_LOCAL(local) => jcode.load(indexOf(local), local.kind)
+ case STORE_LOCAL(local) => jcode.store(indexOf(local), local.kind)
+ case STORE_THIS(_) =>
+ // this only works for impl classes because the self parameter comes first
+ // in the method signature. If that changes, this code has to be revisited.
+ jmethod.visitVarInsn(Opcodes.ASTORE, 0)
+
+ case SCOPE_ENTER(lv) =>
+ // locals removed by closelim (via CopyPropagation) may have left behind SCOPE_ENTER, SCOPE_EXIT that are to be ignored
+ val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv))
+ if (relevant) { // TODO check: does GenICode emit SCOPE_ENTER, SCOPE_EXIT for synthetic vars?
+ // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes)
+ // similarly, these labels aren't tracked in the `labels` map.
+ val start = new asm.Label
+ jmethod.visitLabel(start)
+ scoping.pushScope(lv, start)
+ }
- case DROP(kind) => emit(if(kind.isWideType) Opcodes.POP2 else Opcodes.POP)
+ case SCOPE_EXIT(lv) =>
+ val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv))
+ if (relevant) {
+ // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes)
+ // similarly, these labels aren't tracked in the `labels` map.
+ val end = new asm.Label
+ jmethod.visitLabel(end)
+ scoping.popScope(lv, end, instr.pos)
+ }
+ }
+ genLocalInstr
+
+ case icodes.stackCat =>
+ def genStackInstr = (instr: @unchecked) match {
+
+ case LOAD_MODULE(module) =>
+ // assert(module.isModule, "Expected module: " + module)
+ debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags));
+ if (clasz.symbol == module.moduleClass && jMethodName != nme.readResolve.toString) {
+ jmethod.visitVarInsn(Opcodes.ALOAD, 0)
+ } else {
+ jmethod.visitFieldInsn(
+ Opcodes.GETSTATIC,
+ javaName(module) /* + "$" */ ,
+ strMODULE_INSTANCE_FIELD,
+ descriptor(module))
+ }
- case DUP(kind) => emit(if(kind.isWideType) Opcodes.DUP2 else Opcodes.DUP)
+ case DROP(kind) => emit(if (kind.isWideType) Opcodes.POP2 else Opcodes.POP)
- case LOAD_EXCEPTION(_) => ()
- }
+ case DUP(kind) => emit(if (kind.isWideType) Opcodes.DUP2 else Opcodes.DUP)
- case icodes.constCat => genConstant(jmethod, instr.asInstanceOf[CONSTANT].constant)
+ case LOAD_EXCEPTION(_) => ()
+ }
+ genStackInstr
- case icodes.arilogCat => genPrimitive(instr.asInstanceOf[CALL_PRIMITIVE].primitive, instr.pos)
+ case icodes.constCat => genConstant(jmethod, instr.asInstanceOf[CONSTANT].constant)
- case icodes.castsCat => (instr: @unchecked) match {
+ case icodes.arilogCat => genPrimitive(instr.asInstanceOf[CALL_PRIMITIVE].primitive, instr.pos)
- case IS_INSTANCE(tpe) =>
- val jtyp: asm.Type =
- tpe match {
- case REFERENCE(cls) => asm.Type.getObjectType(javaName(cls))
- case ARRAY(elem) => javaArrayType(javaType(elem))
- case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe)
- }
- jmethod.visitTypeInsn(Opcodes.INSTANCEOF, jtyp.getInternalName)
+ case icodes.castsCat =>
+ def genCastInstr = (instr: @unchecked) match {
- case CHECK_CAST(tpe) =>
+ case IS_INSTANCE(tpe) =>
+ val jtyp: asm.Type =
tpe match {
-
- case REFERENCE(cls) =>
- if (cls != ObjectClass) { // No need to checkcast for Objects
- jmethod.visitTypeInsn(Opcodes.CHECKCAST, javaName(cls))
- }
-
- case ARRAY(elem) =>
- val iname = javaArrayType(javaType(elem)).getInternalName
- jmethod.visitTypeInsn(Opcodes.CHECKCAST, iname)
-
- case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe)
+ case REFERENCE(cls) => asm.Type.getObjectType(javaName(cls))
+ case ARRAY(elem) => javaArrayType(javaType(elem))
+ case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe)
}
+ jmethod.visitTypeInsn(Opcodes.INSTANCEOF, jtyp.getInternalName)
- }
-
- case icodes.objsCat => (instr: @unchecked) match {
+ case CHECK_CAST(tpe) =>
+ tpe match {
- case BOX(kind) =>
- val MethodNameAndType(mname, mdesc) = jBoxTo(kind)
- jcode.invokestatic(BoxesRunTime, mname, mdesc)
+ case REFERENCE(cls) =>
+ if (cls != ObjectClass) { // No need to checkcast for Objects
+ jmethod.visitTypeInsn(Opcodes.CHECKCAST, javaName(cls))
+ }
- case UNBOX(kind) =>
- val MethodNameAndType(mname, mdesc) = jUnboxTo(kind)
- jcode.invokestatic(BoxesRunTime, mname, mdesc)
+ case ARRAY(elem) =>
+ val iname = javaArrayType(javaType(elem)).getInternalName
+ jmethod.visitTypeInsn(Opcodes.CHECKCAST, iname)
- case NEW(REFERENCE(cls)) =>
- val className = javaName(cls)
- jmethod.visitTypeInsn(Opcodes.NEW, className)
+ case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe)
+ }
- case MONITOR_ENTER() => emit(Opcodes.MONITORENTER)
- case MONITOR_EXIT() => emit(Opcodes.MONITOREXIT)
- }
+ }
+ genCastInstr
- case icodes.fldsCat => (instr: @unchecked) match {
+ case icodes.objsCat =>
+ def genObjsInstr = (instr: @unchecked) match {
- case lf @ LOAD_FIELD(field, isStatic) =>
- var owner = javaName(lf.hostClass)
- debuglog("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags))
- val fieldJName = javaName(field)
- val fieldDescr = descriptor(field)
- val opc = if (isStatic) Opcodes.GETSTATIC else Opcodes.GETFIELD
- jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr)
+ case BOX(kind) =>
+ val MethodNameAndType(mname, mdesc) = jBoxTo(kind)
+ jcode.invokestatic(BoxesRunTime, mname, mdesc)
- case STORE_FIELD(field, isStatic) =>
- val owner = javaName(field.owner)
- val fieldJName = javaName(field)
- val fieldDescr = descriptor(field)
- val opc = if (isStatic) Opcodes.PUTSTATIC else Opcodes.PUTFIELD
- jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr)
+ case UNBOX(kind) =>
+ val MethodNameAndType(mname, mdesc) = jUnboxTo(kind)
+ jcode.invokestatic(BoxesRunTime, mname, mdesc)
- }
+ case NEW(REFERENCE(cls)) =>
+ val className = javaName(cls)
+ jmethod.visitTypeInsn(Opcodes.NEW, className)
- case icodes.mthdsCat => (instr: @unchecked) match {
+ case MONITOR_ENTER() => emit(Opcodes.MONITORENTER)
+ case MONITOR_EXIT() => emit(Opcodes.MONITOREXIT)
+ }
+ genObjsInstr
+
+ case icodes.fldsCat =>
+ def genFldsInstr = (instr: @unchecked) match {
+
+ case lf @ LOAD_FIELD(field, isStatic) =>
+ var owner = javaName(lf.hostClass)
+ debuglog("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags))
+ val fieldJName = javaName(field)
+ val fieldDescr = descriptor(field)
+ val opc = if (isStatic) Opcodes.GETSTATIC else Opcodes.GETFIELD
+ jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr)
+
+ case STORE_FIELD(field, isStatic) =>
+ val owner = javaName(field.owner)
+ val fieldJName = javaName(field)
+ val fieldDescr = descriptor(field)
+ val opc = if (isStatic) Opcodes.PUTSTATIC else Opcodes.PUTFIELD
+ jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr)
- /** Special handling to access native Array.clone() */
- case call @ CALL_METHOD(definitions.Array_clone, Dynamic) =>
- val target: String = javaType(call.targetTypeKind).getInternalName
- jcode.invokevirtual(target, "clone", mdesc_arrayClone)
+ }
+ genFldsInstr
- case call @ CALL_METHOD(method, style) => genCallMethod(call)
+ case icodes.mthdsCat =>
+ def genMethodsInstr = (instr: @unchecked) match {
- }
+ /** Special handling to access native Array.clone() */
+ case call @ CALL_METHOD(definitions.Array_clone, Dynamic) =>
+ val target: String = javaType(call.targetTypeKind).getInternalName
+ jcode.invokevirtual(target, "clone", mdesc_arrayClone)
- case icodes.arraysCat => (instr: @unchecked) match {
- case LOAD_ARRAY_ITEM(kind) => jcode.aload(kind)
- case STORE_ARRAY_ITEM(kind) => jcode.astore(kind)
- case CREATE_ARRAY(elem, 1) => jcode newarray elem
- case CREATE_ARRAY(elem, dims) => jmethod.visitMultiANewArrayInsn(descriptor(ArrayN(elem, dims)), dims)
- }
+ case call @ CALL_METHOD(method, style) => genCallMethod(call)
- case icodes.jumpsCat => (instr: @unchecked) match {
-
- case sw @ SWITCH(tagss, branches) =>
- assert(branches.length == tagss.length + 1, sw)
- val flatSize = sw.flatTagsCount
- val flatKeys = new Array[Int](flatSize)
- val flatBranches = new Array[asm.Label](flatSize)
-
- var restTagss = tagss
- var restBranches = branches
- var k = 0 // ranges over flatKeys and flatBranches
- while(restTagss.nonEmpty) {
- val currLabel = labels(restBranches.head)
- for(cTag <- restTagss.head) {
- flatKeys(k) = cTag;
- flatBranches(k) = currLabel
- k += 1
- }
- restTagss = restTagss.tail
- restBranches = restBranches.tail
+ }
+ genMethodsInstr
+
+ case icodes.arraysCat =>
+ def genArraysInstr = (instr: @unchecked) match {
+ case LOAD_ARRAY_ITEM(kind) => jcode.aload(kind)
+ case STORE_ARRAY_ITEM(kind) => jcode.astore(kind)
+ case CREATE_ARRAY(elem, 1) => jcode newarray elem
+ case CREATE_ARRAY(elem, dims) => jmethod.visitMultiANewArrayInsn(descriptor(ArrayN(elem, dims)), dims)
+ }
+ genArraysInstr
+
+ case icodes.jumpsCat =>
+ def genJumpInstr = (instr: @unchecked) match {
+
+ case sw @ SWITCH(tagss, branches) =>
+ assert(branches.length == tagss.length + 1, sw)
+ val flatSize = sw.flatTagsCount
+ val flatKeys = new Array[Int](flatSize)
+ val flatBranches = new Array[asm.Label](flatSize)
+
+ var restTagss = tagss
+ var restBranches = branches
+ var k = 0 // ranges over flatKeys and flatBranches
+ while (restTagss.nonEmpty) {
+ val currLabel = labels(restBranches.head)
+ for (cTag <- restTagss.head) {
+ flatKeys(k) = cTag;
+ flatBranches(k) = currLabel
+ k += 1
}
- val defaultLabel = labels(restBranches.head)
- assert(restBranches.tail.isEmpty)
- debuglog("Emitting SWITCH:\ntags: " + tagss + "\nbranches: " + branches)
- jcode.emitSWITCH(flatKeys, flatBranches, defaultLabel, MIN_SWITCH_DENSITY)
-
- case JUMP(whereto) =>
- if (nextBlock != whereto) {
- jcode goTo labels(whereto)
- } else if(m.exh.exists(eh => eh.covers(b))) {
- // SI-6102: Determine whether eliding this JUMP results in an empty range being covered by some EH.
- // If so, emit a NOP in place of the elided JUMP, to avoid "java.lang.ClassFormatError: Illegal exception table range"
- val isSthgLeft = b.toList.exists {
- case _: LOAD_EXCEPTION => false
- case _: SCOPE_ENTER => false
- case _: SCOPE_EXIT => false
- case _: JUMP => false
- case _ => true
- }
- if(!isSthgLeft) {
- emit(asm.Opcodes.NOP)
- }
+ restTagss = restTagss.tail
+ restBranches = restBranches.tail
+ }
+ val defaultLabel = labels(restBranches.head)
+ assert(restBranches.tail.isEmpty)
+ debuglog("Emitting SWITCH:\ntags: " + tagss + "\nbranches: " + branches)
+ jcode.emitSWITCH(flatKeys, flatBranches, defaultLabel, MIN_SWITCH_DENSITY)
+
+ case JUMP(whereto) =>
+ if (nextBlock != whereto) {
+ jcode goTo labels(whereto)
+ } else if (m.exh.exists(eh => eh.covers(b))) {
+ // SI-6102: Determine whether eliding this JUMP results in an empty range being covered by some EH.
+ // If so, emit a NOP in place of the elided JUMP, to avoid "java.lang.ClassFormatError: Illegal exception table range"
+ val isSthgLeft = b.toList.exists {
+ case _: LOAD_EXCEPTION => false
+ case _: SCOPE_ENTER => false
+ case _: SCOPE_EXIT => false
+ case _: JUMP => false
+ case _ => true
}
+ if (!isSthgLeft) {
+ emit(asm.Opcodes.NOP)
+ }
+ }
- case CJUMP(success, failure, cond, kind) =>
- if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
- if (nextBlock == success) {
- jcode.emitIF_ICMP(cond.negate, labels(failure))
- // .. and fall through to success label
- } else {
- jcode.emitIF_ICMP(cond, labels(success))
- if (nextBlock != failure) { jcode goTo labels(failure) }
- }
- } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_)
- if (nextBlock == success) {
- jcode.emitIF_ACMP(cond.negate, labels(failure))
- // .. and fall through to success label
- } else {
- jcode.emitIF_ACMP(cond, labels(success))
- if (nextBlock != failure) { jcode goTo labels(failure) }
- }
+ case CJUMP(success, failure, cond, kind) =>
+ if (kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
+ if (nextBlock == success) {
+ jcode.emitIF_ICMP(cond.negate, labels(failure))
+ // .. and fall through to success label
} else {
- (kind: @unchecked) match {
- case LONG => emit(Opcodes.LCMP)
- case FLOAT =>
- if (cond == LT || cond == LE) emit(Opcodes.FCMPG)
- else emit(Opcodes.FCMPL)
- case DOUBLE =>
- if (cond == LT || cond == LE) emit(Opcodes.DCMPG)
- else emit(Opcodes.DCMPL)
- }
- if (nextBlock == success) {
- jcode.emitIF(cond.negate, labels(failure))
- // .. and fall through to success label
- } else {
- jcode.emitIF(cond, labels(success))
- if (nextBlock != failure) { jcode goTo labels(failure) }
- }
+ jcode.emitIF_ICMP(cond, labels(success))
+ if (nextBlock != failure) { jcode goTo labels(failure) }
}
-
- case CZJUMP(success, failure, cond, kind) =>
- if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
- if (nextBlock == success) {
- jcode.emitIF(cond.negate, labels(failure))
- } else {
- jcode.emitIF(cond, labels(success))
- if (nextBlock != failure) { jcode goTo labels(failure) }
- }
- } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_)
- val Success = success
- val Failure = failure
- // @unchecked because references aren't compared with GT, GE, LT, LE.
- ((cond, nextBlock) : @unchecked) match {
- case (EQ, Success) => jcode emitIFNONNULL labels(failure)
- case (NE, Failure) => jcode emitIFNONNULL labels(success)
- case (EQ, Failure) => jcode emitIFNULL labels(success)
- case (NE, Success) => jcode emitIFNULL labels(failure)
- case (EQ, _) =>
- jcode emitIFNULL labels(success)
- jcode goTo labels(failure)
- case (NE, _) =>
- jcode emitIFNONNULL labels(success)
- jcode goTo labels(failure)
- }
+ } else if (kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_)
+ if (nextBlock == success) {
+ jcode.emitIF_ACMP(cond.negate, labels(failure))
+ // .. and fall through to success label
} else {
- (kind: @unchecked) match {
- case LONG =>
- emit(Opcodes.LCONST_0)
- emit(Opcodes.LCMP)
- case FLOAT =>
- emit(Opcodes.FCONST_0)
- if (cond == LT || cond == LE) emit(Opcodes.FCMPG)
- else emit(Opcodes.FCMPL)
- case DOUBLE =>
- emit(Opcodes.DCONST_0)
- if (cond == LT || cond == LE) emit(Opcodes.DCMPG)
- else emit(Opcodes.DCMPL)
- }
- if (nextBlock == success) {
- jcode.emitIF(cond.negate, labels(failure))
- } else {
- jcode.emitIF(cond, labels(success))
- if (nextBlock != failure) { jcode goTo labels(failure) }
- }
+ jcode.emitIF_ACMP(cond, labels(success))
+ if (nextBlock != failure) { jcode goTo labels(failure) }
}
+ } else {
+ (kind: @unchecked) match {
+ case LONG => emit(Opcodes.LCMP)
+ case FLOAT =>
+ if (cond == LT || cond == LE) emit(Opcodes.FCMPG)
+ else emit(Opcodes.FCMPL)
+ case DOUBLE =>
+ if (cond == LT || cond == LE) emit(Opcodes.DCMPG)
+ else emit(Opcodes.DCMPL)
+ }
+ if (nextBlock == success) {
+ jcode.emitIF(cond.negate, labels(failure))
+ // .. and fall through to success label
+ } else {
+ jcode.emitIF(cond, labels(success))
+ if (nextBlock != failure) { jcode goTo labels(failure) }
+ }
+ }
- }
-
- case icodes.retCat => (instr: @unchecked) match {
- case RETURN(kind) => jcode emitRETURN kind
- case THROW(_) => emit(Opcodes.ATHROW)
- }
+ case CZJUMP(success, failure, cond, kind) =>
+ if (kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
+ if (nextBlock == success) {
+ jcode.emitIF(cond.negate, labels(failure))
+ } else {
+ jcode.emitIF(cond, labels(success))
+ if (nextBlock != failure) { jcode goTo labels(failure) }
+ }
+ } else if (kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_)
+ val Success = success
+ val Failure = failure
+ // @unchecked because references aren't compared with GT, GE, LT, LE.
+ ((cond, nextBlock): @unchecked) match {
+ case (EQ, Success) => jcode emitIFNONNULL labels(failure)
+ case (NE, Failure) => jcode emitIFNONNULL labels(success)
+ case (EQ, Failure) => jcode emitIFNULL labels(success)
+ case (NE, Success) => jcode emitIFNULL labels(failure)
+ case (EQ, _) =>
+ jcode emitIFNULL labels(success)
+ jcode goTo labels(failure)
+ case (NE, _) =>
+ jcode emitIFNONNULL labels(success)
+ jcode goTo labels(failure)
+ }
+ } else {
+ (kind: @unchecked) match {
+ case LONG =>
+ emit(Opcodes.LCONST_0)
+ emit(Opcodes.LCMP)
+ case FLOAT =>
+ emit(Opcodes.FCONST_0)
+ if (cond == LT || cond == LE) emit(Opcodes.FCMPG)
+ else emit(Opcodes.FCMPL)
+ case DOUBLE =>
+ emit(Opcodes.DCONST_0)
+ if (cond == LT || cond == LE) emit(Opcodes.DCMPG)
+ else emit(Opcodes.DCMPL)
+ }
+ if (nextBlock == success) {
+ jcode.emitIF(cond.negate, labels(failure))
+ } else {
+ jcode.emitIF(cond, labels(success))
+ if (nextBlock != failure) { jcode goTo labels(failure) }
+ }
+ }
}
+ genJumpInstr
+ case icodes.retCat =>
+ def genRetInstr = (instr: @unchecked) match {
+ case RETURN(kind) => jcode emitRETURN kind
+ case THROW(_) => emit(Opcodes.ATHROW)
+ }
+ genRetInstr
}
-
- } // end of genCode()'s genBlock()
+ }
/**
* Emits one or more conversion instructions based on the types given as arguments.
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index 7b749afa2f..3cb33ec1f8 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -931,152 +931,175 @@ abstract class Erasure extends AddInterfaces
* - Reset all other type attributes to null, thus enforcing a retyping.
*/
private val preTransformer = new TypingTransformer(unit) {
- def preErase(tree: Tree): Tree = tree match {
- case ClassDef(_,_,_,_) =>
- debuglog("defs of " + tree.symbol + " = " + tree.symbol.info.decls)
- copyClassDef(tree)(tparams = Nil)
- case DefDef(_,_,_,_,_,_) =>
- copyDefDef(tree)(tparams = Nil)
- case TypeDef(_, _, _, _) =>
- EmptyTree
- case Apply(instanceOf @ TypeApply(fun @ Select(qual, name), args @ List(arg)), List()) // !!! todo: simplify by having GenericArray also extract trees
- if ((fun.symbol == Any_isInstanceOf || fun.symbol == Object_isInstanceOf) &&
- unboundedGenericArrayLevel(arg.tpe) > 0) =>
- val level = unboundedGenericArrayLevel(arg.tpe)
- def isArrayTest(arg: Tree) =
- gen.mkRuntimeCall(nme.isArray, List(arg, Literal(Constant(level))))
-
- global.typer.typedPos(tree.pos) {
- if (level == 1) isArrayTest(qual)
- else gen.evalOnce(qual, currentOwner, unit) { qual1 =>
- gen.mkAnd(
- gen.mkMethodCall(
- qual1(),
- fun.symbol,
- List(specialErasure(fun.symbol)(arg.tpe)),
- Nil
- ),
- isArrayTest(qual1())
- )
- }
+
+ private def preEraseNormalApply(tree: Apply) = {
+ val fn = tree.fun
+ val args = tree.args
+
+ def qualifier = fn match {
+ case Select(qual, _) => qual
+ case TypeApply(Select(qual, _), _) => qual
+ }
+
+ def preEraseAsInstanceOf = {
+ (fn: @unchecked) match {
+ case TypeApply(Select(qual, _), List(targ)) =>
+ if (qual.tpe <:< targ.tpe)
+ atPos(tree.pos) { Typed(qual, TypeTree(targ.tpe)) }
+ else if (isNumericValueClass(qual.tpe.typeSymbol) && isNumericValueClass(targ.tpe.typeSymbol))
+ atPos(tree.pos)(numericConversion(qual, targ.tpe.typeSymbol))
+ else
+ tree
}
- case TypeApply(fun, args) if (fun.symbol.owner != AnyClass &&
- fun.symbol != Object_asInstanceOf &&
- fun.symbol != Object_isInstanceOf) =>
- // leave all other type tests/type casts, remove all other type applications
- preErase(fun)
- case Apply(fn @ Select(qual, name), args) if fn.symbol.owner == ArrayClass =>
- // Have to also catch calls to abstract types which are bounded by Array.
- if (unboundedGenericArrayLevel(qual.tpe.widen) == 1 || qual.tpe.typeSymbol.isAbstractType) {
- // convert calls to apply/update/length on generic arrays to
- // calls of ScalaRunTime.array_xxx method calls
- global.typer.typedPos(tree.pos)({
- val arrayMethodName = name match {
- case nme.apply => nme.array_apply
- case nme.length => nme.array_length
- case nme.update => nme.array_update
- case nme.clone_ => nme.array_clone
- case _ => unit.error(tree.pos, "Unexpected array member, no translation exists.") ; nme.NO_NAME
+ // todo: also handle the case where the singleton type is buried in a compound
+ }
+
+ def preEraseIsInstanceOf = {
+ fn match {
+ case TypeApply(sel @ Select(qual, name), List(targ)) =>
+ if (qual.tpe != null && isPrimitiveValueClass(qual.tpe.typeSymbol) && targ.tpe != null && targ.tpe <:< AnyRefClass.tpe)
+ unit.error(sel.pos, "isInstanceOf cannot test if value types are references.")
+
+ def mkIsInstanceOf(q: () => Tree)(tp: Type): Tree =
+ Apply(
+ TypeApply(
+ Select(q(), Object_isInstanceOf) setPos sel.pos,
+ List(TypeTree(tp) setPos targ.pos)) setPos fn.pos,
+ List()) setPos tree.pos
+ targ.tpe match {
+ case SingleType(_, _) | ThisType(_) | SuperType(_, _) =>
+ val cmpOp = if (targ.tpe <:< AnyValClass.tpe) Any_equals else Object_eq
+ atPos(tree.pos) {
+ Apply(Select(qual, cmpOp), List(gen.mkAttributedQualifier(targ.tpe)))
+ }
+ case RefinedType(parents, decls) if (parents.length >= 2) =>
+ // Optimization: don't generate isInstanceOf tests if the static type
+ // conforms, because it always succeeds. (Or at least it had better.)
+ // At this writing the pattern matcher generates some instance tests
+ // involving intersections where at least one parent is statically known true.
+ // That needs fixing, but filtering the parents here adds an additional
+ // level of robustness (in addition to the short term fix.)
+ val parentTests = parents filterNot (qual.tpe <:< _)
+
+ if (parentTests.isEmpty) Literal(Constant(true))
+ else gen.evalOnce(qual, currentOwner, unit) { q =>
+ atPos(tree.pos) {
+ parentTests map mkIsInstanceOf(q) reduceRight gen.mkAnd
+ }
+ }
+ case _ =>
+ tree
}
- gen.mkRuntimeCall(arrayMethodName, qual :: args)
- })
+ case _ => tree
}
- else {
- // store exact array erasure in map to be retrieved later when we might
- // need to do the cast in adaptMember
- treeCopy.Apply(
- tree,
- SelectFromArray(qual, name, erasure(tree.symbol)(qual.tpe)).copyAttrs(fn),
- args)
- }
- case Apply(fn @ Select(qual, _), Nil) if interceptedMethods(fn.symbol) =>
- if (fn.symbol == Any_## || fn.symbol == Object_##) {
- // This is unattractive, but without it we crash here on ().## because after
- // erasure the ScalaRunTime.hash overload goes from Unit => Int to BoxedUnit => Int.
- // This must be because some earlier transformation is being skipped on ##, but so
- // far I don't know what. For null we now define null.## == 0.
- qual.tpe.typeSymbol match {
- case UnitClass | NullClass => LIT(0)
- case IntClass => qual
- case s @ (ShortClass | ByteClass | CharClass) => numericConversion(qual, s)
- case BooleanClass => If(qual, LIT(true.##), LIT(false.##))
- case _ =>
- global.typer.typed(gen.mkRuntimeCall(nme.hash_, List(qual)))
- }
- }
- // Rewrite 5.getClass to ScalaRunTime.anyValClass(5)
- else if (isPrimitiveValueClass(qual.tpe.typeSymbol))
- global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen))))
- else
- tree
+ }
+ if (fn.symbol == Any_asInstanceOf) {
+ preEraseAsInstanceOf
+ } else if (fn.symbol == Any_isInstanceOf) {
+ preEraseIsInstanceOf
+ } else if (fn.symbol.owner.isRefinementClass && !fn.symbol.isOverridingSymbol) {
+ ApplyDynamic(qualifier, args) setSymbol fn.symbol setPos tree.pos
+ } else if (fn.symbol.isMethodWithExtension) {
+ Apply(gen.mkAttributedRef(extensionMethods.extensionMethod(fn.symbol)), qualifier :: args)
+ } else {
+ tree
+ }
+ }
- case Apply(Select(New(tpt), nme.CONSTRUCTOR), List(arg)) if (tpt.tpe.typeSymbol.isDerivedValueClass) =>
-// println("inject derived: "+arg+" "+tpt.tpe)
- InjectDerivedValue(arg) addAttachment //@@@ setSymbol tpt.tpe.typeSymbol
- new TypeRefAttachment(tree.tpe.asInstanceOf[TypeRef])
- case Apply(fn, args) =>
- def qualifier = fn match {
- case Select(qual, _) => qual
- case TypeApply(Select(qual, _), _) => qual
- }
- if (fn.symbol == Any_asInstanceOf)
- (fn: @unchecked) match {
- case TypeApply(Select(qual, _), List(targ)) =>
- if (qual.tpe <:< targ.tpe)
- atPos(tree.pos) { Typed(qual, TypeTree(targ.tpe)) }
- else if (isNumericValueClass(qual.tpe.typeSymbol) && isNumericValueClass(targ.tpe.typeSymbol))
- atPos(tree.pos)(numericConversion(qual, targ.tpe.typeSymbol))
- else
- tree
+ private def preEraseApply(tree: Apply) = {
+ tree.fun match {
+ case TypeApply(fun @ Select(qual, name), args @ List(arg))
+ if ((fun.symbol == Any_isInstanceOf || fun.symbol == Object_isInstanceOf) &&
+ unboundedGenericArrayLevel(arg.tpe) > 0) => // !!! todo: simplify by having GenericArray also extract trees
+ val level = unboundedGenericArrayLevel(arg.tpe)
+ def isArrayTest(arg: Tree) =
+ gen.mkRuntimeCall(nme.isArray, List(arg, Literal(Constant(level))))
+
+ global.typer.typedPos(tree.pos) {
+ if (level == 1) isArrayTest(qual)
+ else gen.evalOnce(qual, currentOwner, unit) { qual1 =>
+ gen.mkAnd(
+ gen.mkMethodCall(
+ qual1(),
+ fun.symbol,
+ List(specialErasure(fun.symbol)(arg.tpe)),
+ Nil
+ ),
+ isArrayTest(qual1())
+ )
+ }
}
- // todo: also handle the case where the singleton type is buried in a compound
- else if (fn.symbol == Any_isInstanceOf) {
- fn match {
- case TypeApply(sel @ Select(qual, name), List(targ)) =>
- if (qual.tpe != null && isPrimitiveValueClass(qual.tpe.typeSymbol) && targ.tpe != null && targ.tpe <:< AnyRefClass.tpe)
- unit.error(sel.pos, "isInstanceOf cannot test if value types are references.")
-
- def mkIsInstanceOf(q: () => Tree)(tp: Type): Tree =
- Apply(
- TypeApply(
- Select(q(), Object_isInstanceOf) setPos sel.pos,
- List(TypeTree(tp) setPos targ.pos)) setPos fn.pos,
- List()) setPos tree.pos
- targ.tpe match {
- case SingleType(_, _) | ThisType(_) | SuperType(_, _) =>
- val cmpOp = if (targ.tpe <:< AnyValClass.tpe) Any_equals else Object_eq
- atPos(tree.pos) {
- Apply(Select(qual, cmpOp), List(gen.mkAttributedQualifier(targ.tpe)))
- }
- case RefinedType(parents, decls) if (parents.length >= 2) =>
- // Optimization: don't generate isInstanceOf tests if the static type
- // conforms, because it always succeeds. (Or at least it had better.)
- // At this writing the pattern matcher generates some instance tests
- // involving intersections where at least one parent is statically known true.
- // That needs fixing, but filtering the parents here adds an additional
- // level of robustness (in addition to the short term fix.)
- val parentTests = parents filterNot (qual.tpe <:< _)
-
- if (parentTests.isEmpty) Literal(Constant(true))
- else gen.evalOnce(qual, currentOwner, unit) { q =>
- atPos(tree.pos) {
- parentTests map mkIsInstanceOf(q) reduceRight gen.mkAnd
- }
- }
- case _ =>
- tree
+ case fn @ Select(qual, name) =>
+ val args = tree.args
+ if (fn.symbol.owner == ArrayClass) {
+ // Have to also catch calls to abstract types which are bounded by Array.
+ if (unboundedGenericArrayLevel(qual.tpe.widen) == 1 || qual.tpe.typeSymbol.isAbstractType) {
+ // convert calls to apply/update/length on generic arrays to
+ // calls of ScalaRunTime.array_xxx method calls
+ global.typer.typedPos(tree.pos) {
+ val arrayMethodName = name match {
+ case nme.apply => nme.array_apply
+ case nme.length => nme.array_length
+ case nme.update => nme.array_update
+ case nme.clone_ => nme.array_clone
+ case _ => unit.error(tree.pos, "Unexpected array member, no translation exists.") ; nme.NO_NAME
+ }
+ gen.mkRuntimeCall(arrayMethodName, qual :: args)
}
- case _ => tree
- }
- } else if (fn.symbol.owner.isRefinementClass && !fn.symbol.isOverridingSymbol) {
- ApplyDynamic(qualifier, args) setSymbol fn.symbol setPos tree.pos
- } else if (fn.symbol.isMethodWithExtension) {
- Apply(gen.mkAttributedRef(extensionMethods.extensionMethod(fn.symbol)), qualifier :: args)
- } else {
+ } else {
+ // store exact array erasure in map to be retrieved later when we might
+ // need to do the cast in adaptMember
+ treeCopy.Apply(
+ tree,
+ SelectFromArray(qual, name, erasure(tree.symbol)(qual.tpe)).copyAttrs(fn),
+ args)
+ }
+ } else if (args.isEmpty && interceptedMethods(fn.symbol)) {
+ if (fn.symbol == Any_## || fn.symbol == Object_##) {
+ // This is unattractive, but without it we crash here on ().## because after
+ // erasure the ScalaRunTime.hash overload goes from Unit => Int to BoxedUnit => Int.
+ // This must be because some earlier transformation is being skipped on ##, but so
+ // far I don't know what. For null we now define null.## == 0.
+ qual.tpe.typeSymbol match {
+ case UnitClass | NullClass => LIT(0)
+ case IntClass => qual
+ case s @ (ShortClass | ByteClass | CharClass) => numericConversion(qual, s)
+ case BooleanClass => If(qual, LIT(true.##), LIT(false.##))
+ case _ =>
+ global.typer.typed(gen.mkRuntimeCall(nme.hash_, List(qual)))
+ }
+ } else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) {
+ // Rewrite 5.getClass to ScalaRunTime.anyValClass(5)
+ global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen))))
+ } else {
tree
+ }
+ } else qual match {
+ case New(tpt) if name == nme.CONSTRUCTOR && tpt.tpe.typeSymbol.isDerivedValueClass =>
+ // println("inject derived: "+arg+" "+tpt.tpe)
+ val List(arg) = args
+ InjectDerivedValue(arg) addAttachment //@@@ setSymbol tpt.tpe.typeSymbol
+ new TypeRefAttachment(tree.tpe.asInstanceOf[TypeRef])
+ case _ =>
+ preEraseNormalApply(tree)
}
+ case _ =>
+ preEraseNormalApply(tree)
+ }
+ }
+
+ def preErase(tree: Tree): Tree = tree match {
+ case tree: Apply =>
+ preEraseApply(tree)
+
+ case TypeApply(fun, args) if (fun.symbol.owner != AnyClass &&
+ fun.symbol != Object_asInstanceOf &&
+ fun.symbol != Object_isInstanceOf) =>
+ // leave all other type tests/type casts, remove all other type applications
+ preErase(fun)
+
case Select(qual, name) =>
val owner = tree.symbol.owner
// println("preXform: "+ (tree, tree.symbol, tree.symbol.owner, tree.symbol.owner.isRefinementClass))
@@ -1123,6 +1146,14 @@ abstract class Erasure extends AddInterfaces
}
treeCopy.Literal(tree, Constant(erased))
+ case ClassDef(_,_,_,_) =>
+ debuglog("defs of " + tree.symbol + " = " + tree.symbol.info.decls)
+ copyClassDef(tree)(tparams = Nil)
+ case DefDef(_,_,_,_,_,_) =>
+ copyDefDef(tree)(tparams = Nil)
+ case TypeDef(_, _, _, _) =>
+ EmptyTree
+
case _ =>
tree
}
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index aae9625e2d..181463657b 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -693,6 +693,46 @@ abstract class UnCurry extends InfoTransform
else
tree
}
+
+ def isThrowable(pat: Tree): Boolean = pat match {
+ case Typed(Ident(nme.WILDCARD), tpt) =>
+ tpt.tpe =:= ThrowableClass.tpe
+ case Bind(_, pat) =>
+ isThrowable(pat)
+ case _ =>
+ false
+ }
+
+ def isDefaultCatch(cdef: CaseDef) = isThrowable(cdef.pat) && cdef.guard.isEmpty
+
+ def postTransformTry(tree: Try) = {
+ val body = tree.block
+ val catches = tree.catches
+ val finalizer = tree.finalizer
+ if (opt.virtPatmat) {
+ if (catches exists (cd => !treeInfo.isCatchCase(cd)))
+ debugwarn("VPM BUG! illegal try/catch " + catches)
+ tree
+ } else if (catches forall treeInfo.isCatchCase) {
+ tree
+ } else {
+ val exname = unit.freshTermName("ex$")
+ val cases =
+ if ((catches exists treeInfo.isDefaultCase) || isDefaultCatch(catches.last)) catches
+ else catches :+ CaseDef(Ident(nme.WILDCARD), EmptyTree, Throw(Ident(exname)))
+ val catchall =
+ atPos(tree.pos) {
+ CaseDef(
+ Bind(exname, Ident(nme.WILDCARD)),
+ EmptyTree,
+ Match(Ident(exname), cases))
+ }
+ debuglog("rewrote try: " + catches + " ==> " + catchall);
+ val catches1 = localTyper.typedCases(
+ List(catchall), ThrowableClass.tpe, WildcardType)
+ treeCopy.Try(tree, body, catches1, finalizer)
+ }
+ }
tree match {
/* Some uncurry post transformations add members to templates.
@@ -724,35 +764,12 @@ abstract class UnCurry extends InfoTransform
)
addJavaVarargsForwarders(dd, flatdd)
- case Try(body, catches, finalizer) =>
- if (opt.virtPatmat) { if(catches exists (cd => !treeInfo.isCatchCase(cd))) debugwarn("VPM BUG! illegal try/catch "+ catches); tree }
- else if (catches forall treeInfo.isCatchCase) tree
- else {
- val exname = unit.freshTermName("ex$")
- val cases =
- if ((catches exists treeInfo.isDefaultCase) || (catches.last match { // bq: handle try { } catch { ... case ex:Throwable => ...}
- case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) =>
- true
- case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) =>
- true
- case _ =>
- false
- })) catches
- else catches :+ CaseDef(Ident(nme.WILDCARD), EmptyTree, Throw(Ident(exname)))
- val catchall =
- atPos(tree.pos) {
- CaseDef(
- Bind(exname, Ident(nme.WILDCARD)),
- EmptyTree,
- Match(Ident(exname), cases))
- }
- debuglog("rewrote try: " + catches + " ==> " + catchall);
- val catches1 = localTyper.typedCases(
- List(catchall), ThrowableClass.tpe, WildcardType)
- treeCopy.Try(tree, body, catches1, finalizer)
- }
+ case tree: Try =>
+ postTransformTry(tree)
+
case Apply(Apply(fn, args), args1) =>
treeCopy.Apply(tree, fn, args ::: args1)
+
case Ident(name) =>
assert(name != tpnme.WILDCARD_STAR, tree)
applyUnary()
diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala
index 74dc385f99..87b58005cf 100644
--- a/src/library/scala/collection/immutable/List.scala
+++ b/src/library/scala/collection/immutable/List.scala
@@ -302,6 +302,15 @@ sealed abstract class List[+A] extends AbstractSeq[A]
if (isEmpty) Stream.Empty
else new Stream.Cons(head, tail.toStream)
+ @inline override final
+ def foreach[B](f: A => B) {
+ var these = this
+ while (!these.isEmpty) {
+ f(these.head)
+ these = these.tail
+ }
+ }
+
@deprecated("use `distinct` instead", "2.8.0")
def removeDuplicates: List[A] = distinct
}
@@ -378,7 +387,6 @@ final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extend
while (!xs.isEmpty) { out.writeObject(xs.head); xs = xs.tail }
out.writeObject(ListSerializeEnd)
}
-
}
/** $factoryInfo