Source mapping generation on code compilation
This commit is contained in:
@@ -20,10 +20,9 @@ import com.intellij.openapi.progress.ProcessCanceledException;
|
|||||||
import kotlin.Function0;
|
import kotlin.Function0;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||||
import org.jetbrains.kotlin.codegen.context.*;
|
import org.jetbrains.kotlin.codegen.context.*;
|
||||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
|
import org.jetbrains.kotlin.codegen.inline.*;
|
||||||
import org.jetbrains.kotlin.codegen.inline.NameGenerator;
|
|
||||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages;
|
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
@@ -81,6 +80,8 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
|
|||||||
protected ExpressionCodegen clInit;
|
protected ExpressionCodegen clInit;
|
||||||
private NameGenerator inlineNameGenerator;
|
private NameGenerator inlineNameGenerator;
|
||||||
|
|
||||||
|
private SourceMapper sourceMapper;
|
||||||
|
|
||||||
public MemberCodegen(
|
public MemberCodegen(
|
||||||
@NotNull GenerationState state,
|
@NotNull GenerationState state,
|
||||||
@Nullable MemberCodegen<?> parentCodegen,
|
@Nullable MemberCodegen<?> parentCodegen,
|
||||||
@@ -135,8 +136,16 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
|
|||||||
FunctionCodegen.endVisit(clInit.v, "static initializer", element);
|
FunctionCodegen.endVisit(clInit.v, "static initializer", element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
writeInnerClasses();
|
writeInnerClasses();
|
||||||
|
|
||||||
|
if (sourceMapper != null) {
|
||||||
|
List<RawFileMapping> mapping = sourceMapper.getFileMapping();
|
||||||
|
for (RawFileMapping fileMapping : mapping) {
|
||||||
|
v.addSMAP(fileMapping.toFileMapping());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
v.done();
|
v.done();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +207,8 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
|
|||||||
|
|
||||||
if (aClass instanceof JetClass && ((JetClass) aClass).isTrait()) {
|
if (aClass instanceof JetClass && ((JetClass) aClass).isTrait()) {
|
||||||
Type traitImplType = state.getTypeMapper().mapTraitImpl(descriptor);
|
Type traitImplType = state.getTypeMapper().mapTraitImpl(descriptor);
|
||||||
ClassBuilder traitImplBuilder = state.getFactory().newVisitor(TraitImpl(aClass, descriptor), traitImplType, aClass.getContainingFile());
|
ClassBuilder traitImplBuilder = state.getFactory().newVisitor(TraitImpl(aClass, descriptor), traitImplType,
|
||||||
|
aClass.getContainingFile());
|
||||||
ClassContext traitImplContext = parentContext.intoClass(descriptor, OwnerKind.TRAIT_IMPL, state);
|
ClassContext traitImplContext = parentContext.intoClass(descriptor, OwnerKind.TRAIT_IMPL, state);
|
||||||
new TraitImplBodyCodegen(aClass, traitImplContext, traitImplBuilder, state, parentCodegen).generate();
|
new TraitImplBodyCodegen(aClass, traitImplContext, traitImplBuilder, state, parentCodegen).generate();
|
||||||
}
|
}
|
||||||
@@ -505,8 +515,23 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
|
|||||||
return parentCodegen;
|
return parentCodegen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return context.toString();
|
return context.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addSMAP(FileMapping fm) {
|
||||||
|
v.addSMAP(fm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourceMapper getSourceMapper() {
|
||||||
|
if (sourceMapper == null) {
|
||||||
|
assert element != null : "Couldn't create source mapper for null element " + this;
|
||||||
|
Integer lineNumbers = CodegenUtil.getLineNumberForElement(element.getContainingFile(), true);
|
||||||
|
assert lineNumbers != null : "Couldn't extract line count in " + element.getContainingFile();
|
||||||
|
sourceMapper = new SourceMapper(lineNumbers);
|
||||||
|
}
|
||||||
|
return sourceMapper;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -182,7 +182,8 @@ public class AnonymousObjectTransformer {
|
|||||||
parentRemapper);
|
parentRemapper);
|
||||||
|
|
||||||
MethodInliner inliner = new MethodInliner(sourceNode, parameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")),
|
MethodInliner inliner = new MethodInliner(sourceNode, parameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")),
|
||||||
remapper, isSameModule, "Transformer for " + anonymousObjectGen.getOwnerInternalName());
|
remapper, isSameModule, "Transformer for " + anonymousObjectGen.getOwnerInternalName(),
|
||||||
|
null);
|
||||||
|
|
||||||
InlineResult result = inliner.doInline(resultVisitor, new LocalVarRemapper(parameters, 0), false, LabelOwner.NOT_APPLICABLE);
|
InlineResult result = inliner.doInline(resultVisitor, new LocalVarRemapper(parameters, 0), false, LabelOwner.NOT_APPLICABLE);
|
||||||
result.getReifiedTypeParametersUsages().mergeAll(typeParametersToReify);
|
result.getReifiedTypeParametersUsages().mergeAll(typeParametersToReify);
|
||||||
@@ -270,7 +271,8 @@ public class AnonymousObjectTransformer {
|
|||||||
parentRemapper);
|
parentRemapper);
|
||||||
|
|
||||||
MethodInliner inliner = new MethodInliner(constructor, constructorParameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")),
|
MethodInliner inliner = new MethodInliner(constructor, constructorParameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")),
|
||||||
remapper, isSameModule, "Transformer for constructor of " + anonymousObjectGen.getOwnerInternalName());
|
remapper, isSameModule, "Transformer for constructor of " + anonymousObjectGen.getOwnerInternalName(),
|
||||||
|
null);
|
||||||
InlineResult result = inliner.doInline(capturedFieldInitializer, new LocalVarRemapper(constructorParameters, 0), false,
|
InlineResult result = inliner.doInline(capturedFieldInitializer, new LocalVarRemapper(constructorParameters, 0), false,
|
||||||
LabelOwner.NOT_APPLICABLE);
|
LabelOwner.NOT_APPLICABLE);
|
||||||
constructorVisitor.visitMaxs(-1, -1);
|
constructorVisitor.visitMaxs(-1, -1);
|
||||||
|
|||||||
@@ -27,16 +27,18 @@ import java.util.List;
|
|||||||
public class InlineAdapter extends InstructionAdapter {
|
public class InlineAdapter extends InstructionAdapter {
|
||||||
|
|
||||||
private int nextLocalIndex = 0;
|
private int nextLocalIndex = 0;
|
||||||
|
private final SourceMapper sourceMapper;
|
||||||
|
|
||||||
private boolean isInlining = false;
|
private boolean isLambdaInlining = false;
|
||||||
|
|
||||||
private final List<CatchBlock> blocks = new ArrayList<CatchBlock>();
|
private final List<CatchBlock> blocks = new ArrayList<CatchBlock>();
|
||||||
|
|
||||||
private int nextLocalIndexBeforeInline = -1;
|
private int nextLocalIndexBeforeInline = -1;
|
||||||
|
|
||||||
public InlineAdapter(MethodVisitor mv, int localsSize) {
|
public InlineAdapter(MethodVisitor mv, int localsSize, SourceMapper sourceMapper) {
|
||||||
super(InlineCodegenUtil.API, mv);
|
super(InlineCodegenUtil.API, mv);
|
||||||
nextLocalIndex = localsSize;
|
nextLocalIndex = localsSize;
|
||||||
|
this.sourceMapper = sourceMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -63,7 +65,7 @@ public class InlineAdapter extends InstructionAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setLambdaInlining(boolean isInlining) {
|
public void setLambdaInlining(boolean isInlining) {
|
||||||
this.isInlining = isInlining;
|
this.isLambdaInlining = isInlining;
|
||||||
if (isInlining) {
|
if (isInlining) {
|
||||||
nextLocalIndexBeforeInline = nextLocalIndex;
|
nextLocalIndexBeforeInline = nextLocalIndex;
|
||||||
} else {
|
} else {
|
||||||
@@ -74,7 +76,7 @@ public class InlineAdapter extends InstructionAdapter {
|
|||||||
@Override
|
@Override
|
||||||
public void visitTryCatchBlock(Label start,
|
public void visitTryCatchBlock(Label start,
|
||||||
Label end, Label handler, String type) {
|
Label end, Label handler, String type) {
|
||||||
if(!isInlining) {
|
if(!isLambdaInlining) {
|
||||||
blocks.add(new CatchBlock(start, end, handler, type));
|
blocks.add(new CatchBlock(start, end, handler, type));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -82,6 +84,17 @@ public class InlineAdapter extends InstructionAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitLineNumber(int line, Label start) {
|
||||||
|
if (!isLambdaInlining) {
|
||||||
|
if (sourceMapper != null) {
|
||||||
|
sourceMapper.visitLineNumber(mv, line, start);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.visitLineNumber(line, start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitMaxs(int stack, int locals) {
|
public void visitMaxs(int stack, int locals) {
|
||||||
for (CatchBlock b : blocks) {
|
for (CatchBlock b : blocks) {
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ package org.jetbrains.kotlin.codegen.inline;
|
|||||||
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
|
import com.intellij.psi.PsiFile;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||||
import org.jetbrains.kotlin.builtins.InlineStrategy;
|
import org.jetbrains.kotlin.builtins.InlineStrategy;
|
||||||
import org.jetbrains.kotlin.builtins.InlineUtil;
|
import org.jetbrains.kotlin.builtins.InlineUtil;
|
||||||
import org.jetbrains.kotlin.codegen.*;
|
import org.jetbrains.kotlin.codegen.*;
|
||||||
@@ -50,10 +52,7 @@ import org.jetbrains.org.objectweb.asm.tree.MethodNode;
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode;
|
import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.ListIterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
|
||||||
@@ -82,6 +81,8 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
|
|
||||||
private LambdaInfo activeLambda;
|
private LambdaInfo activeLambda;
|
||||||
|
|
||||||
|
private SourceMapper sourceMapper;
|
||||||
|
|
||||||
public InlineCodegen(
|
public InlineCodegen(
|
||||||
@NotNull ExpressionCodegen codegen,
|
@NotNull ExpressionCodegen codegen,
|
||||||
@NotNull GenerationState state,
|
@NotNull GenerationState state,
|
||||||
@@ -109,6 +110,11 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
this.asFunctionInline = false;
|
this.asFunctionInline = false;
|
||||||
|
|
||||||
isSameModule = JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), state.getOutDirectory());
|
isSameModule = JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), state.getOutDirectory());
|
||||||
|
|
||||||
|
Integer lineNumbers = CodegenUtil.getLineNumberForElement(callElement.getContainingFile(), true);
|
||||||
|
assert lineNumbers != null : "Couldn't extract line count in " + callElement.getContainingFile();
|
||||||
|
|
||||||
|
sourceMapper = codegen.getParentCodegen().getSourceMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -120,11 +126,11 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void genCallInner(@NotNull CallableMethod callableMethod, @Nullable ResolvedCall<?> resolvedCall, boolean callDefault, @NotNull ExpressionCodegen codegen) {
|
public void genCallInner(@NotNull CallableMethod callableMethod, @Nullable ResolvedCall<?> resolvedCall, boolean callDefault, @NotNull ExpressionCodegen codegen) {
|
||||||
MethodNode node = null;
|
SMAPAndMethodNode nodeAndSmap = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
node = createMethodNode(callDefault);
|
nodeAndSmap = createMethodNode(callDefault);
|
||||||
endCall(inlineCall(node));
|
endCall(inlineCall(nodeAndSmap));
|
||||||
}
|
}
|
||||||
catch (CompilationException e) {
|
catch (CompilationException e) {
|
||||||
throw e;
|
throw e;
|
||||||
@@ -135,7 +141,7 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
throw new CompilationException("Couldn't inline method call '" +
|
throw new CompilationException("Couldn't inline method call '" +
|
||||||
functionDescriptor.getName() +
|
functionDescriptor.getName() +
|
||||||
"' into \n" + (element != null ? element.getText() : "null psi element " + this.codegen.getContext().getContextDescriptor()) +
|
"' into \n" + (element != null ? element.getText() : "null psi element " + this.codegen.getContext().getContextDescriptor()) +
|
||||||
(generateNodeText ? ("\ncause: " + InlineCodegenUtil.getNodeText(node)) : ""),
|
(generateNodeText ? ("\ncause: " + InlineCodegenUtil.getNodeText(nodeAndSmap != null ? nodeAndSmap.getNode(): null)) : ""),
|
||||||
e, callElement);
|
e, callElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +157,7 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private MethodNode createMethodNode(boolean callDefault) throws ClassNotFoundException, IOException {
|
private SMAPAndMethodNode createMethodNode(boolean callDefault) throws ClassNotFoundException, IOException {
|
||||||
JvmMethodSignature jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind());
|
JvmMethodSignature jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind());
|
||||||
|
|
||||||
Method asmMethod;
|
Method asmMethod;
|
||||||
@@ -162,23 +168,24 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
asmMethod = jvmSignature.getAsmMethod();
|
asmMethod = jvmSignature.getAsmMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodNode node;
|
SMAPAndMethodNode nodeAndSMAP;
|
||||||
if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
|
if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
|
||||||
VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor, state);
|
VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor, state);
|
||||||
node = InlineCodegenUtil.getMethodNode(file.contentsToByteArray(), asmMethod.getName(), asmMethod.getDescriptor());
|
nodeAndSMAP = InlineCodegenUtil.getMethodNode(file.contentsToByteArray(), asmMethod.getName(), asmMethod.getDescriptor());
|
||||||
|
|
||||||
if (node == null) {
|
if (nodeAndSMAP == null) {
|
||||||
throw new RuntimeException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
|
throw new RuntimeException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
|
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
|
||||||
|
Type ownerType = typeMapper.mapOwner(functionDescriptor, false);
|
||||||
|
|
||||||
if (element == null) {
|
if (element == null) {
|
||||||
throw new RuntimeException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
|
throw new RuntimeException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
node = new MethodNode(InlineCodegenUtil.API,
|
MethodNode node = new MethodNode(InlineCodegenUtil.API,
|
||||||
getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
|
getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
|
||||||
asmMethod.getName(),
|
asmMethod.getName(),
|
||||||
asmMethod.getDescriptor(),
|
asmMethod.getDescriptor(),
|
||||||
@@ -188,22 +195,32 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
//for maxLocals calculation
|
//for maxLocals calculation
|
||||||
MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
|
MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
|
||||||
MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor);
|
MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor);
|
||||||
|
SMAP smap;
|
||||||
if (callDefault) {
|
if (callDefault) {
|
||||||
|
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen());
|
||||||
FunctionCodegen.generateDefaultImplBody(
|
FunctionCodegen.generateDefaultImplBody(
|
||||||
methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
||||||
(JetNamedFunction) element, codegen.getParentCodegen()
|
(JetNamedFunction) element, parentCodegen
|
||||||
);
|
);
|
||||||
|
|
||||||
|
smap = new SMAP(parentCodegen.mappings);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, (JetDeclarationWithBody) element, jvmSignature);
|
smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, (JetDeclarationWithBody) element,
|
||||||
|
jvmSignature);
|
||||||
}
|
}
|
||||||
|
PsiFile file = element.getContainingFile();
|
||||||
|
nodeAndSMAP = new SMAPAndMethodNode(node, file.getName(), ownerType.getInternalName(), smap);
|
||||||
maxCalcAdapter.visitMaxs(-1, -1);
|
maxCalcAdapter.visitMaxs(-1, -1);
|
||||||
maxCalcAdapter.visitEnd();
|
maxCalcAdapter.visitEnd();
|
||||||
}
|
}
|
||||||
return node;
|
return nodeAndSMAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InlineResult inlineCall(MethodNode node) {
|
private InlineResult inlineCall(SMAPAndMethodNode nodeAndSmap) {
|
||||||
|
sourceMapper.visitSource(nodeAndSmap.getSource(), nodeAndSmap.getSourcePath());
|
||||||
|
|
||||||
|
MethodNode node = nodeAndSmap.getNode();
|
||||||
ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node.instructions);
|
ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node.instructions);
|
||||||
generateClosuresBodies();
|
generateClosuresBodies();
|
||||||
|
|
||||||
@@ -222,7 +239,8 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
callElement,
|
callElement,
|
||||||
codegen.getParentCodegen().getClassName(), reifiedTypeInliner);
|
codegen.getParentCodegen().getClassName(), reifiedTypeInliner);
|
||||||
|
|
||||||
MethodInliner inliner = new MethodInliner(node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule, "Method inlining " + callElement.getText()); //with captured
|
MethodInliner inliner = new MethodInliner(node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule,
|
||||||
|
"Method inlining " + callElement.getText(), sourceMapper); //with captured
|
||||||
|
|
||||||
LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize);
|
LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize);
|
||||||
|
|
||||||
@@ -283,23 +301,30 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
return methodNode;
|
return methodNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateMethodBody(
|
private SMAP generateMethodBody(
|
||||||
@NotNull MethodVisitor adapter,
|
@NotNull MethodVisitor adapter,
|
||||||
@NotNull FunctionDescriptor descriptor,
|
@NotNull FunctionDescriptor descriptor,
|
||||||
@NotNull MethodContext context,
|
@NotNull MethodContext context,
|
||||||
@NotNull JetDeclarationWithBody declaration,
|
@NotNull JetDeclarationWithBody declaration,
|
||||||
@NotNull JvmMethodSignature jvmMethodSignature
|
@NotNull JvmMethodSignature jvmMethodSignature
|
||||||
) {
|
) {
|
||||||
|
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen());
|
||||||
FunctionCodegen.generateMethodBody(
|
FunctionCodegen.generateMethodBody(
|
||||||
adapter, descriptor, context, jvmMethodSignature,
|
adapter, descriptor, context, jvmMethodSignature,
|
||||||
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration),
|
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration),
|
||||||
// Wrapping for preventing marking actual parent codegen as containing reifier markers
|
// Wrapping for preventing marking actual parent codegen as containing reifier markers
|
||||||
new FakeMemberCodegen(codegen.getParentCodegen())
|
parentCodegen
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return new SMAP(parentCodegen.mappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class FakeMemberCodegen extends MemberCodegen {
|
private static class FakeMemberCodegen extends MemberCodegen {
|
||||||
|
|
||||||
private final MemberCodegen delegate;
|
private final MemberCodegen delegate;
|
||||||
|
|
||||||
|
List<FileMapping> mappings = new ArrayList<FileMapping>();
|
||||||
|
|
||||||
public FakeMemberCodegen(@NotNull MemberCodegen wrapped) {
|
public FakeMemberCodegen(@NotNull MemberCodegen wrapped) {
|
||||||
super(wrapped);
|
super(wrapped);
|
||||||
delegate = wrapped;
|
delegate = wrapped;
|
||||||
@@ -325,6 +350,7 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
public NameGenerator getInlineNameGenerator() {
|
public NameGenerator getInlineNameGenerator() {
|
||||||
return delegate.getInlineNameGenerator();
|
return delegate.getInlineNameGenerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -76,25 +76,34 @@ public class InlineCodegenUtil {
|
|||||||
public static final String INLINE_MARKER_GOTO_TRY_CATCH_BLOCK_END = "goToTryCatchBlockEnd";
|
public static final String INLINE_MARKER_GOTO_TRY_CATCH_BLOCK_END = "goToTryCatchBlockEnd";
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static MethodNode getMethodNode(
|
public static SMAPAndMethodNode getMethodNode(
|
||||||
byte[] classData,
|
byte[] classData,
|
||||||
final String methodName,
|
final String methodName,
|
||||||
final String methodDescriptor
|
final String methodDescriptor
|
||||||
) throws ClassNotFoundException, IOException {
|
) throws ClassNotFoundException, IOException {
|
||||||
ClassReader cr = new ClassReader(classData);
|
ClassReader cr = new ClassReader(classData);
|
||||||
final MethodNode[] methodNode = new MethodNode[1];
|
final MethodNode[] node = new MethodNode[1];
|
||||||
|
final String [] debugInfo = new String[2];
|
||||||
cr.accept(new ClassVisitor(API) {
|
cr.accept(new ClassVisitor(API) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSource(String source, String debug) {
|
||||||
|
super.visitSource(source, debug);
|
||||||
|
debugInfo[0] = source;
|
||||||
|
debugInfo[1] = debug;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodVisitor visitMethod(int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions) {
|
public MethodVisitor visitMethod(int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions) {
|
||||||
if (methodName.equals(name) && methodDescriptor.equals(desc)) {
|
if (methodName.equals(name) && methodDescriptor.equals(desc)) {
|
||||||
return methodNode[0] = new MethodNode(access, name, desc, signature, exceptions);
|
node[0] = new MethodNode(access, name, desc, signature, exceptions);
|
||||||
|
return node[0];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, ClassReader.SKIP_FRAMES);
|
}, ClassReader.SKIP_FRAMES);
|
||||||
|
|
||||||
return methodNode[0];
|
return new SMAPAndMethodNode(node[0], debugInfo[0], "TODO", new SMAPParser(debugInfo[1], "TODO").parse());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public class MethodInliner {
|
|||||||
|
|
||||||
private final String errorPrefix;
|
private final String errorPrefix;
|
||||||
|
|
||||||
|
private SourceMapper sourceMapper;
|
||||||
|
|
||||||
private final JetTypeMapper typeMapper;
|
private final JetTypeMapper typeMapper;
|
||||||
|
|
||||||
private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>();
|
private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>();
|
||||||
@@ -80,7 +82,8 @@ public class MethodInliner {
|
|||||||
@NotNull InliningContext parent,
|
@NotNull InliningContext parent,
|
||||||
@NotNull FieldRemapper nodeRemapper,
|
@NotNull FieldRemapper nodeRemapper,
|
||||||
boolean isSameModule,
|
boolean isSameModule,
|
||||||
@NotNull String errorPrefix
|
@NotNull String errorPrefix,
|
||||||
|
SourceMapper sourceMapper
|
||||||
) {
|
) {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.parameters = parameters;
|
this.parameters = parameters;
|
||||||
@@ -88,6 +91,7 @@ public class MethodInliner {
|
|||||||
this.nodeRemapper = nodeRemapper;
|
this.nodeRemapper = nodeRemapper;
|
||||||
this.isSameModule = isSameModule;
|
this.isSameModule = isSameModule;
|
||||||
this.errorPrefix = errorPrefix;
|
this.errorPrefix = errorPrefix;
|
||||||
|
this.sourceMapper = sourceMapper;
|
||||||
this.typeMapper = parent.state.getTypeMapper();
|
this.typeMapper = parent.state.getTypeMapper();
|
||||||
this.result = InlineResult.create();
|
this.result = InlineResult.create();
|
||||||
}
|
}
|
||||||
@@ -142,7 +146,7 @@ public class MethodInliner {
|
|||||||
RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter(resultNode.access, resultNode.desc, resultNode,
|
RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter(resultNode.access, resultNode.desc, resultNode,
|
||||||
new TypeRemapper(currentTypeMapping));
|
new TypeRemapper(currentTypeMapping));
|
||||||
|
|
||||||
InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.totalSize()) {
|
InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.totalSize(), sourceMapper) {
|
||||||
|
|
||||||
private AnonymousObjectGeneration anonymousObjectGen;
|
private AnonymousObjectGeneration anonymousObjectGen;
|
||||||
private void handleAnonymousObjectGeneration() {
|
private void handleAnonymousObjectGeneration() {
|
||||||
@@ -215,7 +219,8 @@ public class MethodInliner {
|
|||||||
MethodInliner inliner = new MethodInliner(info.getNode(), lambdaParameters,
|
MethodInliner inliner = new MethodInliner(info.getNode(), lambdaParameters,
|
||||||
inliningContext.subInlineLambda(info),
|
inliningContext.subInlineLambda(info),
|
||||||
newCapturedRemapper, true /*cause all calls in same module as lambda*/,
|
newCapturedRemapper, true /*cause all calls in same module as lambda*/,
|
||||||
"Lambda inlining " + info.getLambdaClassType().getInternalName());
|
"Lambda inlining " + info.getLambdaClassType().getInternalName(),
|
||||||
|
IdenticalSourceMapper.INSTANCE$);
|
||||||
|
|
||||||
LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift);
|
LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift);
|
||||||
InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info);//TODO add skipped this and receiver
|
InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info);//TODO add skipped this and receiver
|
||||||
@@ -267,6 +272,7 @@ public class MethodInliner {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
node.accept(lambdaInliner);
|
node.accept(lambdaInliner);
|
||||||
|
|
||||||
return resultNode;
|
return resultNode;
|
||||||
@@ -317,13 +323,6 @@ public class MethodInliner {
|
|||||||
super.visitMaxs(maxStack, maxLocals + capturedParamsSize);
|
super.visitMaxs(maxStack, maxLocals + capturedParamsSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitLineNumber(int line, @NotNull Label start) {
|
|
||||||
if(isInliningLambda) {
|
|
||||||
super.visitLineNumber(line, start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitLocalVariable(
|
public void visitLocalVariable(
|
||||||
@NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index
|
@NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index
|
||||||
@@ -378,7 +377,6 @@ public class MethodInliner {
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
boolean awaitClassReification = false;
|
boolean awaitClassReification = false;
|
||||||
Set<LabelNode> possibleDeadLabels = new HashSet<LabelNode>();
|
|
||||||
|
|
||||||
while (cur != null) {
|
while (cur != null) {
|
||||||
Frame<SourceValue> frame = sources[index];
|
Frame<SourceValue> frame = sources[index];
|
||||||
|
|||||||
@@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2014 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.codegen.inline
|
||||||
|
|
||||||
|
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||||
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
|
import java.util
|
||||||
|
import java.util.HashSet
|
||||||
|
import java.util.Arrays
|
||||||
|
|
||||||
|
public class SMAPBuilder(val source: String,
|
||||||
|
val path: String,
|
||||||
|
val fileMappings: List<FileMapping>,
|
||||||
|
val lineCountInOriginalFile: Int) {
|
||||||
|
|
||||||
|
val header = "SMAP\n$source\nKotlin\n*S Kotlin"
|
||||||
|
|
||||||
|
fun build(): String? {
|
||||||
|
if (fileMappings.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val defaultSourceMapping = RawFileMapping(source, path)
|
||||||
|
for(i in 1..lineCountInOriginalFile) {
|
||||||
|
defaultSourceMapping.mapLine(i, i - 1, true)
|
||||||
|
}
|
||||||
|
val allMappings = arrayListOf(defaultSourceMapping.toFileMapping())
|
||||||
|
allMappings.addAll(fileMappings)
|
||||||
|
|
||||||
|
var id = 1;
|
||||||
|
|
||||||
|
val fileIds = "*F" +
|
||||||
|
allMappings.fold("") {(a, e) ->
|
||||||
|
a + "\n${e.toSMAPFile(id++)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
val fileMappings = "*L" +
|
||||||
|
allMappings.fold("") {(a, e) ->
|
||||||
|
a + "${e.toSMAPMapping()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return header + "\n" + fileIds +"\n" + fileMappings + "\n*E\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RangeMapping.toSMAP(fileId: Int): String {
|
||||||
|
return if (range == 1) "$source#$fileId:$dest" else "$source#$fileId,$range:$dest"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FileMapping.toSMAPFile(id: Int): String {
|
||||||
|
this.id = id;
|
||||||
|
return "+ $id $name\n$path"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FileMapping.toSMAPMapping(): String {
|
||||||
|
return lineMappings.fold("") {
|
||||||
|
(a, e) ->
|
||||||
|
"$a\n${e.toSMAP(id)}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object IdenticalSourceMapper: SourceMapper(-1) {
|
||||||
|
|
||||||
|
override fun visitSource(name: String, path: String) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
|
||||||
|
iv.visitLineNumber(lineNumber, start)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class SourceMapper(val lineNumbers: Int) {
|
||||||
|
|
||||||
|
private var maxUsedValue = lineNumbers;
|
||||||
|
|
||||||
|
private var lastMappedWithChanges: RawFileMapping? = null;
|
||||||
|
|
||||||
|
var fileMapping: MutableList<RawFileMapping> = arrayListOf();
|
||||||
|
|
||||||
|
open fun visitSource(name: String, path: String) {
|
||||||
|
fileMapping.add(RawFileMapping(name, path))
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
|
||||||
|
val fileMapping = fileMapping.last()
|
||||||
|
val mappedLineIndex = fileMapping.mapLine(lineNumber, maxUsedValue, true)
|
||||||
|
iv.visitLineNumber(mappedLineIndex, start)
|
||||||
|
if (mappedLineIndex > maxUsedValue) {
|
||||||
|
lastMappedWithChanges = fileMapping
|
||||||
|
maxUsedValue = mappedLineIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/*Source Mapping*/
|
||||||
|
class SMAP(fileMappings: List<FileMapping>) {
|
||||||
|
var fileMappings: List<FileMapping> = fileMappings
|
||||||
|
|
||||||
|
val intervals = fileMappings.flatMap { it -> it.lineMappings }.sortBy(RangeMapping.Comparator)
|
||||||
|
|
||||||
|
class object {
|
||||||
|
val FILE_SECTION = "*F"
|
||||||
|
|
||||||
|
val LINE_SECTION = "*L"
|
||||||
|
|
||||||
|
val END = "*E"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RawFileMapping(val name: String, val path: String) {
|
||||||
|
private val lineMappings = linkedMapOf<Int, Int>()
|
||||||
|
private val rangeMappings = arrayListOf<RangeMapping>()
|
||||||
|
|
||||||
|
private var lastMappedWithNewIndex = -1000;
|
||||||
|
|
||||||
|
fun toFileMapping(): FileMapping {
|
||||||
|
val fileMapping = FileMapping(name, path)
|
||||||
|
for (range in rangeMappings) {
|
||||||
|
fileMapping.addRangeMapping(range)
|
||||||
|
}
|
||||||
|
return fileMapping
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mapLine(source: Int, currentIndex: Int, isLastMapped: Boolean): Int {
|
||||||
|
var dest = lineMappings.get(source);
|
||||||
|
if (dest == null) {
|
||||||
|
val rangeMapping: RangeMapping
|
||||||
|
if (rangeMappings.isNotEmpty() && isLastMapped && couldFoldInRange(lastMappedWithNewIndex, source)) {
|
||||||
|
rangeMapping = rangeMappings.last()
|
||||||
|
rangeMapping.range += source - lastMappedWithNewIndex;
|
||||||
|
dest = lineMappings.get(lastMappedWithNewIndex) + source - lastMappedWithNewIndex;
|
||||||
|
} else {
|
||||||
|
dest = currentIndex + 1;
|
||||||
|
rangeMapping = RangeMapping(source, dest)
|
||||||
|
rangeMappings.add(rangeMapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
lineMappings.put(source, dest)
|
||||||
|
lastMappedWithNewIndex = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun couldFoldInRange(first: Int, second: Int): Boolean {
|
||||||
|
val delta = second - first
|
||||||
|
return delta > 0 && delta <= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FileMapping(val name: String, val path: String) {
|
||||||
|
val lineMappings = arrayListOf<RangeMapping>()
|
||||||
|
|
||||||
|
var id = -1;
|
||||||
|
|
||||||
|
fun addRangeMapping(lineMapping: RangeMapping) {
|
||||||
|
lineMappings.add(lineMapping)
|
||||||
|
lineMapping.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RangeMapping(val source: Int, val dest: Int, var range: Int = 1) {
|
||||||
|
|
||||||
|
var parent: FileMapping? = null;
|
||||||
|
|
||||||
|
fun contains(destLine: Int): Boolean {
|
||||||
|
return dest <= destLine && destLine < dest + range
|
||||||
|
}
|
||||||
|
|
||||||
|
fun map(destLine: Int): Int {
|
||||||
|
return source + destLine - dest
|
||||||
|
}
|
||||||
|
|
||||||
|
object Comparator : util.Comparator<RangeMapping> {
|
||||||
|
override fun compare(o1: RangeMapping, o2: RangeMapping): Int {
|
||||||
|
if (o1 == o2) return 0
|
||||||
|
|
||||||
|
val res = o1.dest - o2.dest
|
||||||
|
if (res == 0) {
|
||||||
|
return o1.range - o2.range
|
||||||
|
} else {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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.codegen.inline
|
||||||
|
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.common.InsnStream
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.LineNumberNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
import java.util.Collections
|
||||||
|
|
||||||
|
class SMAPAndMethodNode(val node: MethodNode, val source: String, val sourcePath: String, classSMAP: SMAP) {
|
||||||
|
|
||||||
|
val lineNumbers = Delegates.lazy {
|
||||||
|
InsnStream(node.instructions.getFirst(), null).filterIsInstance(javaClass<LineNumberNode>()).map {
|
||||||
|
val index = Collections.binarySearch(classSMAP.intervals, RangeMapping(it.line, it.line, 1), {
|
||||||
|
(value, key) -> if (value.contains(key.dest)) 0 else RangeMapping.Comparator.compare(value, key)
|
||||||
|
})
|
||||||
|
if (index < 0) throw IllegalStateException("Unmapped lable in inlined function " + it)
|
||||||
|
LabelAndMapping(it, classSMAP.intervals[index])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class LabelAndMapping(val lineNumberNode: LineNumberNode, val mapper: RangeMapping) {
|
||||||
|
val getOriginalLine = mapper.map(lineNumberNode.line)
|
||||||
|
val getOriginalFileName = mapper.parent!!.name
|
||||||
|
val getOriginalPath = mapper.parent!!.path
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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.codegen.inline
|
||||||
|
|
||||||
|
class SMAPParser(val input: String?, val path: String) {
|
||||||
|
|
||||||
|
val fileMappings = linkedMapOf<Int, FileMapping>()
|
||||||
|
|
||||||
|
fun parse() : SMAP {
|
||||||
|
if (input == null || input.isEmpty()) {
|
||||||
|
return SMAP(listOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
val fileSectionStart = input.indexOf(SMAP.FILE_SECTION) + SMAP.FILE_SECTION.length()
|
||||||
|
val lineSectionAnchor = input.indexOf(SMAP.LINE_SECTION)
|
||||||
|
val files = input.substring(fileSectionStart, lineSectionAnchor)
|
||||||
|
|
||||||
|
|
||||||
|
val fileEntries = files.trim().split('+')
|
||||||
|
|
||||||
|
for (fileDeclaration in fileEntries) {
|
||||||
|
if (fileDeclaration == "") continue;
|
||||||
|
val fileInternalName = fileDeclaration.trim()
|
||||||
|
|
||||||
|
val indexEnd = fileInternalName.indexOf(' ')
|
||||||
|
val fileIndex = Integer.valueOf(fileInternalName.substring(0, indexEnd))
|
||||||
|
val newLine = fileInternalName.indexOf('\n')
|
||||||
|
val fileName = fileInternalName.substring(indexEnd + 1, newLine)
|
||||||
|
fileMappings.put(fileIndex, FileMapping(fileName, fileInternalName.substring(newLine + 1).trim()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val lines = input.substring(lineSectionAnchor + SMAP.LINE_SECTION.length(), input.indexOf(SMAP.END)).trim().split('\n')
|
||||||
|
for (lineMapping in lines) {
|
||||||
|
/*only simple mapping now*/
|
||||||
|
val targetSplit = lineMapping.indexOf(':')
|
||||||
|
val originalPart = lineMapping.substring(0, targetSplit)
|
||||||
|
var rangeSeparator = originalPart.indexOf(',').let { if (it < 0) targetSplit else it}
|
||||||
|
|
||||||
|
val fileSeparator = lineMapping.indexOf('#')
|
||||||
|
val originalIndex = Integer.valueOf(originalPart.substring(0, fileSeparator))
|
||||||
|
val range = if (rangeSeparator == targetSplit) 1 else Integer.valueOf(originalPart.substring(rangeSeparator + 1, targetSplit))
|
||||||
|
|
||||||
|
val fileIndex = Integer.valueOf(lineMapping.substring(fileSeparator + 1, rangeSeparator))
|
||||||
|
val targetIndex = Integer.valueOf(lineMapping.substring(targetSplit + 1))
|
||||||
|
fileMappings[fileIndex]!!.addRangeMapping(RangeMapping(originalIndex, targetIndex, range))
|
||||||
|
}
|
||||||
|
|
||||||
|
return SMAP(fileMappings.values().toList())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,12 +50,17 @@ import org.jetbrains.kotlin.builtins.InlineUtil
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.*
|
import org.jetbrains.kotlin.idea.caches.resolve.*
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||||
import org.jetbrains.kotlin.idea.util.DebuggerUtils
|
import org.jetbrains.kotlin.idea.util.DebuggerUtils
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.DefaultSearchHelper
|
||||||
import java.util.*
|
import com.intellij.find.findUsages.FindUsagesOptions
|
||||||
|
import org.jetbrains.kotlin.idea.findUsages.toSearchTarget
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
|
import org.jetbrains.kotlin.idea.search.usagesSearch.search
|
||||||
|
import java.util.WeakHashMap
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
|
||||||
|
import java.util.ArrayList
|
||||||
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||||
|
|
||||||
|
|
||||||
public class JetPositionManager(private val myDebugProcess: DebugProcess) : PositionManager {
|
public class JetPositionManager(private val myDebugProcess: DebugProcess) : PositionManager {
|
||||||
private val myTypeMappers = WeakHashMap<Pair<FqName, IdeaModuleInfo>, CachedValue<JetTypeMapper>>()
|
private val myTypeMappers = WeakHashMap<Pair<FqName, IdeaModuleInfo>, CachedValue<JetTypeMapper>>()
|
||||||
@@ -131,9 +136,24 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// JDI names are of form "package.Class$InnerClass"
|
val referenceInternalName: String
|
||||||
val referenceFqName = location.declaringType().name()
|
try {
|
||||||
val referenceInternalName = referenceFqName.replace('.', '/')
|
if (location.declaringType().availableStrata().contains("Kotlin")) {
|
||||||
|
referenceInternalName = location.sourcePath()
|
||||||
|
} else {
|
||||||
|
//no stratum or source path => use default one
|
||||||
|
val referenceFqName = location.declaringType().name()
|
||||||
|
// JDI names are of form "package.Class$InnerClass"
|
||||||
|
referenceInternalName = referenceFqName.replace('.', '/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: AbsentInformationException) {
|
||||||
|
//no stratum or source path => use default one
|
||||||
|
val referenceFqName = location.declaringType().name()
|
||||||
|
// JDI names are of form "package.Class$InnerClass"
|
||||||
|
referenceInternalName = referenceFqName.replace('.', '/')
|
||||||
|
}
|
||||||
|
|
||||||
val className = JvmClassName.byInternalName(referenceInternalName)
|
val className = JvmClassName.byInternalName(referenceInternalName)
|
||||||
|
|
||||||
val project = myDebugProcess.getProject()
|
val project = myDebugProcess.getProject()
|
||||||
@@ -147,20 +167,40 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
|||||||
if (sourcePosition.getFile() !is JetFile) {
|
if (sourcePosition.getFile() !is JetFile) {
|
||||||
throw NoDataException()
|
throw NoDataException()
|
||||||
}
|
}
|
||||||
val name = classNameForPosition(sourcePosition)
|
val names = classNameForPositionAndInlinedOnes(sourcePosition)
|
||||||
val result = ArrayList<ReferenceType>()
|
val result = ArrayList<ReferenceType>()
|
||||||
if (name != null) {
|
for (name in names) {
|
||||||
result.addAll(myDebugProcess.getVirtualMachineProxy().classesByName(name))
|
result.addAll(myDebugProcess.getVirtualMachineProxy().classesByName(name))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun classNameForPositionAndInlinedOnes(sourcePosition: SourcePosition): List<String> {
|
||||||
|
val result = arrayListOf<String>()
|
||||||
|
val name = classNameForPosition(sourcePosition)
|
||||||
|
if (name != null) {
|
||||||
|
result.add(name)
|
||||||
|
}
|
||||||
|
val list = findInlinedCalls(sourcePosition.getElementAt())
|
||||||
|
result.addAll(list)
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private fun classNameForPosition(sourcePosition: SourcePosition): String? {
|
private fun classNameForPosition(sourcePosition: SourcePosition): String? {
|
||||||
|
val psiElement = sourcePosition.getElementAt()
|
||||||
|
if (psiElement == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return classNameForPosition(psiElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun classNameForPosition(element: PsiElement): String? {
|
||||||
return runReadAction {
|
return runReadAction {
|
||||||
val file = sourcePosition.getFile() as JetFile
|
val file = element.getContainingFile() as JetFile
|
||||||
val isInLibrary = LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null
|
val isInLibrary = LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null
|
||||||
val typeMapper = if (!isInLibrary) prepareTypeMapper(file) else createTypeMapperForLibraryFile(sourcePosition.getElementAt(), file)
|
val typeMapper = if (!isInLibrary) prepareTypeMapper(file) else createTypeMapperForLibraryFile(element, file)
|
||||||
getClassNameForElement(sourcePosition.getElementAt(), typeMapper, file, isInLibrary)
|
getClassNameForElement(element, typeMapper, file, isInLibrary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +228,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
|||||||
try {
|
try {
|
||||||
val line = position.getLine() + 1
|
val line = position.getLine() + 1
|
||||||
val locations = if (myDebugProcess.getVirtualMachineProxy().versionHigher("1.4"))
|
val locations = if (myDebugProcess.getVirtualMachineProxy().versionHigher("1.4"))
|
||||||
type.locationsOfLine(DebugProcess.JAVA_STRATUM, null, line)
|
type.locationsOfLine("Kotlin", null, line)
|
||||||
else
|
else
|
||||||
type.locationsOfLine(line)
|
type.locationsOfLine(line)
|
||||||
if (locations == null || locations.isEmpty()) throw NoDataException()
|
if (locations == null || locations.isEmpty()) throw NoDataException()
|
||||||
@@ -203,11 +243,16 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
|||||||
if (sourcePosition.getFile() !is JetFile) {
|
if (sourcePosition.getFile() !is JetFile) {
|
||||||
throw NoDataException()
|
throw NoDataException()
|
||||||
}
|
}
|
||||||
val className = classNameForPosition(sourcePosition)
|
val classNames = classNameForPositionAndInlinedOnes(sourcePosition)
|
||||||
if (className == null) {
|
if (classNames.isEmpty()) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return myDebugProcess.getRequestsManager().createClassPrepareRequest(classPrepareRequestor, className.replace('/', '.'))
|
val classPrepareRequest = myDebugProcess.getRequestsManager().createClassPrepareRequest(classPrepareRequestor, classNames.first().replace('/', '.'))
|
||||||
|
// classNames.subList(1, classNames.size()).forEach {
|
||||||
|
// classPrepareRequest.addClassFilter(it.replace('/', '.'))
|
||||||
|
// }
|
||||||
|
|
||||||
|
return classPrepareRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
TestOnly
|
TestOnly
|
||||||
@@ -242,7 +287,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
|||||||
return getClassNameForElement(element.getParent(), typeMapper, file, isInLibrary)
|
return getClassNameForElement(element.getParent(), typeMapper, file, isInLibrary)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val asmType = asmTypeForAnonymousClass(typeMapper.getBindingContext(), element)
|
val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.getBindingContext(), element)
|
||||||
return asmType.getInternalName()
|
return asmType.getInternalName()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,7 +320,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
|||||||
return getJvmInternalNameForImpl(typeMapper, parent)
|
return getJvmInternalNameForImpl(typeMapper, parent)
|
||||||
}
|
}
|
||||||
else if (parent != null) {
|
else if (parent != null) {
|
||||||
val asmType = asmTypeForAnonymousClass(typeMapper.getBindingContext(), element)
|
val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.getBindingContext(), element)
|
||||||
return asmType.getInternalName()
|
return asmType.getInternalName()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,4 +419,36 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
|||||||
|
|
||||||
private fun createKeyForTypeMapper(file: JetFile) = Pair(file.getPackageFqName(), file.getModuleInfo())
|
private fun createKeyForTypeMapper(file: JetFile) = Pair(file.getPackageFqName(), file.getModuleInfo())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun findInlinedCalls(element: PsiElement?): List<String> {
|
||||||
|
return runReadAction {
|
||||||
|
var result = emptyList<String>()
|
||||||
|
if (element != null) {
|
||||||
|
val psiElement = getElementToCalculateClassName(element)
|
||||||
|
if (psiElement is JetNamedFunction) {
|
||||||
|
val typeMapper = prepareTypeMapper(psiElement.getContainingFile() as JetFile)
|
||||||
|
val descriptor = typeMapper.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement)
|
||||||
|
if (descriptor is SimpleFunctionDescriptor && descriptor.getInlineStrategy().isInline()) {
|
||||||
|
|
||||||
|
val project = myDebugProcess.getProject()
|
||||||
|
val usagesSearchTarget = FindUsagesOptions(project).toSearchTarget(psiElement, true)
|
||||||
|
|
||||||
|
result = arrayListOf<String>()
|
||||||
|
val usagesSearchRequest = DefaultSearchHelper<JetNamedFunction>(true).newRequest(usagesSearchTarget)
|
||||||
|
usagesSearchRequest.search().forEach {
|
||||||
|
val psiElement = it.getElement()
|
||||||
|
if (psiElement is JetElement) {
|
||||||
|
val name = classNameForPosition(psiElement)
|
||||||
|
if (name != null) {
|
||||||
|
(result as MutableList<String>).add(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user