Improved error handling

git-svn-id: https://smali.googlecode.com/svn/trunk@102 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-06-06 20:45:23 +00:00
parent c52343de98
commit 74f73e382d
5 changed files with 115 additions and 39 deletions

View File

@ -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;
}
} }

View File

@ -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?
) )

View File

@ -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);
})*); })*);

View 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;
}
}

View File

@ -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;
} }
} }