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.
This commit is contained in:
Yan Zhulanow
2017-04-10 22:34:50 +03:00
parent 622430a296
commit cb9e90183a
10 changed files with 303 additions and 283 deletions
@@ -2,5 +2,5 @@
package a
fun foo() {
"" // a/ABC
"" // a.ABC
}
@@ -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
}()
}
@@ -4,5 +4,5 @@ class A {
}
fun A.foo() {
"" // a/ExtensionFunctionKt
"" // a.ExtensionFunctionKt
}
+1 -1
View File
@@ -2,7 +2,7 @@ package test
fun foo(): String {
fun bar(): String {
return "" // test/LocalFunctionKt\$foo\$1
return "" // test.LocalFunctionKt\$foo\$1
}
return bar()
}
+1 -1
View File
@@ -1,5 +1,5 @@
package test
fun foo() {
"" // test/PackageKt
"" // test.PackageKt
}
@@ -1,3 +1,3 @@
package prop
val foo: Int = 5 // prop/TopLevelPropertyInitializerKt
val foo: Int = 5 // prop.TopLevelPropertyInitializerKt
+4 -2
View File
@@ -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
@@ -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<String, ReferenceType> getReferenceMap(OutputFileCollection outputFiles) {
Map<String, ReferenceType> 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<String, ReferenceType> referencesByName) {
@@ -238,13 +233,16 @@ public abstract class AbstractPositionManagerTest extends KotlinLightCodeInsight
SourcePosition position = SourcePosition.createFromLine(breakpoint.file, breakpoint.lineNumber);
List<ReferenceType> 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);
@@ -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<String> sourceNames(String s) throws AbsentInformationException {
throw new UnsupportedOperationException();
}
@Override
public List<String> 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<Field> fields() {
throw new UnsupportedOperationException();
}
@Override
public List<Field> visibleFields() {
throw new UnsupportedOperationException();
}
@Override
public List<Field> allFields() {
throw new UnsupportedOperationException();
}
@Override
public Field fieldByName(String s) {
throw new UnsupportedOperationException();
}
@Override
public List<Method> methods() {
throw new UnsupportedOperationException();
}
@Override
public List<Method> visibleMethods() {
throw new UnsupportedOperationException();
}
@Override
public List<Method> allMethods() {
throw new UnsupportedOperationException();
}
@Override
public List<Method> methodsByName(String s) {
throw new UnsupportedOperationException();
}
@Override
public List<Method> methodsByName(String s, String s1) {
throw new UnsupportedOperationException();
}
@Override
public List<ReferenceType> nestedTypes() {
throw new UnsupportedOperationException();
}
@Override
public Value getValue(Field field) {
throw new UnsupportedOperationException();
}
@Override
public Map<Field, Value> getValues(List<? extends Field> fields) {
throw new UnsupportedOperationException();
}
@Override
public ClassObjectReference classObject() {
throw new UnsupportedOperationException();
}
@Override
public List<Location> allLineLocations() throws AbsentInformationException {
throw new UnsupportedOperationException();
}
@Override
public List<Location> allLineLocations(String s, String s1) throws AbsentInformationException {
throw new UnsupportedOperationException();
}
@Override
public List<Location> locationsOfLine(int i) throws AbsentInformationException {
throw new UnsupportedOperationException();
}
@Override
public List<Location> locationsOfLine(String s, String s1, int i) throws AbsentInformationException {
throw new UnsupportedOperationException();
}
@Override
public List<String> availableStrata() {
return Collections.emptyList();
}
@Override
public String defaultStratum() {
throw new UnsupportedOperationException();
}
@Override
public List<ObjectReference> 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();
}
}
@@ -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<OutputFile>) {
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<ReferenceType> 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<ObjectReference>()
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<out Field>?) = TODO()
override fun nestedTypes(): List<ReferenceType> {
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<String>()
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 == "<clinit>"
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<Location> {
val instructionList = methodNode.instructions ?: return emptyList()
var current = instructionList.first
val locations = mutableListOf<Location>()
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 == "<init>"
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
}
}