changeset 520:a8191c4334b8

Custom typemap can be used
author tehtmi
date Fri, 15 Dec 2023 22:45:23 -0800
parents 16ef31a2a27f
children 7f2f283699b5
files src/unluac/Configuration.java src/unluac/Main.java src/unluac/assemble/Assembler.java src/unluac/assemble/Directive.java src/unluac/decompile/Disassembler.java src/unluac/decompile/Type.java src/unluac/decompile/TypeMap.java src/unluac/parse/BHeader.java
diffstat 8 files changed, 192 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/src/unluac/Configuration.java	Fri Dec 15 19:05:25 2023 -0800
+++ b/src/unluac/Configuration.java	Fri Dec 15 22:45:23 2023 -0800
@@ -27,6 +27,7 @@
   public VariableMode variable;
   public boolean strict_scope;
   public boolean luaj;
+  public String typemap;
   public String opmap;
   public String output;
   
--- a/src/unluac/Main.java	Fri Dec 15 19:05:25 2023 -0800
+++ b/src/unluac/Main.java	Fri Dec 15 22:45:23 2023 -0800
@@ -54,6 +54,13 @@
           } else {
             error("option \"" + arg + "\" doesn't have an argument", true);
           }
+        } else if(arg.equals("--typemap")) {
+          if(i + 1 < args.length) {
+            config.typemap = args[i + 1];
+            i++;
+          } else {
+            error("option \"" + arg + "\" doesn't have an argument", true);
+          }
         } else if(arg.equals("--opmap")) {
           if(i + 1 < args.length) {
             config.opmap = args[i + 1];
@@ -148,13 +155,14 @@
     print_unluac_string(System.out);
     print_usage(System.out);
     System.out.println("Available options are:");
-    System.out.println("  --assemble       assemble given disassembly listing");
-    System.out.println("  --disassemble    disassemble instead of decompile");
-    System.out.println("  --nodebug        ignore debugging information in input file");
-    System.out.println("  --opmap <file>   use opcode mapping specified in <file>");
-    System.out.println("  --output <file>  output to <file> instead of stdout");
-    System.out.println("  --rawstring      copy string bytes directly to output");
-    System.out.println("  --luaj           emulate Luaj's permissive parser");
+    System.out.println("  --assemble        assemble given disassembly listing");
+    System.out.println("  --disassemble     disassemble instead of decompile");
+    System.out.println("  --nodebug         ignore debugging information in input file");
+    System.out.println("  --typemap <file>  use type mapping specified in <file>");
+    System.out.println("  --opmap <file>    use opcode mapping specified in <file>");
+    System.out.println("  --output <file>   output to <file> instead of stdout");
+    System.out.println("  --rawstring       copy string bytes directly to output");
+    System.out.println("  --luaj            emulate Luaj's permissive parser");
   }
   
   private static void print_unluac_string(PrintStream out) {
--- a/src/unluac/assemble/Assembler.java	Fri Dec 15 19:05:25 2023 -0800
+++ b/src/unluac/assemble/Assembler.java	Fri Dec 15 22:45:23 2023 -0800
@@ -17,6 +17,8 @@
 import unluac.decompile.Op;
 import unluac.decompile.OpcodeMap;
 import unluac.decompile.OperandFormat;
+import unluac.decompile.Type;
+import unluac.decompile.TypeMap;
 import unluac.parse.BHeader;
 import unluac.parse.BInteger;
 import unluac.parse.BIntegerType;
@@ -480,6 +482,7 @@
   public int b_size;
   public int c_size;
   
+  public Map<Integer, Type> usertypemap;
   public Map<Integer, Op> useropmap;
   
   public boolean number_integral;
@@ -506,7 +509,7 @@
   }
   
   public void processHeaderDirective(Assembler a, Directive d) throws AssemblerException, IOException {
-    if(d != Directive.OP && processed_directives.contains(d)) {
+    if(!d.repeatable && processed_directives.contains(d)) {
       throw new AssemblerException("Duplicate " + d.name() + " directive");
     }
     processed_directives.add(d);
@@ -568,6 +571,19 @@
     case FLOAT_FORMAT:
       lfloat = new LNumberType(a.getInteger(), false, NumberMode.MODE_FLOAT);
       break;
+    case TYPE: {
+      if(usertypemap == null) {
+        usertypemap = new HashMap<Integer, Type>();
+      }
+      int typecode = a.getInteger();
+      String name = a.getName();
+      Type type = Type.get(name);
+      if(type == null) {
+        throw new AssemblerException("Unknown type name \"" + name + "\"");
+      }
+      usertypemap.put(typecode, type);
+      break;
+    }
     case OP: {
       if(useropmap == null) {
         useropmap = new HashMap<Integer, Op>();
@@ -640,10 +656,17 @@
       sizeT = integer;
     }
     
+    TypeMap typemap;
+    if(usertypemap != null) {
+      typemap = new TypeMap(usertypemap);
+    } else {
+      typemap = version.getTypeMap();
+    }
+    
     LHeader lheader = new LHeader(format, endianness, integer, sizeT, bool, number, linteger, lfloat, string, constant, abslineinfo, local, upvalue, function, extract);
-    BHeader header = new BHeader(version, lheader);
+    BHeader header = new BHeader(version, lheader, typemap);
     LFunction main = convert_function(header, this.main);
-    header = new BHeader(version, lheader, main);
+    header = new BHeader(version, lheader, typemap, main);
     
     header.write(out);
   }
--- a/src/unluac/assemble/Directive.java	Fri Dec 15 19:05:25 2023 -0800
+++ b/src/unluac/assemble/Directive.java	Fri Dec 15 22:45:23 2023 -0800
@@ -28,7 +28,8 @@
   NUMBER_FORMAT(".number_format", DirectiveType.HEADER, 2),
   INTEGER_FORMAT(".integer_format", DirectiveType.HEADER, 1),
   FLOAT_FORMAT(".float_format", DirectiveType.HEADER, 1),
-  OP(".op", DirectiveType.HEADER, 2),
+  TYPE(".type", DirectiveType.HEADER, 2, true),
+  OP(".op", DirectiveType.HEADER, 2, true),
   FUNCTION(".function", DirectiveType.NEWFUNCTION, 1),
   SOURCE(".source", DirectiveType.FUNCTION, 1),
   LINEDEFINED(".linedefined", DirectiveType.FUNCTION, 1),
@@ -44,12 +45,18 @@
   UPVALUE(".upvalue", DirectiveType.FUNCTION, 2),
   ;
   Directive(String token, DirectiveType type, int argcount) {
+    this(token, type, argcount, false);
+  }
+  
+  Directive(String token, DirectiveType type, int argcount, boolean repeatable) {
     this.token = token;
     this.type = type;
+    this.repeatable = repeatable;
   }
   
   public final String token;
   public final DirectiveType type;
+  public final boolean repeatable;
   
   static Map<String, Directive> lookup;
   
--- a/src/unluac/decompile/Disassembler.java	Fri Dec 15 19:05:25 2023 -0800
+++ b/src/unluac/decompile/Disassembler.java	Fri Dec 15 22:45:23 2023 -0800
@@ -41,6 +41,17 @@
       }
       out.println();
       
+      if(function.header.typemap != function.header.version.getTypeMap()) {
+        TypeMap typemap = function.header.typemap;
+        for(int typecode = 0; typecode < typemap.size(); typecode++) {
+          Type type = typemap.get(typecode);
+          if(type != null) {
+            out.println(Directive.TYPE.token + "\t" + typecode + "\t" + type.name);
+          }
+        }
+        out.println();
+      }
+      
       if(function.header.opmap != function.header.version.getOpcodeMap()) {
         OpcodeMap opmap = function.header.opmap;
         for(int opcode = 0; opcode < opmap.size(); opcode++) {
--- a/src/unluac/decompile/Type.java	Fri Dec 15 19:05:25 2023 -0800
+++ b/src/unluac/decompile/Type.java	Fri Dec 15 22:45:23 2023 -0800
@@ -1,14 +1,40 @@
 package unluac.decompile;
 
+import java.util.HashMap;
+import java.util.Map;
+
 public enum Type {
-  NIL,
-  BOOLEAN,
-  FALSE,
-  TRUE,
-  NUMBER,
-  FLOAT,
-  INTEGER,
-  STRING,
-  SHORT_STRING,
-  LONG_STRING;
+  NIL("nil"),
+  BOOLEAN("boolean"),
+  FALSE("false"),
+  TRUE("true"),
+  NUMBER("number"),
+  FLOAT("float"),
+  INTEGER("integer"),
+  STRING("string"),
+  SHORT_STRING("short_string"),
+  LONG_STRING("long_string");
+  
+  private static Map<String, Type> lookup = null;
+  
+  public final String name;
+  
+  private Type(String name) {
+    this.name = name;
+  }
+  
+  private static void initialize_lookup() {
+    if(lookup == null) {
+      lookup = new HashMap<String, Type>();
+      for(Type type : values()) {
+        lookup.put(type.name, type);
+      }
+    }
+  }
+  
+  public static Type get(String name) {
+    initialize_lookup();
+    return lookup.get(name);
+  }
+  
 }
--- a/src/unluac/decompile/TypeMap.java	Fri Dec 15 19:05:25 2023 -0800
+++ b/src/unluac/decompile/TypeMap.java	Fri Dec 15 22:45:23 2023 -0800
@@ -1,5 +1,7 @@
 package unluac.decompile;
 
+import java.util.Map;
+
 import unluac.Version;
 
 public class TypeMap {
@@ -93,6 +95,55 @@
     }
   }
   
+  public TypeMap(Map<Integer, Type> usertypemap) {
+    int maximum = 0;
+    for(Integer typecode : usertypemap.keySet()) {
+      maximum = Math.max(maximum, typecode);
+    }
+    types = new Type[maximum + 1];
+    int user_nil = UNMAPPED;
+    int user_boolean = UNMAPPED;
+    int user_false = UNMAPPED;
+    int user_true = UNMAPPED;
+    int user_number = UNMAPPED;
+    int user_float = UNMAPPED;
+    int user_integer = UNMAPPED;
+    int user_string = UNMAPPED;
+    int user_short_string = UNMAPPED;
+    int user_long_string = UNMAPPED;
+    for(Map.Entry<Integer, Type> entry : usertypemap.entrySet()) {
+      int typecode = entry.getKey();
+      Type type = entry.getValue();
+      types[typecode] = type;
+      switch(type) {
+        case NIL: user_nil = typecode; break;
+        case BOOLEAN: user_boolean = typecode; break;
+        case FALSE: user_false = typecode; break;
+        case TRUE: user_true = typecode; break;
+        case NUMBER: user_number = typecode; break;
+        case FLOAT: user_float = typecode; break;
+        case INTEGER: user_integer = typecode; break;
+        case STRING: user_string = typecode; break;
+        case SHORT_STRING: user_short_string = typecode; break;
+        case LONG_STRING: user_long_string = typecode; break;
+      }
+    }
+    NIL = user_nil;
+    BOOLEAN = user_boolean;
+    FALSE = user_false;
+    TRUE = user_true;
+    NUMBER = user_number;
+    FLOAT = user_float;
+    INTEGER = user_integer;
+    STRING = user_string;
+    SHORT_STRING = user_short_string;
+    LONG_STRING = user_long_string;
+  }
+  
+  public int size() {
+    return types.length;
+  }
+  
   public Type get(int typecode) {
     if(typecode >= 0 && typecode < types.length) {
       return types[typecode];
--- a/src/unluac/parse/BHeader.java	Fri Dec 15 19:05:25 2023 -0800
+++ b/src/unluac/parse/BHeader.java	Fri Dec 15 22:45:23 2023 -0800
@@ -15,6 +15,7 @@
 import unluac.decompile.CodeExtract;
 import unluac.decompile.Op;
 import unluac.decompile.OpcodeMap;
+import unluac.decompile.Type;
 import unluac.decompile.TypeMap;
 
 
@@ -48,11 +49,11 @@
   
   public final LFunction main;
   
-  public BHeader(Version version, LHeader lheader) {
-    this(version, lheader, null);
+  public BHeader(Version version, LHeader lheader, TypeMap typemap) {
+    this(version, lheader, typemap, null);
   }
   
-  public BHeader(Version version, LHeader lheader, LFunction main) {
+  public BHeader(Version version, LHeader lheader, TypeMap typemap, LFunction main) {
     this.config = null;
     this.version = version;
     this.lheader = lheader;
@@ -70,7 +71,7 @@
     upvalue = lheader.upvalue;
     function = lheader.function;
     extractor = lheader.extractor;
-    typemap = version.getTypeMap();
+    this.typemap = typemap;
     opmap = version.getOpcodeMap();
     this.main = main;
   }
@@ -109,38 +110,64 @@
     function = lheader.function;
     extractor = lheader.extractor;
     
-    typemap = version.getTypeMap();
-    
-    if(config.opmap != null) {
-      try {
+    try {
+      if(config.typemap != null) {
+        Tokenizer t = new Tokenizer(new FileInputStream(new File(config.typemap)));
+        String tok;
+        Map<Integer, Type> usertypemap = new HashMap<Integer, Type>();
+        while((tok = t.next()) != null) {
+          if(tok.equals(".type")) {
+            tok = t.next();
+            if(tok == null) throw new RuntimeException("Unexpected end of typemap file.");
+            int opcode;
+            try {
+              opcode = Integer.parseInt(tok);
+            } catch(NumberFormatException e) {
+              throw new RuntimeException("Excepted number in typemap file, got \"" + tok + "\".");
+            }
+            tok = t.next();
+            if(tok == null) throw new RuntimeException("Unexpected end of typemap file.");
+            Type type = Type.get(tok);
+            if(type == null) throw new RuntimeException("Unknown type name \"" + tok + "\" in typemap file.");
+            usertypemap.put(opcode, type);
+          } else {
+            throw new RuntimeException("Unexpected token \"" + tok + "\" + in typemap file.");
+          }
+        }
+        typemap = new TypeMap(usertypemap);
+      } else {
+        typemap = version.getTypeMap();
+      }
+      
+      if(config.opmap != null) {      
         Tokenizer t = new Tokenizer(new FileInputStream(new File(config.opmap)));
         String tok;
         Map<Integer, Op> useropmap = new HashMap<Integer, Op>();
         while((tok = t.next()) != null) {
           if(tok.equals(".op")) {
             tok = t.next();
-            if(tok == null) throw new IllegalStateException("Unexpected end of opmap file.");
+            if(tok == null) throw new RuntimeException("Unexpected end of opmap file.");
             int opcode;
             try {
               opcode = Integer.parseInt(tok);
             } catch(NumberFormatException e) {
-              throw new IllegalStateException("Excepted number in opmap file, got \"" + tok + "\".");
+              throw new RuntimeException("Excepted number in opmap file, got \"" + tok + "\".");
             }
             tok = t.next();
-            if(tok == null) throw new IllegalStateException("Unexpected end of opmap file.");
+            if(tok == null) throw new RuntimeException("Unexpected end of opmap file.");
             Op op = version.getOpcodeMap().get(tok);
-            if(op == null) throw new IllegalStateException("Unknown op name \"" + tok + "\" in opmap file.");
+            if(op == null) throw new RuntimeException("Unknown op name \"" + tok + "\" in opmap file.");
             useropmap.put(opcode, op);
           } else {
-            throw new IllegalStateException("Unexpected token \"" + tok + "\" + in opmap file.");
+            throw new RuntimeException("Unexpected token \"" + tok + "\" + in opmap file.");
           }
         }
         opmap = new OpcodeMap(useropmap);
-      } catch(IOException e) {
-        throw new IllegalStateException(e.getMessage());
+      } else {
+        opmap = version.getOpcodeMap();
       }
-    } else {
-      opmap = version.getOpcodeMap();
+    } catch(IOException e) {
+      throw new RuntimeException(e.getMessage());
     }
     
     int upvalues = -1;