work in progress on codegen rewrite (no boxing for primitive types)
This commit is contained in:
@@ -19,15 +19,21 @@ import java.util.Stack;
|
||||
public class ExpressionCodegen extends JetVisitor {
|
||||
private final Stack<Label> myLoopStarts = new Stack<Label>();
|
||||
private final Stack<Label> myLoopEnds = new Stack<Label>();
|
||||
private final Stack<StackValue> myStack = new Stack<StackValue>();
|
||||
|
||||
private final InstructionAdapter v;
|
||||
private final JetStandardLibrary stdlib;
|
||||
private final FrameMap myMap;
|
||||
private final JetTypeMapper typeMapper;
|
||||
private final Type returnType;
|
||||
private final BindingContext bindingContext;
|
||||
|
||||
public ExpressionCodegen(MethodVisitor v, BindingContext bindingContext, JetStandardLibrary stdlib, FrameMap myMap) {
|
||||
public ExpressionCodegen(MethodVisitor v, BindingContext bindingContext, JetStandardLibrary stdlib, FrameMap myMap,
|
||||
JetTypeMapper typeMapper, Type returnType) {
|
||||
this.stdlib = stdlib;
|
||||
this.myMap = myMap;
|
||||
this.typeMapper = typeMapper;
|
||||
this.returnType = returnType;
|
||||
this.v = new InstructionAdapter(v);
|
||||
this.bindingContext = bindingContext;
|
||||
}
|
||||
@@ -37,6 +43,15 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
expr.accept(this);
|
||||
}
|
||||
|
||||
private void gen(JetElement expr, Type type) {
|
||||
int oldStackDepth = myStack.size();
|
||||
gen(expr);
|
||||
if (myStack.size() == oldStackDepth+1) {
|
||||
StackValue value = myStack.pop();
|
||||
value.put(type, v);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitExpression(JetExpression expression) {
|
||||
throw new UnsupportedOperationException("Codegen for " + expression + " is not yet implemented");
|
||||
@@ -54,8 +69,9 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
|
||||
@Override
|
||||
public void visitIfExpression(JetIfExpression expression) {
|
||||
gen(expression.getCondition());
|
||||
unboxBoolean();
|
||||
JetType expressionType = bindingContext.getExpressionType(expression);
|
||||
Type asmType = typeMapper.mapType(expressionType);
|
||||
gen(expression.getCondition(), Type.BOOLEAN_TYPE);
|
||||
|
||||
JetExpression thenExpression = expression.getThen();
|
||||
JetExpression elseExpression = expression.getElse();
|
||||
@@ -78,13 +94,13 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
Label elseLabel = new Label();
|
||||
v.ifeq(elseLabel); // == 0, i.e. false
|
||||
|
||||
gen(thenExpression);
|
||||
gen(thenExpression, asmType);
|
||||
|
||||
Label endLabel = new Label();
|
||||
v.goTo(endLabel);
|
||||
v.mark(elseLabel);
|
||||
|
||||
gen(elseExpression);
|
||||
gen(elseExpression, asmType);
|
||||
|
||||
v.mark(endLabel);
|
||||
}
|
||||
@@ -178,6 +194,8 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
|
||||
@Override
|
||||
public void visitConstantExpression(JetConstantExpression expression) {
|
||||
myStack.push(StackValue.constant(expression.getValue()));
|
||||
/*
|
||||
Object value = expression.getValue();
|
||||
v.aconst(value);
|
||||
|
||||
@@ -205,6 +223,7 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
else if (value instanceof Double) {
|
||||
v.invokestatic("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -229,8 +248,11 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
for (JetElement statement : statements) {
|
||||
if (statement instanceof JetProperty) {
|
||||
JetProperty var = (JetProperty) statement;
|
||||
int index = myMap.leave(bindingContext.getPropertyDescriptor(var));
|
||||
v.visitLocalVariable(var.getName(), getType(var).getDescriptor(), null, blockStart, blockEnd, index);
|
||||
PropertyDescriptor propertyDescriptor = bindingContext.getPropertyDescriptor(var);
|
||||
Type outType = typeMapper.mapType(propertyDescriptor.getOutType());
|
||||
|
||||
int index = myMap.leave(propertyDescriptor);
|
||||
v.visitLocalVariable(var.getName(), outType.getDescriptor(), null, blockStart, blockEnd, index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,14 +262,22 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
public void visitReturnExpression(JetReturnExpression expression) {
|
||||
final JetExpression returnedExpression = expression.getReturnedExpression();
|
||||
if (returnedExpression != null) {
|
||||
returnedExpression.accept(this);
|
||||
v.visitInsn(Opcodes.ARETURN);
|
||||
gen(returnedExpression, returnType);
|
||||
v.areturn(returnType);
|
||||
}
|
||||
else {
|
||||
v.visitInsn(Opcodes.RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
public void returnTopOfStack() {
|
||||
if (myStack.size() > 0) {
|
||||
StackValue value = myStack.pop();
|
||||
value.put(returnType, v);
|
||||
v.areturn(returnType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSimpleNameExpression(JetSimpleNameExpression expression) {
|
||||
final DeclarationDescriptor descriptor = bindingContext.resolveReferenceExpression(expression);
|
||||
@@ -267,7 +297,7 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
else {
|
||||
int index = myMap.getIndex(descriptor);
|
||||
if (index >= 0) {
|
||||
v.visitVarInsn(Opcodes.ALOAD, index);
|
||||
myStack.push(StackValue.local(index, ((PropertyDescriptor) descriptor).getOutType()));
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("don't know how to generate reference " + descriptor);
|
||||
@@ -277,11 +307,6 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
|
||||
@Override
|
||||
public void visitCallExpression(JetCallExpression expression) {
|
||||
List<JetArgument> args = expression.getValueArguments();
|
||||
for (JetArgument arg : args) {
|
||||
gen(arg.getArgumentExpression());
|
||||
}
|
||||
|
||||
JetExpression callee = expression.getCalleeExpression();
|
||||
|
||||
if (callee instanceof JetSimpleNameExpression) {
|
||||
@@ -290,6 +315,14 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
PsiElement declarationPsiElement = bindingContext.getDeclarationPsiElement(funDescriptor);
|
||||
if (declarationPsiElement instanceof PsiMethod) {
|
||||
PsiMethod method = (PsiMethod) declarationPsiElement;
|
||||
PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
|
||||
List<JetArgument> args = expression.getValueArguments();
|
||||
for (int i = 0, argsSize = args.size(); i < argsSize; i++) {
|
||||
JetArgument arg = args.get(i);
|
||||
gen(arg.getArgumentExpression(), psiTypeToAsm(parameters [i].getType()));
|
||||
}
|
||||
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
v.visitMethodInsn(Opcodes.INVOKESTATIC,
|
||||
jvmName(method.getContainingClass()),
|
||||
@@ -302,7 +335,7 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
method.getName(),
|
||||
getMethodDescriptor(method));
|
||||
}
|
||||
boxIfNeeded(method.getReturnType());
|
||||
myStack.push(StackValue.onStack(psiTypeToAsm(method.getReturnType())));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -422,12 +455,10 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
if (op.getName().equals("plus")) {
|
||||
JetType returnType = ((FunctionDescriptor) op).getUnsubstitutedReturnType();
|
||||
if (returnType.equals(stdlib.getIntType())) {
|
||||
gen(expression.getLeft());
|
||||
unbox(PsiType.INT);
|
||||
gen(expression.getRight());
|
||||
unbox(PsiType.INT);
|
||||
gen(expression.getLeft(), Type.INT_TYPE);
|
||||
gen(expression.getRight(), Type.INT_TYPE);
|
||||
v.add(Type.INT_TYPE);
|
||||
boxIfNeeded(PsiType.INT);
|
||||
myStack.push(StackValue.onStack(Type.INT_TYPE));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -436,24 +467,22 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
throw new UnsupportedOperationException("Don't know how to generate binary op " + expression);
|
||||
}
|
||||
|
||||
private Type getType(JetProperty var) {
|
||||
return InstructionAdapter.OBJECT_TYPE; // TODO:
|
||||
}
|
||||
|
||||
private boolean isUnitType(JetExpression e) {
|
||||
return false; // TODO:!!!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitProperty(JetProperty property) {
|
||||
int index = myMap.getIndex(bindingContext.getPropertyDescriptor(property));
|
||||
PropertyDescriptor propertyDescriptor = bindingContext.getPropertyDescriptor(property);
|
||||
int index = myMap.getIndex(propertyDescriptor);
|
||||
|
||||
assert index >= 0;
|
||||
|
||||
JetExpression initializer = property.getInitializer();
|
||||
if (initializer != null) {
|
||||
gen(initializer);
|
||||
v.store(index, getType(property));
|
||||
Type type = typeMapper.mapType(propertyDescriptor.getOutType());
|
||||
gen(initializer, type);
|
||||
v.store(index, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,20 +56,21 @@ public class FunctionCodegen {
|
||||
frameMap.enter(parameter);
|
||||
}
|
||||
|
||||
bodyExpression.accept(new ExpressionCodegen(mv, bindingContext, standardLibrary, frameMap));
|
||||
generateReturn(mv, bodyExpression);
|
||||
ExpressionCodegen codegen = new ExpressionCodegen(mv, bindingContext, standardLibrary, frameMap, typeMapper, returnType);
|
||||
bodyExpression.accept(codegen);
|
||||
generateReturn(mv, bodyExpression, codegen);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void generateReturn(MethodVisitor mv, JetExpression bodyExpression) {
|
||||
private void generateReturn(MethodVisitor mv, JetExpression bodyExpression, ExpressionCodegen codegen) {
|
||||
if (!endsWithReturn(bodyExpression)) {
|
||||
final JetType expressionType = bindingContext.getExpressionType(bodyExpression);
|
||||
if (expressionType.equals(JetStandardClasses.getUnitType())) {
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
}
|
||||
else {
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
codegen.returnTopOfStack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,13 @@ public class JetTypeMapper {
|
||||
return Type.VOID_TYPE;
|
||||
}
|
||||
if (jetType.equals(standardLibrary.getIntType())) {
|
||||
return Type.getType(Integer.class);
|
||||
return Type.INT_TYPE;
|
||||
}
|
||||
if (jetType.equals(standardLibrary.getLongType())) {
|
||||
return Type.getType(Long.class);
|
||||
return Type.LONG_TYPE;
|
||||
}
|
||||
if (jetType.equals(standardLibrary.getBooleanType())) {
|
||||
return Type.BOOLEAN_TYPE;
|
||||
}
|
||||
if (jetType.equals(standardLibrary.getStringType())) {
|
||||
return Type.getType(String.class);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public abstract class StackValue {
|
||||
public abstract void put(Type type, InstructionAdapter v);
|
||||
|
||||
public static StackValue local(int index, JetType type) {
|
||||
return new Local(index, type);
|
||||
}
|
||||
|
||||
public static StackValue onStack(Type type) {
|
||||
return new OnStack(type);
|
||||
}
|
||||
|
||||
public static StackValue constant(Object value) {
|
||||
return new Constant(value);
|
||||
}
|
||||
|
||||
public static class Local extends StackValue {
|
||||
private final int index;
|
||||
private final JetType type;
|
||||
|
||||
public Local(int index, JetType type) {
|
||||
this.index = index;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Type type, InstructionAdapter v) {
|
||||
v.load(index, type);
|
||||
// TODO box/unbox
|
||||
}
|
||||
}
|
||||
|
||||
public static class OnStack extends StackValue {
|
||||
private final Type type;
|
||||
|
||||
public OnStack(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Type type, InstructionAdapter v) {
|
||||
}
|
||||
}
|
||||
|
||||
public static class Constant extends StackValue {
|
||||
private final Object value;
|
||||
|
||||
public Constant(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Type type, InstructionAdapter v) {
|
||||
if (type == Type.INT_TYPE) {
|
||||
if (value instanceof Number) {
|
||||
v.iconst(((Number) value).intValue());
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("don't know how to put this value");
|
||||
}
|
||||
}
|
||||
// TODO other primitive types
|
||||
else {
|
||||
v.aconst(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun f(): Int {
|
||||
var x: Int = 1
|
||||
x = x + 1
|
||||
return x
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
fun foo(b: Boolean): Int { return if (b) 15 else 20 }
|
||||
@@ -0,0 +1,4 @@
|
||||
fun foo(b: Boolean) : Int {
|
||||
if (b) return 15;
|
||||
return 20;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.jetbrains.jet.codegen;
|
||||
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.jet.lang.psi.JetProperty;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.util.TraceMethodVisitor;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -13,36 +14,7 @@ import java.io.StringWriter;
|
||||
* @author max
|
||||
*/
|
||||
public class ExpressionGenTest extends LightDaemonAnalyzerTestCase {
|
||||
public void testIntegerZeroExpression() throws Exception {
|
||||
checkCode("0",
|
||||
"LDC 0\n" +
|
||||
"INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;");
|
||||
}
|
||||
|
||||
public void testIf() throws Exception {
|
||||
checkCode("if (false) 15 else 20",
|
||||
"LDC false\n" +
|
||||
"INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;\n" +
|
||||
"INVOKEVIRTUAL java/lang/Boolean.booleanValue ()Z\n" +
|
||||
"IFEQ L0\n" +
|
||||
"LDC 15\n" +
|
||||
"INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;\n" +
|
||||
"GOTO L1\n" +
|
||||
"L0\n" +
|
||||
"LDC 20\n" +
|
||||
"INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;\n" +
|
||||
"L1");
|
||||
|
||||
checkCode("if (false) 15",
|
||||
"LDC false\n" +
|
||||
"INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;\n" +
|
||||
"INVOKEVIRTUAL java/lang/Boolean.booleanValue ()Z\n" +
|
||||
"IFEQ L0\n" +
|
||||
"LDC 15\n" +
|
||||
"INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;\n" +
|
||||
"POP\n" +
|
||||
"L0");
|
||||
|
||||
checkCode("if (false) else 20",
|
||||
"LDC false\n" +
|
||||
"INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;\n" +
|
||||
@@ -60,7 +32,7 @@ public class ExpressionGenTest extends LightDaemonAnalyzerTestCase {
|
||||
JetProperty p = PsiTreeUtil.getParentOfType(getFile().findElementAt(0), JetProperty.class);
|
||||
|
||||
TraceMethodVisitor trace = new TraceMethodVisitor();
|
||||
p.getInitializer().accept(new ExpressionCodegen(trace, null, null, new FrameMap()));
|
||||
p.getInitializer().accept(new ExpressionCodegen(trace, null, null, new FrameMap(), new JetTypeMapper(null), Type.VOID_TYPE));
|
||||
|
||||
StringWriter out = new StringWriter();
|
||||
trace.print(new PrintWriter(out));
|
||||
|
||||
@@ -28,8 +28,7 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
|
||||
final String text = generateToText();
|
||||
System.out.println(text);
|
||||
|
||||
final Class aClass = generateToClass();
|
||||
final Method main = firstMethod(aClass);
|
||||
final Method main = generateFunction();
|
||||
Object[] args = new Object[] { new String[0] };
|
||||
main.invoke(null, args);
|
||||
}
|
||||
@@ -39,8 +38,7 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
|
||||
final String text = generateToText();
|
||||
System.out.println(text);
|
||||
|
||||
final Class aClass = generateToClass();
|
||||
final Method main = firstMethod(aClass);
|
||||
final Method main = generateFunction();
|
||||
final Object returnValue = main.invoke(null, new Object[0]);
|
||||
assertEquals(new Integer(42), returnValue);
|
||||
}
|
||||
@@ -50,8 +48,7 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
|
||||
final String text = generateToText();
|
||||
System.out.println(text);
|
||||
|
||||
final Class aClass = generateToClass();
|
||||
final Method main = firstMethod(aClass);
|
||||
final Method main = generateFunction();
|
||||
final Object returnValue = main.invoke(null, 50);
|
||||
assertEquals(new Integer(50), returnValue);
|
||||
}
|
||||
@@ -61,16 +58,15 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
|
||||
final String text = generateToText();
|
||||
System.out.println(text);
|
||||
|
||||
final Class aClass = generateToClass();
|
||||
final Method main = firstMethod(aClass);
|
||||
final Method main = generateFunction();
|
||||
final Object returnValue = main.invoke(null, 76);
|
||||
assertEquals(new Integer(50), returnValue);
|
||||
}
|
||||
|
||||
public void testCurrentTime() throws Exception {
|
||||
loadFile("currentTime.jet");
|
||||
final Class aClass = generateToClass();
|
||||
final Method main = firstMethod(aClass);
|
||||
System.out.println(generateToText());
|
||||
final Method main = generateFunction();
|
||||
final long returnValue = (Long) main.invoke(null);
|
||||
long currentTime = System.currentTimeMillis();
|
||||
assertTrue(Math.abs(returnValue - currentTime) <= 1L);
|
||||
@@ -78,8 +74,8 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
public void testIdentityHashCode() throws Exception {
|
||||
loadFile("identityHashCode.jet");
|
||||
final Class aClass = generateToClass();
|
||||
final Method main = firstMethod(aClass);
|
||||
System.out.println(generateToText());
|
||||
final Method main = generateFunction();
|
||||
Object o = new Object();
|
||||
final int returnValue = (Integer) main.invoke(null, o);
|
||||
assertEquals(returnValue, System.identityHashCode(o));
|
||||
@@ -87,8 +83,7 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
public void testSystemOut() throws Exception {
|
||||
loadFile("systemOut.jet");
|
||||
final Class aClass = generateToClass();
|
||||
final Method main = firstMethod(aClass);
|
||||
final Method main = generateFunction();
|
||||
final Object returnValue = main.invoke(null);
|
||||
assertEquals(returnValue, System.out);
|
||||
}
|
||||
@@ -98,8 +93,7 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
System.out.println(generateToText());
|
||||
|
||||
final Class aClass = generateToClass();
|
||||
final Method main = firstMethod(aClass); // assert that it can be verified
|
||||
generateFunction(); // assert that it can be verified
|
||||
}
|
||||
|
||||
public void testPlus() throws Exception {
|
||||
@@ -107,12 +101,37 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
System.out.println(generateToText());
|
||||
|
||||
final Class aClass = generateToClass();
|
||||
final Method main = firstMethod(aClass);
|
||||
final Method main = generateFunction();
|
||||
final int returnValue = (Integer) main.invoke(null, 37, 5);
|
||||
assertEquals(42, returnValue);
|
||||
}
|
||||
|
||||
public void testIf() throws Exception {
|
||||
loadFile("if.jet");
|
||||
|
||||
System.out.println(generateToText());
|
||||
final Method main = generateFunction();
|
||||
assertEquals(15, main.invoke(null, true));
|
||||
assertEquals(20, main.invoke(null, false));
|
||||
}
|
||||
|
||||
public void testSingleBranchIf() throws Exception {
|
||||
loadFile("singleBranchIf.jet");
|
||||
|
||||
System.out.println(generateToText());
|
||||
final Method main = generateFunction();
|
||||
assertEquals(15, main.invoke(null, true));
|
||||
assertEquals(20, main.invoke(null, false));
|
||||
}
|
||||
|
||||
public void testAssign() throws Exception {
|
||||
loadFile("assign.jet");
|
||||
|
||||
System.out.println(generateToText());
|
||||
final Method main = generateFunction();
|
||||
assertEquals(2, main.invoke(null));
|
||||
}
|
||||
|
||||
private void loadFile(final String name) {
|
||||
myFixture.configureByFile(JetParsingTest.getTestDataDir() + "/codegen/" + name);
|
||||
}
|
||||
@@ -143,7 +162,8 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
|
||||
return aClass;
|
||||
}
|
||||
|
||||
private static Method firstMethod(Class aClass) {
|
||||
private Method generateFunction() {
|
||||
Class aClass = generateToClass();
|
||||
return aClass.getMethods()[0];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user