diff --git a/idea/testData/debugger/positionManager/JvmNameAnnotation.kt b/idea/testData/debugger/positionManager/JvmNameAnnotation.kt index 324eeb484ef..dafc9605851 100644 --- a/idea/testData/debugger/positionManager/JvmNameAnnotation.kt +++ b/idea/testData/debugger/positionManager/JvmNameAnnotation.kt @@ -2,5 +2,5 @@ package a fun foo() { - "" // a/ABC + "" // a.ABC } diff --git a/idea/testData/debugger/positionManager/anonymousNamedFunction.kt b/idea/testData/debugger/positionManager/anonymousNamedFunction.kt index 673ec0883d4..c5512f76982 100644 --- a/idea/testData/debugger/positionManager/anonymousNamedFunction.kt +++ b/idea/testData/debugger/positionManager/anonymousNamedFunction.kt @@ -2,6 +2,6 @@ package insertInBlock fun foo() { val lambda = { - val a = 1 // insertInBlock/AnonymousNamedFunctionKt\$foo\$lambda\$1 + val a = 1 // insertInBlock.AnonymousNamedFunctionKt\$foo\$lambda\$1 }() } diff --git a/idea/testData/debugger/positionManager/extensionFunction.kt b/idea/testData/debugger/positionManager/extensionFunction.kt index 4c1088529aa..69383ee40e7 100644 --- a/idea/testData/debugger/positionManager/extensionFunction.kt +++ b/idea/testData/debugger/positionManager/extensionFunction.kt @@ -4,5 +4,5 @@ class A { } fun A.foo() { - "" // a/ExtensionFunctionKt + "" // a.ExtensionFunctionKt } diff --git a/idea/testData/debugger/positionManager/localFunction.kt b/idea/testData/debugger/positionManager/localFunction.kt index b2cec05da20..5a9c10b6fb4 100644 --- a/idea/testData/debugger/positionManager/localFunction.kt +++ b/idea/testData/debugger/positionManager/localFunction.kt @@ -2,7 +2,7 @@ package test fun foo(): String { fun bar(): String { - return "" // test/LocalFunctionKt\$foo\$1 + return "" // test.LocalFunctionKt\$foo\$1 } return bar() } diff --git a/idea/testData/debugger/positionManager/package.kt b/idea/testData/debugger/positionManager/package.kt index 04b78232954..525bc7ab2a6 100644 --- a/idea/testData/debugger/positionManager/package.kt +++ b/idea/testData/debugger/positionManager/package.kt @@ -1,5 +1,5 @@ package test fun foo() { - "" // test/PackageKt + "" // test.PackageKt } diff --git a/idea/testData/debugger/positionManager/topLevelPropertyInitializer.kt b/idea/testData/debugger/positionManager/topLevelPropertyInitializer.kt index b0852e43bf3..0328442e472 100644 --- a/idea/testData/debugger/positionManager/topLevelPropertyInitializer.kt +++ b/idea/testData/debugger/positionManager/topLevelPropertyInitializer.kt @@ -1,3 +1,3 @@ package prop -val foo: Int = 5 // prop/TopLevelPropertyInitializerKt +val foo: Int = 5 // prop.TopLevelPropertyInitializerKt diff --git a/idea/testData/debugger/positionManager/twoClasses.kt b/idea/testData/debugger/positionManager/twoClasses.kt index caec305f62f..e24da09aed9 100644 --- a/idea/testData/debugger/positionManager/twoClasses.kt +++ b/idea/testData/debugger/positionManager/twoClasses.kt @@ -1,2 +1,4 @@ -class A { fun f() {} } // A -class B { fun g() {} } // B +package test + +class A { fun f() {} } // test.A +class B { fun g() {} } // test.B diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractPositionManagerTest.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractPositionManagerTest.java index b1ce13aaf90..fef23b739fa 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractPositionManagerTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractPositionManagerTest.java @@ -64,6 +64,7 @@ import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; public abstract class AbstractPositionManagerTest extends KotlinLightCodeInsightFixtureTestCase { // Breakpoint is given as a line comment on a specific line, containing the regexp to match the name of the class where that line @@ -204,13 +205,7 @@ public abstract class AbstractPositionManagerTest extends KotlinLightCodeInsight } private static Map getReferenceMap(OutputFileCollection outputFiles) { - Map referencesByName = Maps.newHashMap(); - for (OutputFile outputFile : outputFiles.asList()) { - String classFileName = outputFile.getRelativePath(); - String name = classFileName.substring(0, classFileName.lastIndexOf('.')); - referencesByName.put(name, new MockReferenceType(name)); - } - return referencesByName; + return new SmartMockReferenceTypeContext(outputFiles).getReferenceTypesByName(); } private DebugProcessEvents createDebugProcess(final Map referencesByName) { @@ -238,13 +233,16 @@ public abstract class AbstractPositionManagerTest extends KotlinLightCodeInsight SourcePosition position = SourcePosition.createFromLine(breakpoint.file, breakpoint.lineNumber); List classes = positionManager.getAllClasses(position); assertNotNull(classes); - assertEquals(1, classes.size()); - ReferenceType type = classes.get(0); - assertTrue("Type name " + type.name() + " doesn't match " + breakpoint.classNameRegexp + " for line " + (breakpoint.lineNumber + 1), - type.name().matches(breakpoint.classNameRegexp)); + assertFalse("Classes not found for line " + (breakpoint.lineNumber + 1) + ", expected " + breakpoint.classNameRegexp, + classes.isEmpty()); - // JDI names are of form "package.Class$InnerClass" - ReferenceType typeWithFqName = new MockReferenceType(type.name().replace('/', '.')); + if (classes.stream().noneMatch(clazz -> clazz.name().matches(breakpoint.classNameRegexp))) { + throw new AssertionError("Breakpoint class '" + breakpoint.classNameRegexp + + "' from line " + (breakpoint.lineNumber + 1) + " was not found in the PositionManager classes names: " + + classes.stream().map(ReferenceType::name).collect(Collectors.joining(","))); + } + + ReferenceType typeWithFqName = classes.get(0); Location location = new MockLocation(typeWithFqName, breakpoint.file.getName(), breakpoint.lineNumber + 1); SourcePosition actualPosition = positionManager.getSourcePosition(location); diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/MockReferenceType.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/MockReferenceType.java deleted file mode 100644 index d72be66c1c4..00000000000 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/MockReferenceType.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2010-2015 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.kotlin.idea.debugger; - -import com.sun.jdi.*; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class MockReferenceType implements ReferenceType { - private final String name; - - public MockReferenceType(String name) { - this.name = name; - } - - @Override - public String name() { - return name; - } - - - @Override - public String signature() { - throw new UnsupportedOperationException(); - } - - @Override - public String genericSignature() { - throw new UnsupportedOperationException(); - } - - @Override - public ClassLoaderReference classLoader() { - throw new UnsupportedOperationException(); - } - - @Override - public String sourceName() throws AbsentInformationException { - throw new UnsupportedOperationException(); - } - - @Override - public List sourceNames(String s) throws AbsentInformationException { - throw new UnsupportedOperationException(); - } - - @Override - public List sourcePaths(String s) throws AbsentInformationException { - throw new UnsupportedOperationException(); - } - - @Override - public String sourceDebugExtension() throws AbsentInformationException { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isStatic() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isAbstract() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isFinal() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPrepared() { - return false; - } - - @Override - public boolean isVerified() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isInitialized() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean failedToInitialize() { - throw new UnsupportedOperationException(); - } - - @Override - public List fields() { - throw new UnsupportedOperationException(); - } - - @Override - public List visibleFields() { - throw new UnsupportedOperationException(); - } - - @Override - public List allFields() { - throw new UnsupportedOperationException(); - } - - @Override - public Field fieldByName(String s) { - throw new UnsupportedOperationException(); - } - - @Override - public List methods() { - throw new UnsupportedOperationException(); - } - - @Override - public List visibleMethods() { - throw new UnsupportedOperationException(); - } - - @Override - public List allMethods() { - throw new UnsupportedOperationException(); - } - - @Override - public List methodsByName(String s) { - throw new UnsupportedOperationException(); - } - - @Override - public List methodsByName(String s, String s1) { - throw new UnsupportedOperationException(); - } - - @Override - public List nestedTypes() { - throw new UnsupportedOperationException(); - } - - @Override - public Value getValue(Field field) { - throw new UnsupportedOperationException(); - } - - @Override - public Map getValues(List fields) { - throw new UnsupportedOperationException(); - } - - @Override - public ClassObjectReference classObject() { - throw new UnsupportedOperationException(); - } - - @Override - public List allLineLocations() throws AbsentInformationException { - throw new UnsupportedOperationException(); - } - - @Override - public List allLineLocations(String s, String s1) throws AbsentInformationException { - throw new UnsupportedOperationException(); - } - - @Override - public List locationsOfLine(int i) throws AbsentInformationException { - throw new UnsupportedOperationException(); - } - - @Override - public List locationsOfLine(String s, String s1, int i) throws AbsentInformationException { - throw new UnsupportedOperationException(); - } - - @Override - public List availableStrata() { - return Collections.emptyList(); - } - - @Override - public String defaultStratum() { - throw new UnsupportedOperationException(); - } - - @Override - public List instances(long l) { - throw new UnsupportedOperationException(); - } - - @Override - public int majorVersion() { - throw new UnsupportedOperationException(); - } - - @Override - public int minorVersion() { - throw new UnsupportedOperationException(); - } - - @Override - public int constantPoolCount() { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] constantPool() { - throw new UnsupportedOperationException(); - } - - @Override - public int modifiers() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPrivate() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPackagePrivate() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isProtected() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPublic() { - throw new UnsupportedOperationException(); - } - - @Override - public int compareTo(ReferenceType o) { - throw new UnsupportedOperationException(); - } - - @Override - public VirtualMachine virtualMachine() { - throw new UnsupportedOperationException(); - } -} diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/SmartMockReferenceType.kt b/idea/tests/org/jetbrains/kotlin/idea/debugger/SmartMockReferenceType.kt new file mode 100644 index 00000000000..6645e157c6e --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/SmartMockReferenceType.kt @@ -0,0 +1,282 @@ +/* + * Copyright 2010-2017 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.kotlin.idea.debugger + +import com.sun.jdi.* +import org.jetbrains.kotlin.backend.common.output.OutputFile +import org.jetbrains.kotlin.backend.common.output.OutputFileCollection +import org.jetbrains.org.objectweb.asm.ClassReader +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.tree.ClassNode +import org.jetbrains.org.objectweb.asm.tree.LineNumberNode +import org.jetbrains.org.objectweb.asm.tree.MethodNode + +class SmartMockReferenceTypeContext(outputFiles: List) { + constructor(outputFiles: OutputFileCollection) : this(outputFiles.asList()) + + val virtualMachine = MockVirtualMachine() + + val classes = outputFiles.filter { it.relativePath.endsWith(".class") }.map { file -> + ClassNode().also { ClassReader(file.asByteArray()).accept(it, ClassReader.EXPAND_FRAMES) } + } + + val referenceTypes: List by lazy { classes.map { SmartMockReferenceType(it, this) } } + + val referenceTypesByName by lazy { referenceTypes.map { Pair(it.name(), it) }.toMap() } +} + +class SmartMockReferenceType(val classNode: ClassNode, private val context: SmartMockReferenceTypeContext) : ReferenceType { + override fun instances(maxInstances: Long) = emptyList() + + override fun isPublic() = (classNode.access and Opcodes.ACC_PUBLIC) != 0 + + override fun classLoader() = null + + override fun sourceName(): String? = classNode.sourceFile + + override fun fields() = TODO() + + override fun defaultStratum() = "Java" + + override fun isVerified() = true + + override fun allFields() = TODO() + + override fun isPackagePrivate() = (classNode.access and Opcodes.ACC_PUBLIC) == 0 + && (classNode.access and Opcodes.ACC_PROTECTED) == 0 + && (classNode.access and Opcodes.ACC_PRIVATE) == 0 + + override fun isStatic() = (classNode.access and Opcodes.ACC_STATIC) != 0 + + override fun fieldByName(fieldName: String) = TODO() + + override fun getValue(p0: Field?) = TODO() + + private val methodsCached by lazy { classNode.methods.map { MockMethod(it, this) } } + + override fun methods() = methodsCached + + override fun visibleFields() = TODO() + + override fun modifiers() = classNode.access + + override fun isProtected() = (classNode.access and Opcodes.ACC_PROTECTED) != 0 + + override fun isFinal() = (classNode.access and Opcodes.ACC_FINAL) != 0 + + override fun allLineLocations() = methodsCached.flatMap { it.allLineLocations() } + + override fun allLineLocations(stratum: String, sourceName: String) = TODO() + + override fun genericSignature(): String? = classNode.signature + + override fun majorVersion() = TODO() + + override fun constantPoolCount() = TODO() + + override fun constantPool() = TODO() + + override fun isAbstract() = (classNode.access and Opcodes.ACC_ABSTRACT) != 0 + + override fun compareTo(other: ReferenceType?) = TODO() + + override fun sourceDebugExtension() = TODO() + + override fun visibleMethods() = TODO() + + override fun isPrepared() = true + + override fun name() = classNode.name.replace('/', '.') + + override fun isInitialized() = true + + override fun locationsOfLine(lineNumber: Int) = TODO() + + override fun locationsOfLine(stratum: String, sourceName: String, lineNumber: Int) = TODO() + + override fun getValues(p0: MutableList?) = TODO() + + override fun nestedTypes(): List { + val fromInnerClasses = classNode.innerClasses + .filter { it.outerName == classNode.name } + .mapNotNull { context.classes.find { c -> it.name == c.name } } + + val fromOuterClasses = context.classes.filter { it.outerClass == classNode.name } + + return (fromInnerClasses + fromOuterClasses).distinctBy { it.name }.map { SmartMockReferenceType(it, context) } + } + + override fun sourcePaths(stratum: String) = listOf(classNode.sourceFile) + + override fun failedToInitialize() = false + + override fun virtualMachine() = context.virtualMachine + + override fun minorVersion() = TODO() + + override fun classObject() = TODO() + + override fun isPrivate() = (classNode.access and Opcodes.ACC_PRIVATE) != 0 + + override fun signature(): String? = classNode.signature + + override fun sourceNames(stratum: String) = listOf(classNode.sourceFile) + + override fun methodsByName(p0: String?) = TODO() + + override fun methodsByName(p0: String?, p1: String?) = TODO() + + override fun availableStrata() = emptyList() + + override fun allMethods() = TODO() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other?.javaClass != javaClass) return false + + other as SmartMockReferenceType + + return classNode.name == other.classNode.name + + } + + override fun hashCode(): Int { + return classNode.name.hashCode() + } + + class MockMethod(val methodNode: MethodNode, val containingClass: SmartMockReferenceType) : Method { + override fun isStaticInitializer() = methodNode.name == "" + + override fun isPublic() = (methodNode.access and Opcodes.ACC_PUBLIC) != 0 + + override fun argumentTypeNames() = TODO() + + override fun isNative() = (methodNode.access and Opcodes.ACC_NATIVE) != 0 + + override fun arguments() = TODO() + + override fun location(): Location? { + val instructionList = methodNode.instructions ?: return null + var current = instructionList.first + while (current != null) { + if (current is LineNumberNode) { + return MockLocation(this, current.line) + } + current = current.next + } + return null + } + + override fun isPackagePrivate() = (methodNode.access and Opcodes.ACC_PUBLIC) == 0 + && (methodNode.access and Opcodes.ACC_PROTECTED) == 0 + && (methodNode.access and Opcodes.ACC_PRIVATE) == 0 + + override fun isStatic() = (methodNode.access and Opcodes.ACC_STATIC) != 0 + + override fun modifiers() = methodNode.access + + override fun isBridge() = (methodNode.access and Opcodes.ACC_BRIDGE) != 0 + + override fun isProtected() = (methodNode.access and Opcodes.ACC_PROTECTED) != 0 + + override fun isFinal() = (methodNode.access and Opcodes.ACC_FINAL) != 0 + + override fun allLineLocations(): List { + val instructionList = methodNode.instructions ?: return emptyList() + var current = instructionList.first + val locations = mutableListOf() + while (current != null) { + if (current is LineNumberNode) { + locations += MockLocation(this, current.line) + } + current = current.next + } + return locations + } + + override fun allLineLocations(p0: String?, p1: String?) = TODO() + + override fun genericSignature() = TODO() + + override fun isAbstract() = (methodNode.access and Opcodes.ACC_ABSTRACT) != 0 + + override fun returnType() = TODO() + + override fun compareTo(other: Method?) = TODO() + + override fun isObsolete() = false + + override fun variablesByName(p0: String?) = TODO() + + override fun declaringType() = containingClass + + override fun argumentTypes() = TODO() + + override fun locationOfCodeIndex(p0: Long) = TODO() + + override fun bytecodes() = TODO() + + override fun name(): String? = methodNode.name + + override fun returnTypeName() = TODO() + + override fun locationsOfLine(p0: Int) = TODO() + + override fun locationsOfLine(p0: String?, p1: String?, p2: Int) = TODO() + + override fun variables() = TODO() + + override fun isVarArgs() = TODO() + + override fun isSynthetic() = (methodNode.access and Opcodes.ACC_SYNTHETIC) != 0 + + override fun isConstructor() = methodNode.name == "" + + override fun virtualMachine() = containingClass.context.virtualMachine + + override fun isSynchronized() = TODO() + + override fun isPrivate() = (methodNode.access and Opcodes.ACC_PRIVATE) != 0 + + override fun signature(): String? = methodNode.signature + } + + private class MockLocation(val method: MockMethod, val line: Int) : Location { + override fun sourceName() = method.containingClass.sourceName() + + override fun sourceName(stratum: String) = TODO() + + override fun codeIndex() = TODO() + + override fun lineNumber() = line + + override fun lineNumber(stratum: String) = TODO() + + override fun virtualMachine() = method.containingClass.context.virtualMachine + + override fun compareTo(other: Location?) = TODO() + + override fun sourcePath() = sourceName() + + override fun sourcePath(stratum: String) = TODO() + + override fun declaringType() = method.containingClass + + override fun method() = method + } +} +