Skip to content

Commit

Permalink
partial: dalvik implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
jumanji144 committed Jan 15, 2024
1 parent 6d79e14 commit 7ac7903
Show file tree
Hide file tree
Showing 6 changed files with 485 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,15 @@ public <T extends ASTElement> T argument(int index, Class<T> type) {
return (T) arguments.get(index);
}

public ASTIdentifier argument(int index) {
return argument(index, ASTIdentifier.class);
}

public ASTArray argumentArray(int index) {
return argument(index, ASTArray.class);
}

public ASTObject argumentObject(int index) {
return argument(index, ASTObject.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum DefaultOperands implements Operands {
}
}
}),
NUMBER((context, element) -> context.isNotType(element, ElementType.NUMBER, "number literal")),
IDENTIFIER((context, element) -> context.isNotType(element, ElementType.IDENTIFIER, "identifier")),
LITERAL((context, element) -> {
// literals can be: number or identifier
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package me.darknet.assembler.instructions.dalvik;

import me.darknet.assembler.ast.primitive.ASTInstruction;
import me.darknet.assembler.ast.primitive.ASTNumber;
import me.darknet.assembler.instructions.DefaultOperands;
import me.darknet.assembler.instructions.Instructions;
import me.darknet.assembler.visitor.ASTDalvikInstructionVisitor;

public class DalvikInstructions extends Instructions<ASTDalvikInstructionVisitor> {

@Override
protected void registerInstructions() {
register("nop", ops(), (inst, visitor) -> visitor.visitNop());
register("move", ops(DefaultOperands.LITERAL, DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitMove(inst.argument(0), inst.argument(1)));
register("move-wide", ops(DefaultOperands.LITERAL, DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitMove(inst.argument(0), inst.argument(1)));
register("move-object", ops(DefaultOperands.LITERAL, DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitMove(inst.argument(0), inst.argument(1)));
register("move-result", ops(DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitMoveResult(inst.argument(0)));
register("move-result-wide", ops(DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitMoveResult(inst.argument(0)));
register("move-result-object", ops(DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitMoveResult(inst.argument(0)));
register("move-exception", ops(DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitMoveException(inst.argument(0)));
register("return-void", ops(), (inst, visitor) -> visitor.visitReturnVoid());
register("return", ops(DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitReturn(inst.argument(0)));
register("return-wide", ops(DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitReturn(inst.argument(0)));
register("return-object", ops(DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitReturn(inst.argument(0)));
register("const", ops(DefaultOperands.LITERAL, DefaultOperands.NUMBER),
(inst, visitor) -> visitor.visitConst(inst.argument(0), inst.argument(1)));
register("const-string", ops(DefaultOperands.LITERAL, DefaultOperands.STRING),
(inst, visitor) -> visitor.visitConst(inst.argument(0), inst.argument(1)));
register("const-class", ops(DefaultOperands.LITERAL, DalvikOperands.CLASS_TYPE),
(inst, visitor) -> visitor.visitConst(inst.argument(0), inst.argument(1)));
register("const-method-handle", ops(DefaultOperands.LITERAL, DalvikOperands.HANDLE),
(inst, visitor) -> visitor.visitConst(inst.argument(0), inst.argument(1)));
register("const-method-type", ops(DefaultOperands.LITERAL, DalvikOperands.METHOD_TYPE),
(inst, visitor) -> visitor.visitConst(inst.argument(0), inst.argument(1)));
register("monitor-enter", ops(DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitMonitorEnter(inst.argument(0)));
register("monitor-exit", ops(DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitMonitorExit(inst.argument(0)));
register("check-cast", ops(DefaultOperands.LITERAL, DalvikOperands.CLASS_TYPE),
(inst, visitor) -> visitor.visitCheckCast(inst.argument(0), inst.argument(1)));
register("instance-of", ops(DefaultOperands.LITERAL, DefaultOperands.LITERAL, DalvikOperands.CLASS_TYPE),
(inst, visitor) -> visitor.visitInstanceOf(inst.argument(0),
inst.argument(1), inst.argument(2)));
register("array-length", ops(DefaultOperands.LITERAL, DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitArrayLength(inst.argument(0), inst.argument(1)));
register("new-instance", ops(DefaultOperands.LITERAL, DalvikOperands.CLASS_TYPE),
(inst, visitor) -> visitor.visitNewInstance(inst.argument(0), inst.argument(1)));
register("new-array", ops(DefaultOperands.LITERAL, DefaultOperands.LITERAL, DalvikOperands.CLASS_TYPE),
(inst, visitor) -> visitor.visitNewArray(inst.argument(0),
inst.argument(1), inst.argument(2)));
register("filled-new-array", ops(DalvikOperands.REGISTER_ARRAY, DalvikOperands.CLASS_TYPE),
(inst, visitor) -> visitor.visitFilledNewArray(inst.argumentArray(0), inst.argument(1)));
register("fill-array-data", ops(DefaultOperands.LITERAL, DefaultOperands.IDENTIFIER),
(inst, visitor) -> visitor.visitFillArrayData(inst.argument(0), inst.argument(1)));
register("fill-array-data-payload", ops(DefaultOperands.INTEGER, DalvikOperands.DATA_ARRAY),
(inst, visitor) -> visitor.visitFillArrayDataPayload(inst.argument(0, ASTNumber.class),
inst.argumentArray(1)));
register("throw", ops(DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitThrow(inst.argument(0)));
register("goto", ops(DefaultOperands.LABEL),
(inst, visitor) -> visitor.visitGoto(inst.argument(0)));
register("packed-switch", ops(DalvikOperands.PACKED_SWITCH),
(inst, visitor) -> visitor.visitPackedSwitch(inst.argumentObject(0)));
register("sparse-switch", ops(DalvikOperands.SPARSE_SWITCH),
(inst, visitor) -> visitor.visitSparseSwitch(inst.argumentObject(0)));
registerCmp("cmpl-float", "cmpg-float", "cmpl-double", "cmpg-double", "cmp-long");
registerIf("if-eq", "if-ne", "if-lt", "if-ge", "if-gt", "if-le",
"if-eqz", "if-nez");
registerArrayOperation("aget", "aget-object", "aget-boolean", "aget-byte",
"aget-char", "aget-short", "aput", "aput-object",
"aput-boolean", "aput-byte", "aput-char", "aput-short");
registerVirtualFieldOperation("iget", "iget-object", "iget-boolean", "iget-byte",
"iget-char", "iget-short", "iput", "iput-object",
"iput-boolean", "iput-byte", "iput-char", "iput-short");
registerStaticFieldOperation("sget", "sget-object", "sget-boolean", "sget-byte",
"sget-char", "sget-short", "sput", "sput-object",
"sput-boolean", "sput-byte", "sput-char", "sput-short");


}

void registerCmp(String... names) {
for (String name : names) {
register(name, ops(DefaultOperands.LITERAL, DefaultOperands.LITERAL, DefaultOperands.LABEL),
(inst, visitor) -> visitor.visitCmp(inst.argument(0), inst.argument(1), inst.argument(2)));
}
}

void registerIf(String... names) {
for (String name : names) {
register(name, ops(DefaultOperands.LITERAL, DefaultOperands.LABEL),
(inst, visitor) -> visitor.visitIf(inst.argument(0), inst.argument(1)));
}
}

void registerArrayOperation(String... names) {
for (String name : names) {
register(name, ops(DefaultOperands.LITERAL, DefaultOperands.LITERAL, DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitArrayOperation(inst.argument(0), inst.argument(1), inst.argument(2)));
}
}

void registerVirtualFieldOperation(String... names) {
for (String name : names) {
register(name, ops(DefaultOperands.LITERAL, DefaultOperands.LITERAL, DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitVirtualFieldOperation(inst.argument(0), inst.argument(1), inst.argument(2)));
}
}

void registerStaticFieldOperation(String... names) {
for (String name : names) {
register(name, ops(DefaultOperands.LITERAL, DefaultOperands.LITERAL),
(inst, visitor) -> visitor.visitStaticFieldOperation(inst.argument(0), inst.argument(1)));
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package me.darknet.assembler.instructions.dalvik;

import me.darknet.assembler.ast.ASTElement;
import me.darknet.assembler.ast.ElementType;
import me.darknet.assembler.ast.primitive.ASTArray;
import me.darknet.assembler.ast.primitive.ASTNumber;
import me.darknet.assembler.ast.primitive.ASTObject;
import me.darknet.assembler.instructions.Operand;
import me.darknet.assembler.instructions.Operands;
import me.darknet.assembler.instructions.jvm.JvmOperands;
import me.darknet.assembler.parser.processor.ASTProcessor;

public enum DalvikOperands implements Operands {

CONSTANT(DalvikOperands::verifyConstant),
CLASS_TYPE((context, element) -> {
// class type can be: class or array
if (context.isNotType(element, ElementType.IDENTIFIER, "class type"))
return;

char first = element.content().charAt(0);
switch (first) {
case 'L', '[' -> {
}
default -> context.throwUnexpectedElementError("class or array descriptor", element);
}
}),
METHOD_TYPE((context, element) -> {
// method type can be: method or array
if (context.isNotType(element, ElementType.IDENTIFIER, "method type"))
return;

char first = element.content().charAt(0);
switch (first) {
case '(', '[' -> {
}
default -> context.throwUnexpectedElementError("method or array descriptor", element);
}
}),
HANDLE(JvmOperands::verifyHandle),
REGISTER_ARRAY((context, element) -> {
// register array can be: register or array
ASTArray array = context.validateEmptyableElement(element, ElementType.ARRAY, "register array", element);
for (ASTElement value : array.values()) {
if (context.isNull(value, "register array element", array.location()))
return;
assert value != null;
if(value.type() != ElementType.NUMBER || value.type() != ElementType.IDENTIFIER)
context.throwUnexpectedElementError("register", value);
}
}),
DATA_ARRAY((context, element) -> {
// data array can be: number or array
ASTArray array = context.validateEmptyableElement(element, ElementType.ARRAY, "data array", element);
for (ASTElement value : array.values()) {
if (context.isNull(value, "data array element", array.location()))
return;
assert value != null;
if(value.type() != ElementType.NUMBER)
context.throwUnexpectedElementError("number", value);
}
}),
PACKED_SWITCH((context, element) -> {
ASTObject object = context.validateObject(element, "packed switch", element, "first", "targets");

if (object == null)
return;

// start, end should be numbers
if (context.validateCorrect(object.value("first"), ElementType.NUMBER, "number", object))
return;

ASTNumber min = object.value("min");

if (min.isFloatingPoint())
context.throwUnexpectedElementError("integer literal", min);

// targets should be array
ASTArray array = context.validateEmptyableElement(object.value("targets"), ElementType.ARRAY, "targets", object);
if (array == null)
return;

context.validateArray(array, ElementType.IDENTIFIER, "label", element);
}),
SPARSE_SWITCH((context, element) -> {
// lookup switch can be: default label, pairs
if (context.isNotType(element, ElementType.OBJECT, "sparse switch"))
return;

ASTObject object = (ASTObject) element;

// cases should be identifier
for (ASTElement elem : object.values().elements()) {
if (context.isNotType(elem, ElementType.IDENTIFIER, "identifier"))
return;
}
});

private final Operand operand;

DalvikOperands(Operand.Processor operand) {
this.operand = new Operand(operand);
}

@Override
public Operand getOperand() {
return operand;
}

static void verifyConstant(ASTProcessor.ParserContext ctx, ASTElement element) {
switch (element.type()) {
case NUMBER, STRING, CHARACTER -> {
}
case IDENTIFIER -> {
// must be class or method type
char first = element.content().charAt(0);
// TODO: maybe replace with actual descriptor verification?
switch (first) {
case 'L', '(', '[' -> {
return;
}
}
ctx.throwUnexpectedElementError("class, method or array descriptor", element);
}
case ARRAY -> // only handle
JvmOperands.verifyHandle(ctx, element);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public static void verifyConstant(ASTProcessor.ParserContext context, ASTElement
}
case IDENTIFIER -> {
char first = element.content().charAt(0);
// TODO: maybe replace with actual descriptor verification?
switch (first) {
case 'L', '(', '[' -> {
}
Expand Down
Loading

0 comments on commit 7ac7903

Please sign in to comment.