@@ -274,9 +274,6 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {
274274      TextBuffer  innerBuffer  = new  TextBuffer ();
275275
276276      boolean  hasContent  = false ;
277-       boolean  enumFields  = false ;
278- 
279-       List <StructRecordComponent > components  = cl .getRecordComponents ();
280277
281278      Set <StructField > fieldsToIgnore  = new  HashSet <>();
282279      Set <StructMethod > methodsToIgnore  = new  HashSet <>();
@@ -315,71 +312,37 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {
315312        methodsToIgnore .addAll (companionFunctions .keySet ());
316313      }
317314
318-       // FIXME: fields don't have line mappings 
319315      // fields 
316+       List <StructRecordComponent > components  = cl .getRecordComponents ();
320317
321-       // Find the last field marked as an enum 
322-       int  maxEnumIdx  = 0 ;
323-       for  (int  i  = 0 ; i  < cl .getFields ().size (); i ++) {
324-         StructField  fd  = cl .getFields ().get (i );
325-         boolean  isEnum  = fd .hasModifier (CodeConstants .ACC_ENUM ) && DecompilerContext .getOption (IFernflowerPreferences .DECOMPILE_ENUM );
326-         if  (isEnum ) {
327-           maxEnumIdx  = i ;
328-         }
329-       }
330- 
331-       List <StructField > deferredEnumFields  = new  ArrayList <>();
318+       List <StructField > enumFields  = new  ArrayList <>();
319+       List <StructField > nonEnumFields  = new  ArrayList <>();
332320
333-       // Find any regular fields mixed in with the enum fields 
334-       // This is invalid but allowed in bytecode. 
335-       for  (int  i  = 0 ; i  < cl .getFields ().size (); i ++) {
336-         StructField  fd  = cl .getFields ().get (i );
321+       for  (StructField  fd  : cl .getFields ()) {
337322        boolean  isEnum  = fd .hasModifier (CodeConstants .ACC_ENUM ) && DecompilerContext .getOption (IFernflowerPreferences .DECOMPILE_ENUM );
338-         if  (i  < maxEnumIdx  && !isEnum ) {
339-           deferredEnumFields .add (fd );
323+         if  (isEnum ) {
324+           enumFields .add (fd );
325+         } else  if  (!fieldsToIgnore .contains (fd )) {
326+           nonEnumFields .add (fd );
340327        }
341328      }
342329
343-       for  (StructField  fd  : cl .getFields ()) {
344-         boolean  hide  = fd .isSynthetic () && DecompilerContext .getOption (IFernflowerPreferences .REMOVE_SYNTHETIC ) ||
345-           wrapper .getHiddenMembers ().contains (InterpreterUtil .makeUniqueKey (fd .getName (), fd .getDescriptor ())) ||
346-           deferredEnumFields .contains (fd ) ||
347-           fieldsToIgnore .contains (fd );
348-         if  (hide ) continue ;
349- 
350-         if  (components  != null  && fd .getAccessFlags () == (CodeConstants .ACC_FINAL  | CodeConstants .ACC_PRIVATE ) &&
351-           components .stream ().anyMatch (c  -> c .getName ().equals (fd .getName ()) && c .getDescriptor ().equals (fd .getDescriptor ()))) {
352-           // Record component field: skip it 
353-           continue ;
330+       boolean  enums  = false ;
331+       for  (StructField  fd  : enumFields ) {
332+         if  (enums ) {
333+           innerBuffer .append (',' ).appendLineSeparator ();
354334        }
335+         enums  = true ;
355336
356-         boolean  isEnum  = fd .hasModifier (CodeConstants .ACC_ENUM ) && DecompilerContext .getOption (IFernflowerPreferences .DECOMPILE_ENUM );
357-         if  (isEnum ) {
358-           if  (enumFields ) {
359-             innerBuffer .append (',' ).appendLineSeparator ();
360-           }
361-           enumFields  = true ;
362-         } else  if  (enumFields ) {
363-           innerBuffer .append (';' );
364-           innerBuffer .appendLineSeparator ();
365-           innerBuffer .appendLineSeparator ();
366-           enumFields  = false ;
367-         }
368- 
369-         if  (propertyData  == null  || enumFields ) { // Enum fields are not considered Kotlin properties 
370-           TextBuffer  fieldBuffer  = new  TextBuffer ();
371-           writeField (wrapper , cl , fd , fieldBuffer , indent  + 1 );
372-           fieldBuffer .clearUnassignedBytecodeMappingData ();
373-           innerBuffer .append (fieldBuffer );
337+         writeField (innerBuffer , indent , fd , wrapper );
338+         hasContent  = true ;
339+       }
374340
375-            hasContent  =  true ; 
376-         } 
341+       if  ( enums ) { 
342+         innerBuffer . append ( ';' ). appendLineSeparator (); 
377343      }
378344
379345      if  (propertyData  != null ) {
380-         // Any deferred fields that are property fields should be removed to prevent duplication 
381-         deferredEnumFields .removeAll (propertyData .associatedFields ());
382- 
383346        boolean  addedLineAtEnd  = false ;
384347        for  (KProperty  prop  : propertyData .properties ()) {
385348          if  (hasContent ) {
@@ -394,7 +357,14 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {
394357            innerBuffer .appendLineSeparator ();
395358          }
396359
397-           innerBuffer .append (propBuffer );
360+           StructField  fd  = prop .underlyingField ();
361+           if  (fd  != null ) {
362+             nonEnumFields .remove (fd );
363+             String  initializer  = fd .hasModifier (CodeConstants .ACC_STATIC ) ? "<clinit> ()V"  : "<init> ()V" ;
364+             innerBuffer .append (propBuffer , cl .qualifiedName , initializer );
365+           } else  {
366+             innerBuffer .append (propBuffer );
367+           }
398368
399369          if  (isMultiline ) {
400370            innerBuffer .appendLineSeparator ();
@@ -407,14 +377,25 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {
407377        if  (!addedLineAtEnd  && !propertyData .properties ().isEmpty ()) {
408378          innerBuffer .appendLineSeparator ();
409379        }
410-       }
380+       } else  {
381+         for  (StructField  fd  : nonEnumFields ) {
382+           boolean  hide  = fd .isSynthetic () && DecompilerContext .getOption (IFernflowerPreferences .REMOVE_SYNTHETIC ) ||
383+             wrapper .getHiddenMembers ().contains (InterpreterUtil .makeUniqueKey (fd .getName (), fd .getDescriptor ()));
384+           if  (hide ) continue ;
411385
412-       // If any fields remaining were deferred but not enum fields, re-add them 
413-       for  (StructField  fd2  : deferredEnumFields ) {
414-         TextBuffer  fieldBuffer  = new  TextBuffer ();
415-         writeField (wrapper , cl , fd2 , fieldBuffer , indent  + 1 );
416-         fieldBuffer .clearUnassignedBytecodeMappingData ();
417-         innerBuffer .append (fieldBuffer );
386+           if  (components  != null  && fd .getAccessFlags () == (CodeConstants .ACC_FINAL  | CodeConstants .ACC_PRIVATE ) &&
387+             components .stream ().anyMatch (c  -> c .getName ().equals (fd .getName ()) && c .getDescriptor ().equals (fd .getDescriptor ()))) {
388+             // Record component field: skip it 
389+             continue ;
390+           }
391+ 
392+           DecompilerContext .getLogger ().writeMessage ("Non-enum field "  + fd .getName () + " is not associated with Kotlin property" , IFernflowerLogger .Severity .WARN );
393+ 
394+           TextBuffer  fieldBuffer  = new  TextBuffer ();
395+           writeField (wrapper , wrapper .getClassStruct (), fd , fieldBuffer , indent  + 1 );
396+           String  initializer  = fd .hasModifier (CodeConstants .ACC_STATIC ) ? "<clinit> ()V"  : "<init> ()V" ;
397+           innerBuffer .append (fieldBuffer , wrapper .getClassStruct ().qualifiedName , initializer );
398+         }
418399      }
419400
420401      // methods 
@@ -526,7 +507,13 @@ private void writeKotlinFile(ClassNode node, TextBuffer buffer, int indent, KPro
526507          buffer .appendLineSeparator ();
527508        }
528509
529-         buffer .append (propBuffer );
510+         StructField  fd  = prop .underlyingField ();
511+         if  (fd  != null ) {
512+           String  initializer  = fd .hasModifier (CodeConstants .ACC_STATIC ) ? "<clinit> ()V"  : "<init> ()V" ;
513+           buffer .append (propBuffer , cl .qualifiedName , initializer );
514+         } else  {
515+           buffer .append (propBuffer );
516+         }
530517
531518        if  (isMultiline ) {
532519          buffer .appendLineSeparator ();
@@ -543,11 +530,7 @@ private void writeKotlinFile(ClassNode node, TextBuffer buffer, int indent, KPro
543530
544531    for  (StructField  fd  : cl .getFields ()) {
545532      if  (propertyData .associatedFields ().contains (fd )) continue ;
546- 
547-       TextBuffer  fieldBuffer  = new  TextBuffer ();
548-       writeField (wrapper , cl , fd , fieldBuffer , indent );
549-       fieldBuffer .clearUnassignedBytecodeMappingData ();
550-       buffer .append (fieldBuffer );
533+       writeField (buffer , indent , fd , wrapper );
551534    }
552535
553536    for  (int  i  = 0 ; i  < cl .getMethods ().size (); i ++) {
@@ -827,6 +810,13 @@ private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent,
827810    buffer .popNewlineGroup ();
828811  }
829812
813+   private  void  writeField (TextBuffer  buffer , int  indent , StructField  fd , ClassWrapper  wrapper ) {
814+     TextBuffer  fieldBuffer  = new  TextBuffer ();
815+     writeField (wrapper , wrapper .getClassStruct (), fd , fieldBuffer , indent  + 1 );
816+     String  initializer  = fd .hasModifier (CodeConstants .ACC_STATIC ) ? "<clinit> ()V"  : "<init> ()V" ;
817+     buffer .append (fieldBuffer , wrapper .getClassStruct ().qualifiedName , initializer );
818+   }
819+ 
830820  public  void  writeField (ClassWrapper  wrapper , StructClass  cl , StructField  fd , TextBuffer  buffer , int  indent ) {
831821    boolean  isInterface  = cl .hasModifier (CodeConstants .ACC_INTERFACE );
832822    boolean  isDeprecated  = fd .hasAttribute (StructGeneralAttribute .ATTRIBUTE_DEPRECATED );
0 commit comments