Generate "$kotlinClass"/"$kotlinPackage" reflection fields to every class

Some seemingly irrelevant tests were changed because now there's <clinit> in
almost every class and class initialization begins with executing it
This commit is contained in:
Alexander Udalov
2014-05-20 20:20:53 +04:00
parent 4ef089d2ed
commit 59777e7df6
17 changed files with 99 additions and 41 deletions
@@ -72,8 +72,7 @@ import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver; import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration; import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.JAVA_STRING_TYPE; import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass; import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.DelegationToTraitImpl; import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.DelegationToTraitImpl;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin; import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin;
@@ -429,7 +428,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
@Override @Override
protected void generateSyntheticParts() { protected void generateSyntheticParts() {
generateDelegatedPropertyMetadataArray(); generateStaticSyntheticFields();
generateFieldForSingleton(); generateFieldForSingleton();
@@ -463,10 +462,20 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
generateToArray(); generateToArray();
genClosureFields(context.closure, v, state.getTypeMapper()); genClosureFields(context.closure, v, typeMapper);
} }
private void generateDelegatedPropertyMetadataArray() { private void generateStaticSyntheticFields() {
if (isAnnotationClass(descriptor)) {
// There's a bug in JDK 6 and 7 that prevents us from generating a static field in an annotation class:
// http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6857918
// TODO: make reflection work on annotation classes somehow
return;
}
generateReflectionObjectField(state, classAsmType, v, K_CLASS_IMPL_TYPE, JvmAbi.KOTLIN_CLASS_FIELD_NAME,
createOrGetClInitCodegen().v);
generatePropertyMetadataArrayFieldIfNeeded(classAsmType); generatePropertyMetadataArrayFieldIfNeeded(classAsmType);
} }
@@ -50,12 +50,12 @@ import java.util.List;
import static org.jetbrains.jet.codegen.AsmUtil.boxType; import static org.jetbrains.jet.codegen.AsmUtil.boxType;
import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive; import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.TraitImpl;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED; import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED;
import static org.jetbrains.jet.lang.resolve.BindingContext.VARIABLE; import static org.jetbrains.jet.lang.resolve.BindingContext.VARIABLE;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*; import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.TraitImpl;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.org.objectweb.asm.Opcodes.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclarationContainer*/> extends ParentCodegenAware { public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclarationContainer*/> extends ParentCodegenAware {
@@ -316,6 +316,27 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
return false; return false;
} }
public static void generateReflectionObjectField(
@NotNull GenerationState state,
@NotNull Type thisAsmType,
@NotNull ClassBuilder classBuilder,
@NotNull Type kImplType,
@NotNull String fieldName,
@NotNull InstructionAdapter v
) {
// TODO: generic signature
classBuilder.newField(NO_ORIGIN, ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, fieldName, kImplType.getDescriptor(),
null, null);
if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES) return;
v.anew(kImplType);
v.dup();
v.aconst(thisAsmType);
v.invokespecial(kImplType.getInternalName(), "<init>", "(Ljava/lang/Class;)V", false);
v.putstatic(thisAsmType.getInternalName(), fieldName, kImplType.getDescriptor());
}
protected void generatePropertyMetadataArrayFieldIfNeeded(@NotNull Type thisAsmType) { protected void generatePropertyMetadataArrayFieldIfNeeded(@NotNull Type thisAsmType) {
List<JetProperty> delegatedProperties = new ArrayList<JetProperty>(); List<JetProperty> delegatedProperties = new ArrayList<JetProperty>();
for (JetDeclaration declaration : ((JetDeclarationContainer) element).getDeclarations()) { for (JetDeclaration declaration : ((JetDeclarationContainer) element).getDeclarations()) {
@@ -56,27 +56,32 @@ import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor; import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
import org.jetbrains.org.objectweb.asm.MethodVisitor; import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.*; import java.util.*;
import static org.jetbrains.jet.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses; import static org.jetbrains.jet.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses;
import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver; import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.K_PACKAGE_IMPL_TYPE;
import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName; import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.*; import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.*;
import static org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.org.objectweb.asm.Opcodes.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class PackageCodegen { public class PackageCodegen {
private final GenerationState state;
private final ClassBuilderOnDemand v; private final ClassBuilderOnDemand v;
private final GenerationState state;
private final Collection<JetFile> files; private final Collection<JetFile> files;
private final Type packageClassType;
private final PackageFragmentDescriptor packageFragment; private final PackageFragmentDescriptor packageFragment;
private final PackageFragmentDescriptor compiledPackageFragment; private final PackageFragmentDescriptor compiledPackageFragment;
private final List<DeserializedCallableMemberDescriptor> previouslyCompiledCallables; private final List<DeserializedCallableMemberDescriptor> previouslyCompiledCallables;
public PackageCodegen(@NotNull GenerationState state, @NotNull Collection<JetFile> files, @NotNull final FqName fqName) { public PackageCodegen(@NotNull GenerationState state, @NotNull Collection<JetFile> files, @NotNull FqName fqName) {
this.state = state; this.state = state;
this.files = files; this.files = files;
this.packageFragment = getOnlyPackageFragment(fqName); this.packageFragment = getOnlyPackageFragment(fqName);
this.packageClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(getPackageClassFqName(fqName));
this.compiledPackageFragment = getCompiledPackageFragment(fqName); this.compiledPackageFragment = getCompiledPackageFragment(fqName);
this.previouslyCompiledCallables = filterDeserializedCallables(compiledPackageFragment); this.previouslyCompiledCallables = filterDeserializedCallables(compiledPackageFragment);
@@ -88,14 +93,13 @@ public class PackageCodegen {
Collection<JetFile> files = PackageCodegen.this.files; Collection<JetFile> files = PackageCodegen.this.files;
JetFile sourceFile = getRepresentativePackageFile(files); JetFile sourceFile = getRepresentativePackageFile(files);
String className = AsmUtil.internalNameByFqNameWithoutInnerClasses(getPackageClassFqName(fqName)); ClassBuilder v = PackageCodegen.this.state.getFactory().newVisitor(
ClassBuilder v = PackageCodegen.this.state.getFactory() PackageFacade(packageFragment == null ? compiledPackageFragment : packageFragment),
.newVisitor( packageClassType, PackagePartClassUtils.getPackageFilesWithCallables(files)
PackageFacade(packageFragment == null ? compiledPackageFragment : packageFragment), );
Type.getObjectType(className), PackagePartClassUtils.getPackageFilesWithCallables(files));
v.defineClass(sourceFile, V1_6, v.defineClass(sourceFile, V1_6,
ACC_PUBLIC | ACC_FINAL, ACC_PUBLIC | ACC_FINAL,
className, packageClassType.getInternalName(),
null, null,
"java/lang/Object", "java/lang/Object",
ArrayUtil.EMPTY_STRING_ARRAY ArrayUtil.EMPTY_STRING_ARRAY
@@ -216,18 +220,35 @@ public class PackageCodegen {
} }
} }
generateDelegationsToPreviouslyCompiled(generateCallableMemberTasks); if (!generateCallableMemberTasks.isEmpty()) {
generatePackageFacadeClass(generateCallableMemberTasks, bindings);
}
}
if (generateCallableMemberTasks.isEmpty()) return; private void generatePackageFacadeClass(
@NotNull Map<CallableMemberDescriptor, Runnable> tasks,
@NotNull List<JvmSerializationBindings> bindings
) {
generateKotlinPackageReflectionField();
for (CallableMemberDescriptor member : Ordering.from(MemberComparator.INSTANCE).sortedCopy(generateCallableMemberTasks.keySet())) { generateDelegationsToPreviouslyCompiled(tasks);
generateCallableMemberTasks.get(member).run();
for (CallableMemberDescriptor member : Ordering.from(MemberComparator.INSTANCE).sortedCopy(tasks.keySet())) {
tasks.get(member).run();
} }
bindings.add(v.getSerializationBindings()); bindings.add(v.getSerializationBindings());
writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings)); writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings));
} }
private void generateKotlinPackageReflectionField() {
MethodVisitor mv = v.newMethod(NO_ORIGIN, ACC_STATIC, "<clinit>", "()V", null, null);
MemberCodegen.generateReflectionObjectField(state, packageClassType, v, K_PACKAGE_IMPL_TYPE, JvmAbi.KOTLIN_PACKAGE_FIELD_NAME,
new InstructionAdapter(mv));
mv.visitInsn(RETURN);
FunctionCodegen.endVisit(mv, "static initializer", null);
}
private void writeKotlinPackageAnnotationIfNeeded(@NotNull JvmSerializationBindings bindings) { private void writeKotlinPackageAnnotationIfNeeded(@NotNull JvmSerializationBindings bindings) {
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) { if (state.getClassBuilderMode() != ClassBuilderMode.FULL) {
return; return;
@@ -266,6 +266,9 @@ public class InlineCodegenUtil {
} }
public static boolean isCapturedFieldName(@NotNull String fieldName) { public static boolean isCapturedFieldName(@NotNull String fieldName) {
return fieldName.startsWith(CAPTURED_FIELD_PREFIX) || THIS$0.equals(fieldName) || RECEIVER$0.equals(fieldName); // TODO: improve this heuristic
return (fieldName.startsWith(CAPTURED_FIELD_PREFIX) && !fieldName.equals(JvmAbi.KOTLIN_CLASS_FIELD_NAME)) ||
THIS$0.equals(fieldName) ||
RECEIVER$0.equals(fieldName);
} }
} }
@@ -38,6 +38,9 @@ public class AsmTypeConstants {
public static final Type PROPERTY_METADATA_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/PropertyMetadata"); public static final Type PROPERTY_METADATA_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/PropertyMetadata");
public static final Type PROPERTY_METADATA_IMPL_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/PropertyMetadataImpl"); public static final Type PROPERTY_METADATA_IMPL_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/PropertyMetadataImpl");
public static final Type K_CLASS_IMPL_TYPE = Type.getObjectType("kotlin/reflect/jvm/internal/KClassImpl");
public static final Type K_PACKAGE_IMPL_TYPE = Type.getObjectType("kotlin/reflect/jvm/internal/KPackageImpl");
public static final Type OBJECT_REF_TYPE = Type.getObjectType("kotlin/jvm/internal/Ref$ObjectRef"); public static final Type OBJECT_REF_TYPE = Type.getObjectType("kotlin/jvm/internal/Ref$ObjectRef");
public static Type getType(@NotNull Class<?> javaClass) { public static Type getType(@NotNull Class<?> javaClass) {
@@ -20,15 +20,14 @@ import org.junit.Test;
import java.io.File; import java.io.File;
import static junit.framework.Assert.*; import static org.junit.Assert.assertEquals;
public class CompilerSmokeTest extends KotlinIntegrationTestBase { public class CompilerSmokeTest extends KotlinIntegrationTestBase {
@Test @Test
public void compileAndRunHelloApp() throws Exception { public void compileAndRunHelloApp() throws Exception {
String jar = tmpdir.getTmpDir().getAbsolutePath() + File.separator + "hello.jar"; String jar = tmpdir.getTmpDir().getAbsolutePath() + File.separator + "hello.jar";
assertEquals("compilation failed", 0, runCompiler("hello.compile", "-src", "hello.kt", "-jar", jar)); assertEquals("compilation failed", 0, runCompiler("hello.compile", "-includeRuntime", "hello.kt", "-jar", jar));
runJava("hello.run", "-cp", jar, "Hello.HelloPackage"); runJava("hello.run", "-cp", jar, "Hello.HelloPackage");
} }
@@ -36,7 +35,7 @@ public class CompilerSmokeTest extends KotlinIntegrationTestBase {
public void compileAndRunHelloAppFQMain() throws Exception { public void compileAndRunHelloAppFQMain() throws Exception {
String jar = tmpdir.getTmpDir().getAbsolutePath() + File.separator + "hello.jar"; String jar = tmpdir.getTmpDir().getAbsolutePath() + File.separator + "hello.jar";
assertEquals("compilation failed", 0, runCompiler("hello.compile", "-src", "hello.kt", "-jar", jar)); assertEquals("compilation failed", 0, runCompiler("hello.compile", "-includeRuntime", "hello.kt", "-jar", jar));
runJava("hello.run", "-cp", jar, "Hello.HelloPackage"); runJava("hello.run", "-cp", jar, "Hello.HelloPackage");
} }
@@ -44,7 +43,7 @@ public class CompilerSmokeTest extends KotlinIntegrationTestBase {
public void compileAndRunHelloAppVarargMain() throws Exception { public void compileAndRunHelloAppVarargMain() throws Exception {
String jar = tmpdir.getTmpDir().getAbsolutePath() + File.separator + "hello.jar"; String jar = tmpdir.getTmpDir().getAbsolutePath() + File.separator + "hello.jar";
assertEquals("compilation failed", 0, runCompiler("hello.compile", "-src", "hello.kt", "-jar", jar)); assertEquals("compilation failed", 0, runCompiler("hello.compile", "-includeRuntime", "hello.kt", "-jar", jar));
runJava("hello.run", "-cp", jar, "Hello.HelloPackage"); runJava("hello.run", "-cp", jar, "Hello.HelloPackage");
} }
@@ -18,4 +18,4 @@ class B {
} }
// 0 INVOKEVIRTUAL // 0 INVOKEVIRTUAL
// 4 INVOKESPECIAL // 2 INVOKESPECIAL [AB]\.
@@ -13,4 +13,4 @@ fun box(): String {
} }
// 0 INVOKEVIRTUAL // 0 INVOKEVIRTUAL
// 3 INVOKESPECIAL // 2 INVOKESPECIAL B\.foo
@@ -40,11 +40,7 @@ public class PropertyGenTest extends CodegenTestCase {
public void testPrivateVal() throws Exception { public void testPrivateVal() throws Exception {
loadFile(); loadFile();
Class<?> aClass = generateClass("PrivateVal"); generateClass("PrivateVal").getDeclaredField("prop");
Field[] fields = aClass.getDeclaredFields();
assertEquals(1, fields.length); // prop
Field field = fields[0];
assertEquals("prop", field.getName());
} }
public void testPrivateVar() throws Exception { public void testPrivateVar() throws Exception {
@@ -45,6 +45,8 @@ public final class JvmAbi {
public static final String CLASS_OBJECT_FIELD = "object$"; public static final String CLASS_OBJECT_FIELD = "object$";
public static final FqName K_OBJECT = new FqName("kotlin.jvm.internal.KObject"); public static final FqName K_OBJECT = new FqName("kotlin.jvm.internal.KObject");
public static final String KOTLIN_CLASS_FIELD_NAME = "$kotlinClass";
public static final String KOTLIN_PACKAGE_FIELD_NAME = "$kotlinPackage";
public static boolean isClassObjectFqName(@NotNull FqName fqName) { public static boolean isClassObjectFqName(@NotNull FqName fqName) {
return fqName.lastSegmentIs(Name.identifier(CLASS_OBJECT_CLASS_NAME)); return fqName.lastSegmentIs(Name.identifier(CLASS_OBJECT_CLASS_NAME));
@@ -1,7 +1,7 @@
LineBreakpoint created at memberFunFromTopLevel.kt:11 LineBreakpoint created at memberFunFromTopLevel.kt:13
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! memberFunFromTopLevel.MemberFunFromTopLevelPackage !JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! memberFunFromTopLevel.MemberFunFromTopLevelPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
memberFunFromTopLevel.kt:10 memberFunFromTopLevel.kt:12
memberFunFromTopLevel.kt:4 memberFunFromTopLevel.kt:4
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
@@ -1,7 +1,7 @@
LineBreakpoint created at memberGetterFromTopLevel.kt:13 LineBreakpoint created at memberGetterFromTopLevel.kt:15
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! memberGetterFromTopLevel.MemberGetterFromTopLevelPackage !JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! memberGetterFromTopLevel.MemberGetterFromTopLevelPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
memberGetterFromTopLevel.kt:12 memberGetterFromTopLevel.kt:14
memberGetterFromTopLevel.kt:5 memberGetterFromTopLevel.kt:5
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
@@ -7,6 +7,8 @@ class A {
} }
fun main(args: Array<String>) { fun main(args: Array<String>) {
A()
//Breakpoint! //Breakpoint!
A().bar() A().bar()
} }
@@ -9,6 +9,8 @@ class A {
} }
fun main(args: Array<String>) { fun main(args: Array<String>) {
A()
//Breakpoint! //Breakpoint!
A().bar A().bar
} }
@@ -220,8 +220,8 @@ public class KotlinJpsBuildTest extends AbstractKotlinJpsBuildTestCase {
// Check that outputs are located properly // Check that outputs are located properly
File facadeWithA = findFileInOutputDir(findModule("module1"), "test/TestPackage.class"); File facadeWithA = findFileInOutputDir(findModule("module1"), "test/TestPackage.class");
File facadeWithB = findFileInOutputDir(findModule("module2"), "test/TestPackage.class"); File facadeWithB = findFileInOutputDir(findModule("module2"), "test/TestPackage.class");
assertSameElements(getMethodsOfClass(facadeWithA), "a", "getA"); assertSameElements(getMethodsOfClass(facadeWithA), "<clinit>", "a", "getA");
assertSameElements(getMethodsOfClass(facadeWithB), "b", "getB", "setB"); assertSameElements(getMethodsOfClass(facadeWithB), "<clinit>", "b", "getB", "setB");
checkPackageDeletedFromOutputWhen(Operation.CHANGE, "module1", "module1/src/a.kt", "test.TestPackage"); checkPackageDeletedFromOutputWhen(Operation.CHANGE, "module1", "module1/src/a.kt", "test.TestPackage");
checkPackageDeletedFromOutputWhen(Operation.CHANGE, "module2", "module2/src/b.kt", "test.TestPackage"); checkPackageDeletedFromOutputWhen(Operation.CHANGE, "module2", "module2/src/b.kt", "test.TestPackage");
@@ -4,4 +4,4 @@ fun a() {
} }
val a = "" val a = ""
@@ -4,4 +4,4 @@ fun b() {
} }
var b = "" var b = b()