mirror of
https://github.com/revanced/smali.git
synced 2025-05-29 12:20:11 +02:00
Improved error handling
git-svn-id: https://smali.googlecode.com/svn/trunk@102 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
parent
c52343de98
commit
74f73e382d
@ -185,9 +185,15 @@ import java.util.ArrayDeque;
|
|||||||
tokens.add(token);
|
tokens.add(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int lexerErrors = 0;
|
||||||
public String getErrorHeader(RecognitionException e) {
|
public String getErrorHeader(RecognitionException e) {
|
||||||
|
lexerErrors++;
|
||||||
return getSourceName()+"["+ e.line+","+e.charPositionInLine+"]";
|
return getSourceName()+"["+ e.line+","+e.charPositionInLine+"]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getNumberOfLexerErrors() {
|
||||||
|
return lexerErrors;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -207,6 +207,7 @@ access_list
|
|||||||
|
|
||||||
|
|
||||||
field : FIELD_DIRECTIVE access_list MEMBER_NAME nonvoid_type_descriptor literal?
|
field : FIELD_DIRECTIVE access_list MEMBER_NAME nonvoid_type_descriptor literal?
|
||||||
|
//TODO: get rid of this predicate
|
||||||
( (annotation+ END_FIELD_DIRECTIVE)=> annotation+ END_FIELD_DIRECTIVE
|
( (annotation+ END_FIELD_DIRECTIVE)=> annotation+ END_FIELD_DIRECTIVE
|
||||||
| END_FIELD_DIRECTIVE?
|
| END_FIELD_DIRECTIVE?
|
||||||
)
|
)
|
||||||
|
@ -50,48 +50,60 @@ import org.JesusFreke.dexlib.code.Format.*;
|
|||||||
public ClassDefItem classDefItem;
|
public ClassDefItem classDefItem;
|
||||||
public ClassDataItem classDataItem;
|
public ClassDataItem classDataItem;
|
||||||
|
|
||||||
private static byte parseRegister_nibble(String register, int totalMethodRegisters, int methodParameterRegisters) {
|
private byte parseRegister_nibble(String register, int totalMethodRegisters, int methodParameterRegisters)
|
||||||
|
throws SemanticException {
|
||||||
//register should be in the format "v12"
|
//register should be in the format "v12"
|
||||||
int val = Byte.parseByte(register.substring(1));
|
int val = Byte.parseByte(register.substring(1));
|
||||||
if (register.charAt(0) == 'p') {
|
if (register.charAt(0) == 'p') {
|
||||||
val = totalMethodRegisters - methodParameterRegisters + val;
|
val = totalMethodRegisters - methodParameterRegisters + val;
|
||||||
}
|
}
|
||||||
if (val >= 2<<4) {
|
if (val >= 2<<4) {
|
||||||
//TODO: throw correct exception type
|
throw new SemanticException(input, "The maximum allowed register in this context is list of registers is v15");
|
||||||
throw new RuntimeException("The maximum allowed register in this context is list of registers is v15");
|
|
||||||
}
|
}
|
||||||
//the parser wouldn't accept a negative register, i.e. v-1, so we don't have to check for val<0;
|
//the parser wouldn't have accepted a negative register, i.e. v-1, so we don't have to check for val<0;
|
||||||
return (byte)val;
|
return (byte)val;
|
||||||
}
|
}
|
||||||
|
|
||||||
//return a short, because java's byte is signed
|
//return a short, because java's byte is signed
|
||||||
private static short parseRegister_byte(String register, int totalMethodRegisters, int methodParameterRegisters) {
|
private short parseRegister_byte(String register, int totalMethodRegisters, int methodParameterRegisters)
|
||||||
|
throws SemanticException {
|
||||||
//register should be in the format "v123"
|
//register should be in the format "v123"
|
||||||
int val = Short.parseShort(register.substring(1));
|
int val = Short.parseShort(register.substring(1));
|
||||||
if (register.charAt(0) == 'p') {
|
if (register.charAt(0) == 'p') {
|
||||||
val = totalMethodRegisters - methodParameterRegisters + val;
|
val = totalMethodRegisters - methodParameterRegisters + val;
|
||||||
}
|
}
|
||||||
if (val >= 2<<8) {
|
if (val >= 2<<8) {
|
||||||
//TODO: throw correct exception type
|
throw new SemanticException(input, "The maximum allowed register in this context is v255");
|
||||||
throw new RuntimeException("The maximum allowed register in this context is v255");
|
|
||||||
}
|
}
|
||||||
return (short)val;
|
return (short)val;
|
||||||
}
|
}
|
||||||
|
|
||||||
//return an int because java's short is signed
|
//return an int because java's short is signed
|
||||||
private static int parseRegister_short(String register, int totalMethodRegisters, int methodParameterRegisters) {
|
private int parseRegister_short(String register, int totalMethodRegisters, int methodParameterRegisters)
|
||||||
|
throws SemanticException {
|
||||||
//register should be in the format "v12345"
|
//register should be in the format "v12345"
|
||||||
int val = Integer.parseInt(register.substring(1));
|
int val = Integer.parseInt(register.substring(1));
|
||||||
if (register.charAt(0) == 'p') {
|
if (register.charAt(0) == 'p') {
|
||||||
val = totalMethodRegisters - methodParameterRegisters + val;
|
val = totalMethodRegisters - methodParameterRegisters + val;
|
||||||
}
|
}
|
||||||
if (val >= 2<<16) {
|
if (val >= 2<<16) {
|
||||||
//TODO: throw correct exception type
|
throw new SemanticException(input, "The maximum allowed register in this context is v65535");
|
||||||
throw new RuntimeException("The maximum allowed register in this context is v65535");
|
|
||||||
}
|
}
|
||||||
//the parser wouldn't accept a negative register, i.e. v-1, so we don't have to check for val<0;
|
//the parser wouldn't accept a negative register, i.e. v-1, so we don't have to check for val<0;
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getErrorMessage(RecognitionException e, String[] tokenNames) {
|
||||||
|
if ( e instanceof SemanticException ) {
|
||||||
|
return e.getMessage();
|
||||||
|
} else {
|
||||||
|
return super.getErrorMessage(e, tokenNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorHeader(RecognitionException e) {
|
||||||
|
return getSourceName()+"["+ e.line+","+e.charPositionInLine+"]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -217,8 +229,7 @@ field returns[ClassDataItem.EncodedField encodedField, EncodedValue encodedValue
|
|||||||
|
|
||||||
if ($field_initial_value.encodedValue != null) {
|
if ($field_initial_value.encodedValue != null) {
|
||||||
if (($access_list.value & AccessFlags.STATIC) == 0) {
|
if (($access_list.value & AccessFlags.STATIC) == 0) {
|
||||||
//TODO: change to an appropriate exception type?
|
throw new SemanticException(input, "Initial field values can only be specified for static fields.");
|
||||||
throw new RuntimeException("Initial field values can only be specified for static fields.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$encodedValue = $field_initial_value.encodedValue;
|
$encodedValue = $field_initial_value.encodedValue;
|
||||||
@ -408,16 +419,14 @@ method returns[ ClassDataItem.EncodedMethod encodedMethod,
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (totalMethodRegisters < methodParameterRegisters) {
|
if (totalMethodRegisters < methodParameterRegisters) {
|
||||||
//TODO: throw the correct exception type
|
throw new SemanticException(input, "This method requires at least " +
|
||||||
throw new RuntimeException( "This method requires at least " +
|
|
||||||
Integer.toString(methodParameterRegisters) +
|
Integer.toString(methodParameterRegisters) +
|
||||||
" registers, for the method parameters");
|
" registers, for the method parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
int methodParameterCount = methodIdItem.getParameterCount();
|
int methodParameterCount = methodIdItem.getParameterCount();
|
||||||
if ($method::debugInfo.getParameterNameCount() > methodParameterCount) {
|
if ($method::debugInfo.getParameterNameCount() > methodParameterCount) {
|
||||||
//TODO: throw the correct exception type
|
throw new SemanticException(input, "Too many parameter names specified. This method only has " +
|
||||||
throw new RuntimeException( "Too many parameter names specified. This method only has " +
|
|
||||||
Integer.toString(methodParameterCount) +
|
Integer.toString(methodParameterCount) +
|
||||||
" parameters.");
|
" parameters.");
|
||||||
}
|
}
|
||||||
@ -505,8 +514,7 @@ label_def
|
|||||||
{
|
{
|
||||||
String labelName = $label.labelName;
|
String labelName = $label.labelName;
|
||||||
if ($method::labels.containsKey(labelName)) {
|
if ($method::labels.containsKey(labelName)) {
|
||||||
//TODO: use appropriate exception type
|
throw new SemanticException(input, "Label " + labelName + " has multiple defintions.");
|
||||||
throw new RuntimeException("Label " + labelName + " has multiple defintions.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -635,8 +643,10 @@ statements[int totalMethodRegisters, int methodParameterRegisters] returns[Array
|
|||||||
}
|
}
|
||||||
: ^(I_STATEMENTS (instruction[$totalMethodRegisters, $methodParameterRegisters]
|
: ^(I_STATEMENTS (instruction[$totalMethodRegisters, $methodParameterRegisters]
|
||||||
{
|
{
|
||||||
$instructions.add($instruction.instruction);
|
if ($instruction.instruction != null) {
|
||||||
$method::currentAddress += $instruction.instruction.getBytes().length/2;
|
$instructions.add($instruction.instruction);
|
||||||
|
$method::currentAddress += $instruction.instruction.getBytes().length/2;
|
||||||
|
}
|
||||||
})*);
|
})*);
|
||||||
|
|
||||||
label_ref returns[int labelAddress]
|
label_ref returns[int labelAddress]
|
||||||
@ -647,8 +657,7 @@ label_ref returns[int labelAddress]
|
|||||||
Integer labelAdd = $method::labels.get(labelName);
|
Integer labelAdd = $method::labels.get(labelName);
|
||||||
|
|
||||||
if (labelAdd == null) {
|
if (labelAdd == null) {
|
||||||
//TODO: throw correct exception type
|
throw new SemanticException(input, "Label \"" + labelName + "\" is not defined.");
|
||||||
throw new RuntimeException("Label \"" + labelName + "\" is not defined.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$labelAddress = labelAdd;
|
$labelAddress = labelAdd;
|
||||||
@ -686,8 +695,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters] returns[Ins
|
|||||||
int addressOffset = $offset_or_label.offsetValue;
|
int addressOffset = $offset_or_label.offsetValue;
|
||||||
|
|
||||||
if (addressOffset < Byte.MIN_VALUE || addressOffset > Byte.MAX_VALUE) {
|
if (addressOffset < Byte.MIN_VALUE || addressOffset > Byte.MAX_VALUE) {
|
||||||
//TODO: throw correct exception type
|
throw new SemanticException(input, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-128, 127].");
|
||||||
throw new RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-128, 127].");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$instruction = Format10t.Format.make(dexFile, opcode.value, (byte)addressOffset);
|
$instruction = Format10t.Format.make(dexFile, opcode.value, (byte)addressOffset);
|
||||||
@ -734,8 +742,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters] returns[Ins
|
|||||||
int addressOffset = $offset_or_label.offsetValue;
|
int addressOffset = $offset_or_label.offsetValue;
|
||||||
|
|
||||||
if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) {
|
if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) {
|
||||||
//TODO: throw correct exception type
|
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 RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767].");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$instruction = Format20t.Format.make(dexFile, opcode.value, (short)addressOffset);
|
$instruction = Format20t.Format.make(dexFile, opcode.value, (short)addressOffset);
|
||||||
@ -799,8 +806,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters] returns[Ins
|
|||||||
int addressOffset = $offset_or_label.offsetValue;
|
int addressOffset = $offset_or_label.offsetValue;
|
||||||
|
|
||||||
if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) {
|
if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) {
|
||||||
//TODO: throw correct exception type
|
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 RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767].");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$instruction = Format21t.Format.make(dexFile, opcode.value, regA, (short)addressOffset);
|
$instruction = Format21t.Format.make(dexFile, opcode.value, regA, (short)addressOffset);
|
||||||
@ -860,8 +866,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters] returns[Ins
|
|||||||
int addressOffset = $offset_or_label.offsetValue;
|
int addressOffset = $offset_or_label.offsetValue;
|
||||||
|
|
||||||
if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) {
|
if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) {
|
||||||
//TODO: throw correct exception type
|
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 RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767].");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$instruction = Format22t.Format.make(dexFile, opcode.value, regA, regB, (short)addressOffset);
|
$instruction = Format22t.Format.make(dexFile, opcode.value, regA, regB, (short)addressOffset);
|
||||||
@ -959,12 +964,10 @@ instruction[int totalMethodRegisters, int methodParameterRegisters] returns[Ins
|
|||||||
|
|
||||||
int registerCount = endRegister-startRegister+1;
|
int registerCount = endRegister-startRegister+1;
|
||||||
if (registerCount > 256) {
|
if (registerCount > 256) {
|
||||||
//TODO: throw appropriate exception type
|
throw new SemanticException(input, "A register range can span a maximum of 256 registers");
|
||||||
throw new RuntimeException("A register range can span a maximum of 256 registers");
|
|
||||||
}
|
}
|
||||||
if (registerCount < 1) {
|
if (registerCount < 1) {
|
||||||
//TODO: throw appropriate exception type
|
throw new SemanticException(input, "A register range must have the lower register listed first");
|
||||||
throw new RuntimeException("A register range must have the lower register listed first");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem;
|
MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem;
|
||||||
@ -1019,9 +1022,9 @@ register_list[int totalMethodRegisters, int methodParameterRegisters] returns[by
|
|||||||
: ^(I_REGISTER_LIST
|
: ^(I_REGISTER_LIST
|
||||||
(REGISTER
|
(REGISTER
|
||||||
{
|
{
|
||||||
|
//TODO: shouldn't this be == 6?
|
||||||
if ($registerCount == 5) {
|
if ($registerCount == 5) {
|
||||||
//TODO: throw the correct type of exception
|
throw new SemanticException(input, "A list of registers can only have a maximum of 5 registers. Use the <op>/range alternate opcode instead.");
|
||||||
throw new RuntimeException("A list of registers can only have a maximum of 5 registers. Use the <op>/range alternate opcode instead.");
|
|
||||||
}
|
}
|
||||||
$registers[$registerCount++] = parseRegister_nibble($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters);
|
$registers[$registerCount++] = parseRegister_nibble($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters);
|
||||||
})*);
|
})*);
|
||||||
|
46
src/main/java/org/JesusFreke/smali/SemanticException.java
Normal file
46
src/main/java/org/JesusFreke/smali/SemanticException.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* [The "BSD licence"]
|
||||||
|
* Copyright (c) 2009 Ben Gruver
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.JesusFreke.smali;
|
||||||
|
|
||||||
|
import org.antlr.runtime.RecognitionException;
|
||||||
|
import org.antlr.runtime.IntStream;
|
||||||
|
|
||||||
|
public class SemanticException extends RecognitionException {
|
||||||
|
private String errorMessage;
|
||||||
|
|
||||||
|
|
||||||
|
SemanticException(IntStream input, String errorMessage) {
|
||||||
|
super(input);
|
||||||
|
this.errorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
}
|
@ -117,8 +117,16 @@ public class smali
|
|||||||
|
|
||||||
DexFile dexFile = DexFile.makeBlankDexFile();
|
DexFile dexFile = DexFile.makeBlankDexFile();
|
||||||
|
|
||||||
|
boolean errors = false;
|
||||||
|
|
||||||
for (File file: filesToProcess) {
|
for (File file: filesToProcess) {
|
||||||
assembleSmaliFile(file, dexFile);
|
if (!assembleSmaliFile(file, dexFile)) {
|
||||||
|
errors = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
dexFile.place();
|
dexFile.place();
|
||||||
@ -152,6 +160,7 @@ public class smali
|
|||||||
} catch (Exception ex)
|
} catch (Exception ex)
|
||||||
{
|
{
|
||||||
System.out.println(ex.toString());
|
System.out.println(ex.toString());
|
||||||
|
System.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,17 +174,23 @@ public class smali
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assembleSmaliFile(File smaliFile, DexFile dexFile)
|
private static boolean assembleSmaliFile(File smaliFile, DexFile dexFile)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
ANTLRInputStream input = new ANTLRInputStream(new FileInputStream(smaliFile));
|
ANTLRInputStream input = new ANTLRInputStream(new FileInputStream(smaliFile));
|
||||||
input.name = smaliFile.getAbsolutePath();
|
input.name = smaliFile.getAbsolutePath();
|
||||||
|
|
||||||
smaliLexer lexer = new smaliLexer(input);
|
smaliLexer lexer = new smaliLexer(input);
|
||||||
|
|
||||||
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||||
smaliParser parser = new smaliParser(tokens);
|
smaliParser parser = new smaliParser(tokens);
|
||||||
|
|
||||||
smaliParser.smali_file_return result = parser.smali_file();
|
smaliParser.smali_file_return result = parser.smali_file();
|
||||||
|
|
||||||
|
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfLexerErrors() > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
CommonTree t = (CommonTree) result.getTree();
|
CommonTree t = (CommonTree) result.getTree();
|
||||||
|
|
||||||
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
||||||
@ -186,6 +201,11 @@ public class smali
|
|||||||
dexGen.dexFile = dexFile;
|
dexGen.dexFile = dexFile;
|
||||||
dexGen.smali_file();
|
dexGen.smali_file();
|
||||||
|
|
||||||
|
if (dexGen.getNumberOfSyntaxErrors() > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
dexFile.ClassDefsSection.intern(dexFile, dexGen.classDefItem);
|
dexFile.ClassDefsSection.intern(dexFile, dexGen.classDefItem);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user