diff --git a/smali/src/main/antlr3/org/jf/smali/smaliParser.g b/smali/src/main/antlr3/org/jf/smali/smaliParser.g index 29f17260..0e16126a 100644 --- a/smali/src/main/antlr3/org/jf/smali/smaliParser.g +++ b/smali/src/main/antlr3/org/jf/smali/smaliParser.g @@ -511,7 +511,7 @@ fully_qualified_field -> reference_type_descriptor simple_name nonvoid_type_descriptor; label - : COLON simple_name -> ^(I_LABEL simple_name I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + : COLON simple_name -> ^(I_LABEL[$COLON, "I_LABEL"] simple_name I_ADDRESS[$start, Integer.toString($method::currentAddress)]); label_ref_or_offset : COLON simple_name -> simple_name @@ -741,7 +741,8 @@ instruction returns [int size] -> {needsNop}? ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x[$start, "nop"]) ^(I_STATEMENT_ARRAY_DATA ^(I_ARRAY_ELEMENT_SIZE integral_literal) ^(I_ARRAY_ELEMENTS fixed_literal*)) - -> ^(I_STATEMENT_ARRAY_DATA ^(I_ARRAY_ELEMENT_SIZE integral_literal) ^(I_ARRAY_ELEMENTS fixed_literal*)) + -> ^(I_STATEMENT_ARRAY_DATA[$start, "I_STATEMENT_ARRAY_DATA"] ^(I_ARRAY_ELEMENT_SIZE integral_literal) + ^(I_ARRAY_ELEMENTS fixed_literal*)) | PACKED_SWITCH_DIRECTIVE { diff --git a/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g b/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g index ee493400..6ced2e28 100644 --- a/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g +++ b/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g @@ -502,21 +502,64 @@ method returns[ ClassDataItem.EncodedMethod encodedMethod, List tries = temp.first; List handlers = temp.second; - DebugInfoItem debugInfoItem = $method::debugInfo.encodeDebugInfo(dexFile); CodeItem codeItem; + + boolean isAbstract = false; + boolean isNative = false; - if (totalMethodRegisters == 0 && - $statements.instructions.size() == 0 && - $method::labels.size()== 0 && - (tries == null || tries.size() == 0) && - (handlers == null || handlers.size() == 0) && - debugInfoItem == null) { + if ((accessFlags & AccessFlags.ABSTRACT.getValue()) != 0) { + isAbstract = true; + } else if ((accessFlags & AccessFlags.NATIVE.getValue()) != 0) { + isNative = true; + } - codeItem = null; + if ($statements.instructions.size() == 0) { + if (!isAbstract && !isNative) { + throw new SemanticException(input, $I_METHOD, "A non-abstract/non-native method must have at least 1 instruction"); + } + + String methodType; + if (isAbstract) { + methodType = "an abstract"; + } else { + methodType = "a native"; + } + + if ($registers_directive.start != null) { + if ($registers_directive.isLocalsDirective) { + throw new SemanticException(input, $registers_directive.start, "A .locals directive is not valid in \%s method", methodType); + } else { + throw new SemanticException(input, $registers_directive.start, "A .registers directive is not valid in \%s method", methodType); + } + } + if ($method::labels.size() > 0) { + throw new SemanticException(input, $I_METHOD, "Labels cannot be present in \%s method", methodType); + } + + if ((tries != null && tries.size() > 0) || (handlers != null && handlers.size() > 0)) { + throw new SemanticException(input, $I_METHOD, "try/catch blocks cannot be present in \%s method", methodType); + } + + if (debugInfoItem != null) { + throw new SemanticException(input, $I_METHOD, "debug directives cannot be present in \%s method", methodType); + } + + codeItem = null; } else { + if (isAbstract) { + throw new SemanticException(input, $I_METHOD, "An abstract method cannot have any instructions"); + } + if (isNative) { + throw new SemanticException(input, $I_METHOD, "A native method cannot have any instructions"); + } + + if ($registers_directive.start == null) { + throw new SemanticException(input, $I_METHOD, "A .registers or .locals directive must be present for a non-abstract/non-final method"); + } + if ($registers_directive.isLocalsDirective) { totalMethodRegisters = $registers_directive.registers + methodParameterRegisters; } else { @@ -524,14 +567,14 @@ method returns[ ClassDataItem.EncodedMethod encodedMethod, } if (totalMethodRegisters < methodParameterRegisters) { - throw new SemanticException(input, "This method requires at least " + + throw new SemanticException(input, $registers_directive.start, "This method requires at least " + Integer.toString(methodParameterRegisters) + " registers, for the method parameters"); } int methodParameterCount = methodIdItem.getPrototype().getParameterRegisterCount(); if ($method::debugInfo.getParameterNameCount() > methodParameterCount) { - throw new SemanticException(input, "Too many parameter names specified. This method only has " + + throw new SemanticException(input, $I_METHOD, "Too many parameter names specified. This method only has " + Integer.toString(methodParameterCount) + " parameters."); } @@ -809,7 +852,7 @@ label_ref returns[int labelAddress] Integer labelAdd = $method::labels.get($SIMPLE_NAME.text); if (labelAdd == null) { - throw new SemanticException(input, "Label \"" + $SIMPLE_NAME.text + "\" is not defined."); + throw new SemanticException(input, $SIMPLE_NAME, "Label \"" + $SIMPLE_NAME.text + "\" is not defined."); } $labelAddress = labelAdd; @@ -844,7 +887,7 @@ register_list[int totalMethodRegisters, int methodParameterRegisters] returns[by (REGISTER { if ($registerCount == 5) { - throw new SemanticException(input, "A list of registers can only have a maximum of 5 registers. Use the /range alternate opcode instead."); + throw new SemanticException(input, $I_REGISTER_LIST, "A list of registers can only have a maximum of 5 registers. Use the /range alternate opcode instead."); } $registers[$registerCount++] = parseRegister_nibble($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); })*); @@ -858,6 +901,14 @@ register_range[int totalMethodRegisters, int methodParameterRegisters] returns[i } else { $endRegister = parseRegister_short($endReg.text, $totalMethodRegisters, $methodParameterRegisters); } + + int registerCount = $endRegister-$startRegister+1; + if (registerCount > 256) { + throw new SemanticException(input, $I_REGISTER_RANGE, "A register range can span a maximum of 256 registers"); + } + if (registerCount < 1) { + throw new SemanticException(input, $I_REGISTER_RANGE, "A register range must have the lower register listed first"); + } } ; @@ -974,7 +1025,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, List Short.MAX_VALUE) { - throw new SemanticException(input, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); + throw new SemanticException(input, $offset_or_label.start, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); } $instructions.add(new Instruction21t(opcode, regA, (short)addressOffset)); @@ -1034,7 +1085,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, List Short.MAX_VALUE) { - throw new SemanticException(input, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); + throw new SemanticException(input, $offset_or_label.start, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); } $instructions.add(new Instruction22t(opcode, regA, regB, (short)addressOffset)); @@ -1146,13 +1197,6 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, List 256) { - throw new SemanticException(input, "A register range can span a maximum of 256 registers"); - } - if (registerCount < 1) { - throw new SemanticException(input, "A register range must have the lower register listed first"); - } - $outRegisters = registerCount; MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem; @@ -1167,13 +1211,6 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, List 256) { - throw new SemanticException(input, "A register range can span a maximum of 256 registers"); - } - if (registerCount < 1) { - throw new SemanticException(input, "A register range must have the lower register listed first"); - } - $outRegisters = registerCount; TypeIdItem typeIdItem = $nonvoid_type_descriptor.type; diff --git a/smali/src/main/java/org/jf/smali/SemanticException.java b/smali/src/main/java/org/jf/smali/SemanticException.java index 54f5524d..fc67920d 100644 --- a/smali/src/main/java/org/jf/smali/SemanticException.java +++ b/smali/src/main/java/org/jf/smali/SemanticException.java @@ -30,14 +30,15 @@ package org.jf.smali; import org.antlr.runtime.IntStream; import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.tree.CommonTree; public class SemanticException extends RecognitionException { private String errorMessage; - SemanticException(IntStream input, String errorMessage) { + SemanticException(IntStream input, String errorMessage, Object... messageArguments) { super(input); - this.errorMessage = errorMessage; + this.errorMessage = String.format(errorMessage, messageArguments); } SemanticException(IntStream input, Exception ex) { @@ -45,6 +46,16 @@ public class SemanticException extends RecognitionException { this.errorMessage = ex.getMessage(); } + SemanticException(IntStream input, CommonTree tree, String errorMessage, Object... messageArguments) { + super(); + this.input = input; + this.token = tree.getToken(); + this.index = tree.getTokenStartIndex(); + this.line = token.getLine(); + this.charPositionInLine = token.getCharPositionInLine(); + this.errorMessage = String.format(errorMessage, messageArguments); + } + public String getMessage() { return errorMessage; }