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 org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.context.*;
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.inline.NameGenerator;
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages;
|
||||
import org.jetbrains.kotlin.codegen.inline.*;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
@@ -81,6 +80,8 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
|
||||
protected ExpressionCodegen clInit;
|
||||
private NameGenerator inlineNameGenerator;
|
||||
|
||||
private SourceMapper sourceMapper;
|
||||
|
||||
public MemberCodegen(
|
||||
@NotNull GenerationState state,
|
||||
@Nullable MemberCodegen<?> parentCodegen,
|
||||
@@ -135,8 +136,16 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
|
||||
FunctionCodegen.endVisit(clInit.v, "static initializer", element);
|
||||
}
|
||||
|
||||
|
||||
writeInnerClasses();
|
||||
|
||||
if (sourceMapper != null) {
|
||||
List<RawFileMapping> mapping = sourceMapper.getFileMapping();
|
||||
for (RawFileMapping fileMapping : mapping) {
|
||||
v.addSMAP(fileMapping.toFileMapping());
|
||||
}
|
||||
}
|
||||
|
||||
v.done();
|
||||
}
|
||||
|
||||
@@ -198,7 +207,8 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
|
||||
|
||||
if (aClass instanceof JetClass && ((JetClass) aClass).isTrait()) {
|
||||
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);
|
||||
new TraitImplBodyCodegen(aClass, traitImplContext, traitImplBuilder, state, parentCodegen).generate();
|
||||
}
|
||||
@@ -505,8 +515,23 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
|
||||
return parentCodegen;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String 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);
|
||||
|
||||
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);
|
||||
result.getReifiedTypeParametersUsages().mergeAll(typeParametersToReify);
|
||||
@@ -270,7 +271,8 @@ public class AnonymousObjectTransformer {
|
||||
parentRemapper);
|
||||
|
||||
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,
|
||||
LabelOwner.NOT_APPLICABLE);
|
||||
constructorVisitor.visitMaxs(-1, -1);
|
||||
|
||||
@@ -27,16 +27,18 @@ import java.util.List;
|
||||
public class InlineAdapter extends InstructionAdapter {
|
||||
|
||||
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 int nextLocalIndexBeforeInline = -1;
|
||||
|
||||
public InlineAdapter(MethodVisitor mv, int localsSize) {
|
||||
public InlineAdapter(MethodVisitor mv, int localsSize, SourceMapper sourceMapper) {
|
||||
super(InlineCodegenUtil.API, mv);
|
||||
nextLocalIndex = localsSize;
|
||||
this.sourceMapper = sourceMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,7 +65,7 @@ public class InlineAdapter extends InstructionAdapter {
|
||||
}
|
||||
|
||||
public void setLambdaInlining(boolean isInlining) {
|
||||
this.isInlining = isInlining;
|
||||
this.isLambdaInlining = isInlining;
|
||||
if (isInlining) {
|
||||
nextLocalIndexBeforeInline = nextLocalIndex;
|
||||
} else {
|
||||
@@ -74,7 +76,7 @@ public class InlineAdapter extends InstructionAdapter {
|
||||
@Override
|
||||
public void visitTryCatchBlock(Label start,
|
||||
Label end, Label handler, String type) {
|
||||
if(!isInlining) {
|
||||
if(!isLambdaInlining) {
|
||||
blocks.add(new CatchBlock(start, end, handler, type));
|
||||
}
|
||||
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
|
||||
public void visitMaxs(int stack, int locals) {
|
||||
for (CatchBlock b : blocks) {
|
||||
|
||||
@@ -18,8 +18,10 @@ package org.jetbrains.kotlin.codegen.inline;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||
import org.jetbrains.kotlin.builtins.InlineStrategy;
|
||||
import org.jetbrains.kotlin.builtins.InlineUtil;
|
||||
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 java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
|
||||
@@ -82,6 +81,8 @@ public class InlineCodegen extends CallGenerator {
|
||||
|
||||
private LambdaInfo activeLambda;
|
||||
|
||||
private SourceMapper sourceMapper;
|
||||
|
||||
public InlineCodegen(
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull GenerationState state,
|
||||
@@ -109,6 +110,11 @@ public class InlineCodegen extends CallGenerator {
|
||||
this.asFunctionInline = false;
|
||||
|
||||
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
|
||||
@@ -120,11 +126,11 @@ public class InlineCodegen extends CallGenerator {
|
||||
|
||||
@Override
|
||||
public void genCallInner(@NotNull CallableMethod callableMethod, @Nullable ResolvedCall<?> resolvedCall, boolean callDefault, @NotNull ExpressionCodegen codegen) {
|
||||
MethodNode node = null;
|
||||
SMAPAndMethodNode nodeAndSmap = null;
|
||||
|
||||
try {
|
||||
node = createMethodNode(callDefault);
|
||||
endCall(inlineCall(node));
|
||||
nodeAndSmap = createMethodNode(callDefault);
|
||||
endCall(inlineCall(nodeAndSmap));
|
||||
}
|
||||
catch (CompilationException e) {
|
||||
throw e;
|
||||
@@ -135,7 +141,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
throw new CompilationException("Couldn't inline method call '" +
|
||||
functionDescriptor.getName() +
|
||||
"' 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);
|
||||
}
|
||||
|
||||
@@ -151,7 +157,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private MethodNode createMethodNode(boolean callDefault) throws ClassNotFoundException, IOException {
|
||||
private SMAPAndMethodNode createMethodNode(boolean callDefault) throws ClassNotFoundException, IOException {
|
||||
JvmMethodSignature jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind());
|
||||
|
||||
Method asmMethod;
|
||||
@@ -162,23 +168,24 @@ public class InlineCodegen extends CallGenerator {
|
||||
asmMethod = jvmSignature.getAsmMethod();
|
||||
}
|
||||
|
||||
MethodNode node;
|
||||
SMAPAndMethodNode nodeAndSMAP;
|
||||
if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
else {
|
||||
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
|
||||
Type ownerType = typeMapper.mapOwner(functionDescriptor, false);
|
||||
|
||||
if (element == null) {
|
||||
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),
|
||||
asmMethod.getName(),
|
||||
asmMethod.getDescriptor(),
|
||||
@@ -188,22 +195,32 @@ public class InlineCodegen extends CallGenerator {
|
||||
//for maxLocals calculation
|
||||
MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
|
||||
MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor);
|
||||
SMAP smap;
|
||||
if (callDefault) {
|
||||
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen());
|
||||
FunctionCodegen.generateDefaultImplBody(
|
||||
methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
||||
(JetNamedFunction) element, codegen.getParentCodegen()
|
||||
(JetNamedFunction) element, parentCodegen
|
||||
);
|
||||
|
||||
smap = new SMAP(parentCodegen.mappings);
|
||||
}
|
||||
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.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);
|
||||
generateClosuresBodies();
|
||||
|
||||
@@ -222,7 +239,8 @@ public class InlineCodegen extends CallGenerator {
|
||||
callElement,
|
||||
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);
|
||||
|
||||
@@ -283,23 +301,30 @@ public class InlineCodegen extends CallGenerator {
|
||||
return methodNode;
|
||||
}
|
||||
|
||||
private void generateMethodBody(
|
||||
private SMAP generateMethodBody(
|
||||
@NotNull MethodVisitor adapter,
|
||||
@NotNull FunctionDescriptor descriptor,
|
||||
@NotNull MethodContext context,
|
||||
@NotNull JetDeclarationWithBody declaration,
|
||||
@NotNull JvmMethodSignature jvmMethodSignature
|
||||
) {
|
||||
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen());
|
||||
FunctionCodegen.generateMethodBody(
|
||||
adapter, descriptor, context, jvmMethodSignature,
|
||||
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration),
|
||||
// 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 final MemberCodegen delegate;
|
||||
|
||||
List<FileMapping> mappings = new ArrayList<FileMapping>();
|
||||
|
||||
public FakeMemberCodegen(@NotNull MemberCodegen wrapped) {
|
||||
super(wrapped);
|
||||
delegate = wrapped;
|
||||
@@ -325,6 +350,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
public NameGenerator getInlineNameGenerator() {
|
||||
return delegate.getInlineNameGenerator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -76,25 +76,34 @@ public class InlineCodegenUtil {
|
||||
public static final String INLINE_MARKER_GOTO_TRY_CATCH_BLOCK_END = "goToTryCatchBlockEnd";
|
||||
|
||||
@Nullable
|
||||
public static MethodNode getMethodNode(
|
||||
public static SMAPAndMethodNode getMethodNode(
|
||||
byte[] classData,
|
||||
final String methodName,
|
||||
final String methodDescriptor
|
||||
) throws ClassNotFoundException, IOException {
|
||||
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) {
|
||||
|
||||
@Override
|
||||
public void visitSource(String source, String debug) {
|
||||
super.visitSource(source, debug);
|
||||
debugInfo[0] = source;
|
||||
debugInfo[1] = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions) {
|
||||
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;
|
||||
}
|
||||
}, 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 SourceMapper sourceMapper;
|
||||
|
||||
private final JetTypeMapper typeMapper;
|
||||
|
||||
private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>();
|
||||
@@ -80,7 +82,8 @@ public class MethodInliner {
|
||||
@NotNull InliningContext parent,
|
||||
@NotNull FieldRemapper nodeRemapper,
|
||||
boolean isSameModule,
|
||||
@NotNull String errorPrefix
|
||||
@NotNull String errorPrefix,
|
||||
SourceMapper sourceMapper
|
||||
) {
|
||||
this.node = node;
|
||||
this.parameters = parameters;
|
||||
@@ -88,6 +91,7 @@ public class MethodInliner {
|
||||
this.nodeRemapper = nodeRemapper;
|
||||
this.isSameModule = isSameModule;
|
||||
this.errorPrefix = errorPrefix;
|
||||
this.sourceMapper = sourceMapper;
|
||||
this.typeMapper = parent.state.getTypeMapper();
|
||||
this.result = InlineResult.create();
|
||||
}
|
||||
@@ -142,7 +146,7 @@ public class MethodInliner {
|
||||
RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter(resultNode.access, resultNode.desc, resultNode,
|
||||
new TypeRemapper(currentTypeMapping));
|
||||
|
||||
InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.totalSize()) {
|
||||
InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.totalSize(), sourceMapper) {
|
||||
|
||||
private AnonymousObjectGeneration anonymousObjectGen;
|
||||
private void handleAnonymousObjectGeneration() {
|
||||
@@ -215,7 +219,8 @@ public class MethodInliner {
|
||||
MethodInliner inliner = new MethodInliner(info.getNode(), lambdaParameters,
|
||||
inliningContext.subInlineLambda(info),
|
||||
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);
|
||||
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);
|
||||
|
||||
return resultNode;
|
||||
@@ -317,13 +323,6 @@ public class MethodInliner {
|
||||
super.visitMaxs(maxStack, maxLocals + capturedParamsSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLineNumber(int line, @NotNull Label start) {
|
||||
if(isInliningLambda) {
|
||||
super.visitLineNumber(line, start);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(
|
||||
@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;
|
||||
|
||||
boolean awaitClassReification = false;
|
||||
Set<LabelNode> possibleDeadLabels = new HashSet<LabelNode>();
|
||||
|
||||
while (cur != null) {
|
||||
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.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.util.DebuggerUtils
|
||||
|
||||
import java.util.*
|
||||
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass
|
||||
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.DefaultSearchHelper
|
||||
import com.intellij.find.findUsages.FindUsagesOptions
|
||||
import org.jetbrains.kotlin.idea.findUsages.toSearchTarget
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
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.stubindex.PackageIndexUtil
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
|
||||
|
||||
public class JetPositionManager(private val myDebugProcess: DebugProcess) : PositionManager {
|
||||
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 referenceFqName = location.declaringType().name()
|
||||
val referenceInternalName = referenceFqName.replace('.', '/')
|
||||
val referenceInternalName: String
|
||||
try {
|
||||
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 project = myDebugProcess.getProject()
|
||||
@@ -147,20 +167,40 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
||||
if (sourcePosition.getFile() !is JetFile) {
|
||||
throw NoDataException()
|
||||
}
|
||||
val name = classNameForPosition(sourcePosition)
|
||||
val names = classNameForPositionAndInlinedOnes(sourcePosition)
|
||||
val result = ArrayList<ReferenceType>()
|
||||
if (name != null) {
|
||||
for (name in names) {
|
||||
result.addAll(myDebugProcess.getVirtualMachineProxy().classesByName(name))
|
||||
}
|
||||
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? {
|
||||
val psiElement = sourcePosition.getElementAt()
|
||||
if (psiElement == null) {
|
||||
return null
|
||||
}
|
||||
return classNameForPosition(psiElement)
|
||||
}
|
||||
|
||||
private fun classNameForPosition(element: PsiElement): String? {
|
||||
return runReadAction {
|
||||
val file = sourcePosition.getFile() as JetFile
|
||||
val file = element.getContainingFile() as JetFile
|
||||
val isInLibrary = LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null
|
||||
val typeMapper = if (!isInLibrary) prepareTypeMapper(file) else createTypeMapperForLibraryFile(sourcePosition.getElementAt(), file)
|
||||
getClassNameForElement(sourcePosition.getElementAt(), typeMapper, file, isInLibrary)
|
||||
val typeMapper = if (!isInLibrary) prepareTypeMapper(file) else createTypeMapperForLibraryFile(element, file)
|
||||
getClassNameForElement(element, typeMapper, file, isInLibrary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +228,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
||||
try {
|
||||
val line = position.getLine() + 1
|
||||
val locations = if (myDebugProcess.getVirtualMachineProxy().versionHigher("1.4"))
|
||||
type.locationsOfLine(DebugProcess.JAVA_STRATUM, null, line)
|
||||
type.locationsOfLine("Kotlin", null, line)
|
||||
else
|
||||
type.locationsOfLine(line)
|
||||
if (locations == null || locations.isEmpty()) throw NoDataException()
|
||||
@@ -203,11 +243,16 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
||||
if (sourcePosition.getFile() !is JetFile) {
|
||||
throw NoDataException()
|
||||
}
|
||||
val className = classNameForPosition(sourcePosition)
|
||||
if (className == null) {
|
||||
val classNames = classNameForPositionAndInlinedOnes(sourcePosition)
|
||||
if (classNames.isEmpty()) {
|
||||
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
|
||||
@@ -242,7 +287,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
||||
return getClassNameForElement(element.getParent(), typeMapper, file, isInLibrary)
|
||||
}
|
||||
else {
|
||||
val asmType = asmTypeForAnonymousClass(typeMapper.getBindingContext(), element)
|
||||
val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.getBindingContext(), element)
|
||||
return asmType.getInternalName()
|
||||
}
|
||||
}
|
||||
@@ -275,7 +320,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Posi
|
||||
return getJvmInternalNameForImpl(typeMapper, parent)
|
||||
}
|
||||
else if (parent != null) {
|
||||
val asmType = asmTypeForAnonymousClass(typeMapper.getBindingContext(), element)
|
||||
val asmType = CodegenBinding.asmTypeForAnonymousClass(typeMapper.getBindingContext(), element)
|
||||
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 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