Skip to content

Commit

Permalink
misc: Add tests to validate recent fix commits
Browse files Browse the repository at this point in the history
  • Loading branch information
Col-E committed Jun 26, 2024
1 parent 16ede9a commit a0ff756
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,8 @@ public void execute(MethodInstruction instruction) {
Value value = frame.pop(type);
parameters.add(0, value);
canLookup &= value.isKnown();
if (value instanceof Value.VoidValue)
warn(instruction, "Cannot pass 'void' as method argument");
}

Value.ObjectValue contextObject = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.darknet.assembler;

import dev.xdark.blw.code.instruction.MethodInstruction;
import dev.xdark.blw.type.Types;
import me.darknet.assembler.compile.analysis.AnalysisResults;
import me.darknet.assembler.compile.analysis.Local;
Expand All @@ -8,6 +9,7 @@
import me.darknet.assembler.compile.analysis.frame.Frame;
import me.darknet.assembler.compile.analysis.frame.ValuedFrame;
import me.darknet.assembler.compile.analysis.BasicMethodValueLookup;
import me.darknet.assembler.compile.analysis.jvm.MethodValueLookup;
import me.darknet.assembler.compile.analysis.jvm.TypedJvmAnalysisEngine;
import me.darknet.assembler.compile.analysis.jvm.ValuedJvmAnalysisEngine;
import me.darknet.assembler.compiler.ReflectiveInheritanceChecker;
Expand Down Expand Up @@ -99,6 +101,26 @@ void intMath(String name) throws Throwable {
});
}

@Test
void ldcPushType() throws Throwable {
TestArgument arg = TestArgument.fromName("Example-push-type.jasm");
String source = arg.source.get();
TestJvmCompilerOptions options = new TestJvmCompilerOptions();
options.engineProvider(ValuedJvmAnalysisEngine::new);
processJvm(source, options, result -> {
AnalysisResults results = result.analysisLookup().allResults().values().iterator().next();
assertNull(results.getAnalysisFailure());
assertFalse(results.terminalFrames().isEmpty());

ValuedFrame frame = (ValuedFrame) results.terminalFrames().values().iterator().next();
if (frame.peek() instanceof Value.ObjectValue objectValue) {
assertEquals(Types.instanceType(Class.class), objectValue.type(), "Pushing type to stack did not yield class reference");
} else {
fail("Did not yield object value");
}
});
}

@Test
void methodLookup() throws Throwable {
TestArgument arg = TestArgument.fromName("Example-string-ops.jasm");
Expand Down Expand Up @@ -376,6 +398,39 @@ void athrowDoesNotAllowFlowThroughToNextFrameAndClearsStack() throws Throwable {
});
}

@Test
void stackPopForInvokes() throws Throwable {
TestArgument arg = TestArgument.fromName("Example-wide-invoke.jasm");
String source = arg.source.get();

// Create an analysis engine which will observe the invokestatic method in the source.
// If wide types are mishandled it will not get visited.
boolean[] visited = new boolean[1];
TestJvmCompilerOptions options = new TestJvmCompilerOptions();
options.engineProvider(lookup -> {
ValuedJvmAnalysisEngine engine = new ValuedJvmAnalysisEngine(lookup);
engine.setMethodValueLookup(new MethodValueLookup() {
@Override
public @NotNull Value accept(@NotNull MethodInstruction instruction, Value.@Nullable ObjectValue context, @NotNull List<Value> parameters) {
visited[0] = true;
return Values.LONG_VALUE;
}
});
return engine;
});

processJvm(source, options, result -> {
AnalysisResults results = result.analysisLookup().allResults().values().iterator().next();
assertNull(results.getAnalysisFailure());
assertFalse(results.terminalFrames().isEmpty());
}, warns -> {
// Void type usage in the engine for method parameters should emit a warning.
// If this occurs we've broken something.
fail("Expected no warnings, found: " + warns);
});
assertTrue(visited[0], "Method call was not visited");
}

@Test
void checkcastChangesType() throws Throwable {
TestArgument arg = TestArgument.fromName("Example-checkcast.jasm");
Expand Down
108 changes: 63 additions & 45 deletions jasm-composition-jvm/src/test/java/me/darknet/assembler/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Pattern;

public class TestUtils {
Expand All @@ -35,59 +37,75 @@ public class TestUtils {
*
* @param source Jasm source to process.
* @param options Jasm compiler options.
* @param outputConsumer Consumer to act on the compilation result.
*/
public static void processJvm(@NotNull String source, @NotNull CompilerOptions<?> options,
@Nullable ThrowingConsumer<JavaCompileResult> outputConsumer) {
Processor.processSource(source, "<test>", (ast) -> {
JvmCompiler compiler = new JvmCompiler();
compiler.compile(ast, options).ifOk(result -> {
try {
if (outputConsumer != null)
outputConsumer.accept(result);
} catch (AssertionFailedError e) {
// Pass up the chain
throw e;
} catch (Throwable e) {
// Consumer should fail instead of us handling it generically here
fail(e);
return;
}
processJvm(source, options, outputConsumer, null);
}

// Check if bytes are a valid class file
JavaClassRepresentation representation = result.representation();
byte[] bytes = representation.classFile();
try {
compiler.library().read(new ByteArrayInputStream(bytes), new GenericClassBuilder());
} catch (IOException e) {
fail("Generated class was not readable", e);
}
/**
* Asserts that valid output was emitted with no errors <i>(Warnings are ok though)</i>.
*
* @param source Jasm source to process.
* @param options Jasm compiler options.
* @param outputConsumer Consumer to act on the compilation result.
* @param warningConsumer Consumer to act on warnings.
*/
public static void processJvm(@NotNull String source, @NotNull CompilerOptions<?> options,
@Nullable ThrowingConsumer<JavaCompileResult> outputConsumer,
@Nullable Consumer<List<Warn>> warningConsumer) {
Processor.processSource(source, "<test>", (ast) -> {
JvmCompiler compiler = new JvmCompiler();
compiler.compile(ast, options).ifOk(result -> {
try {
if (outputConsumer != null)
outputConsumer.accept(result);
} catch (AssertionFailedError e) {
// Pass up the chain
throw e;
} catch (Throwable e) {
// Consumer should fail instead of us handling it generically here
fail(e);
return;
}

// And double check that its verifiable
try {
CheckClassAdapter.verify(
new ClassReader(bytes),
true,
new PrintWriter(System.out)
);
} catch (Throwable e) {
fail("Generated class was not verifiable", e);
}
// Check if bytes are a valid class file
JavaClassRepresentation representation = result.representation();
byte[] bytes = representation.classFile();
try {
compiler.library().read(new ByteArrayInputStream(bytes), new GenericClassBuilder());
} catch (IOException e) {
fail("Generated class was not readable", e);
}

// And double check that its verifiable
try {
CheckClassAdapter.verify(
new ClassReader(bytes),
true,
new PrintWriter(System.out)
);
} catch (Throwable e) {
fail("Generated class was not verifiable", e);
}

}).ifErr(errors -> {
for (Error error : errors) {
System.err.println(error);
}
fail("Failed to analyze/compile class, errors were reported");
});
}, errors -> {
for (Error error : errors) {
System.err.println(error);
}
fail("Failed to parse class");
}, BytecodeFormat.JVM);
}

}).ifErr(errors -> {
for (Error error : errors) {
System.err.println(error);
}
fail("Failed to analyze/compile class, errors were reported");
}).ifWarn(warns -> {
if (warningConsumer != null) warningConsumer.accept(warns);
});
}, errors -> {
for (Error error : errors) {
System.err.println(error);
}
fail("Failed to parse class");
}, BytecodeFormat.JVM);
}

/**
* Asserts that errors were emitted.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.super java/lang/Object
.class public super Example {
.method public exampleMethod ()Ljava/lang/Class; {
parameters: { this },
code: {
A:
ldc Ljava/lang/String;
areturn
B:
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.super java/lang/Object
.class public super Example {
.method public exampleMethod ()I {
parameters: { this },
code: {
A:
iconst_1
ldc 10000000L
bipush 250
invokestatic java/lang/Math.multiplyExact (JI)J
pop2
ireturn
B:
}
}
}

0 comments on commit a0ff756

Please sign in to comment.