summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIulian Dragos <jaguarul@gmail.com>2005-10-06 13:43:32 +0000
committerIulian Dragos <jaguarul@gmail.com>2005-10-06 13:43:32 +0000
commit8c7d8bd610fbe29cb6ff57de7694648d502ea7f4 (patch)
treee8a231f69273ea128dcb7e7e3ffbcf700da216dd
parent25a98964b5109aa55b71a8a26886c59903193548 (diff)
downloadscala-8c7d8bd610fbe29cb6ff57de7694648d502ea7f4.tar.gz
scala-8c7d8bd610fbe29cb6ff57de7694648d502ea7f4.tar.bz2
scala-8c7d8bd610fbe29cb6ff57de7694648d502ea7f4.zip
Experimental module support.
-rwxr-xr-xsources/scala/tools/nsc/Global.scala16
-rw-r--r--sources/scala/tools/nsc/ast/TreeBrowsers.scala5
-rw-r--r--sources/scala/tools/nsc/backend/icode/BasicBlocks.scala2
-rw-r--r--sources/scala/tools/nsc/backend/icode/Checkers.scala30
-rw-r--r--sources/scala/tools/nsc/backend/icode/GenICode.scala81
-rw-r--r--sources/scala/tools/nsc/backend/icode/Members.scala58
-rw-r--r--sources/scala/tools/nsc/backend/icode/Opcodes.scala12
-rw-r--r--sources/scala/tools/nsc/backend/icode/Printers.scala18
-rw-r--r--sources/scala/tools/nsc/backend/jvm/GenJVM.scala227
9 files changed, 354 insertions, 95 deletions
diff --git a/sources/scala/tools/nsc/Global.scala b/sources/scala/tools/nsc/Global.scala
index e82af18eb9..3e7b8b7fde 100755
--- a/sources/scala/tools/nsc/Global.scala
+++ b/sources/scala/tools/nsc/Global.scala
@@ -456,7 +456,19 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
}
private def writeICode(): Unit = {
- val printer = new icodePrinter.TextPrinter(new PrintWriter(System.out, true));
- icodes.classes.foreach(printer.printClass);
+ val printer = new icodePrinter.TextPrinter(null);
+ icodes.classes.foreach((cls) => {
+ val file = getFile(cls.symbol, ".icode");
+ try {
+ val stream = new FileOutputStream(file);
+ printer.setWriter(new PrintWriter(stream, true));
+ printer.printClass(cls);
+ informProgress("wrote " + file);
+ } catch {
+ case ex: IOException =>
+ if (settings.debug.value) ex.printStackTrace();
+ error("could not write file " + file);
+ }
+ });
}
}
diff --git a/sources/scala/tools/nsc/ast/TreeBrowsers.scala b/sources/scala/tools/nsc/ast/TreeBrowsers.scala
index 1695bc7ac1..2c9aaaaa37 100644
--- a/sources/scala/tools/nsc/ast/TreeBrowsers.scala
+++ b/sources/scala/tools/nsc/ast/TreeBrowsers.scala
@@ -526,7 +526,10 @@ abstract class TreeBrowsers {
var att = "";
if (s != null) {
- flagsToString(s.flags);
+ var str = flagsToString(s.flags);
+ if (s.hasFlag(STATIC) || s.hasFlag(STATICMEMBER))
+ str = str + " isStatic ";
+ str
}
else "";
}
diff --git a/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala
index 277b3de748..edf2d5021b 100644
--- a/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala
+++ b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala
@@ -130,6 +130,8 @@ trait BasicBlocks: ICodes {
/** Close the block */
def close = {
+ assert(instructionList.length > 0,
+ "Empty block.");
closed = true;
instrs = toInstructionArray(instructionList.reverse);
}
diff --git a/sources/scala/tools/nsc/backend/icode/Checkers.scala b/sources/scala/tools/nsc/backend/icode/Checkers.scala
index 527cf64919..2a1c802b23 100644
--- a/sources/scala/tools/nsc/backend/icode/Checkers.scala
+++ b/sources/scala/tools/nsc/backend/icode/Checkers.scala
@@ -54,6 +54,7 @@ abstract class Checkers {
val emptyStack = new TypeStack();
val STRING = REFERENCE(definitions.StringClass);
+ val SCALA_ALL = REFERENCE(definitions.AllClass);
def checkICodes: Unit = classes foreach check;
@@ -66,7 +67,8 @@ abstract class Checkers {
def check(m: IMethod): Unit = {
log("Checking method " + m);
method = m;
- check(m.code);
+ if (!m.isDeferred)
+ check(m.code);
}
def check(c: Code): Unit = {
@@ -136,8 +138,8 @@ abstract class Checkers {
else
();
- def checkLocal(local: Symbol) =
- method.lookupLocal(local.name) match {
+ def checkLocal(local: Local) =
+ method.lookupLocal(local.sym.name) match {
case None => error(" " + local + " is not defined in method " + method);
case _ => ()
}
@@ -228,7 +230,7 @@ abstract class Checkers {
case LOAD_LOCAL(local, isArg) =>
checkLocal(local);
- stack.push(toTypeKind(local.info));
+ stack.push(local.kind);
case LOAD_FIELD(field, isStatic) =>
if (isStatic) {
@@ -261,10 +263,10 @@ abstract class Checkers {
case STORE_LOCAL(local, isArg) =>
checkLocal(local);
checkStack(1);
- val localType = toTypeKind(local.info);
+
val actualType = stack.pop;
- if (!(actualType <:< localType))
- typeError(localType, actualType);
+ if (!(actualType <:< local.kind))
+ typeError(local.kind, actualType);
case STORE_FIELD(field, isStatic) =>
if (isStatic) {
@@ -376,12 +378,13 @@ abstract class Checkers {
}
- case NEW(ctor) =>
- checkBool(ctor.isConstructor,
- "'new' call to non-constructor method");
- checkMethodArgs(ctor);
- checkMethod(REFERENCE(ctor.owner), ctor);
- stack.push(REFERENCE(ctor.owner));
+ case NEW(kind) =>
+ kind match {
+ case REFERENCE(cls) =>
+ stack.push(kind);
+
+ case _ => error("NEW call to non-reference type: " + kind);
+ }
case CREATE_ARRAY(elem) =>
checkStack(1);
@@ -435,6 +438,7 @@ abstract class Checkers {
val thrown = stack.pop;
checkBool(thrown.toType <:< definitions.ThrowableClass.tpe,
"Element on top of stack should implement 'Throwable': " + thrown);
+ stack.push(SCALA_ALL);
case DROP(kind) =>
checkType(stack.pop, kind);
diff --git a/sources/scala/tools/nsc/backend/icode/GenICode.scala b/sources/scala/tools/nsc/backend/icode/GenICode.scala
index c9f0566a8d..68ac59d21b 100644
--- a/sources/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/sources/scala/tools/nsc/backend/icode/GenICode.scala
@@ -102,10 +102,20 @@ abstract class GenICode extends SubComponent {
val resTpe = if (tree.symbol.isConstructor) UNIT
else toTypeKind(ctx1.method.symbol.info.resultType);
- ctx1 = genLoad(rhs, ctx1, resTpe);
+ if (!m.isDeferred) {
+ ctx1 = genLoad(rhs, ctx1, resTpe);
- ctx1.bb.emit(RETURN(resTpe));
- ctx1.bb.close;
+ // reverse the order of the local variables, to match the source-order
+ m.locals = m.locals.reverse;
+
+ rhs match {
+ case Block(_, Return(_)) => ();
+ case Return(_) => ();
+ case _ => ctx1.bb.emit(RETURN(resTpe));
+ }
+ ctx1.bb.close;
+ } else
+ ctx1.method.setCode(null);
ctx1;
case Template(parents, body) =>
@@ -170,7 +180,8 @@ abstract class GenICode extends SubComponent {
// assert(ctx.method.locals.contains(lhs.symbol) | ctx.clazz.fields.contains(lhs.symbol),
// "Assignment to inexistent local or field: " + lhs.symbol);
val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info));
- ctx1.bb.emit(STORE_LOCAL(lhs.symbol, lhs.symbol.isValueParameter));
+ val Some(l) = ctx.method.lookupLocal(lhs.symbol);
+ ctx1.bb.emit(STORE_LOCAL(l, lhs.symbol.isValueParameter));
ctx1
case _ =>
@@ -337,7 +348,7 @@ abstract class GenICode extends SubComponent {
// genLoad
val resCtx: Context = tree match {
case LabelDef(name, params, rhs) =>
- ctx.method.addLocals(params map (.symbol));
+ ctx.method.addLocals(params map (p => new Local(p.symbol, toTypeKind(p.symbol.info))));
val ctx1 = ctx.newBlock;
ctx1.labels.get(tree.symbol) match {
case Some(label) => label.anchor(ctx1.bb);
@@ -354,10 +365,11 @@ abstract class GenICode extends SubComponent {
if (rhs == EmptyTree)
log("Uninitialized variable " + tree + " at: " + unit.position(tree.pos));
val sym = tree.symbol;
- ctx.method.addLocal(sym);
- val ctx1 = genLoad(rhs, ctx, toTypeKind(sym.info));
+ val local = new Local(sym, toTypeKind(sym.info));
+ ctx.method.addLocal(local);
+ val ctx1 = genLoad(rhs, ctx, local.kind);
if (rhs != EmptyTree)
- ctx1.bb.emit(STORE_LOCAL(sym, false));
+ ctx1.bb.emit(STORE_LOCAL(local, false));
generatedType = UNIT;
ctx1
@@ -434,17 +446,27 @@ abstract class GenICode extends SubComponent {
// 'new' constructor call
case Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) =>
val ctor = fun.symbol;
- assert(fun != null && ctor.isClassConstructor,
+ assert(ctor.isClassConstructor,
"'new' call to non-constructor: " + tree);
- var ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx);
+
generatedType = toTypeKind(tpt.tpe);
+ assert(generatedType.isReferenceType || generatedType.isArrayType, "Non reference type cannot be instantiated: " + generatedType);
+
+ var ctx1 = ctx;
+
generatedType match {
case ARRAY(elem) =>
+ ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx);
ctx1.bb.emit(CREATE_ARRAY(elem));
+
case REFERENCE(cls) =>
- ctx1.bb.emit(NEW(ctor));
assert(ctor.owner == cls,
"Symbol " + ctor.owner.fullNameString + "is different than " + tpt);
+ ctx.bb.emit(NEW(generatedType));
+ ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx);
+
+ ctx1.bb.emit(CALL_METHOD(ctor, Static(true)));
+
case _ =>
abort("Cannot instantiate " + tpt + "of kind: " + generatedType);
}
@@ -539,10 +561,11 @@ abstract class GenICode extends SubComponent {
}
case This(qual) =>
- assert(tree.symbol == ctx.clazz.symbol | tree.symbol.isModuleClass,
+ assert(tree.symbol == ctx.clazz.symbol || tree.symbol.isModuleClass,
"Trying to access the this of another class: " +
"tree.symbol = " + tree.symbol + ", ctx.clazz.symbol = " + ctx.clazz.symbol);
if (tree.symbol.isModuleClass && tree.symbol != ctx.clazz.symbol) {
+ log("LOAD_MODULE from 'This'");
ctx.bb.emit(LOAD_MODULE(tree.symbol));
generatedType = REFERENCE(tree.symbol);
} else {
@@ -556,6 +579,7 @@ abstract class GenICode extends SubComponent {
"Selection of non-module from empty package: " + tree.toString() +
" sym: " + tree.symbol +
" at: " + unit.position(tree.pos));
+ log("LOAD_MODULE from Select(<emptypackage>)");
ctx.bb.emit(LOAD_MODULE(tree.symbol));
ctx
@@ -564,6 +588,7 @@ abstract class GenICode extends SubComponent {
val generatedType = toTypeKind(sym.info);
if (sym.isModule) {
+ log("LOAD_MODULE from Select(qualifier, selector)");
ctx.bb.emit(LOAD_MODULE(sym));
ctx
} else if (isStaticSymbol(sym)) {
@@ -577,11 +602,15 @@ abstract class GenICode extends SubComponent {
case Ident(name) =>
if (!tree.symbol.isPackage) {
- if (tree.symbol.isModule)
+ if (tree.symbol.isModule) {
+ log("LOAD_MODULE from Ident(name)");
ctx.bb.emit(LOAD_MODULE(tree.symbol));
- else
- ctx.bb.emit(LOAD_LOCAL(tree.symbol, tree.symbol.isValueParameter));
- generatedType = toTypeKind(tree.symbol.info);
+ generatedType = toTypeKind(tree.symbol.info);
+ } else {
+ val Some(l) = ctx.method.lookupLocal(tree.symbol);
+ ctx.bb.emit(LOAD_LOCAL(l, tree.symbol.isValueParameter));
+ generatedType = l.kind;
+ }
}
ctx
@@ -699,8 +728,9 @@ abstract class GenICode extends SubComponent {
var param = label.params;
while (arg != Nil) {
- ctx1 = genLoad(arg.head, ctx1, toTypeKind(param.head.info));
- ctx1.bb.emit(STORE_LOCAL(param.head, param.head.isValueParameter));
+ val Some(l) = ctx.method.lookupLocal(param.head);
+ ctx1 = genLoad(arg.head, ctx1, l.kind);
+ ctx1.bb.emit(STORE_LOCAL(l, param.head.isValueParameter));
arg = arg.tail;
param = param.tail;
}
@@ -899,29 +929,32 @@ abstract class GenICode extends SubComponent {
*/
def genEqEqPrimitive(l: Tree, r: Tree, ctx: Context, thenCtx: Context, elseCtx: Context): Unit = {
var eqEqTempVar: Symbol = null;
+ var eqEqTempLocal: Local = null;
+
ctx.method.lookupLocal(eqEqTemp) match {
- case Some(sym) => eqEqTempVar = sym;
+ case Some(local) => eqEqTempVar = local.sym;
case None =>
eqEqTempVar = ctx.method.symbol.newVariable(l.pos, eqEqTemp);
eqEqTempVar.setInfo(definitions.AnyRefClass.typeConstructor);
- ctx.method.addLocal(eqEqTempVar);
+ eqEqTempLocal = new Local(eqEqTempVar, REFERENCE(definitions.AnyRefClass));
+ ctx.method.addLocal(eqEqTempLocal);
}
var ctx1 = genLoad(l, ctx, ANY_REF_CLASS);
ctx1 = genLoad(r, ctx1, ANY_REF_CLASS);
val tmpNullCtx = ctx1.newBlock;
val tmpNonNullCtx = ctx1.newBlock;
- ctx1.bb.emit(STORE_LOCAL(eqEqTempVar, false));
+ ctx1.bb.emit(STORE_LOCAL(eqEqTempLocal, false));
ctx1.bb.emit(DUP(ANY_REF_CLASS));
ctx1.bb.emit(CZJUMP(tmpNullCtx.bb, tmpNonNullCtx.bb, EQ, ANY_REF_CLASS));
ctx1.bb.close;
tmpNullCtx.bb.emit(DROP(ANY_REF_CLASS)); // type of AnyRef
- tmpNullCtx.bb.emit(LOAD_LOCAL(eqEqTempVar, false));
+ tmpNullCtx.bb.emit(LOAD_LOCAL(eqEqTempLocal, false));
tmpNullCtx.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS));
tmpNullCtx.bb.close;
- tmpNonNullCtx.bb.emit(LOAD_LOCAL(eqEqTempVar, false));
+ tmpNonNullCtx.bb.emit(LOAD_LOCAL(eqEqTempLocal, false));
tmpNonNullCtx.bb.emit(CALL_METHOD(definitions.Object_equals, Dynamic));
tmpNonNullCtx.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL));
tmpNonNullCtx.bb.close;
@@ -950,7 +983,7 @@ abstract class GenICode extends SubComponent {
case vparams :: Nil =>
for (val p <- vparams)
- ctx.method.addParam(p.symbol);
+ ctx.method.addParam(new Local(p.symbol, toTypeKind(p.symbol.info)));
ctx.method.params = ctx.method.params.reverse;
case _ =>
diff --git a/sources/scala/tools/nsc/backend/icode/Members.scala b/sources/scala/tools/nsc/backend/icode/Members.scala
index 3fe21b1856..3d8798d0f9 100644
--- a/sources/scala/tools/nsc/backend/icode/Members.scala
+++ b/sources/scala/tools/nsc/backend/icode/Members.scala
@@ -11,6 +11,8 @@ import scala.collection.mutable.HashMap;
import scala.collection.mutable.HashSet;
import scala.{Symbol => scala_Symbol};
+import scala.tools.nsc.symtab.Flags;
+
trait Members: ICodes {
import global._;
@@ -141,45 +143,73 @@ trait Members: ICodes {
class IField(val symbol: Symbol) {
}
- /** Represent a method in ICode */
+ /**
+ * Represents a method in ICode. Local variables contain
+ * both locals and parameters, similar to the way the JVM
+ * 'sees' them.
+ *
+ * Locals and parameters are added in reverse order, as they
+ * are kept in cons-lists. The 'builder' is responsible for
+ * reversing them and putting them back, when the generation is
+ * finished (GenICode does that).
+ */
class IMethod(val symbol: Symbol) {
var code: Code = null;
var exh: List[ExceptionHandler] = _;
/** local variables and method parameters */
- var locals: List[Symbol] = Nil;
+ var locals: List[Local] = Nil;
/** method parameters */
- var params: List[Symbol] = Nil;
+ var params: List[Local] = Nil;
def setCode(code: Code): IMethod = {
this.code = code;
this
}
- def addLocal(sym: Symbol): Unit =
- if (!(locals contains sym))
- locals = sym :: locals;
+ def addLocal(l: Local): Unit =
+ if (!(locals contains l))
+ locals = l :: locals;
- def addLocals(ls: List[Symbol]): Unit =
+ def addLocals(ls: List[Local]): Unit =
ls foreach addLocal;
- def addParam(sym: Symbol): Unit =
- if (!(params contains sym)) {
- params = sym :: params;
- locals = sym :: locals;
+ def addParam(p: Local): Unit =
+ if (!(params contains p)) {
+ params = p :: params;
+ locals = p :: locals;
}
- def addParams(as: List[Symbol]): Unit =
+ def addParams(as: List[Local]): Unit =
as foreach addParam;
- def lookupLocal(n: Name): Option[Symbol] =
- locals find ((sym) => sym.name == n);
+ def lookupLocal(n: Name): Option[Local] =
+ locals find ((l) => l.sym.name == n);
+
+ def lookupLocal(sym: Symbol): Option[Local] =
+ locals find ((l) => l.sym == sym);
def addHandler(e: ExceptionHandler): Unit =
exh = e :: exh;
+ /** Is this method deferred ('abstract' in Java sense) */
+ def isDeferred =
+ symbol.hasFlag(Flags.DEFERRED) ||
+ symbol.owner.hasFlag(Flags.INTERFACE);
override def toString() = symbol.fullNameString;
}
+
+ /** Represent local variables and parameters */
+ class Local(val sym: Symbol, val kind: TypeKind) {
+ var index: Int = -1;
+
+ override def equals(other: Any): Boolean =
+ other.isInstanceOf[Local] &&
+ other.asInstanceOf[Local].sym == this.sym;
+
+ override def toString(): String = sym.toString();
+ }
+
}
diff --git a/sources/scala/tools/nsc/backend/icode/Opcodes.scala b/sources/scala/tools/nsc/backend/icode/Opcodes.scala
index 2c5e3e73a6..f0fbf05959 100644
--- a/sources/scala/tools/nsc/backend/icode/Opcodes.scala
+++ b/sources/scala/tools/nsc/backend/icode/Opcodes.scala
@@ -24,7 +24,7 @@ import scala.tools.nsc.ast._;
case STORE_FIELD(field, isStatic) =>
case CALL_PRIMITIVE(primitive) =>
case CALL_METHOD(method, style) =>
- case NEW(clasz) =>
+ case NEW(kind) =>
case CREATE_ARRAY(elem) =>
case IS_INSTANCE(tpe) =>
case CHECK_CAST(tpe) =>
@@ -108,7 +108,7 @@ import scala.tools.nsc.ast._;
* Stack: ...
* ->: ...:value
*/
- case class LOAD_LOCAL(local: Symbol, isArgument: boolean) extends Instruction {
+ case class LOAD_LOCAL(local: Local, isArgument: boolean) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String = "LOAD_LOCAL "+local.toString(); //+isArgument?" (argument)":"";
@@ -157,7 +157,7 @@ import scala.tools.nsc.ast._;
* Stack: ...:value
* ->: ...
*/
- case class STORE_LOCAL(local: Symbol, isArgument: boolean) extends Instruction {
+ case class STORE_LOCAL(local: Local, isArgument: boolean) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String = "STORE_LOCAL "+local.toString(); //+isArgument?" (argument)":"";
@@ -232,9 +232,9 @@ import scala.tools.nsc.ast._;
* Stack: ...:arg1:arg2:...:argn
* ->: ...:ref
*/
- case class NEW(ctor: Symbol) extends Instruction {
+ case class NEW(kind: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
- override def toString(): String = "NEW "+ctor.fullNameString;
+ override def toString(): String = "NEW "+ kind;
override def consumed = 0;
override def produced = 1;
@@ -334,7 +334,7 @@ import scala.tools.nsc.ast._;
cond: TestOp,
kind: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
- override def toString(): String ="CZJUMP )" + kind + ")" +
+ override def toString(): String ="CZJUMP (" + kind + ")" +
cond.toString()+" ? "+successBlock.label+" : "+failureBlock.label;
override def consumed = 1;
diff --git a/sources/scala/tools/nsc/backend/icode/Printers.scala b/sources/scala/tools/nsc/backend/icode/Printers.scala
index 717af7ef73..22362756c6 100644
--- a/sources/scala/tools/nsc/backend/icode/Printers.scala
+++ b/sources/scala/tools/nsc/backend/icode/Printers.scala
@@ -9,6 +9,8 @@ package scala.tools.nsc.backend.icode;
import java.io.PrintWriter;
+import scala.tools.nsc.symtab.Flags;
+
abstract class Printers {
val global: Global;
import global._;
@@ -78,13 +80,19 @@ abstract class Printers {
def printMethod(m: IMethod): Unit = {
print("def "); print(m.symbol.name);
print("("); printList(printParam)(m.params.reverse, ", "); print(")");
- print(": "); print(m.symbol.info.resultType); println(" {");
- printCode(m.code);
- println("}");
+ print(": "); print(m.symbol.info.resultType);
+
+ if (!m.isDeferred) {
+ println(" {");
+ printCode(m.code);
+ println("}");
+ } else
+ println;
}
- def printParam(p: Symbol): Unit = {
- print(p.name); print(": "); print(p.info);
+ def printParam(p: Local): Unit = {
+ print(p.sym.name); print(": "); print(p.sym.info);
+ print(" ("); print(p.kind); print(")");
}
def printCode(code: Code): Unit = {
diff --git a/sources/scala/tools/nsc/backend/jvm/GenJVM.scala b/sources/scala/tools/nsc/backend/jvm/GenJVM.scala
index 1f3ce47e89..ffb0b7a2a8 100644
--- a/sources/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/sources/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -7,6 +7,8 @@
package scala.tools.nsc.backend.jvm;
+import java.io.File;
+
import scala.collection.mutable.{Map, HashMap};
import scala.tools.nsc.symtab._;
@@ -24,7 +26,8 @@ abstract class BytecodeGenerators extends SubComponent {
/** Create a new phase */
override def newPhase(p: Phase) = new JvmPhase(p);
- /** JVM code generation phase */
+ /** JVM code generation phase
+ */
class JvmPhase(prev: Phase) extends GlobalPhase(prev) {
def name = phaseName;
override def newFlags = phaseNewFlags;
@@ -39,10 +42,15 @@ abstract class BytecodeGenerators extends SubComponent {
}
/**
- * Java bytecode generator
+ * Java bytecode generator.
+ *
+ * TODO: take care of different size in local variables (LOAD/STORE_LOCAL)
*/
class GenJVM {
val MIN_SWITCH_DENSITY = 0.7;
+ val MODULE_INSTANCE_NAME = "MODULE$";
+ val JAVA_LANG_STRINGBUFFER = "java.lang.StringBuffer";
+ val stringBufferType = new JObjectType(JAVA_LANG_STRINGBUFFER);
var clasz: IClass = _;
var method: IMethod = _;
@@ -53,11 +61,13 @@ abstract class BytecodeGenerators extends SubComponent {
val fjbgContext = new FJBGContext();
+
def genClass(c: IClass): Unit = {
- log("Generating class " + c);
+ log("Generating class " + c.symbol + " flags: " + Flags.flagsToString(c.symbol.flags));
clasz = c;
var parents = c.symbol.info.parents;
var ifaces = JClass.NO_INTERFACES;
+ val name = javaName(c.symbol); // + (if (c.symbol.isModuleClass) "$" else "");
if (parents.isEmpty)
parents = definitions.ObjectClass.tpe :: parents;
@@ -69,7 +79,7 @@ abstract class BytecodeGenerators extends SubComponent {
}
jclass = fjbgContext.JClass(javaFlags(c.symbol),
- javaName(c.symbol),
+ name,
javaName(parents(0).symbol),
ifaces,
c.cunit.source.toString());
@@ -77,7 +87,19 @@ abstract class BytecodeGenerators extends SubComponent {
clasz.fields foreach genField;
clasz.methods foreach genMethod;
- jclass.writeTo(getFile(c.symbol, ".class"));
+ if (isTopLevelModule(c.symbol)) {
+ addModuleInstanceField;
+ dumpMirrorClass;
+ }
+
+ jclass.writeTo(getFile(jclass, ".class"));
+ }
+
+ def isTopLevelModule(sym: Symbol): Boolean = {
+ log("Symbol: " + sym + " isNestedClass? " + atPhase(currentRun.erasurePhase)(sym.isNestedClass) +
+ " hasFlag LIFTED? " + sym.hasFlag(Flags.LIFTED));
+
+ sym.isModuleClass && !sym.isImplClass && !sym.hasFlag(Flags.LIFTED) /* && !atPhase(currentRun.erasurePhase)(sym.isNestedClass) */
}
def genField(f: IField): Unit = {
@@ -88,8 +110,10 @@ abstract class BytecodeGenerators extends SubComponent {
}
def genMethod(m: IMethod): Unit = {
- log("Adding method " + m.symbol.fullNameString + " ctor: " + m.symbol.isClassConstructor);
+ log("Adding method " + m.symbol + " flags: " + Flags.flagsToString(m.symbol.flags) +
+ " owner: " + m.symbol.owner);
method = m;
+ computeLocalVarsIndex(m);
var resTpe = javaType(toTypeKind(m.symbol.tpe.resultType));
if (m.symbol.isClassConstructor)
@@ -98,15 +122,83 @@ abstract class BytecodeGenerators extends SubComponent {
jmethod = jclass.addNewMethod(javaFlags(m.symbol),
javaName(m.symbol),
resTpe,
- javaTypes(m.params map (p => toTypeKind(p.tpe))),
- javaNames(m.params));
+ javaTypes(m.params map (.kind)),
+ javaNames(m.params map (.sym)));
if (!jmethod.isAbstract()) {
+ for (val local <- m.locals; !local.sym.isValueParameter)
+ jmethod.addNewLocalVariable(javaType(local.kind), javaName(local.sym));
+
jcode = jmethod.getCode().asInstanceOf[JExtendedCode];
genCode(m.code);
}
}
+ def addModuleInstanceField: Unit = {
+ import JAccessFlags._;
+ jclass.addNewField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC,
+ MODULE_INSTANCE_NAME,
+ jclass.getType());
+ }
+
+ def addStaticInit(cls: JClass): Unit = {
+ import JAccessFlags._;
+ val clinitMethod = cls.addNewMethod(ACC_PUBLIC | ACC_STATIC,
+ "<clinit>",
+ JType.VOID,
+ JType.EMPTY_ARRAY,
+ new Array[String](0));
+ val clinit = clinitMethod.getCode();
+ clinit.emitNEW(cls.getName());
+ clinit.emitDUP();
+ clinit.emitINVOKESPECIAL(cls.getName(),
+ JMethod.INSTANCE_CONSTRUCTOR_NAME,
+ JMethodType.ARGLESS_VOID_FUNCTION);
+ clinit.emitPUTSTATIC(cls.getName(),
+ MODULE_INSTANCE_NAME,
+ jclass.getType());
+ clinit.emitRETURN();
+ }
+
+ def dumpMirrorClass: Unit = {
+ import JAccessFlags._;
+ assert(clasz.symbol.isModuleClass);
+
+ log("Dumping mirror class for object: " + clasz);
+ val moduleName = javaName(clasz.symbol); // + "$";
+ val mirrorName = moduleName.substring(0, moduleName.length() - 1);
+ val mirrorClass = fjbgContext.JClass(ACC_SUPER | ACC_PUBLIC | ACC_FINAL,
+ mirrorName,
+ "java.lang.Object",
+ JClass.NO_INTERFACES,
+ clasz.cunit.source.toString());
+ for (val m <- clasz.methods; !(m.symbol.hasFlag(Flags.PRIVATE)) && !m.symbol.isClassConstructor && !isStaticSymbol(m.symbol) ) {
+ val mirrorMethod = mirrorClass.addNewMethod(ACC_PUBLIC | ACC_FINAL | ACC_STATIC,
+ javaName(m.symbol),
+ javaType(toTypeKind(m.symbol.tpe.resultType)),
+ javaTypes(m.params map (.kind)),
+ javaNames(m.params map (.sym)));
+ val mirrorCode = mirrorMethod.getCode().asInstanceOf[JExtendedCode];
+ mirrorCode.emitGETSTATIC(moduleName,
+ MODULE_INSTANCE_NAME,
+ new JObjectType(moduleName));
+ var i = 0;
+ var index = 0;
+ var argTypes = mirrorMethod.getArgumentTypes();
+ while (i < argTypes.length) {
+ mirrorCode.emitLOAD(index, argTypes(i));
+ index = index + argTypes(i).getSize();
+ i = i + 1;
+ }
+
+ mirrorCode.emitINVOKEVIRTUAL(moduleName, mirrorMethod.getName(), mirrorMethod.getType().asInstanceOf[JMethodType]);
+ mirrorCode.emitRETURN(mirrorMethod.getReturnType());
+ }
+
+ addStaticInit(jclass); // should be the current module class
+ mirrorClass.writeTo(getFile(mirrorClass, ".class"));
+ }
+
val linearizer = new NormalLinearizer();
@@ -145,39 +237,49 @@ abstract class BytecodeGenerators extends SubComponent {
case LOAD_LOCAL(local, isArg) =>
if (isArg)
- jcode.emitLOAD(1 + method.params.indexOf(local), javaType(local));
+ jcode.emitLOAD(indexOf(local), javaType(local.kind));
else
- jcode.emitLOAD(1 + method.locals.indexOf(local), javaType(local));
+ jcode.emitLOAD(indexOf(local), javaType(local.kind));
case LOAD_FIELD(field, isStatic) =>
+ var owner = javaName(field.owner);
+// if (field.owner.hasFlag(Flags.MODULE)) owner = owner + "$";
+
+ log("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags));
if (isStatic)
- jcode.emitGETSTATIC(javaName(field.owner),
+ jcode.emitGETSTATIC(owner,
javaName(field),
javaType(field));
else
- jcode.emitGETFIELD(javaName(field.owner),
+ jcode.emitGETFIELD(owner,
javaName(field),
javaType(field));
case LOAD_MODULE(module) =>
- ();
+ assert(module.isModule);
+ log("genearting LOAD_MODULE for: " + module + " flags: " +
+ Flags.flagsToString(module.flags));
+ jcode.emitGETSTATIC(javaName(module) /* + "$" */ ,
+ MODULE_INSTANCE_NAME,
+ javaType(module));
case STORE_ARRAY_ITEM(kind) =>
jcode.emitASTORE(javaType(kind));
case STORE_LOCAL(local, isArg) =>
if (isArg)
- jcode.emitSTORE(1 + method.params.indexOf(local), javaType(local));
+ jcode.emitSTORE(indexOf(local), javaType(local.kind));
else
- jcode.emitSTORE(1 + method.locals.indexOf(local), javaType(local));
+ jcode.emitSTORE(indexOf(local), javaType(local.kind));
case STORE_FIELD(field, isStatic) =>
+ val owner = javaName(field.owner); // + (if (field.owner.hasFlag(Flags.MODULE)) "$" else "");
if (isStatic)
- jcode.emitPUTSTATIC(javaName(field.owner),
+ jcode.emitPUTSTATIC(owner,
javaName(field),
javaType(field));
else
- jcode.emitPUTFIELD(javaName(field.owner),
+ jcode.emitPUTFIELD(owner,
javaName(field),
javaType(field));
@@ -187,40 +289,42 @@ abstract class BytecodeGenerators extends SubComponent {
// TODO: reference the type of the receiver instead of the
// method owner.
case CALL_METHOD(method, style) =>
+ val owner = javaName(method.owner); // + (if (method.owner.isModuleClass) "$" else "");
+
style match {
case Dynamic =>
if (method.owner.hasFlag(Flags.INTERFACE))
- jcode.emitINVOKEINTERFACE(javaName(method.owner),
+ jcode.emitINVOKEINTERFACE(owner,
javaName(method),
javaType(method).asInstanceOf[JMethodType])
else
- jcode.emitINVOKEVIRTUAL(javaName(method.owner),
+ jcode.emitINVOKEVIRTUAL(owner,
javaName(method),
javaType(method).asInstanceOf[JMethodType]);
case Static(instance) =>
if (instance) {
- jcode.emitINVOKESPECIAL(javaName(method.owner),
+ jcode.emitINVOKESPECIAL(owner,
javaName(method),
javaType(method).asInstanceOf[JMethodType]);
} else
- jcode.emitINVOKESTATIC(javaName(method.owner),
+ jcode.emitINVOKESTATIC(owner,
javaName(method),
javaType(method).asInstanceOf[JMethodType]);
case SuperCall(_) =>
- jcode.emitINVOKESPECIAL(javaName(method.owner),
+ jcode.emitINVOKESPECIAL(owner,
javaName(method),
javaType(method).asInstanceOf[JMethodType]);
}
- case NEW(ctor) =>
- val className = javaName(ctor.owner);
+ case NEW(REFERENCE(cls)) =>
+ val className = javaName(cls);
jcode.emitNEW(className);
jcode.emitDUP();
- jcode.emitINVOKESPECIAL(className,
- JMethod.INSTANCE_CONSTRUCTOR_NAME,
- javaType(ctor).asInstanceOf[JMethodType]);
+// jcode.emitINVOKESPECIAL(className,
+// JMethod.INSTANCE_CONSTRUCTOR_NAME,
+// javaType(ctor).asInstanceOf[JMethodType]);
case CREATE_ARRAY(elem) =>
jcode.emitNEWARRAY(javaType(elem));
@@ -396,6 +500,18 @@ abstract class BytecodeGenerators extends SubComponent {
case Conversion(src, dst) =>
jcode.emitT2T(javaType(src), javaType(dst));
+ case StringConcat(lf, rg) =>
+// jcode.emitNEW(JAVA_LANG_STRINGBUFFER);
+// jcode.emitDUP();
+// jcode.emitINVOKESPECIAL(JAVA_LANG_STRINGBUFFER,
+// JMethod.INSTANCE_CONSTRUCTOR_NAME,
+// JMethodType.ARGLESS_VOID_FUNCTION);
+// jcode.emitINVOKEVIRTUAL(JAVA_LANG_STRINGBUFFER,
+// "append",
+// new JMethodType(stringBufferType,
+// javaType(lf)));
+
+
case _ => log("Unimplemented primitive " + primitive);
}
}
@@ -415,13 +531,58 @@ abstract class BytecodeGenerators extends SubComponent {
bs foreach (bb => labels += bb -> jcode.newLabel() );
}
+
+ ////////////////////// local vars ///////////////////////
+
+ def sizeOf(sym: Symbol): Int = sizeOf(toTypeKind(sym.tpe));
+
+
+ def sizeOf(k: TypeKind): Int = k match {
+ case DOUBLE | LONG => 2;
+ case _ => 1;
+ }
+
+ def indexOf(m: IMethod, sym: Symbol): Int = {
+ val Some(local) = m.lookupLocal(sym);
+ assert (local.index >= 0,
+ "Invalid index for: " + local);
+ local.index
+ }
+
+ def indexOf(local: Local): Int = {
+ assert (local.index >= 0,
+ "Invalid index for: " + local);
+ local.index
+ }
+
+ /**
+ * Compute the indexes of each local variable of the given
+ * method.
+ */
+ def computeLocalVarsIndex(m: IMethod): Unit = {
+ var idx = 1;
+ if (isStaticSymbol(m.symbol))
+ idx = 0;
+
+ for (val l <- m.locals) {
+ log("Index value for " + l + ": " + idx);
+ l.index = idx;
+ idx = idx + sizeOf(l.kind);
+ }
+ }
+
////////////////////// Utilities ////////////////////////
- def javaName(sym: Symbol) =
- if (sym.isClass)
+ def javaName(sym: Symbol) = {
+ val suffix = if (sym.hasFlag(Flags.MODULE) && !sym.isMethod &&
+ !sym.isImplClass &&
+ !sym.hasFlag(Flags.JAVA)) "$" else "";
+
+ (if (sym.isClass || (sym.isModule && !sym.isMethod))
sym.fullNameString('/')
else
- sym.simpleName.toString();
+ sym.simpleName.toString()) + suffix;
+ }
def javaNames(syms: List[Symbol]): Array[String] = {
val res = new Array[String](syms.length);
@@ -497,6 +658,12 @@ abstract class BytecodeGenerators extends SubComponent {
// syms foreach ( s => { res(i) = javaType(toTypeKind(s.tpe)); i = i + 1; } );
// res
// }
+
+ def getFile(cls: JClass, suffix: String): String = {
+ val path = cls.getName().replace('.', File.separatorChar);
+ settings.outdir.value + File.separatorChar + path + suffix
+ }
+
}
}