/* NSC -- new scala compiler
* Copyright 2005 LAMP/EPFL
* @author Martin Odersky
*/
// $Id$
package scala.tools.nsc.backend.icode;
import scala.tools.nsc.ast._;
/*
A pattern match
case THIS(clasz) =>
case CONSTANT(const) =>
case LOAD_ARRAY_ITEM(kind) =>
case LOAD_LOCAL(local, isArg) =>
case LOAD_FIELD(field, isStatic) =>
case LOAD_MODULE(module) =>
case STORE_ARRAY_ITEM(kind) =>
case STORE_LOCAL(local, isArg) =>
case STORE_FIELD(field, isStatic) =>
case CALL_PRIMITIVE(primitive) =>
case CALL_METHOD(method, style) =>
case NEW(clasz) =>
case CREATE_ARRAY(elem) =>
case IS_INSTANCE(tpe) =>
case CHECK_CAST(tpe) =>
case SWITCH(tags, labels) =>
case JUMP(where) =>
case CJUMP(success, failure, cond) =>
case CZJUMP(success, failure, cond, kind) =>
case RETURN() =>
case THROW() =>
case DROP(kind) =>
case DUP(kind) =>
case MONITOR_ENTER() =>
case MONITOR_EXIT() =>
*/
/**
* The ICode intermediate representation. It is a stack-based
* representation, very close to the JVM and .NET. It uses the
* erased types of Scala and references Symbols to refer named entities
* in the source files.
*/
abstract class Opcodes: ICodes {
import global.{Symbol, NoSymbol, Type, Name, Constant};
/** This class represents an instruction of the intermediate code.
* Each case subclass will represent a specific operation.
*/
abstract class Instruction {
/** This abstract method returns the number of used elements on the stack */
def consumed : Int = 0;
/** This abstract method returns the number of produced elements on the stack */
def produced : Int = 0;
/** This method returns the difference of size of the stack when the instruction is used */
def difference = produced-consumed;
}
object opcodes {
/** Loads the "this" references on top of the stack.
* Stack: ...
* ->: ...:ref
*/
case class THIS(clasz: Symbol) extends Instruction {
/** Returns a string representation of this constant */
override def toString(): String = "THIS";
override def consumed = 0;
override def produced = 1;
}
/** Loads a constant on the stack.
* Stack: ...
* ->: ...:constant
*/
case class CONSTANT(constant: Constant) extends Instruction{
/** Returns a string representation of this constant */
override def toString(): String = "CONSTANT ("+constant.toString()+")";
override def consumed = 0;
override def produced = 1;
}
/** Loads an element of an array. The array and the index should
* be on top of the stack.
* Stack: ...:array[a](Ref):index(Int)
* ->: ...:element(a)
*/
case class LOAD_ARRAY_ITEM(kind: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String = "LOAD_ARRAY_ITEM (" + kind + ")";
override def consumed = 2;
override def produced = 1;
}
/** Load a local variable on the stack. It can be a method argument.
* Stack: ...
* ->: ...:value
*/
case class LOAD_LOCAL(local: Symbol, isArgument: boolean) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String = "LOAD_LOCAL "+local.toString(); //+isArgument?" (argument)":"";
override def consumed = 0;
override def produced = 1;
}
/** Load a field on the stack. The object to which it refers should be
* on the stack.
* Stack: ...:ref
* ->: ...:value
*/
case class LOAD_FIELD(field: Symbol, isStatic: boolean) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String =
"LOAD_FIELD " + (if (isStatic) field.fullNameString else field.toString());
override def consumed = 1;
override def produced = 1;
}
case class LOAD_MODULE(module: Symbol) extends Instruction {
assert(module != NoSymbol,
"Invalid module symbol");
/** Returns a string representation of this instruction */
override def toString(): String =
"LOAD_MODULE " + module.toString();
override def consumed = 0;
override def produced = 1;
}
/** Store a value into an array at a specified index.
* Stack: ...:array[a](Ref):index(Int):value(a)
* ->: ...
*/
case class STORE_ARRAY_ITEM(kind: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String = "STORE_ARRAY_ITEM (" + kind + ")";
override def consumed = 3;
override def produced = 0;
}
/** Store a value into a local variable. It can be an argument.
* Stack: ...:value
* ->: ...
*/
case class STORE_LOCAL(local: Symbol, isArgument: boolean) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String = "STORE_LOCAL "+local.toString(); //+isArgument?" (argument)":"";
override def consumed = 1;
override def produced = 0;
}
/** Store a value into a field.
* Stack: ...:ref:value
* ->: ...
*/
case class STORE_FIELD(field: Symbol, isStatic: boolean) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String = "STORE_FIELD "+field.toString(); //+isStatic?" (static)":"";
override def consumed = 2;
override def produced = 0;
}
/** Call a primitive function.
* Stack: ...:arg1:arg2:...:argn
* ->: ...:result
*/
case class CALL_PRIMITIVE(primitive: Primitive) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="CALL_PRIMITIVE "+primitive.toString();
override def consumed = primitive match {
case (Negation(_)) => 1;
case (Test(_,_,true)) => 1;
case (Test(_,_,false)) => 2;
case (Comparison(_,_)) => 2;
case (Arithmetic(_,_)) => 2;
case (Logical(_,_)) => 2;
case (Shift(_,_)) => 2;
case (Conversion(_,_)) => 1;
case (ArrayLength(_)) => 1;
case (StringConcat(_,_)) => 2;
}
override def produced = 1;
}
/** This class represents a CALL_METHOD instruction
* STYLE: dynamic / static(StaticInstance)
* Stack: ...:ref:arg1:arg2:...:argn
* ->: ...:result
*
* STYLE: static(StaticClass)
* Stack: ...:arg1:arg2:...:argn
* ->: ...:result
*
*/
case class CALL_METHOD(method: Symbol, style: InvokeStyle) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String =
"CALL_METHOD " + method.fullNameString +" ("+style.toString()+")";
override def consumed = {
var result = method.tpe.paramTypes.length;
result = result + (style match {
case Dynamic => 1
case Static(true) => 1
case _ => 0
});
result;
}
override def produced = 1;
}
/** Create a new instance of a class through the specified constructor
* Stack: ...:arg1:arg2:...:argn
* ->: ...:ref
*/
case class NEW(ctor: Symbol) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String = "NEW "+ctor.fullNameString;
override def consumed = 0;
override def produced = 1;
}
/** This class represents a CREATE_ARRAY instruction
* Stack: ...:size(int)
* ->: ...:arrayref
*/
case class CREATE_ARRAY(element: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="CREATE_ARRAY "+element.toString();
override def consumed = 1;
override def produced = 1;
}
/** This class represents a IS_INSTANCE instruction
* Stack: ...:ref
* ->: ...:result(boolean)
*/
case class IS_INSTANCE(typ: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="IS_INSTANCE "+typ.toString();
override def consumed = 1;
override def produced = 1;
}
/** This class represents a CHECK_CAST instruction
* Stack: ...:ref(oldtype)
* ->: ...:ref(typ <=: oldtype)
*/
case class CHECK_CAST(typ: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="CHECK_CAST "+typ.toString();
override def consumed = 1;
override def produced = 1;
}
/** This class represents a SWITCH instruction
* Stack: ...:index(int)
* ->: ...:
*
* The tags array contains one entry per label, each entry consisting of
* an array of ints, any of which will trigger the jump to the corresponding label.
* labels should contain an extra label, which is the 'default' jump.
*/
case class SWITCH(tags: List[List[Int]], labels: List[BasicBlock]) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="SWITCH ...";
override def consumed = 1;
override def produced = 0;
}
/** This class represents a JUMP instruction
* Stack: ...
* ->: ...
*/
case class JUMP(where: BasicBlock) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="JUMP "+where.label;
override def consumed = 0;
override def produced = 0;
}
/** This class represents a CJUMP instruction
* It compares the two values on the stack with the 'cond' test operator
* Stack: ...:value1:value2
* ->: ...
*/
case class CJUMP(successBlock: BasicBlock,
failureBlock: BasicBlock,
cond: TestOp,
kind: TypeKind) extends Instruction
{
/** Returns a string representation of this instruction */
override def toString(): String ="CJUMP (" + kind + ")" +
cond.toString()+" ? "+successBlock.label+" : "+failureBlock.label;
override def consumed = 2;
override def produced = 0;
}
/** This class represents a CZJUMP instruction
* It compares the one value on the stack and zero with the 'cond' test operator
* Stack: ...:value:
* ->: ...
*/
case class CZJUMP(successBlock: BasicBlock,
failureBlock: BasicBlock,
cond: TestOp,
kind: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="CZJUMP )" + kind + ")" +
cond.toString()+" ? "+successBlock.label+" : "+failureBlock.label;
override def consumed = 1;
override def produced = 0;
}
/** This class represents a RETURN instruction
* Stack: ...
* ->: ...
*/
case class RETURN() extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="RETURN";
override def consumed = 0;
override def produced = 0;
}
/** This class represents a THROW instruction
* Stack: ...:Throwable(Ref)
* ->: ...:
*/
case class THROW() extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="THROW";
override def consumed = 1;
override def produced = 0;
}
/** This class represents a DROP instruction
* Stack: ...:something
* ->: ...
*/
case class DROP (typ: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="DROP "+typ.toString();
override def consumed = 1;
override def produced = 0;
}
/** This class represents a DUP instruction
* Stack: ...:something
* ->: ...:something:something
*/
case class DUP (typ: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="DUP";
override def consumed = 1;
override def produced = 2;
}
/** This class represents a MONITOR_ENTER instruction
* Stack: ...:object(ref)
* ->: ...:
*/
case class MONITOR_ENTER() extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="MONITOR_ENTER";
override def consumed = 1;
override def produced = 0;
}
/** This class represents a MONITOR_EXIT instruction
* Stack: ...:object(ref)
* ->: ...:
*/
case class MONITOR_EXIT() extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="MONITOR_EXIT";
override def consumed = 1;
override def produced = 0;
}
/** This class represents a method invocation style. */
trait InvokeStyle {
/** Is this a dynamic method call? */
def isDynamic: Boolean = this match {
case Dynamic => true;
case _ => false;
}
/** Is this a static method call? */
def isStatic: Boolean = this match {
case Static(_) => true;
case _ => false;
}
/** Is this an instance method call? */
def hasInstance: Boolean = this match {
case Dynamic => true;
case Static(onInstance) => onInstance;
case SuperCall(_) => true;
case _ => false;
}
/** Returns a string representation of this style. */
override def toString(): String = this match {
case Dynamic => "dynamic";
case Static(false) => "static-class";
case Static(true) => "static-instance";
case SuperCall(mixin) => "super(" + mixin + ")";
}
}
case object Dynamic extends InvokeStyle;
/**
* Special invoke. Static(true) is used for calls to private
* members.
*/
case class Static(onInstance: Boolean) extends InvokeStyle;
/** Call through super[mixin]. */
case class SuperCall(mixin: Name) extends InvokeStyle;
}
}