diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassContext.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassContext.java index e0fe6e08cb3..77aa758764c 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassContext.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassContext.java @@ -146,7 +146,15 @@ public class ClassContext { if (answer != null) return answer; final StackValue thisContext = getThisExpression(); - thisContext.put(thisContext.type, v); + if(thisContext instanceof StackValue.Local) { + } + else if(thisContext instanceof StackValue.InstanceField) { + StackValue.InstanceField instanceField = (StackValue.InstanceField) thisContext; + v.getfield(instanceField.owner, instanceField.name, instanceField.type.getDescriptor()); + } + else { + throw new UnsupportedOperationException(); + } } return parentContext != null ? parentContext.lookupInContext(d, v) : null; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java index 2e04ada073d..7341765d51e 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java @@ -67,11 +67,13 @@ public class ClosureCodegen { final int idx = exprContext.lookupLocal(vd); if (idx < 0) return null; - final Type type = state.getTypeMapper().mapType(vd.getOutType()); + final Type sharedVarType = exprContext.getSharedVarType(vd); + Type localType = state.getTypeMapper().mapType(vd.getOutType()); + final Type type = sharedVarType != null ? sharedVarType : localType; StackValue outerValue = StackValue.local(idx, type); - final String fieldName = "$" + (closure.size() + 1); - StackValue innerValue = StackValue.field(type, name, fieldName, false); + final String fieldName = "$" + (closure.size() + 1); // + "$" + vd.getName(); + StackValue innerValue = sharedVarType != null ? StackValue.fieldForSharedVar(localType, name, fieldName) : StackValue.field(type, name, fieldName, false); cv.visitField(Opcodes.ACC_PUBLIC, fieldName, type.getDescriptor(), null, null); answer = new EnclosedValueDescriptor(d, innerValue, outerValue); closure.put(d, answer); @@ -120,7 +122,7 @@ public class ClosureCodegen { final Method constructor = generateConstructor(funClass, captureThis, funDescriptor.getReturnType()); if (captureThis) { - cv.visitField(Opcodes.ACC_PRIVATE, "this$0", enclosingType.getDescriptor(), null, null); + cv.visitField(0, "this$0", enclosingType.getDescriptor(), null, null); } cv.visitEnd(); @@ -192,7 +194,9 @@ public class ClosureCodegen { } for (DeclarationDescriptor descriptor : closure.keySet()) { - argTypes[i++] = state.getTypeMapper().mapType(((VariableDescriptor) descriptor).getOutType()); + final Type sharedVarType = exprContext.getSharedVarType(descriptor); + final Type type = sharedVarType != null ? sharedVarType : state.getTypeMapper().mapType(((VariableDescriptor) descriptor).getOutType()); + argTypes[i++] = type; } final Method constructor = new Method("", Type.VOID_TYPE, argTypes); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index f231cdf3426..2a5a9bed97e 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -551,7 +551,7 @@ public class ExpressionCodegen extends JetVisitor { final Method cons = closure.getConstructor(); if (closure.isCaptureThis()) { - thisToStack(); + v.load(0, JetTypeMapper.TYPE_OBJECT); } for (int i = 0; i < closure.getArgs().size(); i++) { @@ -576,6 +576,20 @@ public class ExpressionCodegen extends JetVisitor { return StackValue.onStack(type); } + Type getSharedVarType(DeclarationDescriptor variableDescriptor) { + if(!(variableDescriptor instanceof VariableDescriptor)) + return null; + + Boolean aBoolean = bindingContext.get(BindingContext.MUST_BE_WRAPPED_IN_A_REF, (VariableDescriptor) variableDescriptor); + if (aBoolean != null && aBoolean) { + JetType outType = ((VariableDescriptor) variableDescriptor).getOutType(); + return StackValue.sharedTypeForType(typeMapper.mapType(outType)); + } + else { + return null; + } + } + private StackValue generateBlock(List statements) { Label blockStart = new Label(); v.mark(blockStart); @@ -584,7 +598,9 @@ public class ExpressionCodegen extends JetVisitor { if (statement instanceof JetProperty) { final VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, statement); assert variableDescriptor != null; - final Type type = typeMapper.mapType(variableDescriptor.getOutType()); + + final Type sharedVarType = getSharedVarType(variableDescriptor); + final Type type = sharedVarType != null ? sharedVarType : typeMapper.mapType(variableDescriptor.getOutType()); myMap.enter(variableDescriptor, type.getSize()); } } @@ -608,10 +624,17 @@ public class ExpressionCodegen extends JetVisitor { JetProperty var = (JetProperty) statement; VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, var); assert variableDescriptor != null; - Type outType = typeMapper.mapType(variableDescriptor.getOutType()); int index = myMap.leave(variableDescriptor); - v.visitLocalVariable(var.getName(), outType.getDescriptor(), null, blockStart, blockEnd, index); + + final Type sharedVarType = getSharedVarType(variableDescriptor); + final Type type = sharedVarType != null ? sharedVarType : typeMapper.mapType(variableDescriptor.getOutType()); + if(sharedVarType != null) { + v.aconst(null); + v.store(index, JetTypeMapper.TYPE_OBJECT); + } + + v.visitLocalVariable(var.getName(), type.getDescriptor(), null, blockStart, blockEnd, index); } } @@ -699,8 +722,14 @@ public class ExpressionCodegen extends JetVisitor { else { int index = lookupLocal(descriptor); if (index >= 0) { + Type sharedVarType = getSharedVarType(descriptor); final JetType outType = ((VariableDescriptor) descriptor).getOutType(); - return StackValue.local(index, typeMapper.mapType(outType)); + if(sharedVarType != null) { + return StackValue.shared(index, typeMapper.mapType(outType)); + } + else { + return StackValue.local(index, typeMapper.mapType(outType)); + } } else if (descriptor instanceof PropertyDescriptor) { final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor; @@ -785,6 +814,12 @@ public class ExpressionCodegen extends JetVisitor { if (value == null) { throw new UnsupportedOperationException("don't know how to generate reference " + descriptor); } + + if(value instanceof StackValue.FieldForSharedVar) { + StackValue.FieldForSharedVar fieldForSharedVar = (StackValue.FieldForSharedVar) value; + Type sharedType = StackValue.sharedTypeForType(value.type); + v.visitFieldInsn(Opcodes.GETFIELD, fieldForSharedVar.owner, fieldForSharedVar.name, sharedType.getDescriptor()); + } return value; } } @@ -1617,12 +1652,27 @@ public class ExpressionCodegen extends JetVisitor { assert index >= 0; + final Type sharedVarType = getSharedVarType(variableDescriptor); + assert variableDescriptor != null; + Type varType = typeMapper.mapType(variableDescriptor.getOutType()); + if(sharedVarType != null) { + v.anew(sharedVarType); + v.dup(); + v.invokespecial(sharedVarType.getInternalName(), "", "()V"); + v.store(index, JetTypeMapper.TYPE_OBJECT); + } + JetExpression initializer = property.getInitializer(); if (initializer != null) { - assert variableDescriptor != null; - Type type = typeMapper.mapType(variableDescriptor.getOutType()); - gen(initializer, type); - v.store(index, type); + if(sharedVarType == null) { + gen(initializer, varType); + v.store(index, varType); + } + else { + v.load(index, JetTypeMapper.TYPE_OBJECT); + gen(initializer, varType); + v.putfield(sharedVarType.getInternalName(), "ref", sharedVarType == JetTypeMapper.TYPE_SHARED_VAR ? "Ljava/lang/Object;" : varType.getDescriptor()); + } } return StackValue.none(); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FrameMap.java b/compiler/backend/src/org/jetbrains/jet/codegen/FrameMap.java index bf6c4710c31..3bcaf17e855 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FrameMap.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FrameMap.java @@ -3,6 +3,7 @@ package org.jetbrains.jet.codegen; import gnu.trove.TObjectIntHashMap; import gnu.trove.TObjectIntIterator; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.resolve.BindingContext; import java.util.ArrayList; import java.util.List; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index d7b0b188569..1fcd98e320c 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -98,6 +98,23 @@ public class FunctionCodegen { iv.areturn(jvmSignature.getReturnType()); } else { + for (int i = 0; i < paramDescrs.size(); i++) { + ValueParameterDescriptor parameter = paramDescrs.get(i); + + Type sharedVarType = codegen.getSharedVarType(parameter); + Type localVarType = state.getTypeMapper().mapType(parameter.getOutType()); + if(sharedVarType != null) { + int index = frameMap.getIndex(parameter); + mv.visitTypeInsn(Opcodes.NEW, sharedVarType.getInternalName()); + mv.visitInsn(Opcodes.DUP); + mv.visitInsn(Opcodes.DUP); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, sharedVarType.getInternalName(), "", "()V"); + mv.visitVarInsn(localVarType.getOpcode(Opcodes.ILOAD), index); + mv.visitFieldInsn(Opcodes.PUTFIELD, sharedVarType.getInternalName(), "ref", StackValue.refType(localVarType).getDescriptor()); + mv.visitVarInsn(sharedVarType.getOpcode(Opcodes.ISTORE), index); + } + } + codegen.returnExpression(bodyExpressions); } mv.visitMaxs(0, 0); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java index b8e221c52a9..9cf08cd2482 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java @@ -59,6 +59,15 @@ public class JetTypeMapper { public static final Type TYPE_FUNCTION1 = Type.getObjectType("jet/Function1"); public static final Type TYPE_ITERATOR = Type.getObjectType("jet/Iterator"); public static final Type TYPE_INT_RANGE = Type.getObjectType("jet/IntRange"); + public static final Type TYPE_SHARED_VAR = Type.getObjectType("jet/refs/SharedVar$Object"); + public static final Type TYPE_SHARED_INT = Type.getObjectType("jet/refs/SharedVar$Int"); + public static final Type TYPE_SHARED_DOUBLE = Type.getObjectType("jet/refs/SharedVar$Double"); + public static final Type TYPE_SHARED_FLOAT = Type.getObjectType("jet/refs/SharedVar$Float"); + public static final Type TYPE_SHARED_BYTE = Type.getObjectType("jet/refs/SharedVar$Byte"); + public static final Type TYPE_SHARED_SHORT = Type.getObjectType("jet/refs/SharedVar$Short"); + public static final Type TYPE_SHARED_CHAR = Type.getObjectType("jet/refs/SharedVar$Char"); + public static final Type TYPE_SHARED_LONG = Type.getObjectType("jet/refs/SharedVar$Long"); + public static final Type TYPE_SHARED_BOOLEAN = Type.getObjectType("jet/refs/SharedVar$Boolean"); public JetTypeMapper(JetStandardLibrary standardLibrary, BindingContext bindingContext) { this.standardLibrary = standardLibrary; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java index f60fc9804cc..52ff48a6183 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java @@ -11,6 +11,7 @@ import org.objectweb.asm.commons.Method; /** * @author yole + * @author alex.tkachman */ public abstract class StackValue { public final Type type; @@ -59,6 +60,10 @@ public abstract class StackValue { return new Local(index, type); } + public static StackValue shared(int index, Type type) { + return new Shared(index, type); + } + public static StackValue onStack(Type type) { return type == Type.VOID_TYPE ? none() : new OnStack(type); } @@ -225,7 +230,11 @@ public abstract class StackValue { public static StackValue none() { return None.INSTANCE; } - + + public static StackValue fieldForSharedVar(Type type, String name, String fieldName) { + return new FieldForSharedVar(type, name, fieldName); + } + private static class None extends StackValue { public static None INSTANCE = new None(); private None() { @@ -496,9 +505,9 @@ public abstract class StackValue { } - private static class Field extends StackValue { - private final String owner; - private final String name; + static class Field extends StackValue { + final String owner; + final String name; private final boolean isStatic; public Field(Type type, String owner, String name, boolean isStatic) { @@ -531,9 +540,9 @@ public abstract class StackValue { } } - private static class InstanceField extends StackValue { - private final String owner; - private final String name; + static class InstanceField extends StackValue { + final String owner; + final String name; public InstanceField(Type type, String owner, String name) { super(type); @@ -625,4 +634,106 @@ public abstract class StackValue { generator.gen(expression, type); } } + + public static class Shared extends StackValue { + private final int index; + + public Shared(int index, Type type) { + super(type); + this.index = index; + } + + @Override + public void put(Type type, InstructionAdapter v) { + v.load(index, JetTypeMapper.TYPE_OBJECT); + Type refType = refType(this.type); + Type sharedType = sharedTypeForType(this.type); + v.visitFieldInsn(Opcodes.GETFIELD, sharedType.getInternalName(), "ref", refType.getDescriptor()); + StackValue.onStack(refType).coerce(this.type, v); + StackValue.onStack(this.type).coerce(type, v); + } + + @Override + public void store(InstructionAdapter v) { + v.load(index, JetTypeMapper.TYPE_OBJECT); + v.swap(); + Type refType = refType(this.type); + Type sharedType = sharedTypeForType(this.type); + v.visitFieldInsn(Opcodes.PUTFIELD, sharedType.getInternalName(), "ref", refType.getDescriptor()); + } + } + + public static Type sharedTypeForType(Type type) { + switch(type.getSort()) { + case Type.OBJECT: + case Type.ARRAY: + return JetTypeMapper.TYPE_SHARED_VAR; + + case Type.BYTE: + return JetTypeMapper.TYPE_SHARED_BYTE; + + case Type.SHORT: + return JetTypeMapper.TYPE_SHARED_SHORT; + + case Type.CHAR: + return JetTypeMapper.TYPE_SHARED_CHAR; + + case Type.INT: + return JetTypeMapper.TYPE_SHARED_INT; + + case Type.BOOLEAN: + return JetTypeMapper.TYPE_SHARED_BOOLEAN; + + case Type.FLOAT: + return JetTypeMapper.TYPE_SHARED_FLOAT; + + case Type.DOUBLE: + return JetTypeMapper.TYPE_SHARED_DOUBLE; + + default: + throw new UnsupportedOperationException(); + } + } + + public static Type refType(Type type) { + if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) + return JetTypeMapper.TYPE_OBJECT; + + return type; + } + + static class FieldForSharedVar extends StackValue { + final String owner; + final String name; + + public FieldForSharedVar(Type type, String owner, String name) { + super(type); + this.owner = owner; + this.name = name; + } + + @Override + public void dupReceiver(InstructionAdapter v, int below) { + if (below == 1) { + v.dupX1(); + } + else { + v.dup(); + } + } + + @Override + public void put(Type type, InstructionAdapter v) { + Type sharedType = sharedTypeForType(this.type); + Type refType = refType(this.type); + v.visitFieldInsn(Opcodes.GETFIELD, sharedType.getInternalName(), "ref", refType.getDescriptor()); + StackValue.onStack(refType).coerce(this.type, v); + StackValue.onStack(this.type).coerce(type, v); + } + + @Override + public void store(InstructionAdapter v) { + v.visitFieldInsn(Opcodes.PUTFIELD, sharedTypeForType(type).getInternalName(), "ref", refType(type).getDescriptor()); + } + } } diff --git a/idea/testData/codegen/classes/enclosingLocalVariable.jet b/idea/testData/codegen/classes/enclosingLocalVariable.jet index aaf5081cefb..d2dbe243469 100644 --- a/idea/testData/codegen/classes/enclosingLocalVariable.jet +++ b/idea/testData/codegen/classes/enclosingLocalVariable.jet @@ -1,6 +1,6 @@ fun box() : String { val cl = 39 - return if (sum(200, { cl }) == 239) "OK" else "FAIL" + return if (sum(200, { val m = { val r = { cl }; r() }; m() }) == 239) "OK" else "FAIL" } fun sum(arg:Int, f : fun () : Int) : Int { diff --git a/idea/testData/codegen/regressions/kt344.jet b/idea/testData/codegen/regressions/kt344.jet new file mode 100644 index 00000000000..c551963b701 --- /dev/null +++ b/idea/testData/codegen/regressions/kt344.jet @@ -0,0 +1,179 @@ +fun s0() : Boolean { + val y = "222" + val foo = { + val bar = { y } + bar () + } + return foo() == "222" +} + +fun s1() : Boolean { + var x = "222" + val foo = { + val bar = { + x = "aaa" + } + bar () + } + foo() + return x == "aaa" +} + +fun t1() : Boolean { + var x = "111" + + val y = x + "22" + val foo = { + x = x + "45" + y + x = x.substring(3) + x += "aaa" + () + } + foo() + + x += "bbb" + System.out?.println(x) + return x == "4511122aaabbb" +} + +fun t2() : Boolean { + var x = 111 + val y = x + 22 + val foo = { + x = x + 5 + y + x += 5 + x++ + () + } + foo() + x -= 55 + System.out?.println(x) + return x == 200 +} + +fun t3() : Boolean { + var x = true + val foo = { + x = false + () + } + foo() + return !x +} + +fun t4() : Boolean { + var x = 100.flt + val y = x + 22 + val foo = { + x = x + 200.flt + y + x += 18 + () + } + foo() + System.out?.println(x) + return x == 440.flt +} + +fun t5() : Boolean { + var x = 100.dbl + val y = x + 22 + val foo = { + x = x + 200.dbl + y + x -= 22 + () + } + foo() + System.out?.println(x) + return x == 400.dbl +} + +fun t6() : Boolean { + var x = 20.byt + val y = x + 22 + val foo = { + x = (x + 20.byt + y).byt + x += 2 + x-- + () + } + foo() + System.out?.println(x) + return x == 83.byt +} + +fun t7() : Boolean { + var x : Char = 'a' + val foo = { + x = 'b' + () + } + foo() + System.out?.println(x) + return x == 'b' +} + +fun t8() : Boolean { + var x = 20.sht + val foo = { + val bar = { + x = 30.sht + () + } + bar() + () + } + foo() + return x == 30.sht +} + +fun t9(x: Int) : Boolean { + while(x < 100) { + x++ + } + return x == 100 +} + +fun t10() : Boolean { + var y = 1 + val foo = { + val bar = { + y = y + 1 + } + bar() + } + foo() + return y == 2 +} + +fun t11(x: Int) : Int { + val foo = { + x = x + 1 + val bar = { + x = x + 1 + x += 3 + } + bar() + } + while(x < 100) { + foo() + } + return x +} + +fun box(): String { + if (!s0()) return "fail" + if (!s1()) return "fail" + if (!t1()) return "fail" + if (!t2()) return "fail" + if (!t3()) return "fail" + if (!t4()) return "fail" + if (!t5()) return "fail" + if (!t6()) return "fail" + if (!t7()) return "fail" + if (!t8()) return "fail" + if (!t9(0)) return "fail" + if (!t10()) return "fail" + if (t11(1) != 104) return "fail" + + return "OK" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java b/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java index 72a131c61c6..97e4098243d 100644 --- a/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java @@ -190,4 +190,10 @@ public class ClassGenTest extends CodegenTestCase { blackBoxFile("regressions/kt343.jet"); System.out.println(generateToText()); } + + public void testKt344 () throws Exception { + loadFile("regressions/kt344.jet"); + System.out.println(generateToText()); + blackBox(); + } } diff --git a/idea/tests/org/jetbrains/jet/codegen/ClosuresGenTest.java b/idea/tests/org/jetbrains/jet/codegen/ClosuresGenTest.java index 7d5f4a68fc8..07b094c344d 100644 --- a/idea/tests/org/jetbrains/jet/codegen/ClosuresGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/ClosuresGenTest.java @@ -27,6 +27,7 @@ public class ClosuresGenTest extends CodegenTestCase { public void testEnclosingLocalVariable() throws Exception { blackBoxFile("classes/enclosingLocalVariable.jet"); + System.out.println(generateToText()); } public void testDoubleEnclosedLocalVariable() throws Exception { diff --git a/stdlib/src/jet/refs/SharedVar.java b/stdlib/src/jet/refs/SharedVar.java new file mode 100644 index 00000000000..0bd99c66965 --- /dev/null +++ b/stdlib/src/jet/refs/SharedVar.java @@ -0,0 +1,39 @@ +package jet.refs; + +public final class SharedVar { + public static final class Object { + public T ref; + } + + public static final class Byte { + public byte ref; + } + + public static final class Short { + public short ref; + } + + public static final class Int { + public int ref; + } + + public static final class Long { + public long ref; + } + + public static final class Float { + public float ref; + } + + public static final class Double { + public double ref; + } + + public static final class Char { + public char ref; + } + + public static final class Boolean { + public boolean ref; + } +}