Skip to content

Commit

Permalink
Re-org class file parsing code
Browse files Browse the repository at this point in the history
- handling individual attributes in their respective classes
- added support for array types
- added assertion of arguments and return types
  • Loading branch information
jbalint committed Sep 14, 2016
1 parent 2d2bb98 commit a35ca14
Show file tree
Hide file tree
Showing 19 changed files with 474 additions and 254 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,24 @@
# jcfl
Java class file loader

Introduction
============

JCFL is a system used to represent Java code in a way suitable for
analysis.

Loading Classes
===============

The Gradle main class is `com.jbalint.jora.proto.JarToRdf`. A jar must
be given on the command line. The result will be created in a
subdirectory called `output` under the directory from which Gradle is
invoked.

```
$ gradle run -Pjar=$JAVA_HOME/jre/lib/rt.jar
```

```
$ stardog data add db -g "http://jbalint/javap/jar#rt.jar" output/rt.jar.javap.ttl
```
39 changes: 21 additions & 18 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "jdepend:jdepend:2.9.1"
}
repositories {
jcenter()
}
dependencies {
classpath "jdepend:jdepend:2.9.1"
}
}

apply plugin: "java"
apply plugin: "idea"

compileJava.options.encoding = "UTF-8"
javadoc.options.encoding = "UTF-8"
Expand All @@ -17,27 +18,29 @@ defaultTasks "build"
version = "0.1"

repositories {
jcenter()
maven {
url "http://maven.stardog.com"
}
maven {
url "/home/jbalint/sw/java-sw/stardog/settings/mvn"
}
mavenLocal()
jcenter()
maven {
url "http://maven.stardog.com"
}
}

dependencies {
compile "com.complexible.stardog:client-snarl:4.1"
testCompile "junit:junit:4.12"
compile "com.complexible.stardog:client-snarl:4.2-SNAPSHO"
testCompile "junit:junit:4.12"
}

sourceSets {
main {
java {
srcDir "src/main/java"
main {
java {
srcDir "src/main/java"
}
}
}
}



apply plugin: 'application'

// mainClassName = "java.util.prefs.Base64"
Expand Down
56 changes: 55 additions & 1 deletion src/main/java/com/jbalint/jcfl/AttributeInfo.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,59 @@
package com.jbalint.jcfl;

public class AttributeInfo {
import java.io.IOException;

public abstract class AttributeInfo {
public String type;

public static AttributeInfo parseAttribute(ConstantPoolInfo constantPool[], UnsignedDataInputStream is) throws IOException {
int typeIndex = is.readUShort();
String type = constantPool[typeIndex].asString();
long length = is.readUInt();
if ("ConstantValue".equals(type)) {
return ConstantValue.parse(constantPool, is);
} else if ("Code".equals(type)) {
return Code.parse(constantPool, is);
} else if ("StackMapTable".equals(type)) {
} else if ("Exceptions".equals(type)) {
return Exceptions.parse(constantPool, is);
} else if ("BootstrapMethods".equals(type)) {
} else if ("InnerClasses".equals(type)) {
return InnerClasses.parse(constantPool, is);
} else if ("EnclosingMethod".equals(type)) {
return EnclosingMethod.parse(constantPool, is);
} else if ("Synthetic".equals(type)) {
Synthetic info = new Synthetic();
info.type = type;
return info;
} else if ("Signature".equals(type)) {
return Signature.parse(constantPool, is);
} else if ("RuntimeVisibleAnnotations".equals(type)) {
} else if ("RuntimeInvisibleAnnotations".equals(type)) {
} else if ("RuntimeVisibleParameterAnnotations".equals(type)) {
} else if ("RuntimeInvisibleParameterAnnotations".equals(type)) {
} else if ("RuntimeVisibleTypeAnnotations".equals(type)) {
} else if ("RuntimeInvisibleTypeAnnotations".equals(type)) {
} else if ("AnnotationDefault".equals(type)) {
} else if ("MethodParameters".equals(type)) {
} else if ("SourceFile".equals(type)) {
SourceFile info = new SourceFile();
info.type = type;
info.sourceFile = constantPool[is.readUShort()].asString();
return info;
} else if ("SourceDebugExtension".equals(type)) {
} else if ("LineNumberTable".equals(type)) {
return LineNumberTable.parse(constantPool, is);
} else if ("LocalVariableTable".equals(type)) {
return LocalVariableTable.parse(constantPool, is);
} else if ("LocalVariableTypeTable".equals(type)) {
return LocalVariableTypeTable.parse(constantPool, is);
} else if ("Deprecated".equals(type)) {
Deprecated info = new Deprecated();
info.type = type;
return info;
}
// TODO handle ignored attributes. ideally they will all be parsed
for (int i = 0; i < length; ++i) { is.read(); }
return null;
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/jbalint/jcfl/ClassFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public String getClassName() {
public String getSuperclassName() {
if (constantPool[superclassIndex] == null) {
// this should only happen for java.lang.Object. just make it point to itself.
return "java/lang/Object;";
return "java/lang/Object";
} else {
return constantPool[superclassIndex].asString();
}
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/jbalint/jcfl/Code.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jbalint.jcfl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -16,4 +17,29 @@ public static class CodeException {
public byte[] code;
public CodeException exceptionTable[];
public List<AttributeInfo> attributes = new ArrayList<>();

public static Code parse(ConstantPoolInfo constantPool[], UnsignedDataInputStream is) throws IOException {
Code info = new Code();
info.type = "Code";
info.maxStack = is.readUShort();
info.maxLocals = is.readUShort();
long codeLength = is.readUInt();
info.code = new byte[(int) codeLength];
is.readFully(info.code);
int exceptionTableLength = is.readUShort();
info.exceptionTable = new CodeException[exceptionTableLength];
for (int i = 0; i < exceptionTableLength; ++i) {
CodeException exc = new CodeException();
exc.startPc = is.readUShort();
exc.endPc = is.readUShort();
exc.handlerPc = is.readUShort();
exc.catchType = is.readUShort();
info.exceptionTable[i] = exc;
}
int attributesCount = is.readUShort();
for (int i = 0; i < attributesCount; ++i) {
info.attributes.add(AttributeInfo.parseAttribute(constantPool, is));
}
return info;
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/jbalint/jcfl/ConstantValue.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
package com.jbalint.jcfl;

import java.io.IOException;

public class ConstantValue extends AttributeInfo {
public static final String TYPE_NAME = ConstantValue.class.getSimpleName();

public ConstantPoolInfo constantValue;

public static ConstantValue parse(ConstantPoolInfo constantPool[], UnsignedDataInputStream is) throws IOException {
ConstantValue info = new ConstantValue();
info.type = TYPE_NAME;
info.constantValue = constantPool[is.readUShort()];
return info;
}

@Override
public String toString() {
return "Constant: " + constantValue.asString();
Expand Down
15 changes: 14 additions & 1 deletion src/main/java/com/jbalint/jcfl/EnclosingMethod.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
package com.jbalint.jcfl;

import java.io.IOException;

public class EnclosingMethod extends AttributeInfo {
public static final String TYPE_NAME = EnclosingMethod.class.getSimpleName();

public ClassInfo clazz;
public NameAndType method;
}

public static EnclosingMethod parse(ConstantPoolInfo constantPool[], UnsignedDataInputStream is) throws IOException {
EnclosingMethod info = new EnclosingMethod();
info.type = TYPE_NAME;
info.clazz = (ClassInfo) constantPool[is.readUShort()];
// may be null
info.method = (NameAndType) constantPool[is.readUShort()];
return info;
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/jbalint/jcfl/Exceptions.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
package com.jbalint.jcfl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Exceptions extends AttributeInfo {
public List<ClassInfo> exceptions = new ArrayList<>();

public static Exceptions parse(ConstantPoolInfo constantPool[], UnsignedDataInputStream is) throws IOException {
Exceptions info = new Exceptions();
info.type = "Exceptions";
int numberOfExceptions = is.readUShort();
for (int i = 0; i < numberOfExceptions; ++i) {
info.exceptions.add((ClassInfo) constantPool[is.readUShort()]);
}
return info;
}

@Override
public String toString() {
StringBuffer sb = new StringBuffer("Exceptions: [ ");
Expand Down
69 changes: 67 additions & 2 deletions src/main/java/com/jbalint/jcfl/FieldOrMethodInfo.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.jbalint.jcfl;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Supplier;

public class FieldOrMethodInfo {
public char type;
Expand All @@ -11,11 +13,17 @@ public class FieldOrMethodInfo {
public List<AttributeInfo> attributes = new ArrayList<>();
public ConstantPoolInfo constantPool[];

static class Descriptor {
String returnType;
List<String> argumentTypes = new LinkedList<>();
}

private Descriptor descriptor;

// method-only fields
public Code codeAttr;
/**
* List of methods called by this method. Defined after {@link
* analyzeCode()} has been called.
* List of methods called by this method. Defined after {@link #analyzeCode()} has been called.
*/
public List<Methodref> calledMethods = new ArrayList<>();

Expand All @@ -27,6 +35,63 @@ public String getDescriptor() {
return constantPool[descriptorIndex].asString();
}

private static int arrayDepth(String s, int startIndex) {
int end = startIndex;
while (s.charAt(end++) == '[');
return end - startIndex - 1;
}

private void parseDescriptor() {
descriptor = new Descriptor();
String desc = constantPool[descriptorIndex].asString();
// start at 1 to skip first opening paren
for (int i = 1; i < desc.length(); /* increment inline */) {
if (desc.charAt(i) == ')') {
i++;
descriptor.returnType = desc.substring(i);
break;
} else {
switch (desc.charAt(i)) {
case '[':
// need to look at first char after array depth prefix
int d = arrayDepth(desc, i);
if (desc.charAt(d + i) == 'L') {
String type = desc.substring(i, desc.indexOf(';', d + i) + 1);
i += type.length();
descriptor.argumentTypes.add(type);
} else {
descriptor.argumentTypes.add(desc.substring(i, i + d + 1));
i += d + 1;
}
break;
case 'L':
String type = desc.substring(i, desc.indexOf(';', i) + 1);
i += type.length();
descriptor.argumentTypes.add(type);
break;
default:
descriptor.argumentTypes.add(desc.substring(i, i+1));
i++;
break;
}
}
}
}

public String getReturnTypeName() {
if (descriptor == null) {
parseDescriptor();
}
return descriptor.returnType;
}

public List<String> getArgumentTypeNames() {
if (descriptor == null) {
parseDescriptor();
}
return descriptor.argumentTypes;
}

public boolean isAbstract() {
return (accessFlags & 0x0400) > 0;
}
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/jbalint/jcfl/InnerClasses.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
package com.jbalint.jcfl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class InnerClasses extends AttributeInfo {
public static final String TYPE_NAME = InnerClasses.class.getSimpleName();

public static class InnerClass {
public int innerClassInfoIndex;
public int outerClassInfoIndex;
public int innerNameIndex;
public int innerClassAccessFlags;
}
public List<InnerClass> classes = new ArrayList<>();

public static InnerClasses parse(ConstantPoolInfo constantPool[], UnsignedDataInputStream is) throws IOException {
InnerClasses info = new InnerClasses();
info.type = TYPE_NAME;
int numberOfClasses = is.readUShort();
for (int i = 0; i < numberOfClasses; ++i) {
InnerClasses.InnerClass inner = new InnerClasses.InnerClass();
inner.innerClassInfoIndex = is.readUShort();
inner.outerClassInfoIndex = is.readUShort();
inner.innerNameIndex = is.readUShort();
inner.innerClassAccessFlags = is.readUShort();
info.classes.add(inner);
}
return info;
}
}
Loading

0 comments on commit a35ca14

Please sign in to comment.