@@ -1199,104 +1199,10 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex
11991199 }
12001200
12011201 buffer .appendMethod (toValidJavaIdentifier (name ), true , cl .qualifiedName , mt .getName (), md );
1202- buffer .append ('(' );
12031202
1204- List <VarVersionPair > mask = methodWrapper .synthParameters ;
1205-
1206- int lastVisibleParameterIndex = -1 ;
1207- for (int i = 0 ; i < md .params .length ; i ++) {
1208- if (mask == null || mask .get (i ) == null ) {
1209- lastVisibleParameterIndex = i ;
1210- }
1211- }
1212- if (lastVisibleParameterIndex != -1 ) {
1213- buffer .pushNewlineGroup (indent , 1 );
1214- buffer .appendPossibleNewline ();
1215- }
1216-
1217- List <StructMethodParametersAttribute .Entry > methodParameters = null ;
1218- if (DecompilerContext .getOption (IFernflowerPreferences .USE_METHOD_PARAMETERS )) {
1219- StructMethodParametersAttribute attr = mt .getAttribute (StructGeneralAttribute .ATTRIBUTE_METHOD_PARAMETERS );
1220- if (attr != null ) {
1221- methodParameters = attr .getEntries ();
1222- }
1223- }
1224-
1225- int index = isEnum && init ? 3 : thisVar ? 1 : 0 ;
1226- int start = isEnum && init ? 2 : 0 ;
1227- boolean hasDescriptor = descriptor != null ;
1228- //mask should now have the Outer.this in it... so this *shouldn't* be nessasary.
1229- //if (init && !isEnum && ((node.access & CodeConstants.ACC_STATIC) == 0) && node.type == ClassNode.CLASS_MEMBER)
1230- // index++;
1231-
1232- buffer .pushNewlineGroup (indent , 0 );
1233- for (int i = start ; i < md .params .length ; i ++) {
1234- boolean real = mask == null || mask .get (i ) == null ;
1235- VarType parameterType = real && hasDescriptor && paramCount < descriptor .parameterTypes .size () ? descriptor .parameterTypes .get (paramCount ) : md .params [i ];
1236- if (real ) {
1237- if (paramCount > 0 ) {
1238- buffer .append ("," );
1239- buffer .appendPossibleNewline (" " );
1240- }
1241-
1242- appendParameterAnnotations (buffer , mt , paramCount );
1243-
1244- if (methodParameters != null && i < methodParameters .size ()) {
1245- appendModifiers (buffer , methodParameters .get (i ).myAccessFlags , CodeConstants .ACC_FINAL , isInterface , 0 );
1246- }
1247- else if (methodWrapper .varproc .getVarFinal (new VarVersionPair (index , 0 )) == VarTypeProcessor .FinalType .EXPLICIT_FINAL ) {
1248- buffer .append ("final " );
1249- }
1250-
1251- String typeName ;
1252- boolean isVarArg = i == lastVisibleParameterIndex && mt .hasModifier (CodeConstants .ACC_VARARGS ) && parameterType .arrayDim > 0 ;
1253- if (isVarArg ) {
1254- parameterType = parameterType .decreaseArrayDim ();
1255- }
1256- typeName = ExprProcessor .getCastTypeName (parameterType );
1257-
1258- if (ExprProcessor .UNDEFINED_TYPE_STRING .equals (typeName ) &&
1259- DecompilerContext .getOption (IFernflowerPreferences .UNDEFINED_PARAM_TYPE_OBJECT )) {
1260- typeName = ExprProcessor .getCastTypeName (VarType .VARTYPE_OBJECT );
1261- }
1262- buffer .appendCastTypeName (typeName , parameterType );
1263- if (isVarArg ) {
1264- buffer .append ("..." );
1265- }
1266-
1267- buffer .append (' ' );
1268-
1269- String parameterName ;
1270- String clashingName = methodWrapper .varproc .getClashingName (new VarVersionPair (index , 0 ));
1271- if (clashingName != null ) {
1272- parameterName = clashingName ;
1273- } else if (methodParameters != null && i < methodParameters .size () && methodParameters .get (i ).myName != null ) {
1274- parameterName = methodParameters .get (i ).myName ;
1275- } else {
1276- parameterName = methodWrapper .varproc .getVarName (new VarVersionPair (index , 0 ));
1277- }
1278-
1279- String newParameterName = methodWrapper .methodStruct .getVariableNamer ().renameParameter (flags , parameterType , parameterName , index );
1280- if ((flags & (CodeConstants .ACC_ABSTRACT | CodeConstants .ACC_NATIVE )) != 0 && Objects .equals (newParameterName , parameterName )) {
1281- newParameterName = DecompilerContext .getStructContext ().renameAbstractParameter (methodWrapper .methodStruct .getClassQualifiedName (), mt .getName (), mt .getDescriptor (), index - (((flags & CodeConstants .ACC_STATIC ) == 0 ) ? 1 : 0 ), parameterName );
1282- }
1283- parameterName = newParameterName ;
1284-
1285- buffer .appendVariable (parameterName == null ? "param" + index : parameterName , // null iff decompiled with errors
1286- true , true , cl .qualifiedName , mt .getName (), md , index , parameterName );
1287-
1288- paramCount ++;
1289- }
1290-
1291- index += parameterType .stackSize ;
1292- }
1293- buffer .popNewlineGroup ();
1294-
1295- if (lastVisibleParameterIndex != -1 ) {
1296- buffer .appendPossibleNewline ("" , true );
1297- buffer .popNewlineGroup ();
1203+ if (!methodWrapper .isCompactRecordConstructor ) {
1204+ paramCount = writeMethodParameterHeader (mt , buffer , indent , methodWrapper , md , isEnum , init , thisVar , descriptor , paramCount , isInterface , flags , cl );
12981205 }
1299- buffer .append (')' );
13001206
13011207 StructExceptionsAttribute attr = mt .getAttribute (StructGeneralAttribute .ATTRIBUTE_EXCEPTIONS );
13021208 if ((descriptor != null && !descriptor .exceptionTypes .isEmpty ()) || attr != null ) {
@@ -1344,7 +1250,7 @@ else if (methodWrapper.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarT
13441250 } else {
13451251 TextBuffer code = root .toJava (indent + 1 );
13461252 code .addBytecodeMapping (root .getDummyExit ().bytecode );
1347- hideMethod = code .length () == 0 && (clInit || dInit || hideConstructor (node , init , throwsExceptions , paramCount , flags ));
1253+ hideMethod = code .length () == 0 && (clInit || dInit || hideConstructor (node , init , throwsExceptions , paramCount , flags , mt ));
13481254 buffer .append (code , cl .qualifiedName , InterpreterUtil .makeUniqueKey (mt .getName (), mt .getDescriptor ()));
13491255 }
13501256 }
@@ -1375,6 +1281,108 @@ else if (methodWrapper.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarT
13751281 return !hideMethod ;
13761282 }
13771283
1284+ private static int writeMethodParameterHeader (StructMethod mt , TextBuffer buffer , int indent , MethodWrapper methodWrapper , MethodDescriptor md , boolean isEnum , boolean init , boolean thisVar , GenericMethodDescriptor descriptor , int paramCount , boolean isInterface , int flags , StructClass cl ) {
1285+ buffer .append ('(' );
1286+
1287+ List <VarVersionPair > mask = methodWrapper .synthParameters ;
1288+
1289+ int lastVisibleParameterIndex = -1 ;
1290+ for (int i = 0 ; i < md .params .length ; i ++) {
1291+ if (mask == null || mask .get (i ) == null ) {
1292+ lastVisibleParameterIndex = i ;
1293+ }
1294+ }
1295+ if (lastVisibleParameterIndex != -1 ) {
1296+ buffer .pushNewlineGroup (indent , 1 );
1297+ buffer .appendPossibleNewline ();
1298+ }
1299+
1300+ List <StructMethodParametersAttribute .Entry > methodParameters = null ;
1301+ if (DecompilerContext .getOption (IFernflowerPreferences .USE_METHOD_PARAMETERS )) {
1302+ StructMethodParametersAttribute attr = mt .getAttribute (StructGeneralAttribute .ATTRIBUTE_METHOD_PARAMETERS );
1303+ if (attr != null ) {
1304+ methodParameters = attr .getEntries ();
1305+ }
1306+ }
1307+
1308+ int index = isEnum && init ? 3 : thisVar ? 1 : 0 ;
1309+ int start = isEnum && init ? 2 : 0 ;
1310+ boolean hasDescriptor = descriptor != null ;
1311+ //mask should now have the Outer.this in it... so this *shouldn't* be nessasary.
1312+ //if (init && !isEnum && ((node.access & CodeConstants.ACC_STATIC) == 0) && node.type == ClassNode.CLASS_MEMBER)
1313+ // index++;
1314+
1315+ buffer .pushNewlineGroup (indent , 0 );
1316+ for (int i = start ; i < md .params .length ; i ++) {
1317+ boolean real = mask == null || mask .get (i ) == null ;
1318+ VarType parameterType = real && hasDescriptor && paramCount < descriptor .parameterTypes .size () ? descriptor .parameterTypes .get (paramCount ) : md .params [i ];
1319+ if (real ) {
1320+ if (paramCount > 0 ) {
1321+ buffer .append ("," );
1322+ buffer .appendPossibleNewline (" " );
1323+ }
1324+
1325+ appendParameterAnnotations (buffer , mt , paramCount );
1326+
1327+ if (methodParameters != null && i < methodParameters .size ()) {
1328+ appendModifiers (buffer , methodParameters .get (i ).myAccessFlags , CodeConstants .ACC_FINAL , isInterface , 0 );
1329+ }
1330+ else if (methodWrapper .varproc .getVarFinal (new VarVersionPair (index , 0 )) == VarTypeProcessor .FinalType .EXPLICIT_FINAL ) {
1331+ buffer .append ("final " );
1332+ }
1333+
1334+ String typeName ;
1335+ boolean isVarArg = i == lastVisibleParameterIndex && mt .hasModifier (CodeConstants .ACC_VARARGS ) && parameterType .arrayDim > 0 ;
1336+ if (isVarArg ) {
1337+ parameterType = parameterType .decreaseArrayDim ();
1338+ }
1339+ typeName = ExprProcessor .getCastTypeName (parameterType );
1340+
1341+ if (ExprProcessor .UNDEFINED_TYPE_STRING .equals (typeName ) &&
1342+ DecompilerContext .getOption (IFernflowerPreferences .UNDEFINED_PARAM_TYPE_OBJECT )) {
1343+ typeName = ExprProcessor .getCastTypeName (VarType .VARTYPE_OBJECT );
1344+ }
1345+ buffer .appendCastTypeName (typeName , parameterType );
1346+ if (isVarArg ) {
1347+ buffer .append ("..." );
1348+ }
1349+
1350+ buffer .append (' ' );
1351+
1352+ String parameterName ;
1353+ String clashingName = methodWrapper .varproc .getClashingName (new VarVersionPair (index , 0 ));
1354+ if (clashingName != null ) {
1355+ parameterName = clashingName ;
1356+ } else if (methodParameters != null && i < methodParameters .size () && methodParameters .get (i ).myName != null ) {
1357+ parameterName = methodParameters .get (i ).myName ;
1358+ } else {
1359+ parameterName = methodWrapper .varproc .getVarName (new VarVersionPair (index , 0 ));
1360+ }
1361+
1362+ String newParameterName = methodWrapper .methodStruct .getVariableNamer ().renameParameter (flags , parameterType , parameterName , index );
1363+ if ((flags & (CodeConstants .ACC_ABSTRACT | CodeConstants .ACC_NATIVE )) != 0 && Objects .equals (newParameterName , parameterName )) {
1364+ newParameterName = DecompilerContext .getStructContext ().renameAbstractParameter (methodWrapper .methodStruct .getClassQualifiedName (), mt .getName (), mt .getDescriptor (), index - (((flags & CodeConstants .ACC_STATIC ) == 0 ) ? 1 : 0 ), parameterName );
1365+ }
1366+ parameterName = newParameterName ;
1367+
1368+ buffer .appendVariable (parameterName == null ? "param" + index : parameterName , // null iff decompiled with errors
1369+ true , true , cl .qualifiedName , mt .getName (), md , index , parameterName );
1370+
1371+ paramCount ++;
1372+ }
1373+
1374+ index += parameterType .stackSize ;
1375+ }
1376+ buffer .popNewlineGroup ();
1377+
1378+ if (lastVisibleParameterIndex != -1 ) {
1379+ buffer .appendPossibleNewline ("" , true );
1380+ buffer .popNewlineGroup ();
1381+ }
1382+ buffer .append (')' );
1383+ return paramCount ;
1384+ }
1385+
13781386 private static void dumpError (TextBuffer buffer , MethodWrapper wrapper , int indent ) {
13791387 List <String > lines = new ArrayList <>();
13801388 lines .add ("$VF: Couldn't be decompiled" );
@@ -1561,7 +1569,7 @@ private static void appendConstant(StringBuilder sb, PooledConstant constant) {
15611569 }
15621570 }
15631571
1564- private static boolean hideConstructor (ClassNode node , boolean init , boolean throwsExceptions , int paramCount , int methodAccessFlags ) {
1572+ private static boolean hideConstructor (ClassNode node , boolean init , boolean throwsExceptions , int paramCount , int methodAccessFlags , StructMethod structMethod ) {
15651573 if (!init || throwsExceptions || paramCount > 0 || !DecompilerContext .getOption (IFernflowerPreferences .HIDE_DEFAULT_CONSTRUCTOR )) {
15661574 return false ;
15671575 }
@@ -1571,17 +1579,27 @@ private static boolean hideConstructor(ClassNode node, boolean init, boolean thr
15711579
15721580 int classAccessFlags = node .type == ClassNode .Type .ROOT ? cl .getAccessFlags () : node .access ;
15731581 boolean isEnum = cl .hasModifier (CodeConstants .ACC_ENUM ) && DecompilerContext .getOption (IFernflowerPreferences .DECOMPILE_ENUM );
1582+ boolean isRecord = cl .getRecordComponents () != null ;
15741583
15751584 // default constructor requires same accessibility flags. Exception: enum constructor which is always private
1576- if (!isEnum && ((classAccessFlags & ACCESSIBILITY_FLAGS ) != (methodAccessFlags & ACCESSIBILITY_FLAGS ))) {
1585+ // Another exception: record classes can sometimes be generated with a private constructor
1586+ if (!isEnum && !isRecord && ((classAccessFlags & ACCESSIBILITY_FLAGS ) != (methodAccessFlags & ACCESSIBILITY_FLAGS ))) {
15771587 return false ;
15781588 }
15791589
1580- int count = 0 ;
1581- for (StructMethod mt : cl .getMethods ()) {
1582- if (CodeConstants .INIT_NAME .equals (mt .getName ())) {
1583- if (++count > 1 ) {
1584- return false ;
1590+ // Constructor with an annotation, we dont want to hide this.
1591+ if (isRecord && RecordHelper .hasAnnotations (structMethod )) {
1592+ return false ;
1593+ }
1594+
1595+ // We should not run this check in records
1596+ if (!isRecord ) {
1597+ int count = 0 ;
1598+ for (StructMethod mt : cl .getMethods ()) {
1599+ if (CodeConstants .INIT_NAME .equals (mt .getName ())) {
1600+ if (++count > 1 ) {
1601+ return false ;
1602+ }
15851603 }
15861604 }
15871605 }
0 commit comments