diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index c9475688601..eeab96e059c 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -105,6 +105,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { if (isInterface) { access |= ACC_INTERFACE; // ACC_SUPER } + else { + access |= ACC_SUPER; + } if (isFinal) { access |= ACC_FINAL; } diff --git a/compiler/testData/writeAccessFlags/Foo$publicFinalInnerClass.kt b/compiler/testData/writeAccessFlags/Foo$publicFinalInnerClass.kt new file mode 100644 index 00000000000..2f8dc09ccea --- /dev/null +++ b/compiler/testData/writeAccessFlags/Foo$publicFinalInnerClass.kt @@ -0,0 +1,12 @@ +public class Foo() { + public class publicFinalInnerClass() { + } +} + +// ACC_ABSTRACT : false +// ACC_ANNOTATION : false +// ACC_FINAL : true +// ACC_INTERFACE : false +// ACC_PUBLIC : true +// ACC_STATIC : false +// ACC_SUPER : true \ No newline at end of file diff --git a/compiler/testData/writeAccessFlags/Foo$publicInnerInterface.kt b/compiler/testData/writeAccessFlags/Foo$publicInnerInterface.kt new file mode 100644 index 00000000000..e7f7f92265a --- /dev/null +++ b/compiler/testData/writeAccessFlags/Foo$publicInnerInterface.kt @@ -0,0 +1,12 @@ +public class Foo() { + public trait publicInnerInterface { + } +} + +// ACC_ABSTRACT : true +// ACC_ANNOTATION : false +// ACC_FINAL : false +// ACC_INTERFACE : true +// ACC_PUBLIC : true +// ACC_STATIC : false +// ACC_SUPER : false \ No newline at end of file diff --git a/compiler/testData/writeAccessFlags/publicFinalClass.kt b/compiler/testData/writeAccessFlags/publicFinalClass.kt new file mode 100644 index 00000000000..ca5c373ba8e --- /dev/null +++ b/compiler/testData/writeAccessFlags/publicFinalClass.kt @@ -0,0 +1,10 @@ +public class publicFinalClass() { +} + +// ACC_ABSTRACT : false +// ACC_ANNOTATION : false +// ACC_FINAL : true +// ACC_INTERFACE : false +// ACC_PUBLIC : true +// ACC_STATIC : false +// ACC_SUPER : true \ No newline at end of file diff --git a/compiler/testData/writeAccessFlags/publicInterface.kt b/compiler/testData/writeAccessFlags/publicInterface.kt new file mode 100644 index 00000000000..9a3a02336fa --- /dev/null +++ b/compiler/testData/writeAccessFlags/publicInterface.kt @@ -0,0 +1,10 @@ +public trait publicInterface { +} + +// ACC_ABSTRACT : true +// ACC_ANNOTATION : false +// ACC_FINAL : false +// ACC_INTERFACE : true +// ACC_PUBLIC : true +// ACC_STATIC : false +// ACC_SUPER : false \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/codegen/WriteAccessFlagsTest.java b/compiler/tests/org/jetbrains/jet/codegen/WriteAccessFlagsTest.java new file mode 100644 index 00000000000..5a4b391e295 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/codegen/WriteAccessFlagsTest.java @@ -0,0 +1,201 @@ +/* + * 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 com.google.common.io.Files; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import com.intellij.testFramework.LightVirtualFile; +import junit.framework.Test; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.JetTestCaseBuilder; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; +import org.jetbrains.jet.cli.jvm.compiler.TestCaseWithTmpdir; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.plugin.JetLanguage; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Opcodes; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Test correctness of written access flags in class file + * + * @author Natalia.Ukhorskaya + */ +public class WriteAccessFlagsTest extends TestCaseWithTmpdir { + + private final File ktFile; + private JetCoreEnvironment jetCoreEnvironment; + + public WriteAccessFlagsTest(File ktFile) { + this.ktFile = ktFile; + } + + @Override + public String getName() { + return ktFile.getName(); + } + + @Override + protected void runTest() throws Throwable { + jetCoreEnvironment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(myTestRootDisposable); + + String text = FileUtil.loadFile(ktFile); + + LightVirtualFile virtualFile = new LightVirtualFile(ktFile.getName(), JetLanguage.INSTANCE, text); + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); + JetFile psiFile = (JetFile) ((PsiFileFactoryImpl) PsiFileFactory.getInstance(jetCoreEnvironment.getProject())) + .trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); + assert psiFile != null; + + final ClassFileFactory factory = GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, CompilerSpecialMode.REGULAR); + + String modifiedTestName = this.getName().replace(".kt", ".class"); + boolean isClassFound = false; + for (String filename : factory.files()) { + if (filename.equals(modifiedTestName)) { + isClassFound = true; + ClassReader cr = new ClassReader(factory.asBytes(filename)); + final Expectation expectation = parseExpectations(); + int expectedAccess = 0; + if (expectation.isAbstract) { + expectedAccess |= Opcodes.ACC_ABSTRACT; + } + if (expectation.isAnnotation) { + expectedAccess |= Opcodes.ACC_ANNOTATION; + } + if (expectation.isFinal) { + expectedAccess |= Opcodes.ACC_FINAL; + } + if (expectation.isPublic) { + expectedAccess |= Opcodes.ACC_PUBLIC; + } + if (expectation.isInterface) { + expectedAccess |= Opcodes.ACC_INTERFACE; + } + if (expectation.isStatic) { + expectedAccess |= Opcodes.ACC_STATIC; + } + if (expectation.isSuper) { + expectedAccess |= Opcodes.ACC_SUPER; + } + + assertEquals("Wrong access flag", expectedAccess, cr.getAccess()); + } + } + + if (!isClassFound) { + throw new AssertionError("file name should be the same as class name. File name is " + modifiedTestName); + } + + Disposer.dispose(myTestRootDisposable); + } + + + public static Test suite() { + return JetTestCaseBuilder.suiteForDirectory(JetTestCaseBuilder.getTestDataPathBase(), "/writeAccessFlags", true, + new JetTestCaseBuilder.NamedTestFactory() { + @NotNull + @Override + public Test createTest(@NotNull String dataPath, + @NotNull String name, + @NotNull File file) { + return new WriteAccessFlagsTest(file); + } + }); + } + + private static class Expectation { + private final boolean isAbstract; + private final boolean isAnnotation; + private final boolean isFinal; + private final boolean isInterface; + private final boolean isPublic; + private final boolean isStatic; + private final boolean isSuper; + + private Expectation(@NotNull String isAbstract, + @NotNull String isAnnotation, + @NotNull String isFinal, + @NotNull String isInterface, + @NotNull String isPublic, + @NotNull String isStatic, + @NotNull String isSuper) { + this.isAbstract = isAbstract.equals("true"); + this.isAnnotation = isAnnotation.equals("true"); + this.isFinal = isFinal.equals("true"); + this.isPublic = isPublic.equals("true"); + this.isInterface = isInterface.equals("true"); + this.isStatic = isStatic.equals("true"); + this.isSuper = isSuper.equals("true"); + } + } + + @NotNull + private static final Pattern isAbstract = Pattern.compile("^// ACC_ABSTRACT : *(.*)"); + private static final Pattern isAnnotation = Pattern.compile("^// ACC_ANNOTATION : *(.*)"); + private static final Pattern isFinal = Pattern.compile("^// ACC_FINAL : *(.*)"); + private static final Pattern isInterface = Pattern.compile("^// ACC_INTERFACE : *(.*)"); + private static final Pattern isPublic = Pattern.compile("^// ACC_PUBLIC : *(.*)"); + private static final Pattern isStatic = Pattern.compile("^// ACC_STATIC : *(.*)"); + private static final Pattern isSuper = Pattern.compile("^// ACC_SUPER : *(.*)"); + + private Expectation parseExpectations() throws IOException { + List lines = Files.readLines(ktFile, Charset.forName("utf-8")); + for (int i = 0; i < lines.size() - 6; ++i) { + Matcher abstractMatcher = isAbstract.matcher(lines.get(i)); + if (abstractMatcher.matches()) { + Matcher annotationMatcher = isAnnotation.matcher(lines.get(i + 1)); + Matcher finalMatcher = isFinal.matcher(lines.get(i + 2)); + Matcher interfaceMatcher = isInterface.matcher(lines.get(i + 3)); + Matcher publicMatcher = isPublic.matcher(lines.get(i + 4)); + Matcher staticMatcher = isStatic.matcher(lines.get(i + 5)); + Matcher superMatcher = isSuper.matcher(lines.get(i + 6)); + + if (!interfaceMatcher.matches() + || !annotationMatcher.matches() + || !finalMatcher.matches() + || !interfaceMatcher.matches() + || !publicMatcher.matches() + || !staticMatcher.matches() + || !superMatcher.matches()) { + throw new AssertionError("Incorrect test instructions format"); + } + + return new Expectation(abstractMatcher.group(1), + annotationMatcher.group(1), + finalMatcher.group(1), + interfaceMatcher.group(1), + publicMatcher.group(1), + staticMatcher.group(1), + superMatcher.group(1)); + } + } + throw new AssertionError("test instructions not found in " + ktFile); + } +}