Smap refactorings
This commit is contained in:
@@ -19,9 +19,14 @@ package org.jetbrains.kotlin.codegen;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.inline.FileMapping;
|
||||
import org.jetbrains.kotlin.codegen.inline.SMAPBuilder;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractClassBuilder implements ClassBuilder {
|
||||
protected static final MethodVisitor EMPTY_METHOD_VISITOR = new MethodVisitor(Opcodes.ASM5) {};
|
||||
protected static final FieldVisitor EMPTY_FIELD_VISITOR = new FieldVisitor(Opcodes.ASM5) {};
|
||||
@@ -30,6 +35,12 @@ public abstract class AbstractClassBuilder implements ClassBuilder {
|
||||
|
||||
private final JvmSerializationBindings serializationBindings = new JvmSerializationBindings();
|
||||
|
||||
private final List<FileMapping> fileMappings = new ArrayList<FileMapping>();
|
||||
|
||||
private String sourceName;
|
||||
|
||||
private String debugInfo;
|
||||
|
||||
public static class Concrete extends AbstractClassBuilder {
|
||||
private final ClassVisitor v;
|
||||
|
||||
@@ -92,6 +103,15 @@ public abstract class AbstractClassBuilder implements ClassBuilder {
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
if (!fileMappings.isEmpty()) {
|
||||
FileMapping origin = fileMappings.get(0);
|
||||
assert sourceName == null || origin.getName().equals(sourceName) : "Error " + origin.getName() + " != " + sourceName;
|
||||
getVisitor().visitSource(origin.getName(), new SMAPBuilder(origin.getName(), origin.getPath(), fileMappings).build());
|
||||
}
|
||||
else {
|
||||
getVisitor().visitSource(sourceName, debugInfo);
|
||||
}
|
||||
|
||||
getVisitor().visitEnd();
|
||||
}
|
||||
|
||||
@@ -111,7 +131,8 @@ public abstract class AbstractClassBuilder implements ClassBuilder {
|
||||
|
||||
@Override
|
||||
public void visitSource(@NotNull String name, @Nullable String debug) {
|
||||
getVisitor().visitSource(name, debug);
|
||||
sourceName = name;
|
||||
debugInfo = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,4 +151,9 @@ public abstract class AbstractClassBuilder implements ClassBuilder {
|
||||
assert thisName != null : "This name isn't set";
|
||||
return thisName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSMAP(FileMapping mapping) {
|
||||
fileMappings.add(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.inline.FileMapping;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor;
|
||||
@@ -75,4 +76,6 @@ public interface ClassBuilder {
|
||||
|
||||
@NotNull
|
||||
String getThisName();
|
||||
|
||||
void addSMAP(FileMapping mapping);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
|
||||
import org.jetbrains.kotlin.codegen.context.ClosureContext;
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||
@@ -141,6 +142,9 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
|
||||
superClassAsmType.getInternalName(),
|
||||
superInterfaceAsmTypes
|
||||
);
|
||||
|
||||
InlineCodegenUtil.initDefaultSourceMappingIfNeeded(context, this, state);
|
||||
|
||||
v.visitSource(element.getContainingFile().getName(), null);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.inline.FileMapping;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor;
|
||||
@@ -111,4 +112,9 @@ public abstract class DelegatingClassBuilder implements ClassBuilder {
|
||||
public String getThisName() {
|
||||
return getDelegate().getThisName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSMAP(FileMapping mapping) {
|
||||
getDelegate().addSMAP(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.codegen.binding.MutableClosure;
|
||||
import org.jetbrains.kotlin.codegen.context.*;
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||
@@ -209,6 +210,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
);
|
||||
v.visitSource(myClass.getContainingFile().getName(), null);
|
||||
|
||||
InlineCodegenUtil.initDefaultSourceMappingIfNeeded(context, this, state);
|
||||
|
||||
writeEnclosingMethod();
|
||||
|
||||
AnnotationCodegen.forClass(v.getVisitor(), typeMapper).genAnnotations(descriptor, null);
|
||||
|
||||
@@ -20,7 +20,6 @@ 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.*;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
@@ -100,8 +99,8 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
|
||||
this.parentCodegen = parentCodegen;
|
||||
}
|
||||
|
||||
protected MemberCodegen(@NotNull MemberCodegen<T> wrapped) {
|
||||
this(wrapped.state, wrapped.getParentCodegen(), wrapped.getContext(), wrapped.element, wrapped.v);
|
||||
protected MemberCodegen(@NotNull MemberCodegen<T> wrapped, T declaration, FieldOwnerContext codegenContext) {
|
||||
this(wrapped.state, wrapped.getParentCodegen(), codegenContext, declaration, wrapped.v);
|
||||
}
|
||||
|
||||
public void generate() {
|
||||
@@ -136,14 +135,10 @@ 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());
|
||||
}
|
||||
SourceMapper.Default.flushToClassBuilder(sourceMapper, v);
|
||||
}
|
||||
|
||||
v.done();
|
||||
@@ -207,8 +202,7 @@ 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();
|
||||
}
|
||||
@@ -515,22 +509,15 @@ 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() {
|
||||
@NotNull
|
||||
public SourceMapper getOrCreateSourceMapper() {
|
||||
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);
|
||||
sourceMapper = new DefaultSourceMapper(SourceInfo.Default.createInfo(element, getClassName()), null);
|
||||
}
|
||||
return sourceMapper;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.JetElement
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetNamedFunction
|
||||
|
||||
data public class SourceInfo(val source: String, val pathOrCleanFQN: String, val linesInFile: Int) {
|
||||
|
||||
default object {
|
||||
fun createInfo(element: JetElement?, internalClassName: String): SourceInfo {
|
||||
assert(element != null) { "Couldn't create source mapper for null element " + internalClassName }
|
||||
val lineNumbers = CodegenUtil.getLineNumberForElement(element!!.getContainingFile(), true)
|
||||
assert(lineNumbers != null) { "Couldn't extract line count in " + element.getContainingFile() }
|
||||
|
||||
//TODO hack condition for package parts cleaning
|
||||
val isTopLevel = element is JetFile || (element is JetNamedFunction && element.getParent() is JetFile)
|
||||
val cleanedClassFqName = if (!isTopLevel) internalClassName else internalClassName.substringBefore('$')
|
||||
|
||||
return SourceInfo(element.getContainingJetFile().getName(), cleanedClassFqName, lineNumbers!!)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+33
-2
@@ -44,6 +44,14 @@ public class AnonymousObjectTransformer {
|
||||
|
||||
private MethodNode constructor;
|
||||
|
||||
private String sourceInfo;
|
||||
|
||||
private String debugInfo;
|
||||
|
||||
private SMAP smap;
|
||||
|
||||
private SourceMapper sourceMapper;
|
||||
|
||||
private final InliningContext inliningContext;
|
||||
|
||||
private final Type oldObjectType;
|
||||
@@ -140,8 +148,29 @@ public class AnonymousObjectTransformer {
|
||||
return super.visitField(access, name, desc, signature, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSource(String source, String debug) {
|
||||
sourceInfo = source;
|
||||
debugInfo = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
|
||||
}
|
||||
}, ClassReader.SKIP_FRAMES);
|
||||
|
||||
if (!inliningContext.isInliningLambda) {
|
||||
assert debugInfo != null && !debugInfo.isEmpty() : "Debug info is null for " + oldObjectType;
|
||||
smap = new SMAPParser(debugInfo, sourceInfo, "notused", 1, 2).parse();
|
||||
sourceMapper = SourceMapper.Default.createFromSmap(smap);
|
||||
}
|
||||
else {
|
||||
classBuilder.visitSource(sourceInfo, debugInfo);
|
||||
sourceMapper = IdenticalSourceMapper.INSTANCE$;
|
||||
}
|
||||
|
||||
ParametersBuilder allCapturedParamBuilder = ParametersBuilder.newBuilder();
|
||||
ParametersBuilder constructorParamBuilder = ParametersBuilder.newBuilder();
|
||||
List<CapturedParamInfo> additionalFakeParams =
|
||||
@@ -159,6 +188,8 @@ public class AnonymousObjectTransformer {
|
||||
|
||||
result.addAllClassesToRemove(constructorResult);
|
||||
|
||||
SourceMapper.Default.flushToClassBuilder(sourceMapper, classBuilder);
|
||||
|
||||
classBuilder.done();
|
||||
|
||||
anonymousObjectGen.setNewLambdaType(newLambdaType);
|
||||
@@ -183,7 +214,7 @@ public class AnonymousObjectTransformer {
|
||||
|
||||
MethodInliner inliner = new MethodInliner(sourceNode, parameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")),
|
||||
remapper, isSameModule, "Transformer for " + anonymousObjectGen.getOwnerInternalName(),
|
||||
null);
|
||||
sourceMapper);
|
||||
|
||||
InlineResult result = inliner.doInline(resultVisitor, new LocalVarRemapper(parameters, 0), false, LabelOwner.NOT_APPLICABLE);
|
||||
result.getReifiedTypeParametersUsages().mergeAll(typeParametersToReify);
|
||||
@@ -272,7 +303,7 @@ public class AnonymousObjectTransformer {
|
||||
|
||||
MethodInliner inliner = new MethodInliner(constructor, constructorParameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")),
|
||||
remapper, isSameModule, "Transformer for constructor of " + anonymousObjectGen.getOwnerInternalName(),
|
||||
null);
|
||||
sourceMapper);
|
||||
InlineResult result = inliner.doInline(capturedFieldInitializer, new LocalVarRemapper(constructorParameters, 0), false,
|
||||
LabelOwner.NOT_APPLICABLE);
|
||||
constructorVisitor.visitMaxs(-1, -1);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.org.objectweb.asm.Label;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
@@ -35,7 +36,7 @@ public class InlineAdapter extends InstructionAdapter {
|
||||
|
||||
private int nextLocalIndexBeforeInline = -1;
|
||||
|
||||
public InlineAdapter(MethodVisitor mv, int localsSize, SourceMapper sourceMapper) {
|
||||
public InlineAdapter(MethodVisitor mv, int localsSize, @NotNull SourceMapper sourceMapper) {
|
||||
super(InlineCodegenUtil.API, mv);
|
||||
nextLocalIndex = localsSize;
|
||||
this.sourceMapper = sourceMapper;
|
||||
@@ -86,13 +87,7 @@ 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);
|
||||
}
|
||||
sourceMapper.visitLineNumber(mv, line, start);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,11 +26,13 @@ import org.jetbrains.kotlin.builtins.InlineStrategy;
|
||||
import org.jetbrains.kotlin.builtins.InlineUtil;
|
||||
import org.jetbrains.kotlin.codegen.*;
|
||||
import org.jetbrains.kotlin.codegen.context.CodegenContext;
|
||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
|
||||
import org.jetbrains.kotlin.codegen.context.MethodContext;
|
||||
import org.jetbrains.kotlin.codegen.context.PackageContext;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils;
|
||||
import org.jetbrains.kotlin.name.ClassId;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
@@ -38,7 +40,6 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||
@@ -83,7 +84,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
|
||||
private LambdaInfo activeLambda;
|
||||
|
||||
private SourceMapper sourceMapper;
|
||||
private final SourceMapper sourceMapper;
|
||||
|
||||
public InlineCodegen(
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@@ -113,10 +114,10 @@ public class InlineCodegen extends CallGenerator {
|
||||
|
||||
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();
|
||||
Integer lastLineNumber = CodegenUtil.getLineNumberForElement(callElement.getContainingFile(), true);
|
||||
assert lastLineNumber != null : "Couldn't extract line count in " + callElement.getContainingFile();
|
||||
|
||||
sourceMapper = codegen.getParentCodegen().getSourceMapper();
|
||||
sourceMapper = codegen.getParentCodegen().getOrCreateSourceMapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,8 +177,14 @@ public class InlineCodegen extends CallGenerator {
|
||||
(DeserializedSimpleFunctionDescriptor) functionDescriptor);
|
||||
|
||||
VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable(containerClassId, state);
|
||||
nodeAndSMAP = InlineCodegenUtil.getMethodNode(file.contentsToByteArray(), asmMethod.getName(), asmMethod.getDescriptor(),
|
||||
JvmClassName.byClassId(containerClassId).getInternalName());
|
||||
if (functionDescriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor) {
|
||||
/*use facade class*/
|
||||
containerClassId = PackageClassUtils.getPackageClassId(containerClassId.getPackageFqName());
|
||||
}
|
||||
nodeAndSMAP = InlineCodegenUtil.getMethodNode(file.contentsToByteArray(),
|
||||
asmMethod.getName(),
|
||||
asmMethod.getDescriptor(),
|
||||
containerClassId);
|
||||
|
||||
if (nodeAndSMAP == null) {
|
||||
throw new RuntimeException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
|
||||
@@ -185,11 +192,11 @@ public class InlineCodegen extends CallGenerator {
|
||||
}
|
||||
else {
|
||||
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
|
||||
Type ownerType = typeMapper.mapOwner(functionDescriptor, false);
|
||||
|
||||
if (element == null) {
|
||||
if (element == null || !(element instanceof JetNamedFunction)) {
|
||||
throw new RuntimeException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
|
||||
}
|
||||
JetNamedFunction inliningFunction = (JetNamedFunction) element;
|
||||
|
||||
MethodNode node = new MethodNode(InlineCodegenUtil.API,
|
||||
getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
|
||||
@@ -201,22 +208,24 @@ 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());
|
||||
Type ownerType = typeMapper.mapOwner(functionDescriptor, false/*use facade class*/);
|
||||
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen(), inliningFunction,
|
||||
(FieldOwnerContext) methodContext.getParentContext(),
|
||||
ownerType.getInternalName());
|
||||
FunctionCodegen.generateDefaultImplBody(
|
||||
methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
||||
(JetNamedFunction) element, parentCodegen
|
||||
inliningFunction, parentCodegen
|
||||
);
|
||||
|
||||
smap = new SMAP(parentCodegen.mappings);
|
||||
smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.getOrCreateSourceMapper().getResultMappings());
|
||||
}
|
||||
else {
|
||||
smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, (JetDeclarationWithBody) element,
|
||||
jvmSignature);
|
||||
smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, inliningFunction, jvmSignature, false);
|
||||
}
|
||||
PsiFile file = element.getContainingFile();
|
||||
nodeAndSMAP = new SMAPAndMethodNode(node, file.getName(), ownerType.getInternalName(), smap);
|
||||
|
||||
nodeAndSMAP = new SMAPAndMethodNode(node, smap);
|
||||
maxCalcAdapter.visitMaxs(-1, -1);
|
||||
maxCalcAdapter.visitEnd();
|
||||
}
|
||||
@@ -224,8 +233,6 @@ public class InlineCodegen extends CallGenerator {
|
||||
}
|
||||
|
||||
private InlineResult inlineCall(SMAPAndMethodNode nodeAndSmap) {
|
||||
sourceMapper.visitSource(nodeAndSmap.getSource(), nodeAndSmap.getSourcePath());
|
||||
|
||||
MethodNode node = nodeAndSmap.getNode();
|
||||
ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node.instructions);
|
||||
generateClosuresBodies();
|
||||
@@ -246,7 +253,8 @@ public class InlineCodegen extends CallGenerator {
|
||||
codegen.getParentCodegen().getClassName(), reifiedTypeInliner);
|
||||
|
||||
MethodInliner inliner = new MethodInliner(node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule,
|
||||
"Method inlining " + callElement.getText(), sourceMapper); //with captured
|
||||
"Method inlining " + callElement.getText(),
|
||||
createNestedSourceMapper(nodeAndSmap)); //with captured
|
||||
|
||||
LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize);
|
||||
|
||||
@@ -287,7 +295,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private MethodNode generateLambdaBody(LambdaInfo info) {
|
||||
private SMAPAndMethodNode generateLambdaBody(LambdaInfo info) {
|
||||
JetFunctionLiteral declaration = info.getFunctionLiteral();
|
||||
FunctionDescriptor descriptor = info.getFunctionDescriptor();
|
||||
|
||||
@@ -301,10 +309,9 @@ public class InlineCodegen extends CallGenerator {
|
||||
|
||||
MethodVisitor adapter = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode);
|
||||
|
||||
generateMethodBody(adapter, descriptor, context, declaration, jvmMethodSignature);
|
||||
SMAP smap = generateMethodBody(adapter, descriptor, context, declaration, jvmMethodSignature, true);
|
||||
adapter.visitMaxs(-1, -1);
|
||||
|
||||
return methodNode;
|
||||
return new SMAPAndMethodNode(methodNode, smap);
|
||||
}
|
||||
|
||||
private SMAP generateMethodBody(
|
||||
@@ -312,28 +319,44 @@ public class InlineCodegen extends CallGenerator {
|
||||
@NotNull FunctionDescriptor descriptor,
|
||||
@NotNull MethodContext context,
|
||||
@NotNull JetDeclarationWithBody declaration,
|
||||
@NotNull JvmMethodSignature jvmMethodSignature
|
||||
@NotNull JvmMethodSignature jvmMethodSignature,
|
||||
boolean isLambda
|
||||
) {
|
||||
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen());
|
||||
FakeMemberCodegen parentCodegen =
|
||||
new FakeMemberCodegen(codegen.getParentCodegen(), declaration,
|
||||
(FieldOwnerContext) context.getParentContext(),
|
||||
isLambda ? codegen.getParentCodegen().getClassName() : typeMapper.mapOwner(descriptor, false).getInternalName());
|
||||
|
||||
FunctionCodegen.generateMethodBody(
|
||||
adapter, descriptor, context, jvmMethodSignature,
|
||||
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration),
|
||||
// Wrapping for preventing marking actual parent codegen as containing reifier markers
|
||||
parentCodegen
|
||||
adapter, descriptor, context, jvmMethodSignature,
|
||||
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration),
|
||||
// Wrapping for preventing marking actual parent codegen as containing reifier markers
|
||||
parentCodegen
|
||||
);
|
||||
|
||||
return new SMAP(parentCodegen.mappings);
|
||||
return createSMAPWithDefaultMapping(declaration, parentCodegen.getOrCreateSourceMapper().getResultMappings());
|
||||
}
|
||||
|
||||
private static SMAP createSMAPWithDefaultMapping(
|
||||
@NotNull JetDeclarationWithBody declaration,
|
||||
@NotNull List<FileMapping> mappings
|
||||
) {
|
||||
PsiFile containingFile = declaration.getContainingFile();
|
||||
Integer lineNumbers = CodegenUtil.getLineNumberForElement(containingFile, true);
|
||||
assert lineNumbers != null : "Couldn't extract line count in " + containingFile;
|
||||
|
||||
return new SMAP(mappings);
|
||||
}
|
||||
|
||||
private static class FakeMemberCodegen extends MemberCodegen {
|
||||
|
||||
private final MemberCodegen delegate;
|
||||
@NotNull private final String className;
|
||||
|
||||
List<FileMapping> mappings = new ArrayList<FileMapping>();
|
||||
|
||||
public FakeMemberCodegen(@NotNull MemberCodegen wrapped) {
|
||||
super(wrapped);
|
||||
public FakeMemberCodegen(@NotNull MemberCodegen wrapped, @NotNull JetElement declaration, @NotNull FieldOwnerContext codegenContext, @NotNull String className) {
|
||||
super(wrapped, declaration, codegenContext);
|
||||
delegate = wrapped;
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -357,6 +380,11 @@ public class InlineCodegen extends CallGenerator {
|
||||
return delegate.getInlineNameGenerator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -627,4 +655,8 @@ public class InlineCodegen extends CallGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private SourceMapper createNestedSourceMapper(@NotNull SMAPAndMethodNode nodeAndSmap) {
|
||||
return new NestedSourceMapper(sourceMapper, nodeAndSmap.getRanges(), nodeAndSmap.getClassSMAP().getSourceInfo());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,8 +24,10 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.jetbrains.kotlin.codegen.MemberCodegen;
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||
import org.jetbrains.kotlin.codegen.context.CodegenContext;
|
||||
import org.jetbrains.kotlin.codegen.context.MethodContext;
|
||||
import org.jetbrains.kotlin.codegen.context.PackageContext;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||
@@ -43,6 +45,7 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage;
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf;
|
||||
@@ -83,11 +86,14 @@ public class InlineCodegenUtil {
|
||||
byte[] classData,
|
||||
final String methodName,
|
||||
final String methodDescriptor,
|
||||
String containerInternalName
|
||||
ClassId classId
|
||||
) throws ClassNotFoundException, IOException {
|
||||
ClassReader cr = new ClassReader(classData);
|
||||
final MethodNode[] node = new MethodNode[1];
|
||||
final String [] debugInfo = new String[2];
|
||||
final String[] debugInfo = new String[2];
|
||||
final int[] lines = new int[2];
|
||||
lines[0] = Integer.MAX_VALUE;
|
||||
lines[1] = Integer.MIN_VALUE;
|
||||
cr.accept(new ClassVisitor(API) {
|
||||
|
||||
@Override
|
||||
@@ -98,22 +104,53 @@ public class InlineCodegenUtil {
|
||||
}
|
||||
|
||||
@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)) {
|
||||
node[0] = new MethodNode(access, name, desc, signature, exceptions);
|
||||
node[0] = new MethodNode(API, access, name, desc, signature, exceptions) {
|
||||
|
||||
@Override
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
super.visitLineNumber(line, start);
|
||||
lines[0] = Math.min(lines[0], line);
|
||||
lines[1] = Math.max(lines[1], line);
|
||||
}
|
||||
};
|
||||
return node[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, ClassReader.SKIP_FRAMES);
|
||||
|
||||
return new SMAPAndMethodNode(node[0], debugInfo[0], containerInternalName, new SMAPParser(debugInfo[1], "TODO").parse());
|
||||
SMAP smap = new SMAPParser(debugInfo[1], debugInfo[0], classId.toString(), lines[0], lines[1]).parse();
|
||||
return new SMAPAndMethodNode(node[0], smap);
|
||||
}
|
||||
|
||||
public static void initDefaultSourceMappingIfNeeded(@NotNull CodegenContext context, @NotNull MemberCodegen codegen, @NotNull GenerationState state) {
|
||||
if (state.isInlineEnabled()) {
|
||||
CodegenContext<?> parentContext = context.getParentContext();
|
||||
while (parentContext != null) {
|
||||
if (parentContext instanceof MethodContext) {
|
||||
if (((MethodContext) parentContext).isInlineFunction()) {
|
||||
//just init default one to one mapping
|
||||
codegen.getOrCreateSourceMapper();
|
||||
break;
|
||||
}
|
||||
}
|
||||
parentContext = parentContext.getParentContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static VirtualFile getVirtualFileForCallable(@NotNull ClassId containerClassId, @NotNull GenerationState state) {
|
||||
VirtualFile file = findVirtualFileWithHeader(state.getProject(), containerClassId);
|
||||
VirtualFileFinder fileFinder = VirtualFileFinder.SERVICE.getInstance(state.getProject());
|
||||
VirtualFile file = fileFinder.findVirtualFileWithHeader(containerClassId.asSingleFqName().toSafe());
|
||||
if (file == null) {
|
||||
throw new IllegalStateException("Couldn't find declaration file for " + containerClassId);
|
||||
}
|
||||
@@ -129,8 +166,7 @@ public class InlineCodegenUtil {
|
||||
throw new IllegalStateException("Function in namespace should have implClassName property in proto: " + deserializedDescriptor);
|
||||
}
|
||||
Name name = deserializedDescriptor.getNameResolver().getName(proto.getExtension(JvmProtoBuf.implClassName));
|
||||
ClassId packageClassId = PackageClassUtils.getPackageClassId(((PackageFragmentDescriptor) parentDeclaration).getFqName());
|
||||
containerClassId = new ClassId(packageClassId.getPackageFqName(), name);
|
||||
containerClassId = new ClassId(((PackageFragmentDescriptor) parentDeclaration).getFqName(), name);
|
||||
} else {
|
||||
containerClassId = getContainerClassId(deserializedDescriptor);
|
||||
}
|
||||
@@ -140,12 +176,6 @@ public class InlineCodegenUtil {
|
||||
return containerClassId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VirtualFile findVirtualFileWithHeader(@NotNull Project project, @NotNull ClassId containerClassId) {
|
||||
VirtualFileFinder fileFinder = VirtualFileFinder.SERVICE.getInstance(project);
|
||||
return fileFinder.findVirtualFileWithHeader(containerClassId.asSingleFqName().toSafe());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VirtualFile findVirtualFile(@NotNull Project project, @NotNull String internalName) {
|
||||
VirtualFileFinder fileFinder = VirtualFileFinder.SERVICE.getInstance(project);
|
||||
@@ -163,8 +193,9 @@ public class InlineCodegenUtil {
|
||||
if (containerDescriptor instanceof ClassDescriptor) {
|
||||
ClassId classId = DescriptorUtilPackage.getClassId((ClassDescriptor) containerDescriptor);
|
||||
if (isTrait(containerDescriptor)) {
|
||||
FqNameUnsafe shortName = classId.getRelativeClassName();
|
||||
classId = new ClassId(classId.getPackageFqName(), Name.identifier(shortName.shortName().toString() + JvmAbi.TRAIT_IMPL_SUFFIX));
|
||||
FqNameUnsafe relativeClassName = classId.getRelativeClassName();
|
||||
//TODO test nested trait fun inlining
|
||||
classId = new ClassId(classId.getPackageFqName(), Name.identifier(relativeClassName.shortName().asString() + JvmAbi.TRAIT_IMPL_SUFFIX));
|
||||
}
|
||||
return classId;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -51,7 +50,7 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner {
|
||||
|
||||
private final CalculatedClosure closure;
|
||||
|
||||
private MethodNode node;
|
||||
private SMAPAndMethodNode node;
|
||||
|
||||
private List<CapturedParamDesc> capturedVars;
|
||||
|
||||
@@ -76,11 +75,11 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner {
|
||||
assert closure != null : "Closure for lambda should be not null " + expression.getText();
|
||||
}
|
||||
|
||||
public MethodNode getNode() {
|
||||
public SMAPAndMethodNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public void setNode(MethodNode node) {
|
||||
public void setNode(SMAPAndMethodNode node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class MethodInliner {
|
||||
|
||||
private final String errorPrefix;
|
||||
|
||||
private SourceMapper sourceMapper;
|
||||
private final SourceMapper sourceMapper;
|
||||
|
||||
private final JetTypeMapper typeMapper;
|
||||
|
||||
@@ -83,7 +83,7 @@ public class MethodInliner {
|
||||
@NotNull FieldRemapper nodeRemapper,
|
||||
boolean isSameModule,
|
||||
@NotNull String errorPrefix,
|
||||
SourceMapper sourceMapper
|
||||
@NotNull SourceMapper sourceMapper
|
||||
) {
|
||||
this.node = node;
|
||||
this.parameters = parameters;
|
||||
@@ -132,6 +132,7 @@ public class MethodInliner {
|
||||
//flush transformed node to output
|
||||
resultNode.accept(new InliningInstructionAdapter(adapter));
|
||||
|
||||
sourceMapper.endMapping();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -216,11 +217,16 @@ public class MethodInliner {
|
||||
new InlinedLambdaRemapper(info.getLambdaClassType().getInternalName(), nodeRemapper, lambdaParameters);
|
||||
|
||||
setLambdaInlining(true);
|
||||
MethodInliner inliner = new MethodInliner(info.getNode(), lambdaParameters,
|
||||
SMAP lambdaSMAP = info.getNode().getClassSMAP();
|
||||
SourceMapper mapper =
|
||||
inliningContext.classRegeneration && !inliningContext.isInliningLambda ?
|
||||
new NestedSourceMapper(sourceMapper, lambdaSMAP.getIntervals(), lambdaSMAP.getSourceInfo())
|
||||
: new InlineLambdaSourceMapper(sourceMapper.getParent(), info.getNode());
|
||||
MethodInliner inliner = new MethodInliner(info.getNode().getNode(), lambdaParameters,
|
||||
inliningContext.subInlineLambda(info),
|
||||
newCapturedRemapper, true /*cause all calls in same module as lambda*/,
|
||||
"Lambda inlining " + info.getLambdaClassType().getInternalName(),
|
||||
IdenticalSourceMapper.INSTANCE$);
|
||||
mapper);
|
||||
|
||||
LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift);
|
||||
InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info);//TODO add skipped this and receiver
|
||||
@@ -233,6 +239,7 @@ public class MethodInliner {
|
||||
StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this);
|
||||
setLambdaInlining(false);
|
||||
addInlineMarker(this, false);
|
||||
mapper.endMapping();
|
||||
}
|
||||
else if (isAnonymousConstructorCall(owner, name)) { //TODO add method
|
||||
assert anonymousObjectGen != null : "<init> call not corresponds to new call" + owner + " " + name;
|
||||
@@ -272,7 +279,6 @@ public class MethodInliner {
|
||||
|
||||
};
|
||||
|
||||
|
||||
node.accept(lambdaInliner);
|
||||
|
||||
return resultNode;
|
||||
|
||||
@@ -81,4 +81,5 @@ public class RemappingClassBuilder extends DelegatingClassBuilder {
|
||||
public ClassVisitor getVisitor() {
|
||||
return new RemappingClassAdapter(builder.getVisitor(), remapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,16 +16,20 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
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
|
||||
import java.util.LinkedHashMap
|
||||
import java.util.Collections
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.kotlin.codegen.SourceInfo
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
|
||||
//TODO join parameter
|
||||
public class SMAPBuilder(val source: String,
|
||||
val path: String,
|
||||
val fileMappings: List<FileMapping>,
|
||||
val lineCountInOriginalFile: Int) {
|
||||
val fileMappings: List<FileMapping>) {
|
||||
|
||||
val header = "SMAP\n$source\nKotlin\n*S Kotlin"
|
||||
|
||||
@@ -34,29 +38,13 @@ public class SMAPBuilder(val source: String,
|
||||
return null;
|
||||
}
|
||||
|
||||
val fileIds = "*F" + fileMappings.mapIndexed {(id, file) -> "\n${file.toSMAPFile(id + 1)}" }.join("")
|
||||
val lineMappings = "*L" + fileMappings.map { it.toSMAPMapping() }.join("")
|
||||
|
||||
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"
|
||||
return "$header\n$fileIds\n$lineMappings\n*E\n"
|
||||
}
|
||||
|
||||
|
||||
fun RangeMapping.toSMAP(fileId: Int): String {
|
||||
return if (range == 1) "$source#$fileId:$dest" else "$source#$fileId,$range:$dest"
|
||||
}
|
||||
@@ -66,55 +54,193 @@ public class SMAPBuilder(val source: String,
|
||||
return "+ $id $name\n$path"
|
||||
}
|
||||
|
||||
//TODO inline
|
||||
fun FileMapping.toSMAPMapping(): String {
|
||||
return lineMappings.fold("") {
|
||||
(a, e) ->
|
||||
"$a\n${e.toSMAP(id)}"
|
||||
return lineMappings.map {
|
||||
"\n${it.toSMAP(id)}"
|
||||
}.join("")
|
||||
}
|
||||
}
|
||||
|
||||
public open class NestedSourceMapper(parent: SourceMapper, val ranges: List<RangeMapping>, sourceInfo: SourceInfo) : DefaultSourceMapper(sourceInfo, parent) {
|
||||
|
||||
override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
|
||||
val index = Collections.binarySearch(ranges, RangeMapping(lineNumber, lineNumber, 1)) {
|
||||
(value, key) ->
|
||||
if (value.contains(key.dest)) 0 else RangeMapping.Comparator.compare(value, key)
|
||||
}
|
||||
if (index < 0) {
|
||||
parent!!.visitSource(sourceInfo.source, sourceInfo.pathOrCleanFQN)
|
||||
parent!!.visitLineNumber(iv, lineNumber, start)
|
||||
}
|
||||
else {
|
||||
val rangeMapping = ranges[index]
|
||||
parent!!.visitSource(rangeMapping.parent!!.name, rangeMapping.parent!!.path)
|
||||
parent!!.visitLineNumber(iv, rangeMapping.map(lineNumber), start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object IdenticalSourceMapper: SourceMapper(-1) {
|
||||
public open class InlineLambdaSourceMapper(parent: SourceMapper, smap: SMAPAndMethodNode) : NestedSourceMapper(parent, smap.ranges, smap.classSMAP.sourceInfo) {
|
||||
|
||||
override fun visitSource(name: String, path: String) {
|
||||
|
||||
super.visitSource(name, path)
|
||||
if (isOriginalVisited()) {
|
||||
parent!!.visitOrigin()
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitOrigin() {
|
||||
super.visitOrigin()
|
||||
parent!!.visitOrigin()
|
||||
}
|
||||
|
||||
private fun isOriginalVisited(): Boolean {
|
||||
return lastVisited == origin
|
||||
}
|
||||
|
||||
override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
|
||||
val index = Collections.binarySearch(ranges, RangeMapping(lineNumber, lineNumber, 1)) {
|
||||
(value, key) ->
|
||||
if (value.contains(key.dest)) 0 else RangeMapping.Comparator.compare(value, key)
|
||||
}
|
||||
if (index >= 0) {
|
||||
val fmapping = ranges[index].parent!!
|
||||
if (fmapping.path == origin.path && fmapping.name == origin.name) {
|
||||
parent!!.visitOrigin()
|
||||
parent!!.visitLineNumber(iv, lineNumber, start)
|
||||
return
|
||||
}
|
||||
}
|
||||
super.visitLineNumber(iv, lineNumber, start)
|
||||
}
|
||||
}
|
||||
|
||||
trait SourceMapper {
|
||||
|
||||
val resultMappings: List<FileMapping>
|
||||
val parent: SourceMapper?
|
||||
|
||||
open fun visitSource(name: String, path: String) {
|
||||
throw UnsupportedOperationException("fail")
|
||||
}
|
||||
|
||||
open fun visitOrigin() {
|
||||
throw UnsupportedOperationException("fail")
|
||||
}
|
||||
|
||||
open fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
|
||||
throw UnsupportedOperationException("fail")
|
||||
}
|
||||
|
||||
open fun endMapping() {
|
||||
parent?.visitOrigin()
|
||||
}
|
||||
|
||||
default object {
|
||||
|
||||
fun flushToClassBuilder(mapper: SourceMapper, v: ClassBuilder) {
|
||||
for (fileMapping in mapper.resultMappings) {
|
||||
v.addSMAP(fileMapping)
|
||||
}
|
||||
}
|
||||
|
||||
fun createFromSmap(smap: SMAP): DefaultSourceMapper {
|
||||
val sourceMapper = DefaultSourceMapper(smap.sourceInfo, null)
|
||||
smap.fileMappings.stream()
|
||||
//default one mapped through sourceInfo
|
||||
.filterNot { it == smap.default }
|
||||
.forEach { fileMapping ->
|
||||
sourceMapper.visitSource(fileMapping.name, fileMapping.path)
|
||||
fileMapping.lineMappings.forEach {
|
||||
sourceMapper.lastVisited!!.mapNewInterval(it.source, it.dest, it.range)
|
||||
}
|
||||
}
|
||||
|
||||
return sourceMapper
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object IdenticalSourceMapper : SourceMapper {
|
||||
override val resultMappings: List<FileMapping>
|
||||
get() = emptyList()
|
||||
|
||||
override val parent: SourceMapper?
|
||||
get() = null
|
||||
|
||||
override fun visitSource(name: String, path: String) {}
|
||||
|
||||
override fun visitOrigin() {}
|
||||
|
||||
override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
|
||||
iv.visitLineNumber(lineNumber, start)
|
||||
}
|
||||
}
|
||||
|
||||
public open class SourceMapper(val lineNumbers: Int) {
|
||||
public open class DefaultSourceMapper(val sourceInfo: SourceInfo, override val parent: SourceMapper?): SourceMapper {
|
||||
|
||||
private var maxUsedValue = lineNumbers;
|
||||
protected var maxUsedValue: Int = sourceInfo.linesInFile
|
||||
|
||||
private var lastMappedWithChanges: RawFileMapping? = null;
|
||||
var lastVisited: RawFileMapping? = null
|
||||
|
||||
var fileMapping: MutableList<RawFileMapping> = arrayListOf();
|
||||
private var lastMappedWithChanges: RawFileMapping? = null
|
||||
|
||||
open fun visitSource(name: String, path: String) {
|
||||
fileMapping.add(RawFileMapping(name, path))
|
||||
var fileMappings: LinkedHashMap<String, RawFileMapping> = linkedMapOf();
|
||||
|
||||
protected val origin: RawFileMapping
|
||||
{
|
||||
visitSource(sourceInfo.source, sourceInfo.pathOrCleanFQN)
|
||||
origin = lastVisited!!
|
||||
//map interval
|
||||
(1..maxUsedValue).forEach {origin.mapLine(it, it - 1, true) }
|
||||
}
|
||||
|
||||
open fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
|
||||
val fileMapping = fileMapping.last()
|
||||
val mappedLineIndex = fileMapping.mapLine(lineNumber, maxUsedValue, true)
|
||||
override val resultMappings: List<FileMapping>
|
||||
get() = fileMappings.values().map { it.toFileMapping() }
|
||||
|
||||
override fun visitSource(name: String, path: String) {
|
||||
lastVisited = fileMappings.getOrPut("$name#$path", { RawFileMapping(name, path) })
|
||||
}
|
||||
|
||||
override fun visitOrigin() {
|
||||
lastVisited = origin
|
||||
}
|
||||
|
||||
override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
|
||||
val mappedLineIndex = createMapping(lineNumber)
|
||||
iv.visitLineNumber(mappedLineIndex, start)
|
||||
}
|
||||
|
||||
protected fun createMapping(lineNumber: Int): Int {
|
||||
val fileMapping = lastVisited!!
|
||||
val mappedLineIndex = fileMapping.mapLine(lineNumber, maxUsedValue, lastMappedWithChanges == lastVisited)
|
||||
if (mappedLineIndex > maxUsedValue) {
|
||||
lastMappedWithChanges = fileMapping
|
||||
maxUsedValue = mappedLineIndex
|
||||
}
|
||||
return mappedLineIndex
|
||||
}
|
||||
}
|
||||
|
||||
/*Source Mapping*/
|
||||
class SMAP(val fileMappings: List<FileMapping>) {
|
||||
init {
|
||||
assert(fileMappings.isNotEmpty(), "File Mappings shouldn't be empty")
|
||||
}
|
||||
|
||||
}
|
||||
/*Source Mapping*/
|
||||
class SMAP(fileMappings: List<FileMapping>) {
|
||||
var fileMappings: List<FileMapping> = fileMappings
|
||||
val default: FileMapping
|
||||
get() = fileMappings.first()
|
||||
|
||||
val intervals = fileMappings.flatMap { it -> it.lineMappings }.sortBy(RangeMapping.Comparator)
|
||||
val intervals = fileMappings.flatMap { it.lineMappings }.sortBy(RangeMapping.Comparator)
|
||||
|
||||
class object {
|
||||
val sourceInfo: SourceInfo
|
||||
init {
|
||||
val defaultMapping = default.lineMappings.single()
|
||||
sourceInfo = SourceInfo(default.name, default.path, defaultMapping.source + defaultMapping.range - 1)
|
||||
}
|
||||
|
||||
default object {
|
||||
val FILE_SECTION = "*F"
|
||||
|
||||
val LINE_SECTION = "*L"
|
||||
@@ -138,27 +264,38 @@ class RawFileMapping(val name: String, val path: String) {
|
||||
}
|
||||
|
||||
fun mapLine(source: Int, currentIndex: Int, isLastMapped: Boolean): Int {
|
||||
var dest = lineMappings.get(source);
|
||||
var dest = lineMappings[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.range += source - lastMappedWithNewIndex
|
||||
dest = lineMappings[lastMappedWithNewIndex] + source - lastMappedWithNewIndex
|
||||
}
|
||||
else {
|
||||
dest = currentIndex + 1
|
||||
rangeMapping = RangeMapping(source, dest)
|
||||
rangeMappings.add(rangeMapping)
|
||||
}
|
||||
|
||||
lineMappings.put(source, dest)
|
||||
lastMappedWithNewIndex = source;
|
||||
lastMappedWithNewIndex = source
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
||||
|
||||
fun mapNewInterval(source: Int, dest: Int, range: Int) {
|
||||
val rangeMapping = RangeMapping(source, dest, range)
|
||||
rangeMappings.add(rangeMapping)
|
||||
|
||||
(source..(source + range - 1)).forEach {
|
||||
lineMappings.put(source, dest)
|
||||
}
|
||||
}
|
||||
|
||||
private fun couldFoldInRange(first: Int, second: Int): Boolean {
|
||||
//TODO
|
||||
val delta = second - first
|
||||
return delta > 0 && delta <= 10;
|
||||
}
|
||||
@@ -175,7 +312,8 @@ public class FileMapping(val name: String, val path: String) {
|
||||
}
|
||||
}
|
||||
|
||||
public class RangeMapping(val source: Int, val dest: Int, var range: Int = 1) {
|
||||
//TODO comparable
|
||||
data public class RangeMapping(val source: Int, val dest: Int, var range: Int = 1) {
|
||||
|
||||
var parent: FileMapping? = null;
|
||||
|
||||
@@ -184,7 +322,7 @@ public class RangeMapping(val source: Int, val dest: Int, var range: Int = 1) {
|
||||
}
|
||||
|
||||
fun map(destLine: Int): Int {
|
||||
return source + destLine - dest
|
||||
return source + (destLine - dest)
|
||||
}
|
||||
|
||||
object Comparator : util.Comparator<RangeMapping> {
|
||||
@@ -194,7 +332,8 @@ public class RangeMapping(val source: Int, val dest: Int, var range: Int = 1) {
|
||||
val res = o1.dest - o2.dest
|
||||
if (res == 0) {
|
||||
return o1.range - o2.range
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,23 +22,24 @@ import org.jetbrains.org.objectweb.asm.tree.LineNumberNode
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import kotlin.properties.Delegates
|
||||
import java.util.Collections
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import org.jetbrains.kotlin.codegen.SourceInfo
|
||||
|
||||
class SMAPAndMethodNode(val node: MethodNode, val source: String, val sourcePath: String, classSMAP: SMAP) {
|
||||
//TODO comment
|
||||
class SMAPAndMethodNode(val node: MethodNode, val 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)
|
||||
val lineNumbers =
|
||||
InsnStream(node.instructions.getFirst(), null).stream().filterIsInstance<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 label in inlined function $it ${it.line}")
|
||||
LabelAndMapping(it, classSMAP.intervals[index])
|
||||
}
|
||||
};
|
||||
}.toList()
|
||||
|
||||
val ranges = lineNumbers.stream().map { it.mapper }.toList().distinct().toList();
|
||||
}
|
||||
|
||||
class LabelAndMapping(val lineNumberNode: LineNumberNode, val mapper: RangeMapping) {
|
||||
val getOriginalLine = mapper.map(lineNumberNode.line)
|
||||
val getOriginalFileName = mapper.parent!!.name
|
||||
val getOriginalPath = mapper.parent!!.path
|
||||
}
|
||||
class LabelAndMapping(val lineNumberNode: LineNumberNode, val mapper: RangeMapping)
|
||||
@@ -16,18 +16,23 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
class SMAPParser(val input: String?, val path: String) {
|
||||
class SMAPParser(val mappingInfo: String?, val source: String, val path: String, val methodStartLine: Int, val methodEndLine: Int) {
|
||||
|
||||
val fileMappings = linkedMapOf<Int, FileMapping>()
|
||||
|
||||
fun parse() : SMAP {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return SMAP(listOf())
|
||||
if (mappingInfo == null || mappingInfo.isEmpty()) {
|
||||
val fm = FileMapping(source, path)
|
||||
if (methodStartLine <= methodEndLine) {
|
||||
//one to one
|
||||
fm.addRangeMapping(RangeMapping(methodStartLine, methodStartLine, methodEndLine - methodStartLine + 1))
|
||||
}
|
||||
return SMAP(listOf(fm))
|
||||
}
|
||||
|
||||
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 fileSectionStart = mappingInfo.indexOf(SMAP.FILE_SECTION) + SMAP.FILE_SECTION.length()
|
||||
val lineSectionAnchor = mappingInfo.indexOf(SMAP.LINE_SECTION)
|
||||
val files = mappingInfo.substring(fileSectionStart, lineSectionAnchor)
|
||||
|
||||
|
||||
val fileEntries = files.trim().split('+')
|
||||
@@ -44,7 +49,7 @@ class SMAPParser(val input: String?, val path: String) {
|
||||
}
|
||||
|
||||
|
||||
val lines = input.substring(lineSectionAnchor + SMAP.LINE_SECTION.length(), input.indexOf(SMAP.END)).trim().split('\n')
|
||||
val lines = mappingInfo.substring(lineSectionAnchor + SMAP.LINE_SECTION.length(), mappingInfo.indexOf(SMAP.END)).trim().split('\n')
|
||||
for (lineMapping in lines) {
|
||||
/*only simple mapping now*/
|
||||
val targetSplit = lineMapping.indexOf(':')
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
inline fun inlineFun(s: () -> Unit) {
|
||||
s()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
inlineFun {
|
||||
test.lineNumber()
|
||||
}
|
||||
}
|
||||
@@ -114,6 +114,12 @@ public class LineNumberTestGenerated extends AbstractLineNumberTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleSmap.kt")
|
||||
public void testSimpleSmap() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/lineNumber/simpleSmap.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("topLevel.kt")
|
||||
public void testTopLevel() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/lineNumber/topLevel.kt");
|
||||
|
||||
Reference in New Issue
Block a user