From cb9e90183a76f191755fe5a0eb6a047d900b5ed7 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Mon, 10 Apr 2017 22:34:50 +0300 Subject: [PATCH] Debugger: Fix AbstractPositionManagerTest MockReferenceType can't be used anymore because the new DebuggerClassNameProvider requires much more from it. The new SmartMockReferenceType implements methods such as nestedTypes() or allLineLocations() properly. Also, as PositionManager can return more than one class, we should check if any of the classes it returned matches the pattern. --- .../positionManager/JvmNameAnnotation.kt | 2 +- .../positionManager/anonymousNamedFunction.kt | 2 +- .../positionManager/extensionFunction.kt | 2 +- .../debugger/positionManager/localFunction.kt | 2 +- .../debugger/positionManager/package.kt | 2 +- .../topLevelPropertyInitializer.kt | 2 +- .../debugger/positionManager/twoClasses.kt | 6 +- .../debugger/AbstractPositionManagerTest.java | 24 +- .../idea/debugger/MockReferenceType.java | 262 ---------------- .../idea/debugger/SmartMockReferenceType.kt | 282 ++++++++++++++++++ 10 files changed, 303 insertions(+), 283 deletions(-) delete mode 100644 idea/tests/org/jetbrains/kotlin/idea/debugger/MockReferenceType.java create mode 100644 idea/tests/org/jetbrains/kotlin/idea/debugger/SmartMockReferenceType.kt 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 + } +} +