Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for parameters in types #7

Merged
merged 2 commits into from
Jan 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ jobs:
with:
java-version: ${{ matrix.java }}
- name: Build with Maven
run: mvn -B package --file pom.xml
run: |
mvn install:install-file -Dfile=lib/HexLib.jar -DgroupId=at.HexLib -DartifactId=HexLib -Dversion=0.0.0 -Dpackaging=jar -DlocalRepositoryPath=./lib
mvn -B package --file pom.xml
79 changes: 64 additions & 15 deletions src/main/java/io/kaitai/struct/visualizer/VisualizerPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

import java.awt.Point;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import at.HexLib.library.HexLib;
import at.HexLib.library.HexLibSelectionModel;

import io.kaitai.struct.ByteBufferKaitaiStream;
import io.kaitai.struct.CompileLog;
import io.kaitai.struct.KaitaiStream;
import io.kaitai.struct.KaitaiStruct;
import io.kaitai.struct.Main;
import io.kaitai.struct.RuntimeConfig;
Expand All @@ -34,7 +39,17 @@

public class VisualizerPanel extends JPanel {
private static final String DEST_PACKAGE = "io.kaitai.struct.visualized";
private static final Pattern TOP_CLASS_NAME = Pattern.compile("public class (.*?) extends KaitaiStruct");
/**
* Regexp with 2 groups: class name and type parameters. Type parameters
* must be parsed with {@link #PARAMETER_NAME}.
*/
private static final Pattern TOP_CLASS_NAME_AND_PARAMETERS = Pattern.compile(
"public class (.+?) extends KaitaiStruct.*" +
"public \\1\\(KaitaiStream _io, KaitaiStruct _parent, \\1 _root(.*?)\\)",
Pattern.DOTALL
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, nowadays top class name should not be that hard to get. Probably we should expose JavaMain.compileOneInput that will clearly return compilation result as Java object, and that object contains reference to top class name.

/** Regexp, used to get parameter names from the generated source. */
private static final Pattern PARAMETER_NAME = Pattern.compile(", \\S+ ([^,\\s]+)");

private final JTree tree = new JTree();
private final DefaultTreeModel model = new DefaultTreeModel(null);
Expand Down Expand Up @@ -107,28 +122,62 @@ private static String compileKSY(String ksyFileName) {
* static methods, etc.
* @throws Exception
*/
private static Class<?> compileAndLoadJava(String javaSrc) throws Exception {
Matcher m = TOP_CLASS_NAME.matcher(javaSrc);
if (!m.find())
throw new RuntimeException("Unable to find top-level class in compiled .java");
String className = m.group(1);
return InMemoryJavaCompiler.compile(DEST_PACKAGE + "." + className, javaSrc);
}

private void parseFileWithKSY(String ksyFileName, String binaryFileName) throws Exception {
String javaSrc = compileKSY(ksyFileName);
Class<?> ksyClass = compileAndLoadJava(javaSrc);
final String javaSrc = compileKSY(ksyFileName);
final Matcher m = TOP_CLASS_NAME_AND_PARAMETERS.matcher(javaSrc);
if (!m.find()) {
throw new RuntimeException("Unable to find top-level class in generated .java");
}
// Parse parameter names
final ArrayList<String> paramNames = new ArrayList<>();
final Matcher p = PARAMETER_NAME.matcher(m.group(2));
while (p.find()) {
paramNames.add(p.group(1));
}

// Find and run "fromFile" helper method to
Method fromFileMethod = ksyClass.getMethod("fromFile", String.class);
Object kso = fromFileMethod.invoke(null, binaryFileName);
struct = (KaitaiStruct) kso;
final Class<?> ksyClass = InMemoryJavaCompiler.compile(DEST_PACKAGE + "." + m.group(1), javaSrc);
struct = construct(ksyClass, paramNames, binaryFileName);

// Find and run "_read" that does actual parsing
// TODO: wrap this in try-catch block
Method readMethod = ksyClass.getMethod("_read");
readMethod.invoke(struct);
}
private static KaitaiStruct construct(Class<?> ksyClass, List<String> paramNames, String binaryFileName) throws Exception {
final Constructor<?> c = findConstructor(ksyClass);
final Class<?>[] types = c.getParameterTypes();
final Object[] args = new Object[types.length];
args[0] = new ByteBufferKaitaiStream(binaryFileName);
for (int i = 3; i < args.length; ++i) {
args[i] = getDefaultValue(types[i]);
}
// TODO: get parameters from user
return (KaitaiStruct)c.newInstance(args);
}
private static <T> Constructor<T> findConstructor(Class<T> ksyClass) {
for (final Constructor c : ksyClass.getDeclaredConstructors()) {
final Class<?>[] types = c.getParameterTypes();
if (types.length >= 3
&& types[0] == KaitaiStream.class
&& types[1] == KaitaiStruct.class
&& types[2] == ksyClass
) {
return c;
}
}
throw new IllegalArgumentException(ksyClass + " has no KaitaiStruct-generated constructor");
}
private static Object getDefaultValue(Class<?> clazz) {
if (clazz == boolean.class) return false;
if (clazz == char.class ) return (char)0;
if (clazz == byte.class ) return (byte)0;
if (clazz == short.class ) return (short)0;
if (clazz == int.class ) return 0;
if (clazz == long.class ) return 0L;
if (clazz == float.class ) return 0.0f;
if (clazz == double.class ) return 0.0;
return null;
}

public class KaitaiTreeListener implements TreeWillExpandListener, TreeSelectionListener {
@Override
Expand Down