diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java index be95711e65a..109166846cd 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java @@ -27,6 +27,8 @@ import org.jetbrains.jet.lang.types.lang.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; import java.util.Collections; +import java.util.Collection; +import java.util.Random; /** * @author abreslav @@ -36,6 +38,8 @@ public class CodegenUtil { private CodegenUtil() { } + private static final Random RANDOM = new Random(55L); + public static boolean isInterface(DeclarationDescriptor descriptor) { return descriptor instanceof ClassDescriptor && ((ClassDescriptor)descriptor).getKind() == ClassKind.TRAIT; } @@ -69,6 +73,14 @@ public class CodegenUtil { !(myClass.getParent() instanceof JetClassObject); } + public static boolean isLocalFun(DeclarationDescriptor fd, BindingContext bindingContext) { + PsiElement psiElement = BindingContextUtils.descriptorToDeclaration(bindingContext, fd); + if(psiElement instanceof JetNamedFunction && psiElement.getParent() instanceof JetBlockExpression) { + return true; + } + return false; + } + public static boolean isNamedFun(DeclarationDescriptor fd, BindingContext bindingContext) { PsiElement psiElement = BindingContextUtils.descriptorToDeclaration(bindingContext, fd); @@ -77,4 +89,16 @@ public class CodegenUtil { } return false; } + + public static String generateTmpVariableName(Collection existingNames) { + String prefix = "tmp"; + int i = RANDOM.nextInt(Integer.MAX_VALUE); + String name = prefix + i; + while (existingNames.contains(name)) { + i++; + name = prefix + i; + } + return name; + } + } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 8129db4ad9e..e93669b377b 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -76,6 +76,7 @@ public class ExpressionCodegen extends JetVisitor { private final CodegenContext context; private final Stack blockStackElements = new Stack(); + private final Collection localVariableNames = new HashSet(); static class BlockStackElement { } @@ -110,7 +111,14 @@ public class ExpressionCodegen extends JetVisitor { this.typeMapper = state.getInjector().getJetTypeMapper(); this.returnType = returnType; this.state = state; - this.v = new InstructionAdapter(v); + this.v = new InstructionAdapter(v) { + @Override + public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { + super.visitLocalVariable(name, desc, signature, start, end, + index); + localVariableNames.add(name); + } + }; this.bindingContext = state.getBindingContext(); this.context = context; } @@ -139,6 +147,10 @@ public class ExpressionCodegen extends JetVisitor { return bindingContext; } + public Collection getLocalVariableNamesForExpression() { + return localVariableNames; + } + public void addTypeParameter(TypeParameterDescriptor typeParameter, StackValue expression) { typeParameterExpressions.put(typeParameter, expression); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index 287328ea27a..198c87ffe52 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -30,6 +30,7 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.JvmClassName; import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; +import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; @@ -37,8 +38,7 @@ import org.objectweb.asm.Type; import org.objectweb.asm.commons.InstructionAdapter; import org.objectweb.asm.commons.Method; -import java.util.List; -import java.util.Set; +import java.util.*; import static org.objectweb.asm.Opcodes.*; @@ -203,6 +203,8 @@ public class FunctionCodegen { throw new IllegalStateException("mismatching kind in " + functionDescriptor); } + Map mapLabelsToDivideLocalVarVisibilityForSharedVar = new HashMap(); + if (kind instanceof OwnerKind.DelegateKind) { OwnerKind.DelegateKind dk = (OwnerKind.DelegateKind) kind; InstructionAdapter iv = new InstructionAdapter(mv); @@ -227,6 +229,11 @@ public class FunctionCodegen { mv.visitMethodInsn(INVOKESPECIAL, sharedVarType.getInternalName(), "", "()V"); mv.visitVarInsn(localVarType.getOpcode(ILOAD), index); mv.visitFieldInsn(PUTFIELD, sharedVarType.getInternalName(), "ref", StackValue.refType(localVarType).getDescriptor()); + + Label labelToDivideLocalVarForSharedVarVisibility = new Label(); + mv.visitLabel(labelToDivideLocalVarForSharedVarVisibility); + mapLabelsToDivideLocalVarVisibilityForSharedVar.put(parameter.getName(), labelToDivideLocalVarForSharedVarVisibility); + mv.visitVarInsn(sharedVarType.getOpcode(ISTORE), index); } } @@ -237,6 +244,12 @@ public class FunctionCodegen { Label methodEnd = new Label(); mv.visitLabel(methodEnd); + Collection localVariableNames = new HashSet(); + localVariableNames.addAll(codegen.getLocalVariableNamesForExpression()); + for (ValueParameterDescriptor parameterDescriptor : paramDescrs) { + localVariableNames.add(parameterDescriptor.getName().getName()); + } + int k = 0; if(expectedThisObject.exists()) { @@ -244,8 +257,7 @@ public class FunctionCodegen { // TODO: specify signature mv.visitLocalVariable("this", type.getDescriptor(), null, methodBegin, methodEnd, k++); } - - if (fun instanceof JetFunctionLiteralExpression) { + else if (fun instanceof JetFunctionLiteralExpression || CodegenUtil.isLocalFun(functionDescriptor, state.getBindingContext())) { Type type = state.getInjector().getJetTypeMapper().mapType( context.getThisDescriptor().getDefaultType(), MapTypeMode.VALUE); mv.visitLocalVariable("this", type.getDescriptor(), null, methodBegin, methodEnd, k++); @@ -261,8 +273,22 @@ public class FunctionCodegen { for (ValueParameterDescriptor parameter : paramDescrs) { Type type = state.getInjector().getJetTypeMapper().mapType(parameter.getType(), MapTypeMode.VALUE); // TODO: specify signature - mv.visitLocalVariable(parameter.getName().getName(), type.getDescriptor(), null, methodBegin, methodEnd, k); - k += type.getSize(); + + Label divideLabel = mapLabelsToDivideLocalVarVisibilityForSharedVar.get(parameter.getName()); + String parameterName = parameter.getName().getName(); + if(divideLabel != null) { + Type sharedVarType = state.getInjector().getJetTypeMapper().getSharedVarType(parameter); + mv.visitLocalVariable(parameterName, type.getDescriptor(), null, methodBegin, divideLabel, k); + + String nameForSharedVar = CodegenUtil.generateTmpVariableName(localVariableNames); + localVariableNames.add(nameForSharedVar); + + mv.visitLocalVariable(nameForSharedVar, sharedVarType.getDescriptor(), null, divideLabel, methodEnd, k); + k += Math.max(type.getSize(), sharedVarType.getSize()); + } else { + mv.visitLocalVariable(parameter.getName().getName(), type.getDescriptor(), null, methodBegin, methodEnd, k); + k += type.getSize(); + } } endVisit(mv, null, fun); diff --git a/compiler/testData/writeThisInLocalVariableTable/namespace$foo$1$1.kt b/compiler/testData/checkLocalVariablesTable/namespace$foo$1$1.kt similarity index 55% rename from compiler/testData/writeThisInLocalVariableTable/namespace$foo$1$1.kt rename to compiler/testData/checkLocalVariablesTable/namespace$foo$1$1.kt index 2ee4222ec50..fb8d0730eda 100644 --- a/compiler/testData/writeThisInLocalVariableTable/namespace$foo$1$1.kt +++ b/compiler/testData/checkLocalVariablesTable/namespace$foo$1$1.kt @@ -7,5 +7,5 @@ fun foo() { } // METHOD : invoke(I)V -// VARIABLE : INDEX=0 NAME=this TYPE=Lnamespace$foo$1$1; -// VARIABLE : INDEX=1 NAME=it TYPE=I +// VARIABLE : NAME=this TYPE=Lnamespace$foo$1$1; INDEX=0 +// VARIABLE : NAME=it TYPE=I INDEX=1 diff --git a/compiler/testData/checkLocalVariablesTable/namespace$foo$1.kt b/compiler/testData/checkLocalVariablesTable/namespace$foo$1.kt new file mode 100644 index 00000000000..9ea8cf9f341 --- /dev/null +++ b/compiler/testData/checkLocalVariablesTable/namespace$foo$1.kt @@ -0,0 +1,7 @@ +fun foo() { + fun bar() { + } +} + +// METHOD : invoke()V +// VARIABLE : NAME=this TYPE=Lnamespace$foo$1; INDEX=0 \ No newline at end of file diff --git a/compiler/testData/writeThisInLocalVariableTable/namespace$foo$a$1.kt b/compiler/testData/checkLocalVariablesTable/namespace$foo$a$1.kt similarity index 50% rename from compiler/testData/writeThisInLocalVariableTable/namespace$foo$a$1.kt rename to compiler/testData/checkLocalVariablesTable/namespace$foo$a$1.kt index a32c013856a..8d1f49449b9 100644 --- a/compiler/testData/writeThisInLocalVariableTable/namespace$foo$a$1.kt +++ b/compiler/testData/checkLocalVariablesTable/namespace$foo$a$1.kt @@ -5,4 +5,4 @@ fun foo() { } // METHOD : invoke()V -// VARIABLE : INDEX=0 NAME=this TYPE=Lnamespace$foo$a$1; +// VARIABLE : NAME=this TYPE=Lnamespace$foo$a$1; INDEX=0 diff --git a/compiler/testData/checkLocalVariablesTable/namespace$foo1$1.kt b/compiler/testData/checkLocalVariablesTable/namespace$foo1$1.kt new file mode 100644 index 00000000000..bab01efd24b --- /dev/null +++ b/compiler/testData/checkLocalVariablesTable/namespace$foo1$1.kt @@ -0,0 +1,9 @@ +fun foo1() { + (1..5).forEach { + println(it) + } +} + +// METHOD : invoke(I)V +// VARIABLE : NAME=this TYPE=Lnamespace$foo1$1; INDEX=0 +// VARIABLE : NAME=it TYPE=I INDEX=1 \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/codegen/WriteThisForClosureTest.java b/compiler/tests/org/jetbrains/jet/codegen/CheckLocalVariablesTableTest.java similarity index 88% rename from compiler/tests/org/jetbrains/jet/codegen/WriteThisForClosureTest.java rename to compiler/tests/org/jetbrains/jet/codegen/CheckLocalVariablesTableTest.java index e19449def0b..9d2015794bd 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/WriteThisForClosureTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/CheckLocalVariablesTableTest.java @@ -51,12 +51,12 @@ import java.util.regex.Pattern; * @author Natalia.Ukhorskaya */ -public class WriteThisForClosureTest extends TestCaseWithTmpdir { +public class CheckLocalVariablesTableTest extends TestCaseWithTmpdir { private final File ktFile; private JetCoreEnvironment jetCoreEnvironment; - public WriteThisForClosureTest(File ktFile) { + public CheckLocalVariablesTableTest(File ktFile) { this.ktFile = ktFile; } @@ -93,9 +93,9 @@ public class WriteThisForClosureTest extends TestCaseWithTmpdir { int index = 0; for (LocalVariable expectedVariable : expectedLocalVariables) { LocalVariable actualVariable = actualLocalVariables.get(index); - assertEquals("Indexes are different", expectedVariable.index, actualVariable.index); assertEquals("Names are different", expectedVariable.name, actualVariable.name); assertEquals("Types are different", expectedVariable.type, actualVariable.type); + assertEquals("Indexes are different", expectedVariable.index, actualVariable.index); index++; } } @@ -106,20 +106,19 @@ public class WriteThisForClosureTest extends TestCaseWithTmpdir { } - Disposer.dispose(myTestRootDisposable); } public static Test suite() { - return JetTestCaseBuilder.suiteForDirectory(JetTestCaseBuilder.getTestDataPathBase(), "/writeThisInLocalVariableTable", true, + return JetTestCaseBuilder.suiteForDirectory(JetTestCaseBuilder.getTestDataPathBase(), "/checkLocalVariablesTable", true, new JetTestCaseBuilder.NamedTestFactory() { @NotNull @Override public Test createTest(@NotNull String dataPath, @NotNull String name, @NotNull File file) { - return new WriteThisForClosureTest(file); + return new CheckLocalVariablesTableTest(file); } }); } @@ -129,9 +128,11 @@ public class WriteThisForClosureTest extends TestCaseWithTmpdir { private final String type; private final int index; - private LocalVariable(int index, + private LocalVariable( @NotNull String name, - @NotNull String type) { + @NotNull String type, + int index + ) { this.name = name; this.type = type; this.index = index; @@ -141,29 +142,29 @@ public class WriteThisForClosureTest extends TestCaseWithTmpdir { @NotNull private static final Pattern methodPattern = Pattern.compile("^// METHOD : *(.*)"); - private static final Pattern namePattern = Pattern.compile("^// VARIABLE : INDEX=*(.*) NAME=.* TYPE=.*"); - private static final Pattern typePattern = Pattern.compile("^// VARIABLE : INDEX=.* NAME=*(.*) TYPE=.*"); - private static final Pattern indexPattern = Pattern.compile("^// VARIABLE : INDEX=.* NAME=.* TYPE=*(.*)"); + private static final Pattern namePattern = Pattern.compile("^// VARIABLE : NAME=*(.*) TYPE=.* INDEX=.*"); + private static final Pattern typePattern = Pattern.compile("^// VARIABLE : NAME=.* TYPE=*(.*) INDEX=.*"); + private static final Pattern indexPattern = Pattern.compile("^// VARIABLE : NAME=.* TYPE=.* INDEX=*(.*)"); private List parseExpectations() throws IOException { List lines = Files.readLines(ktFile, Charset.forName("utf-8")); List expectedLocalVariables = new ArrayList(); for (int i = lines.size() - 3; i < lines.size(); ++i) { - Matcher indexMatcher = namePattern.matcher(lines.get(i)); - if (indexMatcher.matches()) { - Matcher nameMatcher = typePattern.matcher(lines.get(i)); - Matcher typeMatcher = indexPattern.matcher(lines.get(i)); + Matcher nameMatcher = namePattern.matcher(lines.get(i)); + if (nameMatcher.matches()) { + Matcher typeMatcher = typePattern.matcher(lines.get(i)); + Matcher indexMatcher = indexPattern.matcher(lines.get(i)); - if (!nameMatcher.matches() - || !typeMatcher.matches()) { + if (!typeMatcher.matches() + || !indexMatcher.matches()) { throw new AssertionError("Incorrect test instructions format"); } - expectedLocalVariables.add(new LocalVariable(Integer.parseInt(indexMatcher.group(1)), - nameMatcher.group(1), - typeMatcher.group(1))); + expectedLocalVariables.add(new LocalVariable(nameMatcher.group(1), + typeMatcher.group(1), + Integer.parseInt(indexMatcher.group(1)))); } } if (expectedLocalVariables.size() == 0) { @@ -176,7 +177,7 @@ public class WriteThisForClosureTest extends TestCaseWithTmpdir { private String parseMethodName() throws IOException { List lines = Files.readLines(ktFile, Charset.forName("utf-8")); - for (int i = lines.size() - 3; i < lines.size(); ++i) { + for (int i = 0; i < lines.size(); ++i) { Matcher methodMatcher = methodPattern.matcher(lines.get(i)); if (methodMatcher.matches()) { return methodMatcher.group(1); @@ -198,8 +199,9 @@ public class WriteThisForClosureTest extends TestCaseWithTmpdir { return new EmptyVisitor() { @Override public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { - readVariables.add(new LocalVariable(index, name, desc)); + readVariables.add(new LocalVariable(name, desc, index)); } + }; } else {