Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,18 @@ public class TEnum implements Comparable<Enum> {

private final String name;
private final int ordinalNumber;
public final Class getDeclaringClass() throws ClassNotFoundException {
Class clazzObj = getClass();
String clazzname = clazzObj.getName();

if(clazzname.matches(".+\\$\\d+")){
//its a subclassed Enum
// enum E { VAL {private int i;} }
// E.VAL.getClass() == E$1
return Class.forName(clazzname.substring(0,clazzname.lastIndexOf('$')));
}
return clazzObj;
}
@NoExceptionCheck
protected TEnum(final String aName, final int aOrdinalNumber) {
name = aName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public String toString() {
return (String) (Object) this;
}

public native boolean matches(String regex);

static String valueOf(final Object obj) {
if (obj == null) {
return "null";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ private void generateHeader(final CompileUnit compileUnit, final PrintWriter pw)
}
}

pw.println(" throw 'Not supported class for reflective access';");
pw.println(" throw new Error('Not supported class for reflective access');");
pw.println("};");
pw.println();
}
Expand Down Expand Up @@ -246,9 +246,10 @@ private void generateClassInitFor(final PrintWriter pw, final CompileUnit compil
pw.println(" static #rt = undefined;");
pw.println(" static get $rt() {");
pw.println(" if (!this.#rt) {");
pw.print(" this.#rt = bytecoder.newRuntimeClassFor(");
pw.print(generateClassName(cl.type));
pw.print(",[");
pw.println(" this.#rt = bytecoder.newRuntimeClassFor(");
pw.println(" "+generateClassName(cl.type)+",");
pw.println(" '"+cl.type.getClassName()+"',");
pw.print(" [");
boolean f = true;
for (final ResolvedClass type : cl.allTypesOf()) {
if (f) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public ResolvedMethod(final ResolvedClass owner, final MethodNode methodNode, fi
public void parseBody(final AnalysisStack analysisStack) {
if (!Modifier.isAbstract(methodNode.access) && !Modifier.isNative(methodNode.access)) {
// Method is not abstract and not native
final AnalysisStack newStack = analysisStack.addAction(new AnalysisStack.Action("Parsing method body of " + owner.type + "." + methodNode.name));
final AnalysisStack newStack = analysisStack.addAction(new AnalysisStack.Action("Parsing method body of " + owner.type.getClassName() + "." + methodNode.name));
try {
final GraphParser graphParser = new GraphParser(owner.compileUnit, owner.type, methodNode, newStack);
methodBody = graphParser.graph();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,12 @@ public ResolvedClass resolveClass(final Type type, final AnalysisStack analysisS
throw new RuntimeException(e);
}
resolvedClasses.put(resourceName, rs);

boolean isEnumType = rs.allTypesOf().stream().anyMatch(parent -> parent.type.getClassName().equals(Enum.class.getName())) || rs.type.getClassName().equals(Enum.class.getName());
if(isEnumType){
//Why aren't all classes considered for lookups? Performance? It would simplify implementation a lot if Class.forName just looped over classes.#rt and checked that
getReflectionConfiguration().resolve(rs.type.getClassName()).setSupportsClassForName(true);
getConstantPool().resolveFromPool(rs.type.getClassName());//add of not already there
}
// If there are any methods annotated with Export, we resolve them, too
for (final MethodNode mn : rs.classNode.methods) {
if (AnnotationUtils.hasAnnotation("Lde/mirkosertic/bytecoder/api/Export;", mn.visibleAnnotations)) {
Expand Down
29 changes: 24 additions & 5 deletions core/src/main/resources/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,16 @@ const bytecoder = {
Ljava$lang$ClassLoader$$getClassLoader$$: function (classRef) {
return null;
},
Ljava$lang$Class$$forName$Ljava$lang$String$$Z$Ljava$lang$ClassLoader$: function(className, initialize, classLoader) {
throw 'Not supported class for reflective access';
Ljava$lang$Class$$forName$Ljava$lang$String$$Z$Ljava$lang$ClassLoader$: function (className, initialize, classLoader) {
throw new Error('Not supported class for reflective access');
},
Z$desiredAssertionStatus$$: function(classRef) {
return false;
}
},
Ljava$lang$String$$getName$$: function (classRef) {
//I am probably missing some knowledge here. This does work but it seems that @EmulateAtRuntime should do that for me?
return classRef.Ljava$lang$String$$getName$$();
},
},
"java.io.FileInputStream": {
I$open0$Ljava$io$FileDescriptor$$Ljava$lang$String$: function (fis, fdd, name) {
Expand Down Expand Up @@ -317,6 +321,15 @@ const bytecoder = {
I$length$$: function (str) {
return str.nativeObject.length;
},
Z$matches$Ljava$lang$String$: function (self, regex) {
var match = RegExp(regex.nativeObject).exec(self.nativeObject);
if(!match)
return false;
return match[0].length == self.nativeObject.length;
},
Z$contains$Ljava$lang$CharSequence$: function (str, sub) {
return str.nativeObject.includes(sub);
},
V$getChars$I$I$$C$I: function (str, srcBegin, srcEnd, dst, dstBegin) {
let dstOffset = dstBegin;
let s = str.nativeObject;
Expand Down Expand Up @@ -722,7 +735,7 @@ const bytecoder = {
return -1;
}
},
newRuntimeClassFor: function(type,supportedtypes) {
newRuntimeClassFor: function (type,javaName, supportedtypes) {
return {
Ljava$lang$ClassLoader$$getClassLoader$$: function() {
return null;
Expand All @@ -741,7 +754,13 @@ const bytecoder = {
$Ljava$lang$Object$$getEnumConstants$$: function() {
return type.$i.$VALUES;
},
instanceOf: function(a, b) {
Ljava$lang$String$$getName$$: function () {
return bytecoder.toBytecoderString(javaName);
},
Z$equals$Ljava$lang$Object$: function (self, obj) {
return this.Ljava$lang$String$$getName$$(self).nativeObject == this.Ljava$lang$String$$getName$$(obj).nativeObject
},
instanceOf: function (a, b) {
if (supportedtypes.includes(b)) {
return 1;
}
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/resources/wasmruntime.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,12 @@ const bytecoder = {
}
return 0;
},
Z$matches$Ljava$lang$String$: function (self, regex) {
var match = RegExp(bytecoder.getNativeObject(regex)).exec(bytecoder.getNativeObject(self));
if(!match)
return false;
return match[0].length == bytecoder.getNativeObject(self).length;
},
Z$endsWith$Ljava$lang$String$: function (str, otherstr) {
const a = bytecoder.toJSString(str);
const b = bytecoder.toJSString(otherstr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ public void testEquals() {
assertFalse("123".equals(10L));
}

@Test
public void testMatches(){
assertTrue("123".matches("\\d+"));
assertTrue("123".matches("\\d*"));
assertTrue("very nice words".matches("very nice .+"));
assertTrue("false".matches("false"));
assertFalse("true".matches("false"));
assertFalse("true".matches("rue"));
assertFalse("true".matches("t"));
assertFalse("true".matches("e"));
assertFalse("not all matched".matches("."));
assertFalse("not all matched".matches(".{1,3}"));
assertFalse("not all matched".matches("matched"));
assertFalse("not all matched".matches("not"));
assertFalse("not all matched".matches("all"));
}

@Test
public void testEqualsIgnoreCase() {
assertTrue("A123".equalsIgnoreCase(new String("a123")));
Expand Down
39 changes: 39 additions & 0 deletions core/src/test/java/de/mirkosertic/bytecoder/core/EnumTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,21 @@
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.hamcrest.MatcherAssert.assertThat;

@RunWith(UnitTestRunner.class)
public class EnumTest {

public enum Value {
ONE, TWO, THREE
}
public enum SubclassedEnum {
ONE{
private final int innerField1 = 0;
}, TWO{
private final String innerField2 = "0";
}, THREE
}

private static Value getEnum() {
return Value.TWO;
Expand All @@ -49,6 +58,11 @@ public void testValuesRuntimeClass() {
Value[] theValues= Value.class.getEnumConstants();
Assert.assertEquals(3, theValues.length, 0);
}
@Test
public void testValuesRuntimeClassOnSubclassingEnum() {
SubclassedEnum[] theValues= SubclassedEnum.class.getEnumConstants();
Assert.assertEquals(3, theValues.length, 0);
}

@Test
public void testValuesRuntimeClassIndirect() {
Expand Down Expand Up @@ -77,4 +91,29 @@ public void testValues() {
Assert.assertTrue(theValues[1].name().equals("TWO"));
System.out.println(theValues[1].name());
}

@Test
public void testValuesForSubclassedEnum() {
SubclassedEnum[] theValues = SubclassedEnum.values();
Assert.assertEquals(3, theValues.length, 0);
Assert.assertTrue(theValues[0] == SubclassedEnum.ONE);
Assert.assertTrue(theValues[1].name().equals("TWO"));
System.out.println(theValues[1].name());
}
@Test
public void testGetDeclaringClass(){
Value v = Value.TWO;
Class<Value> desclaring = v.getDeclaringClass();
for (Value val : Value.values()){
Assert.assertEquals(desclaring, val.getDeclaringClass());
}
}
@Test
public void testGetDeclaringClassSubclassed(){
SubclassedEnum v = SubclassedEnum.TWO;
Class<SubclassedEnum> declaring = v.getDeclaringClass();
for (SubclassedEnum val : SubclassedEnum.values()){
Assert.assertEquals(declaring, val.getDeclaringClass());
}
}
}