-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6d79e14
commit 7ac7903
Showing
6 changed files
with
485 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
127 changes: 127 additions & 0 deletions
127
jasm-core/src/main/java/me/darknet/assembler/instructions/dalvik/DalvikInstructions.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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))); | ||
} | ||
} | ||
|
||
} |
129 changes: 129 additions & 0 deletions
129
jasm-core/src/main/java/me/darknet/assembler/instructions/dalvik/DalvikOperands.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.