/* * Copyright 2010-2013 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.collect.Lists; import com.google.common.collect.Maps; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.org.objectweb.asm.*; import org.jetbrains.jet.*; import org.jetbrains.jet.cli.common.output.outputUtils.OutputUtilsPackage; import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; import org.jetbrains.jet.codegen.state.GenerationState; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.test.TestCaseWithTmpdir; import org.jetbrains.jet.utils.UtilsPackage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class LineNumberTest extends TestCaseWithTmpdir { private static final String LINE_NUMBER_FUN = "lineNumber"; private static final Pattern TEST_LINE_NUMBER_PATTERN = Pattern.compile("^.*test." + LINE_NUMBER_FUN + "\\(\\).*$"); @NotNull private static String getTestDataPath() { return JetTestCaseBuilder.getTestDataPathBase() + "/lineNumber"; } @NotNull private JetCoreEnvironment createEnvironment() { return JetCoreEnvironment.createForTests(myTestRootDisposable, JetTestUtils .compilerConfigurationForTests(ConfigurationKind.JDK_ONLY, TestJdkKind.MOCK_JDK, JetTestUtils.getAnnotationsJar(), tmpdir)); } @Override public void setUp() throws Exception { super.setUp(); JetCoreEnvironment environment = createEnvironment(); JetFile psiFile = JetTestUtils.createFile(LINE_NUMBER_FUN + ".kt", "package test;\n\npublic fun " + LINE_NUMBER_FUN + "(): Int = 0\n", environment.getProject()); OutputFileCollection outputFiles = GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile); OutputUtilsPackage.writeAllTo(outputFiles, tmpdir); } @NotNull private JetFile createPsiFile(@NotNull String filename) { File file = new File(getTestDataPath() + "/" + filename); JetCoreEnvironment environment = createEnvironment(); String text; try { text = FileUtil.loadFile(file, true); } catch (IOException e) { throw UtilsPackage.rethrow(e); } return JetTestUtils.createFile(file.getName(), text, environment.getProject()); } private void doTest(@NotNull String filename, boolean custom) { JetFile psiFile = createPsiFile(filename); GenerationState state = GenerationUtils.compileFileGetGenerationStateForTest(psiFile); List expectedLineNumbers; List actualLineNumbers; if (custom) { expectedLineNumbers = extractCustomLineNumbersFromSource(psiFile); actualLineNumbers = extractActualLineNumbersFromBytecode(state, false); assertEquals(expectedLineNumbers, actualLineNumbers); } else { expectedLineNumbers = extractSelectedLineNumbersFromSource(psiFile); actualLineNumbers = extractActualLineNumbersFromBytecode(state, true); assertSameElements(actualLineNumbers, expectedLineNumbers); } } @NotNull private static List extractActualLineNumbersFromBytecode(@NotNull GenerationState state, boolean testFunInvoke) { ClassFileFactory factory = state.getFactory(); List actualLineNumbers = Lists.newArrayList(); for (OutputFile outputFile : factory.asList()) { if (PackageClassUtils.isPackageClassFqName(new FqName(FileUtil.getNameWithoutExtension(outputFile.getRelativePath())))) { // Don't test line numbers in *Package facade classes continue; } ClassReader cr = new ClassReader(outputFile.asByteArray()); try { List lineNumbers = testFunInvoke ? readTestFunLineNumbers(cr) : readAllLineNumbers(cr); actualLineNumbers.addAll(lineNumbers); } catch (Throwable e) { System.out.println(factory.createText()); throw UtilsPackage.rethrow(e); } } return actualLineNumbers; } private void doTest() { doTest(getTestName(true) + ".kt", false); } private void doTestCustom() { doTest("custom/" + getTestName(true) + ".kt", true); } @NotNull private static List extractCustomLineNumbersFromSource(@NotNull JetFile file) { String fileContent = file.getText(); List lineNumbers = Lists.newArrayList(); String[] lines = StringUtil.convertLineSeparators(fileContent).split("\n"); for (String line : lines) { if (line.startsWith("//")) { String[] numbers = line.substring("//".length()).trim().split(" +"); for (String number : numbers) { lineNumbers.add(Integer.parseInt(number)); } } } return lineNumbers; } @NotNull private static List extractSelectedLineNumbersFromSource(@NotNull JetFile file) { String fileContent = file.getText(); List lineNumbers = Lists.newArrayList(); String[] lines = StringUtil.convertLineSeparators(fileContent).split("\n"); for (int i = 0; i < lines.length; i++) { Matcher matcher = TEST_LINE_NUMBER_PATTERN.matcher(lines[i]); if (matcher.matches()) { lineNumbers.add(i + 1); } } return lineNumbers; } @NotNull private static List readTestFunLineNumbers(@NotNull ClassReader cr) { final List