From 1651445c909d7b3800ac0fe5c075cb94d46c1335 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 6 Aug 2020 13:30:07 +0700 Subject: [PATCH] [java-decompiler] IDEA-246839 Support java records in decompiler Also: support preview levels in ClsFileImpl Also fixes: IDEA-247551 Exception on first opening of record .class file GitOrigin-RevId: 4362d669d1c16b8230d6d8ab803465b6a7476803 --- .../java/decompiler/main/ClassWriter.java | 81 ++++++++++++++++-- .../java/decompiler/struct/StructClass.java | 12 +++ .../struct/StructRecordComponent.java | 47 ++++++++++ .../struct/attr/StructGeneralAttribute.java | 4 + .../struct/attr/StructRecordAttribute.java | 38 ++++++++ .../java/decompiler/SingleClassesTest.java | 5 ++ testData/classes/records/TestRecordAnno.class | Bin 0 -> 1693 bytes .../classes/records/TestRecordEmpty.class | Bin 0 -> 1173 bytes .../records/TestRecordGenericVararg.class | Bin 0 -> 2028 bytes .../classes/records/TestRecordSimple.class | Bin 0 -> 1448 bytes .../classes/records/TestRecordVararg.class | Bin 0 -> 1464 bytes testData/results/TestRecordAnno.dec | 66 ++++++++++++++ testData/results/TestRecordEmpty.dec | 35 ++++++++ testData/results/TestRecordGenericVararg.dec | 66 ++++++++++++++ testData/results/TestRecordSimple.dec | 64 ++++++++++++++ testData/results/TestRecordVararg.dec | 64 ++++++++++++++ testData/src/records/TestRecordAnno.java | 17 ++++ testData/src/records/TestRecordEmpty.java | 3 + .../src/records/TestRecordGenericVararg.java | 6 ++ testData/src/records/TestRecordSimple.java | 3 + testData/src/records/TestRecordVararg.java | 3 + 21 files changed, 508 insertions(+), 6 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java create mode 100644 testData/classes/records/TestRecordAnno.class create mode 100644 testData/classes/records/TestRecordEmpty.class create mode 100644 testData/classes/records/TestRecordGenericVararg.class create mode 100644 testData/classes/records/TestRecordSimple.class create mode 100644 testData/classes/records/TestRecordVararg.class create mode 100644 testData/results/TestRecordAnno.dec create mode 100644 testData/results/TestRecordEmpty.dec create mode 100644 testData/results/TestRecordGenericVararg.dec create mode 100644 testData/results/TestRecordSimple.dec create mode 100644 testData/results/TestRecordVararg.dec create mode 100644 testData/src/records/TestRecordAnno.java create mode 100644 testData/src/records/TestRecordEmpty.java create mode 100644 testData/src/records/TestRecordGenericVararg.java create mode 100644 testData/src/records/TestRecordSimple.java create mode 100644 testData/src/records/TestRecordVararg.java diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 72fefe4..bb5790b 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -14,10 +14,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; -import org.jetbrains.java.decompiler.struct.StructClass; -import org.jetbrains.java.decompiler.struct.StructField; -import org.jetbrains.java.decompiler.struct.StructMember; -import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.*; import org.jetbrains.java.decompiler.struct.attr.*; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; @@ -28,6 +25,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.TextBuffer; import java.util.*; +import java.util.stream.Collectors; public class ClassWriter { private final PoolInterceptor interceptor; @@ -162,11 +160,19 @@ public class ClassWriter { dummy_tracer.incrementCurrentSourceLine(buffer.countLines(start_class_def)); + List components = cl.getRecordComponents(); + for (StructField fd : cl.getFields()) { boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) || wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); if (hide) continue; + if (components != null && fd.getAccessFlags() == (CodeConstants.ACC_FINAL | CodeConstants.ACC_PRIVATE) && + components.stream().anyMatch(c -> c.getName().equals(fd.getName()) && c.getDescriptor().equals(fd.getDescriptor()))) { + // Record component field: skip it + continue; + } + boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); if (isEnum) { if (enumFields) { @@ -302,6 +308,13 @@ public class ClassWriter { flags &= ~CodeConstants.ACC_FINAL; } + List components = cl.getRecordComponents(); + + if (components != null) { + // records are implicitly final + flags &= ~CodeConstants.ACC_FINAL; + } + appendModifiers(buffer, flags, CLASS_ALLOWED, isInterface, CLASS_EXCLUDED); if (isEnum) { @@ -313,6 +326,9 @@ public class ClassWriter { } buffer.append("interface "); } + else if (components != null) { + buffer.append("record "); + } else { buffer.append("class "); } @@ -324,9 +340,22 @@ public class ClassWriter { appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds); } + if (components != null) { + buffer.append('('); + for (int i = 0; i < components.size(); i++) { + StructRecordComponent cd = components.get(i); + if (i > 0) { + buffer.append(", "); + } + boolean varArgComponent = i == components.size() - 1 && isVarArgRecord(cl); + recordComponentToJava(cd, buffer, varArgComponent); + } + buffer.append(')'); + } + buffer.append(' '); - if (!isEnum && !isInterface && cl.superClass != null) { + if (!isEnum && !isInterface && components == null && cl.superClass != null) { VarType supertype = new VarType(cl.superClass.getString(), true); if (!VarType.VARTYPE_OBJECT.equals(supertype)) { buffer.append("extends "); @@ -362,6 +391,13 @@ public class ClassWriter { buffer.append('{').appendLineSeparator(); } + private boolean isVarArgRecord(StructClass cl) { + String canonicalConstructorDescriptor = + cl.getRecordComponents().stream().map(c -> c.getDescriptor()).collect(Collectors.joining("", "(", ")V")); + StructMethod ctor = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor); + return ctor != null && ctor.hasModifier(CodeConstants.ACC_VARARGS); + } + private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { int start = buffer.length(); boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE); @@ -452,6 +488,33 @@ public class ClassWriter { } } + private static void recordComponentToJava(StructRecordComponent cd, TextBuffer buffer, boolean varArgComponent) { + appendAnnotations(buffer, -1, cd, TypeAnnotation.FIELD); + + VarType fieldType = new VarType(cd.getDescriptor(), false); + + GenericFieldDescriptor descriptor = null; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + StructGenericSignatureAttribute attr = cd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE); + if (attr != null) { + descriptor = GenericMain.parseFieldSignature(attr.getSignature()); + } + } + + if (descriptor != null) { + buffer.append(GenericMain.getGenericCastTypeName(varArgComponent ? descriptor.type.decreaseArrayDim() : descriptor.type)); + } + else { + buffer.append(ExprProcessor.getCastTypeName(varArgComponent ? fieldType.decreaseArrayDim() : fieldType)); + } + if (varArgComponent) { + buffer.append("..."); + } + buffer.append(' '); + + buffer.append(cd.getName()); + } + private static void methodLambdaToJava(ClassNode lambdaNode, ClassWrapper classWrapper, StructMethod mt, @@ -959,7 +1022,13 @@ public class ClassWriter { for (AnnotationExprent annotation : attribute.getAnnotations()) { String text = annotation.toJava(indent, BytecodeMappingTracer.DUMMY).toString(); filter.add(text); - buffer.append(text).appendLineSeparator(); + buffer.append(text); + if (indent < 0) { + buffer.append(' '); + } + else { + buffer.appendLineSeparator(); + } } } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java index 0bb370c..7a46954 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructClass.java +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -2,6 +2,8 @@ package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructRecordAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; @@ -10,6 +12,7 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; +import java.util.List; /* class_file { @@ -132,6 +135,15 @@ public class StructClass extends StructMember { return pool; } + /** + * @return list of record components; null if this class is not a record + */ + public List getRecordComponents() { + StructRecordAttribute recordAttr = getAttribute(StructGeneralAttribute.ATTRIBUTE_RECORD); + if (recordAttr == null) return null; + return recordAttr.getComponents(); + } + public int[] getInterfaces() { return interfaces; } diff --git a/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java b/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java new file mode 100644 index 0000000..830078c --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java @@ -0,0 +1,47 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +import java.io.IOException; + +/* + record_component_info { + u2 name_index; + u2 descriptor_index; + u2 attributes_count; + attribute_info attributes[attributes_count]; + } +*/ +public class StructRecordComponent extends StructMember { + + private final String name; + private final String descriptor; + + + public StructRecordComponent(DataInputFullStream in, ConstantPool pool) throws IOException { + accessFlags = 0; + int nameIndex = in.readUnsignedShort(); + int descriptorIndex = in.readUnsignedShort(); + + name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString(); + descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString(); + + attributes = readAttributes(in, pool); + } + + public String getName() { + return name; + } + + public String getDescriptor() { + return descriptor; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index f421671..8fff5db 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -34,6 +34,7 @@ public class StructGeneralAttribute { public static final Key ATTRIBUTE_DEPRECATED = new Key<>("Deprecated"); public static final Key ATTRIBUTE_LINE_NUMBER_TABLE = new Key<>("LineNumberTable"); public static final Key ATTRIBUTE_METHOD_PARAMETERS = new Key<>("MethodParameters"); + public static final Key ATTRIBUTE_RECORD = new Key<>("Record"); public static class Key { private final String name; @@ -97,6 +98,9 @@ public class StructGeneralAttribute { else if (ATTRIBUTE_METHOD_PARAMETERS.getName().equals(name)) { attr = new StructMethodParametersAttribute(); } + else if (ATTRIBUTE_RECORD.getName().equals(name)) { + attr = new StructRecordAttribute(); + } else { // unsupported attribute return null; diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java new file mode 100644 index 0000000..fdbb271 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java @@ -0,0 +1,38 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.struct.StructRecordComponent; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/* + Record_attribute { + u2 attribute_name_index; + u4 attribute_length; + u2 components_count; + record_component_info components[components_count]; + } + */ +public class StructRecordAttribute extends StructGeneralAttribute { + List components; + + @Override + public void initContent(DataInputFullStream data, + ConstantPool pool) throws IOException { + int componentCount = data.readUnsignedShort(); + StructRecordComponent[] components = new StructRecordComponent[componentCount]; + for (int i = 0; i < componentCount; i++) { + components[i] = new StructRecordComponent(data, pool); + } + this.components = Arrays.asList(components); + } + + public List getComponents() { + return Collections.unmodifiableList(components); + } +} diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 2f2e5e3..c720949 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -129,6 +129,11 @@ public class SingleClassesTest { @Test public void testSuspendLambda() { doTest("pkg/TestSuspendLambdaKt"); } @Test public void testNamedSuspendFun2Kt() { doTest("pkg/TestNamedSuspendFun2Kt"); } @Test public void testGenericArgs() { doTest("pkg/TestGenericArgs"); } + @Test public void testRecordEmpty() { doTest("records/TestRecordEmpty"); } + @Test public void testRecordSimple() { doTest("records/TestRecordSimple"); } + @Test public void testRecordVararg() { doTest("records/TestRecordVararg"); } + @Test public void testRecordGenericVararg() { doTest("records/TestRecordGenericVararg"); } + @Test public void testRecordAnno() { doTest("records/TestRecordAnno"); } private void doTest(String testFile, String... companionFiles) { ConsoleDecompiler decompiler = fixture.getDecompiler(); diff --git a/testData/classes/records/TestRecordAnno.class b/testData/classes/records/TestRecordAnno.class new file mode 100644 index 0000000000000000000000000000000000000000..6751bc0ed00e1c00fdd8589ca5bce8fe14eec6b8 GIT binary patch literal 1693 zcma)6+fEZv6kVqjrY(bo0+pM93fh)S#k&?!E-^MKN`Ua7PeVC@fihEPrUd@U2T3F* zJoo{Al((cdnz`SMI0^*QgUTNRa5m&0>C;^~L zM*`gf5zk)p9Mjqsm`oL#v*gl+nm4|7PI2_=Na8Lzb_{oC(cY2*gV%H96{KIs00tS0 z{9Z9iuE5l_fI6FBHg{#w%cZ{xjI35H&)k=J>%er)&5~R{DoYirXLzPj@^fW>Q#$Jg;R1<*T{KD?hGVLGAPjpuCb{}*Tn%|8Z|Ab~cI>Sc z!!h=yCmm{!UH0wbvBAOi3(^(x^rV_(w_4-8KyRvf76dDsxcbEWUv}Xd%geMJTeB-p zQGPU47P=c7XId4UWjgP@T2faRRqzY8?YW*~l>IEY@ish<;RRl5c%|bt-mp@&o^kw| zW&Jt^5P|6CIg7w?eWa#!VDHJSulUKZwo1|+E7LB1g*HK>;5AcvTf&xS8q{u@Uqcj?#Scy>eAPSM>QMoYAkFOx* zBd%Ta=`qjBj~VXDkd`?`C-9TUtM*k;R0-^N@i&ud0`CES+zLK`bO4{FG38ezGjWQ( z>+nfm=OX+FGMFHPV7w{5sW`ILI}?`q1??s`S_7J(NOW3dNHA^9s5)*#HAwIobU9Ir@Heg*u+4v4Dhyq>V)^F=S8qId?qnpEw^x zM+RMnr32S@^>cihPo=;(U+qlE&)#tfqj z$H6Z`u-_Izi?=;8mT$@q_l|ksnmF1_>Yhs$iZcsmD3qoYKkj3LVXZXHA_J2~ zaMH8LJwy6P4ug((?HWskzuH_i0P4*3rDb18sg(}+;H}U-*;Sb(?4+@aM;0F2D51=- zJrN0pzIOY<3B@7^!*ctYh2b*p*Y(fknQ+3yH{9=fLhUrAJR1(`G4IiR$95uM>tY~Y z3e^eRftEoWa_DiT;u{gir+-P}>VN!ZQOi=;V*rEQ@O=@CiG{LIW!RlmKBp23PZ_r6 zr&7zqvQfh-JtgxcNs}HEjcXJ+G?NW1R=$B<(qaMYG@4@|fejk%Faftvq$o$P>o)Be zaE)e@XH`b9fUmTi1-E{~S8!(v{08vlzu-A+M(_g?GbN=edn06K;n%~QNS?BB7hA-T f#P(S49`284-~r8PI(bNlRaEdKiub8WH57jVVV)N(-H=<8g_AaVwI2nj}Ka< zWfc#8fFGrv*CIRW#OK6L}2K%~K8O3+#Wn-LC+kU}8TG2xOJFpnS(A@)fJPOZG}J z0TzW-G}w@dgE&O6h8;BKyk#jc{uf~LI`*)MBREP`>)vMc0UV@{~v9U)r z95Oz>t8M~Ao$>G1H!)7{7CglordiCuW2qY6@-z0qnpDzf;+)0*bd8$&@bi|doHe=V z1P&?AxUQ#c<#;X~4<-ayuvcW%OaW03SY7TkY|VLr!7jpSrG@uk>f+#U2e)?yy|(c zTkDlX4e=vMSmMAMJK(Mi>b|qCJUMvzQUs6Ap|@I1lmy z27mnm=Df?X66TVDf%^id+G2Ep-z?6+LxGW1EO0mtwFEM9j2^68AdY1lN5Wnv>jXj&oc!38S<@J3exo z5}OZbJIN97R_TH2j|%?fP=t?e*`>&b1~o(7NynCmL@hZ{3pbr$Y)EwEXpCt zHrXuy(`4C8I8Sd~{R~gKwBFKZ7(3P8yRCL@(Qr{KTp&yy7rVgr{}1dXHb<1;GOjRL o3RiKB>j-z9o!Hrj*XgsDly0ySv$%s<-t%4Bai_@% literal 0 HcmV?d00001 diff --git a/testData/classes/records/TestRecordSimple.class b/testData/classes/records/TestRecordSimple.class new file mode 100644 index 0000000000000000000000000000000000000000..91c748e44125980338aff60664e223a85595a093 GIT binary patch literal 1448 zcma)5T~pIQ6g{`JO{?#(&(oO?I-_n*^WXJ;_*GKU0)6eLxokS2`onFprcH0@pe zlc+gvoseF)Y|DQ|7%G-_vdAE-AgAIARKk=SfINL$cz#IOvRdt?AkZPNvc!+5$V)IC z6#$H>DBvm~<2zfvYuUSm)ncVz9-~z0ItuZI%rc>364y9O!}J;(PMrnk2W2Ts(zJ>h z%yLEIYsYMQ1bqM{ru(6~Cu+V?`bNCkp)W6=#=RU?p)0trVhs-ni(M7BWBXQ1=%HFf zA!MuPSqR5*!cu#}>LCo=&dA@DR;pIKA{RC;VCs4Gx6AT}7D1n6t4u&l9 z`FaTD{RZEd6r`r$B$Fp_lh4@%hvi3}R93ykdQxePcL`OrQ;Y)N`FK%54Iz=j7*FU@ z5BfOp?Nam^EJyTp7L)zRXe+0f>c^i9F_Ao@aT|9y193&?M)cqgq_iJU2H8kOX^30V ui#&W8auUU$(h@Y@N7!HZn&Dfn1Tn&0B-!gjJjN3|VlC+a literal 0 HcmV?d00001 diff --git a/testData/classes/records/TestRecordVararg.class b/testData/classes/records/TestRecordVararg.class new file mode 100644 index 0000000000000000000000000000000000000000..a720d7f271369d6dc0427822def2a586a1fb8478 GIT binary patch literal 1464 zcma)6T~pIQ6g^wormcZO1>{3MRM0da5x+GEGvYXQrl=zf5Au|@J2X(THAxwG^#AxE zGcwNb;1BReiFcFKMxh-alHI+z=iYPA-J74k&VHPq!^Bz+3G^vQsz@QtFt}$OT1LZi zc8w3bD%=`Fdc}5Zf0dz6D{g0zK~_Od#TBRwV{YK&8C%@*L%_D>TJA0bJEEr~{RdR! zF-WIlim|g(DgY>`xQbzhj4w8Q*LDc7q?NlxGmGU8uh8F=QASlB$3*;VDP07zURAEGvw#x(=c+lhh+u#RXo5$ zhM7(_x8?Zu0XM>|5riRIX-8o=i4(V-L$S||(D9As)EeBID+{sTYMQYkqTa_W5pe6c z$zO4=>e@|TxN*pO!}7eiA&RkcF-Sc7Uso>DE`!IU`jL5@rz=2DKZ9Cw9PS1=xTnBm znD2lKPibj~ppl)A&q-J4Iin>9iq-nby4i9Za7* zg`zK>qW^?U3CslUjNW~irT1*;iW|5|Cjteb<@bCLJb_!Z&nCz$O~4S1>>P+T?U!kl zu8Mv}c6_Dn#SCib5@i^oS)IQGJ_3CCFZd*GNAML&CiTeZ3uhSbh93=m{uG$O9cUDR zQAKScMsNmF`Zp-OXr!VrjHD<=G~(d?UJ|syN$5eP3s|IefcS;o410Muu+fhYN$U3_ SJi${uBU_HrJ;w@GG5#CoI5FA) literal 0 HcmV?d00001 diff --git a/testData/results/TestRecordAnno.dec b/testData/results/TestRecordAnno.dec new file mode 100644 index 0000000..1df3349 --- /dev/null +++ b/testData/results/TestRecordAnno.dec @@ -0,0 +1,66 @@ +package records; + +public record TestRecordAnno(@RC @TA int x, int y) { + public TestRecordAnno(@TA int x, @P int y) { + this.x = x; + this.y = y; + } + + public final String toString() { + return this.toString(this); + } + + public final int hashCode() { + return this.hashCode(this); + } + + public final boolean equals(Object o) { + return this.equals(this, o); + } + + @TA + public int x() { + return this.x; + } + + @M + public int y() { + return this.y;// 5 + } +} + +class 'records/TestRecordAnno' { + method ' (II)V' { + 6 4 + b 5 + e 6 + } + + method 'toString ()Ljava/lang/String;' { + 1 9 + 6 9 + } + + method 'hashCode ()I' { + 1 13 + 6 13 + } + + method 'equals (Ljava/lang/Object;)Z' { + 2 17 + 7 17 + } + + method 'x ()I' { + 1 22 + 4 22 + } + + method 'y ()I' { + 1 27 + 4 27 + } +} + +Lines mapping: +5 <-> 28 diff --git a/testData/results/TestRecordEmpty.dec b/testData/results/TestRecordEmpty.dec new file mode 100644 index 0000000..6111ddf --- /dev/null +++ b/testData/results/TestRecordEmpty.dec @@ -0,0 +1,35 @@ +package records; + +public record TestRecordEmpty() { + public final String toString() { + return this.toString(this); + } + + public final int hashCode() { + return this.hashCode(this); + } + + public final boolean equals(Object o) { + return this.equals(this, o);// 3 + } +} + +class 'records/TestRecordEmpty' { + method 'toString ()Ljava/lang/String;' { + 1 4 + 6 4 + } + + method 'hashCode ()I' { + 1 8 + 6 8 + } + + method 'equals (Ljava/lang/Object;)Z' { + 2 12 + 7 12 + } +} + +Lines mapping: +3 <-> 13 diff --git a/testData/results/TestRecordGenericVararg.dec b/testData/results/TestRecordGenericVararg.dec new file mode 100644 index 0000000..1a52a25 --- /dev/null +++ b/testData/results/TestRecordGenericVararg.dec @@ -0,0 +1,66 @@ +package records; + +public record TestRecordGenericVararg(T first, T... other) { + @SafeVarargs + public TestRecordGenericVararg(T first, T... other) { + this.first = first;// 5 + this.other = other; + } + + public final String toString() { + return this.toString(this); + } + + public final int hashCode() { + return this.hashCode(this); + } + + public final boolean equals(Object o) { + return this.equals(this, o); + } + + public T first() { + return this.first; + } + + public T[] other() { + return this.other;// 3 + } +} + +class 'records/TestRecordGenericVararg' { + method ' (Ljava/lang/Object;[Ljava/lang/Object;)V' { + 6 5 + b 6 + e 7 + } + + method 'toString ()Ljava/lang/String;' { + 1 10 + 6 10 + } + + method 'hashCode ()I' { + 1 14 + 6 14 + } + + method 'equals (Ljava/lang/Object;)Z' { + 2 18 + 7 18 + } + + method 'first ()Ljava/lang/Object;' { + 1 22 + 4 22 + } + + method 'other ()[Ljava/lang/Object;' { + 1 26 + 4 26 + } +} + +Lines mapping: +3 <-> 27 +5 <-> 6 diff --git a/testData/results/TestRecordSimple.dec b/testData/results/TestRecordSimple.dec new file mode 100644 index 0000000..fd1bf45 --- /dev/null +++ b/testData/results/TestRecordSimple.dec @@ -0,0 +1,64 @@ +package records; + +public record TestRecordSimple(int x, int y) { + public TestRecordSimple(int x, int y) { + this.x = x; + this.y = y; + } + + public final String toString() { + return this.toString(this); + } + + public final int hashCode() { + return this.hashCode(this); + } + + public final boolean equals(Object o) { + return this.equals(this, o); + } + + public int x() { + return this.x; + } + + public int y() { + return this.y;// 3 + } +} + +class 'records/TestRecordSimple' { + method ' (II)V' { + 6 4 + b 5 + e 6 + } + + method 'toString ()Ljava/lang/String;' { + 1 9 + 6 9 + } + + method 'hashCode ()I' { + 1 13 + 6 13 + } + + method 'equals (Ljava/lang/Object;)Z' { + 2 17 + 7 17 + } + + method 'x ()I' { + 1 21 + 4 21 + } + + method 'y ()I' { + 1 25 + 4 25 + } +} + +Lines mapping: +3 <-> 26 diff --git a/testData/results/TestRecordVararg.dec b/testData/results/TestRecordVararg.dec new file mode 100644 index 0000000..d0f4b66 --- /dev/null +++ b/testData/results/TestRecordVararg.dec @@ -0,0 +1,64 @@ +package records; + +public record TestRecordVararg(int x, int[]... y) { + public TestRecordVararg(int x, int[]... y) { + this.x = x; + this.y = y; + } + + public final String toString() { + return this.toString(this); + } + + public final int hashCode() { + return this.hashCode(this); + } + + public final boolean equals(Object o) { + return this.equals(this, o); + } + + public int x() { + return this.x; + } + + public int[][] y() { + return this.y;// 3 + } +} + +class 'records/TestRecordVararg' { + method ' (I[[I)V' { + 6 4 + b 5 + e 6 + } + + method 'toString ()Ljava/lang/String;' { + 1 9 + 6 9 + } + + method 'hashCode ()I' { + 1 13 + 6 13 + } + + method 'equals (Ljava/lang/Object;)Z' { + 2 17 + 7 17 + } + + method 'x ()I' { + 1 21 + 4 21 + } + + method 'y ()[[I' { + 1 25 + 4 25 + } +} + +Lines mapping: +3 <-> 26 diff --git a/testData/src/records/TestRecordAnno.java b/testData/src/records/TestRecordAnno.java new file mode 100644 index 0000000..c61a5b5 --- /dev/null +++ b/testData/src/records/TestRecordAnno.java @@ -0,0 +1,17 @@ +package records; + +import java.lang.annotation.*; + +public record TestRecordAnno(@TA @RC int x, @M @P int y) {} + +@Target(ElementType.TYPE_USE) +@interface TA {} + +@Target(ElementType.RECORD_COMPONENT) +@interface RC {} + +@Target(ElementType.METHOD) +@interface M {} + +@Target(ElementType.PARAMETER) +@interface P {} diff --git a/testData/src/records/TestRecordEmpty.java b/testData/src/records/TestRecordEmpty.java new file mode 100644 index 0000000..321165a --- /dev/null +++ b/testData/src/records/TestRecordEmpty.java @@ -0,0 +1,3 @@ +package records; + +public record TestRecordEmpty() {} \ No newline at end of file diff --git a/testData/src/records/TestRecordGenericVararg.java b/testData/src/records/TestRecordGenericVararg.java new file mode 100644 index 0000000..44e8fdc --- /dev/null +++ b/testData/src/records/TestRecordGenericVararg.java @@ -0,0 +1,6 @@ +package records; + +public record TestRecordGenericVararg(T first, T... other) { + @SafeVarargs + public TestRecordGenericVararg {} +} \ No newline at end of file diff --git a/testData/src/records/TestRecordSimple.java b/testData/src/records/TestRecordSimple.java new file mode 100644 index 0000000..3bc1e1a --- /dev/null +++ b/testData/src/records/TestRecordSimple.java @@ -0,0 +1,3 @@ +package records; + +public record TestRecordSimple(int x, int y) {} \ No newline at end of file diff --git a/testData/src/records/TestRecordVararg.java b/testData/src/records/TestRecordVararg.java new file mode 100644 index 0000000..8d23ed0 --- /dev/null +++ b/testData/src/records/TestRecordVararg.java @@ -0,0 +1,3 @@ +package records; + +public record TestRecordVararg(int x, int[]... y) {} \ No newline at end of file