diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index e5d1668cf6b..8f2958d63f2 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -907,6 +907,13 @@ public class ExpressionCodegen extends JetVisitor { } } + if (statement instanceof JetMultiDeclaration) { + JetMultiDeclaration multiDeclaration = (JetMultiDeclaration) statement; + for (JetMultiDeclarationEntry entry : multiDeclaration.getEntries()) { + generateLocalVariableDeclaration(entry, blockEnd, leaveTasks); + } + } + if (statement instanceof JetVariableDeclaration) { generateLocalVariableDeclaration((JetVariableDeclaration) statement, blockEnd, leaveTasks); } @@ -2629,6 +2636,60 @@ public class ExpressionCodegen extends JetVisitor { return StackValue.none(); } + @Override + public StackValue visitMultiDeclaration(JetMultiDeclaration multiDeclaration, StackValue receiver) { + for (JetMultiDeclarationEntry variableDeclaration : multiDeclaration.getEntries()) { + + VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, variableDeclaration); + + if (JetPsiUtil.isScriptDeclaration(variableDeclaration)) { + return StackValue.none(); + } + int index = lookupLocal(variableDescriptor); + + if (index < 0) { + throw new IllegalStateException("Local variable not found for " + variableDescriptor); + } + + Type sharedVarType = typeMapper.getSharedVarType(variableDescriptor); + assert variableDescriptor != null; + + Type varType = asmType(variableDescriptor.getType()); + + JetExpression initializer = multiDeclaration.getInitializer(); + + ResolvedCall resolvedCall = bindingContext.get(BindingContext.COMPONENT_RESOLVED_CALL, variableDeclaration); + if (initializer != null) { + JetType type = bindingContext.get(EXPRESSION_TYPE, initializer); + assert type != null; + Call call = CallMaker.makeCall(new ExpressionReceiver(initializer, type), (JetExpression) null); + + if (JetPsiUtil.isScriptDeclaration(variableDeclaration)) { + invokeFunction(call, resolvedCall.getResultingDescriptor(), StackValue.none(), resolvedCall); + + JetScript scriptPsi = JetPsiUtil.getScript(variableDeclaration); + assert scriptPsi != null; + JvmClassName scriptClassName = state.getInjector().getClosureAnnotator().classNameForScriptPsi(scriptPsi); + v.putfield(scriptClassName.getInternalName(), variableDeclaration.getName(), varType.getDescriptor()); + } + else if (sharedVarType == null) { + invokeFunction(call, resolvedCall.getResultingDescriptor(), StackValue.none(), resolvedCall); + + v.store(index, varType); + } + else { + v.load(index, TYPE_OBJECT); + invokeFunction(call, resolvedCall.getResultingDescriptor(), StackValue.none(), resolvedCall); + + v.putfield(sharedVarType.getInternalName(), "ref", + sharedVarType == TYPE_SHARED_VAR ? "Ljava/lang/Object;" : varType.getDescriptor()); + } + } + + } + return StackValue.none(); + } + private StackValue generateConstructorCall( JetCallExpression expression, JetSimpleNameExpression constructorReference, diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java index becdd57d077..cf236db9a84 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java @@ -82,6 +82,8 @@ public interface BindingContext { WritableSlice LOOP_RANGE_HAS_NEXT = Slices.createSimpleSlice(); WritableSlice LOOP_RANGE_NEXT = Slices.createSimpleSlice(); + WritableSlice> COMPONENT_RESOLVED_CALL = Slices.createSimpleSlice(); + WritableSlice> INDEXED_LVALUE_GET = Slices.createSimpleSlice(); WritableSlice> INDEXED_LVALUE_SET = Slices.createSimpleSlice(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java index 2a32e2f6e96..ff6476c007d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java @@ -44,6 +44,7 @@ import java.util.Collection; import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.resolve.BindingContext.AMBIGUOUS_REFERENCE_TARGET; +import static org.jetbrains.jet.lang.resolve.BindingContext.COMPONENT_RESOLVED_CALL; import static org.jetbrains.jet.lang.resolve.BindingContext.VARIABLE_REASSIGNMENT; /** @@ -151,8 +152,8 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito JetType componentType = null; if (results.isSuccess()) { - FunctionDescriptor resultingDescriptor = results.getResultingDescriptor(); - componentType = resultingDescriptor.getReturnType(); + context.trace.record(COMPONENT_RESOLVED_CALL, entry, results.getResultingCall()); + componentType = results.getResultingDescriptor().getReturnType(); if (componentType != null && expectedType != TypeUtils.NO_EXPECTED_TYPE && !JetTypeChecker.INSTANCE.isSubtypeOf(componentType, expectedType)) { diff --git a/compiler/testData/codegen/multiDecl/ComplexInitializer.kt b/compiler/testData/codegen/multiDecl/ComplexInitializer.kt new file mode 100644 index 00000000000..f63eaa5c270 --- /dev/null +++ b/compiler/testData/codegen/multiDecl/ComplexInitializer.kt @@ -0,0 +1,13 @@ +class A { + fun component1() = 1 + fun component2() = 2 + +} + +fun A.getA() = this + +fun box() : String { + val (a, b) = A().getA().getA() + + return if (a == 1 && b == 2) "OK" else "fail" +} diff --git a/compiler/testData/codegen/multiDecl/SimpleVals.kt b/compiler/testData/codegen/multiDecl/SimpleVals.kt new file mode 100644 index 00000000000..4804c269f0a --- /dev/null +++ b/compiler/testData/codegen/multiDecl/SimpleVals.kt @@ -0,0 +1,9 @@ +class A { + fun component1() = 1 + fun component2() = 2 +} + +fun box() : String { + val (a, b) = A() + return if (a == 1 && b == 2) "OK" else "fail" +} diff --git a/compiler/testData/codegen/multiDecl/SimpleValsExtensions.kt b/compiler/testData/codegen/multiDecl/SimpleValsExtensions.kt new file mode 100644 index 00000000000..d5d442d7ed7 --- /dev/null +++ b/compiler/testData/codegen/multiDecl/SimpleValsExtensions.kt @@ -0,0 +1,10 @@ +class A { +} + +fun A.component1() = 1 +fun A.component2() = 2 + +fun box() : String { + val (a, b) = A() + return if (a == 1 && b == 2) "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/multiDecl/SimpleVarsExtensions.kt b/compiler/testData/codegen/multiDecl/SimpleVarsExtensions.kt new file mode 100644 index 00000000000..981ba45c9b1 --- /dev/null +++ b/compiler/testData/codegen/multiDecl/SimpleVarsExtensions.kt @@ -0,0 +1,10 @@ +class A { + fun component1() = 1 + fun component2() = 2 +} + +fun box() : String { + var (a, b) = A() + a = b + return if (a == 2 && b == 2) "OK" else "fail" +} diff --git a/compiler/testData/codegen/multiDecl/ValCapturedInFunctionLiteral.kt b/compiler/testData/codegen/multiDecl/ValCapturedInFunctionLiteral.kt new file mode 100644 index 00000000000..091b0f8eaea --- /dev/null +++ b/compiler/testData/codegen/multiDecl/ValCapturedInFunctionLiteral.kt @@ -0,0 +1,13 @@ +class A { + fun component1() = 1 + fun component2() = 2 +} + +fun box() : String { + val (a, b) = A() + + val run = { + a + } + return if (run() == 1 && b == 2) "OK" else "fail" +} diff --git a/compiler/testData/codegen/multiDecl/ValCapturedInLocalFunction.kt b/compiler/testData/codegen/multiDecl/ValCapturedInLocalFunction.kt new file mode 100644 index 00000000000..9f728b333d2 --- /dev/null +++ b/compiler/testData/codegen/multiDecl/ValCapturedInLocalFunction.kt @@ -0,0 +1,13 @@ +class A { + fun component1() = 1 + fun component2() = 2 +} + +fun box() : String { + val (a, b) = A() + + fun run(): Int { + return a + } + return if (run() == 1 && b == 2) "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/multiDecl/ValCapturedInObjectLiteral.kt b/compiler/testData/codegen/multiDecl/ValCapturedInObjectLiteral.kt new file mode 100644 index 00000000000..9f97face796 --- /dev/null +++ b/compiler/testData/codegen/multiDecl/ValCapturedInObjectLiteral.kt @@ -0,0 +1,16 @@ +class A { + fun component1() = 1 + fun component2() = 2 +} + + +fun box() : String { + val (a, b) = A() + + val local = object { + public fun run() : Int { + return a + } + } + return if (local.run() == 1 && b == 2) "OK" else "fail" +} diff --git a/compiler/testData/codegen/multiDecl/VarCapturedInFunctionLiteral.kt b/compiler/testData/codegen/multiDecl/VarCapturedInFunctionLiteral.kt new file mode 100644 index 00000000000..007aaabcdc9 --- /dev/null +++ b/compiler/testData/codegen/multiDecl/VarCapturedInFunctionLiteral.kt @@ -0,0 +1,15 @@ +class A { + fun component1() = 1 + fun component2() = 2 +} + + +fun box() : String { + var (a, b) = A() + + val local = { + a = 3 + } + local() + return if (a == 3 && b == 2) "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/multiDecl/VarCapturedInLocalFunction.kt b/compiler/testData/codegen/multiDecl/VarCapturedInLocalFunction.kt new file mode 100644 index 00000000000..d930cddfdc4 --- /dev/null +++ b/compiler/testData/codegen/multiDecl/VarCapturedInLocalFunction.kt @@ -0,0 +1,15 @@ +class A { +} + +fun A.component1() = 1 +fun A.component2() = 2 + +fun box() : String { + var (a, b) = A() + + fun local() { + a = 3 + } + local() + return if (a == 3 && b == 2) "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/multiDecl/VarCapturedInObjectLiteral.kt b/compiler/testData/codegen/multiDecl/VarCapturedInObjectLiteral.kt new file mode 100644 index 00000000000..94361786b1f --- /dev/null +++ b/compiler/testData/codegen/multiDecl/VarCapturedInObjectLiteral.kt @@ -0,0 +1,17 @@ +class A { + fun component1() = 1 + fun component2() = 2 +} + + +fun box() : String { + var (a, b) = A() + + val local = object { + public fun run() { + a = 3 + } + } + local.run() + return if (a == 3 && b == 2) "OK" else "fail" +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/AbstractMultiDeclTestCase.java b/compiler/tests/org/jetbrains/jet/codegen/AbstractMultiDeclTestCase.java new file mode 100644 index 00000000000..8be8e9e0ade --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/codegen/AbstractMultiDeclTestCase.java @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.codegen; + +import org.jetbrains.jet.test.generator.SimpleTestClassModel; +import org.jetbrains.jet.test.generator.TestGenerator; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +/** + * @author svtk + */ +public abstract class AbstractMultiDeclTestCase extends CodegenTestCase { + @Override + protected String getPrefix() { + return "multiDecl"; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + createEnvironmentWithMockJdkAndIdeaAnnotations(); + } + + public static void main(String[] args) throws IOException { + Class thisClass = AbstractMultiDeclTestCase.class; + String aPackage = thisClass.getPackage().getName(); + new TestGenerator( + "compiler/tests/", + aPackage, + "MultiDeclTestGenerated", + thisClass, + Arrays.asList( + new SimpleTestClassModel(new File("compiler/testData/codegen/multiDecl"), true, "kt", "blackBoxFileByFullPath") + ), + thisClass + ).generateAndSave(); + + } +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java b/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java index 12b25272e29..0bd14f211dc 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java +++ b/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java @@ -20,6 +20,7 @@ import com.google.common.base.Predicates; import com.google.common.collect.Lists; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.psi.PsiFile; import com.intellij.testFramework.UsefulTestCase; import org.jetbrains.annotations.NotNull; @@ -121,6 +122,17 @@ public abstract class CodegenTestCase extends UsefulTestCase { } } + protected String loadFileByFullPath(final String fullPath) { + try { + File file = new File(fullPath); + final String content = FileUtil.loadFile(file, true); + myFiles = CodegenTestFiles.create(file.getName(), content, myEnvironment.getProject()); + return content; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + protected void loadFiles(final String... names) { myFiles = CodegenTestFiles.create(myEnvironment.getProject(), names); } @@ -138,6 +150,11 @@ public abstract class CodegenTestCase extends UsefulTestCase { blackBox(); } + protected void blackBoxFileByFullPath(String filename) { + loadFileByFullPath(filename); + blackBox(); + } + protected void blackBoxMultiFile(String... filenames) { loadFiles(filenames); blackBox(); diff --git a/compiler/tests/org/jetbrains/jet/codegen/MultiDeclTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/MultiDeclTestGenerated.java new file mode 100644 index 00000000000..4ef26ec56bd --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/codegen/MultiDeclTestGenerated.java @@ -0,0 +1,85 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.jet.codegen; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestSuite; + +import java.io.File; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.test.TestMetadata; + +import org.jetbrains.jet.codegen.AbstractMultiDeclTestCase; + +/** This class is generated by {@link org.jetbrains.jet.codegen.AbstractMultiDeclTestCase}. DO NOT MODIFY MANUALLY */ +@TestMetadata("compiler/testData/codegen/multiDecl") +public class MultiDeclTestGenerated extends AbstractMultiDeclTestCase { + public void testAllFilesPresentInMultiDecl() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.codegen.AbstractMultiDeclTestCase", new File("compiler/testData/codegen/multiDecl"), "kt", false); + } + + @TestMetadata("ComplexInitializer.kt") + public void testComplexInitializer() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/multiDecl/ComplexInitializer.kt"); + } + + @TestMetadata("SimpleVals.kt") + public void testSimpleVals() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/multiDecl/SimpleVals.kt"); + } + + @TestMetadata("SimpleValsExtensions.kt") + public void testSimpleValsExtensions() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/multiDecl/SimpleValsExtensions.kt"); + } + + @TestMetadata("SimpleVarsExtensions.kt") + public void testSimpleVarsExtensions() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/multiDecl/SimpleVarsExtensions.kt"); + } + + @TestMetadata("ValCapturedInFunctionLiteral.kt") + public void testValCapturedInFunctionLiteral() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/multiDecl/ValCapturedInFunctionLiteral.kt"); + } + + @TestMetadata("ValCapturedInLocalFunction.kt") + public void testValCapturedInLocalFunction() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/multiDecl/ValCapturedInLocalFunction.kt"); + } + + @TestMetadata("ValCapturedInObjectLiteral.kt") + public void testValCapturedInObjectLiteral() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/multiDecl/ValCapturedInObjectLiteral.kt"); + } + + @TestMetadata("VarCapturedInFunctionLiteral.kt") + public void testVarCapturedInFunctionLiteral() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/multiDecl/VarCapturedInFunctionLiteral.kt"); + } + + @TestMetadata("VarCapturedInLocalFunction.kt") + public void testVarCapturedInLocalFunction() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/multiDecl/VarCapturedInLocalFunction.kt"); + } + + @TestMetadata("VarCapturedInObjectLiteral.kt") + public void testVarCapturedInObjectLiteral() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/multiDecl/VarCapturedInObjectLiteral.kt"); + } + +}