IDEA-157065 "Jump to type source" doesn't work for lambdas

This commit is contained in:
Egor.Ushakov
2016-06-30 16:09:38 +03:00
parent fb989a963c
commit 8d187da5f4
2 changed files with 125 additions and 5 deletions
@@ -21,6 +21,8 @@ import com.intellij.debugger.engine.JVMNameUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.MethodBytecodeUtil;
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
import com.intellij.debugger.ui.tree.ValueDescriptor;
@@ -32,6 +34,11 @@ import com.intellij.psi.PsiClass;
import com.intellij.util.containers.ContainerUtil;
import com.sun.jdi.*;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
public class JumpToObjectAction extends DebuggerAction{
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.actions.JumpToObjectAction");
@@ -96,7 +103,35 @@ public class JumpToObjectAction extends DebuggerAction{
}
if(type instanceof ClassType) {
final ClassType clsType = (ClassType)type;
final Location location = ContainerUtil.getFirstItem(clsType.allLineLocations());
Location lambdaLocation = null;
if (DebuggerUtilsEx.isLambdaClassName(clsType.name())) {
List<Method> notConstructorMethods = ContainerUtil.filter(clsType.methods(), m -> !m.isConstructor());
if (notConstructorMethods.size() == 1) {
AtomicReference<Location> locationRef = new AtomicReference<>();
MethodBytecodeUtil.visit(clsType, notConstructorMethods.get(0), new MethodVisitor(Opcodes.ASM5) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
ReferenceType cls = ContainerUtil.getFirstItem(clsType.virtualMachine().classesByName(owner));
if (cls != null) {
Method method = ContainerUtil.getFirstItem(cls.methodsByName(name));
if (method != null) {
try {
Location loc = ContainerUtil.getFirstItem(method.allLineLocations());
if (loc != null) {
locationRef.set(loc);
}
}
catch (AbsentInformationException e) {
LOG.debug(e);
}
}
}
}
});
lambdaLocation = locationRef.get();
}
}
final Location location = lambdaLocation != null ? lambdaLocation : ContainerUtil.getFirstItem(clsType.allLineLocations());
if (location != null) {
return ApplicationManager.getApplication().runReadAction(new Computable<SourcePosition>() {
@Override
@@ -118,10 +153,7 @@ public class JumpToObjectAction extends DebuggerAction{
}
}
}
catch (ClassNotPreparedException e) {
LOG.debug(e);
}
catch (AbsentInformationException e) {
catch (ClassNotPreparedException | AbsentInformationException e) {
LOG.debug(e);
}
return null;
@@ -0,0 +1,88 @@
/*
* Copyright 2000-2016 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 com.intellij.debugger.jdi;
import com.sun.jdi.ClassType;
import com.sun.jdi.Method;
import com.sun.jdi.ReferenceType;
import org.jetbrains.org.objectweb.asm.*;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* @author egor
*/
public class MethodBytecodeUtil {
private MethodBytecodeUtil() {
}
/**
* Allows to use ASM MethodVisitor with jdi method bytecode
*/
public static void visit(ClassType classType, Method method, MethodVisitor methodVisitor) {
try {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos)) {
dos.writeInt(0xCAFEBABE); // magic
dos.writeInt(Opcodes.V1_8); // version
dos.writeShort(classType.constantPoolCount()); // constant_pool_count
dos.write(classType.constantPool()); // constant_pool
// we generate and put code attribute right after the constant pool
byte[] bytecodes = method.bytecodes();
int codeSize = dos.size();
dos.writeShort(0); // max_stack
dos.writeShort(0); // max_locals
dos.writeInt(bytecodes.length); // code_length
dos.write(bytecodes); // code
dos.writeShort(0); // exception_table_length
dos.writeShort(0); // attributes_count
codeSize = dos.size() - codeSize;
ClassReader clsReader = new ClassReader(bos.toByteArray());
ClassWriter clsWriter = new ClassWriter(clsReader, 0);
clsWriter.visit(Opcodes.V1_8,
Opcodes.ACC_PUBLIC,
classType.name(),
classType.signature(),
classType.superclass().name(),
classType.interfaces().stream().map(ReferenceType::name).toArray(String[]::new));
MethodVisitor mv = clsWriter.visitMethod(Opcodes.ACC_PUBLIC, method.name(), method.signature(), method.signature(), null);
mv.visitAttribute(createCode(clsReader, codeSize));
new ClassReader(clsWriter.toByteArray()).accept(new ClassVisitor(Opcodes.ASM5) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return methodVisitor;
}
}, 0);
}
}
catch (IOException ignored) {
}
}
private static Attribute createCode(ClassReader cr, int len) {
return new Attribute("Code") {
@Override
public Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
return super.read(cr, off, len, buf, codeOff, labels);
}
}.read(cr, cr.header, len, null, 0, null);
}
}