// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * 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. // * Neither the name of the <organization> nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 DAVID J. PEARCE 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 jasm.io; import jasm.io.*; import jasm.lang.*; import java.util.*; public class JasmFileReader { private static final HashSet<String> loadStoreBytecodes = new HashSet<String>() { { add("istore"); add("lstore"); add("astore"); add("fstore"); add("dstore"); add("iload"); add("lload"); add("aload"); add("fload"); add("dload"); } }; private static final HashSet<String> arrayLoadStoreBytecodes = new HashSet<String>() { { add("iastore"); add("lastore"); add("bastore"); add("castore"); add("aastore"); add("fastore"); add("dastore"); add("iaload"); add("laload"); add("baload"); add("caload"); add("aaload"); add("faload"); add("daload"); } }; private static final HashSet<String> fieldBytecodes = new HashSet<String>() { { add("getstatic"); add("getfield"); add("putstatic"); add("putfield"); } }; private static final HashSet<String> invokeBytecodes = new HashSet<String>() { { add("invokespecial"); add("invokevirtual"); add("invokeinterface"); add("invokestatic"); } }; private static final HashSet<String> returnBytecodes = new HashSet<String>() { { add("return"); add("areturn"); add("ireturn"); add("freturn"); add("dreturn"); } }; private static final HashSet<String> constBytecodes = new HashSet<String>() { { add("iconst_m1"); add("iconst_0"); add("iconst_1"); add("iconst_2"); add("iconst_3"); add("iconst_4"); add("iconst_5"); add("lconst_0"); add("lconst_1"); add("bipush"); add("sipush"); add("ldc"); add("ldc_w"); } }; public static Bytecode parseBytecode(String text) throws ParseError { // first, tokenise the string ArrayList<String> tokens = new ArrayList<String>(); int index = 0; while(index < text.length()) { index = parseWhiteSpace(text,index); int start = index; while (index < text.length() && !Character.isWhitespace(text.charAt(index))) { if(text.charAt(index++) == '"') { // parse String while(index < text.length() && text.charAt(index++) != '"'); } } tokens.add(text.substring(start,index)); } if(tokens.size() == 0) { return null; } // second, dispatch on token kind String kind = tokens.get(0); if(loadStoreBytecodes.contains(kind)) { return parseLoadStoreBytecode(tokens); } else if(arrayLoadStoreBytecodes.contains(kind)) { return parseArrayLoadStoreBytecode(tokens); } else if(invokeBytecodes.contains(kind)) { return parseInvokeBytecode(tokens); } else if(returnBytecodes.contains(kind)) { return parseReturnBytecode(tokens); } else if(fieldBytecodes.contains(kind)) { return parseFieldBytecode(tokens); } else if(constBytecodes.contains(kind)) { return parseConstBytecode(tokens); } else if(kind.equals("goto") && tokens.size() == 2) { return new Bytecode.Goto(tokens.get(1)); } else if(tokens.size() == 1 && kind.charAt(kind.length()-1) == ':') { // label return new Bytecode.Label(kind.substring(0,kind.length()-1)); } else { error("syntax error"); return null; } } public static Bytecode parseLoadStoreBytecode(ArrayList<String> tokens) throws ParseError { String kind = tokens.get(0); JvmType type = parseType(kind.charAt(0)); kind = kind.substring(1,kind.length()); int slot; if(tokens.size() == 1) { int idx = kind.indexOf('_'); if(idx == -1) { error("slot argument required"); } slot = Integer.parseInt(kind.substring(idx + 1, kind.length())); } else { slot = Integer.parseInt(tokens.get(1)); } if(kind.startsWith("store")) { return new Bytecode.Store(slot,type); } else { return new Bytecode.Load(slot,type); } } public static Bytecode parseArrayLoadStoreBytecode(ArrayList<String> tokens) throws ParseError { String kind = tokens.get(0); JvmType type = parseType(kind.charAt(0)); kind = kind.substring(2,kind.length()); if(kind.startsWith("store")) { return new Bytecode.ArrayStore(new JvmType.Array(type)); } else { return new Bytecode.ArrayLoad(new JvmType.Array(type)); } } public static Bytecode parseInvokeBytecode(ArrayList<String> tokens) throws ParseError { if (tokens.size() != 2) { error("wrong number of arguments"); } Bytecode.InvokeMode mode; String kind = tokens.get(0); if (kind.equals("invokevirtual")) { mode = Bytecode.InvokeMode.VIRTUAL; } else if (kind.equals("invokeinterface")) { mode = Bytecode.InvokeMode.INTERFACE; } else if (kind.equals("invokespecial")) { mode = Bytecode.InvokeMode.SPECIAL; } else { mode = Bytecode.InvokeMode.STATIC; } String[] split = tokens.get(1).split("\\."); JvmType.Clazz owner = ClassFileReader.parseClassDescriptor("L" + split[0] + ";"); split = split[1].split(":"); String name = split[0]; JvmType.Function type = ClassFileReader.parseMethodDescriptor(split[1]); return new Bytecode.Invoke(owner, name, type, mode); } public static Bytecode parseReturnBytecode(ArrayList<String> tokens) throws ParseError { if (tokens.size() != 1) { error("wrong number of arguments"); } String kind = tokens.get(0); if(kind.equals("return")) { return new Bytecode.Return(null); } else { JvmType type = parseType(kind.charAt(0)); return new Bytecode.Return(type); } } public static Bytecode parseFieldBytecode(ArrayList<String> tokens) throws ParseError { if (tokens.size() != 2) { error("wrong number of arguments"); } String[] split = tokens.get(1).split("\\."); JvmType.Clazz owner = ClassFileReader.parseClassDescriptor("L" + split[0] + ";"); split = split[1].split(":"); String name = split[0]; JvmType type = ClassFileReader.parseDescriptor(split[1]); String kind = tokens.get(0); if (kind.equals("getstatic")) { return new Bytecode.GetField(owner, name, type, Bytecode.FieldMode.STATIC); } else if (kind.equals("getfield")) { return new Bytecode.GetField(owner, name, type, Bytecode.FieldMode.NONSTATIC); } else if (kind.equals("putfield")) { return new Bytecode.PutField(owner, name, type, Bytecode.FieldMode.NONSTATIC); } else { return new Bytecode.PutField(owner, name, type, Bytecode.FieldMode.STATIC); } } public static Bytecode parseConstBytecode(ArrayList<String> tokens) throws ParseError { JvmType type; Object constant; String kind = tokens.get(0); if(tokens.size() == 1) { type = parseType(kind.charAt(0)); int idx = kind.indexOf('_'); if(idx == -1) { error("constant argument required"); } if(type instanceof JvmType.Int) { constant = Integer.parseInt(kind.substring(idx + 1, kind.length())); } else if(type instanceof JvmType.Long) { constant = Long.parseLong(kind.substring(idx + 1, kind.length())); } else { error("syntax error"); return null; } } else if(kind.startsWith("ldc")) { String s = tokens.get(1); if(s.charAt(0) == '\"') { if (s.length() < 2 || s.charAt(s.length() - 1) != '\"') { error("syntax error"); } constant = s.substring(1,s.length()-1); } else if(s.contains(".")) { // need to think here about how to distinguish float/double values constant = Float.parseFloat(s); } else { constant = Long.parseLong(s); } } else { constant = Integer.parseInt(tokens.get(1)); } return new Bytecode.LoadConst(constant); } public static JvmType parseType(char c) throws ParseError { if(c == 'l') { return new JvmType.Long(); } else if(c == 'i') { return new JvmType.Int(); } else if(c == 'f') { return new JvmType.Float(); } else if(c == 'd') { return new JvmType.Double(); } else if(c == 'b') { return new JvmType.Bool(); } else if(c == 'c') { return new JvmType.Char(); } else if(c == 's') { return new JvmType.Short(); } else if(c == 'a') { return JvmTypes.JAVA_LANG_OBJECT; } else { error("invalid bytecode type: " + c); return null; } } private static int parseWhiteSpace(String text, int index) { while (index < text.length() && Character.isWhitespace(text.charAt(index))) { index++; } return index; } public static class ParseError extends Exception { public ParseError(String msg) { super(msg); } } public static void error(String msg) throws ParseError { throw new ParseError(msg); } }