diff --git a/src/org/openjdk/asmtools/Main.java b/src/org/openjdk/asmtools/Main.java new file mode 100644 index 0000000..c06e8d0 --- /dev/null +++ b/src/org/openjdk/asmtools/Main.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools; + +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; + +/** + * Wrapper class that reads the first command line argument and invokes a corresponding + * tool. + */ +public class Main { + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + /** + * Parses the first argument and deligates execution to an appropriate tool + * + * @param args - command line arguments + */ + public static void main(String args[]) { + if (args.length == 0) { + usage(i18n.getString("main.error.no_arguments"), 1); + } + String cmd = args[0]; + if (cmd.equals("-?") || cmd.equals("-h") || cmd.equals("-help")) { + usage(null, 0); + } else if (cmd.equals("-version")) { + printVersion(); + } else { + String[] newArgs = new String[args.length - 1]; + System.arraycopy(args, 1, newArgs, 0, args.length - 1); + if (cmd.equals("jasm")) { + jasm(newArgs); + } else if (cmd.equals("jdis")) { + jdis(newArgs); + } else if (cmd.equals("jcoder")) { + jcoder(newArgs); + } else if (cmd.equals("jdec")) { + jdec(newArgs); + } else if (cmd.equals("jcdec")) { + jcdec(newArgs); + } else { + usage(i18n.getString("main.error.unknown_tool", cmd), 1); + } + } + } + + /** + * Prints usage info and error message, afterwards invokes System.exit() + * + * @param msg - error message to print, or null if no errors occurred + * @param exitCode - exit code to be returned by System.exit() + */ + public static void usage(String msg, int exitCode) { + System.err.println(i18n.getString("main.usage", "asmtools.jar")); + if (msg != null) { + System.err.println(msg); + } + System.exit(exitCode); + } + + /** + * Prints the tools version and calls System.exit(0) + */ + public static void printVersion() { + System.err.println(ProductInfo.FULL_VERSION); + System.exit(0); + } + + /** + * Invokes jasm main class with passed arguments + */ + public static void jasm(String[] args) { + org.openjdk.asmtools.jasm.Main.main(args); + } + + /** + * Invokes jcdec main class with passed arguments + */ + public static void jcdec(String[] args) { + org.openjdk.asmtools.jcdec.Main.main(args); + } + + /** + * Invokes jcoder main class with passed arguments + */ + public static void jcoder(String[] args) { + org.openjdk.asmtools.jcoder.Main.main(args); + } + + /** + * Invokes jdec main class with passed arguments + */ + public static void jdec(String[] args) { + org.openjdk.asmtools.jdec.Main.main(args); + } + + /** + * Invokes jdis main class with passed arguments + */ + public static void jdis(String[] args) { + org.openjdk.asmtools.jdis.Main.main(args); + } +} diff --git a/src/org/openjdk/asmtools/asmutils/HexUtils.java b/src/org/openjdk/asmtools/asmutils/HexUtils.java new file mode 100644 index 0000000..c5b96ee --- /dev/null +++ b/src/org/openjdk/asmtools/asmutils/HexUtils.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.openjdk.asmtools.asmutils; + +/** + * + */ +public class HexUtils { + /*======================================================== Hex */ + + private static final String hexString = "0123456789ABCDEF"; + private static final char hexTable[] = hexString.toCharArray(); + + public static String toHex(long val, int width) { + StringBuffer s = new StringBuffer(); + for (int i = width - 1; i >= 0; i--) { + s.append(hexTable[((int) (val >> (4 * i))) & 0xF]); + } + return "0x" + s.toString(); + } + + public static String toHex(long val) { + int width; + for (width = 16; width > 0; width--) { + if ((val >> (width - 1) * 4) != 0) { + break; + } + } + return toHex(val, width); + } + + public static String toHex(int val) { + int width; + for (width = 8; width > 0; width--) { + if ((val >> (width - 1) * 4) != 0) { + break; + } + } + return toHex(val, width); + } + +} diff --git a/src/org/openjdk/asmtools/i18n.properties b/src/org/openjdk/asmtools/i18n.properties new file mode 100644 index 0000000..3969041 --- /dev/null +++ b/src/org/openjdk/asmtools/i18n.properties @@ -0,0 +1,33 @@ +# Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +main.usage=\ +Usage: \n\ +to run an assembly tool: \n\ +\ $ java -jar {0} toolName [args...] \n\ +\ where toolName one of: jasm, jdis, jcoder, jdec, jcdec \n\ +to get the version: \n\ +\ $ java -jar {0} -version \n\ +to get this message \n\ +\ $ java -jar {0} -?|-h|-help\n + +main.error.no_arguments=No arguments provided! See options above. +main.error.unknown_tool=Tool name ''{0}'' unrecognized. See usage above for possible tool choices. \ No newline at end of file diff --git a/src/org/openjdk/asmtools/jasm/AnnotationData.java b/src/org/openjdk/asmtools/jasm/AnnotationData.java new file mode 100644 index 0000000..b51dbd1 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/AnnotationData.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * See JVMS3, section 4.8.16. + */ +class AnnotationData implements Data { + + boolean invisible; + Argument typeCPX; + ArrayList elemValuePairs; + int annotationLength = 0; + + /** + * AnnotationElemValue + * + * Used to store Annotation Data + */ + static public class ElemValuePair implements Data { + + ConstantPool.ConstCell name; + Data value; + + public ElemValuePair(ConstantPool.ConstCell name, Data value) { + this.name = name; + this.value = value; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + name.write(out); + value.write(out); + } + + @Override + public int getLength() { + return 2 + value.getLength(); + } + } + + public AnnotationData(Argument typeCPX, boolean invisible) { + this.typeCPX = typeCPX; + this.elemValuePairs = new ArrayList<>(); + this.invisible = invisible; + } + + public void add(ElemValuePair elemValuePair) { + elemValuePairs.add(elemValuePair); + annotationLength += elemValuePair.getLength(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(typeCPX.arg); + out.writeShort(elemValuePairs.size()); + + for (Data pair : elemValuePairs) { + pair.write(out); + } + } + + @Override + public int getLength() { + return 4 + annotationLength; + } +} diff --git a/src/org/openjdk/asmtools/jasm/Argument.java b/src/org/openjdk/asmtools/jasm/Argument.java new file mode 100644 index 0000000..316c401 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Argument.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +/** + * + */ +class Argument { + + static final int NotSet = -1; + int arg; + + Argument() { + arg = NotSet; + } + + Argument(int arg) { + this.arg = arg; + } + + public int hashCode() { + return arg; + } + + /** + * Compares this object to the specified object. + * + * @param obj the object to compare with + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + throw new Parser.CompilerError("ConstCell.equals"); + } + + boolean isSet() { + return arg != NotSet; + } +} diff --git a/src/org/openjdk/asmtools/jasm/AttrData.java b/src/org/openjdk/asmtools/jasm/AttrData.java new file mode 100644 index 0000000..f156244 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/AttrData.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +/** + * AttrData + * + * AttrData is the base class for many attributes (or parts of attributes), and it is + * instantiated directly for simple attributes (like Synthetic or Deprecated). + */ +class AttrData implements Data { + + private final ClassData clsData; + private final Argument attrNameCPX; + + AttrData(ClassData cdata, String name) { + clsData = cdata; + attrNameCPX = cdata.pool.FindCellAsciz(name); + } + + // full length of the attribute + // declared in Data + public int getLength() { + return 6 + attrLength(); + } + + // subclasses must redefine this + public int attrLength() { + return 0; + } + + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(attrNameCPX.arg); + out.writeInt(attrLength()); // attr len + } +} // end class AttrData + diff --git a/src/org/openjdk/asmtools/jasm/BootstrapMethodData.java b/src/org/openjdk/asmtools/jasm/BootstrapMethodData.java new file mode 100644 index 0000000..31bdfe4 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/BootstrapMethodData.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * + */ +class BootstrapMethodData extends Argument implements Data { + + ConstantPool.ConstCell bootstrapMethodHandle; + ArrayList arguments; + public int placeholder_index = -1; + + public BootstrapMethodData(ConstantPool.ConstCell bsmHandle, ArrayList arguments) { + super(); + this.bootstrapMethodHandle = bsmHandle; + this.arguments = arguments; + } + + public BootstrapMethodData(int placeholder) { + super(); + this.bootstrapMethodHandle = null; + this.arguments = null; + this.placeholder_index = placeholder; + } + + public int getLength() { + return 4 + arguments.size() * 2; + } + + public boolean isPlaceholder() { + return placeholder_index > -1; + } + + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(bootstrapMethodHandle.arg); + out.writeShort(arguments.size()); + + for (ConstantPool.ConstCell argument : arguments) { + out.writeShort(argument.arg); + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/CPXAttr.java b/src/org/openjdk/asmtools/jasm/CPXAttr.java new file mode 100644 index 0000000..57aede1 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/CPXAttr.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +/** + * Constant Pool Index Attribute + */ +class CPXAttr extends AttrData { + + Argument cell; + + public CPXAttr(ClassData cls, String attrName, Argument cell) { + super(cls, attrName); + this.cell = cell; + } + + public int attrLength() { + return 2; + } + + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); // attr name, attr len + out.writeShort(cell.arg); + } +} // end class CPXAttr + diff --git a/src/org/openjdk/asmtools/jasm/CheckedDataOutputStream.java b/src/org/openjdk/asmtools/jasm/CheckedDataOutputStream.java new file mode 100644 index 0000000..088c1c8 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/CheckedDataOutputStream.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.openjdk.asmtools.jasm; + +import java.io.DataOutput; +import java.io.IOException; + +/** + * + */ +public interface CheckedDataOutputStream { + + public void write(int b) throws IOException; + + public void write(byte b[], int off, int len) throws IOException; + + public void writeBoolean(boolean v) throws IOException; + + public void writeByte(int v) throws IOException; + + public void writeShort(int v) throws IOException; + + public void writeChar(int v) throws IOException; + + public void writeInt(int v) throws IOException; + + public void writeLong(long v) throws IOException; + + public void writeFloat(float v) throws IOException; + + public void writeDouble(double v) throws IOException; + + public void writeBytes(String s) throws IOException; + + public void writeChars(String s) throws IOException; + + public void writeUTF(String s) throws IOException; + +// public int writeUTF(String str, DataOutput out) throws IOException; +} diff --git a/src/org/openjdk/asmtools/jasm/ClassData.java b/src/org/openjdk/asmtools/jasm/ClassData.java new file mode 100644 index 0000000..eef0e1c --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ClassData.java @@ -0,0 +1,577 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.Constants.DEFAULT_MAJOR_VERSION; +import static org.openjdk.asmtools.jasm.Constants.DEFAULT_MINOR_VERSION; +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.*; +import java.util.ArrayList; + +/** + * ClassData + * + * This is the main data structure for representing parsed class data. This structure + * renders directly to a class file. + * + */ +class ClassData extends MemberData { + + /*-------------------------------------------------------- */ + /* ClassData inner classes */ + + /*-------------------------------------------------------- */ + /* ClassData Fields */ + short major_version = DEFAULT_MAJOR_VERSION; + short minor_version = DEFAULT_MINOR_VERSION; + ConstantPool.ConstCell me, father; + String myClassName; + AttrData sourceFileNameAttr; + ArrayList interfaces; + ArrayList fields = new ArrayList<>(); + ArrayList methods = new ArrayList<>(); + DataVectorAttr innerClasses = null; + DataVectorAttr bootstrapMethodsAttr = null; + String mdl = null; + Environment env; + protected ConstantPool pool; + + private static final String DEFAULT_EXTENSION = ".class"; + String fileExtension = DEFAULT_EXTENSION; + public CDOutputStream cdos; + + /*-------------------------------------------------------- */ + /** + * init + * + * Initializes the ClassData. + * + * @param me The constant pool reference to this class + * @param father The constant pool reference to the super class + * @param interfaces A list of interfaces that this class implements + */ + public final void init(int access, ConstantPool.ConstCell me, ConstantPool.ConstCell father, ArrayList interfaces) { + this.access = access; + + // normalize the modifiers to access flags + if (Modifiers.hasPseudoMod(access)) { + createPseudoMod(); + } + + this.me = me; + if (father == null) { + father = pool.FindCellClassByName("java/lang/Object"); + } + this.father = father; + this.interfaces = interfaces; + } + + /** + * default constructor + * + * @param env + */ + public ClassData(Environment env) { + super(null, 0); // for a class, these get inited in the super - later. + cls = this; + + this.env = env; + pool = new ConstantPool(env); + cdos = new CDOutputStream(); + + } + + /** + * canonical default constructor + * + * @param env The error reporting environment. + * @param major_version The major version that this class file supports. + * @param minor_version The minor version that this class file supports + */ + public ClassData(Environment env, short major_version, short minor_version) { + this(env); + this.major_version = major_version; + this.minor_version = minor_version; + } + + public ClassData(Environment env, int acc, ConstantPool.ConstCell me, ConstantPool.ConstCell father, ArrayList impls) { + this(env); + init(acc, me, father, impls); + } + + + /* *********************************************** */ + /** + * isInterface + * + * Predicate that describes if this class has an access flag indicating that it is an + * interface. + * + * @return True if the classes access flag indicates it is an interface. + */ + public final boolean isInterface() { + return Modifiers.isInterface(access); + } + + + /* + * After a constant pool has been explicitly declared, + * this method links the Constant_InvokeDynamic constants + * with any bootstrap methods that they index in the + * Bootstrap Methods Attribute + */ + protected void relinkBootstrapMethods() { + if (bootstrapMethodsAttr == null) { + return; + } + + env.traceln("relinkBootstrapMethods"); + + for (ConstantPool.ConstCell cell : pool) { + ConstantPool.ConstValue ref = null; + if (cell != null) { + ref = cell.ref; + } + if (ref != null + && ref.tag == ConstType.CONSTANT_INVOKEDYNAMIC) { + // Find only the Constant + ConstantPool.ConstValue_IndyPair refval = (ConstantPool.ConstValue_IndyPair) ref; + if (refval != null) { + BootstrapMethodData bsmdata = refval.bsmData; + // only care about BSM Data that were placeholders + if (bsmdata != null && bsmdata.isPlaceholder()) { + // find the real BSM Data at the index + int bsmindex = bsmdata.placeholder_index; + if (bsmindex < 0 || bsmindex > bootstrapMethodsAttr.size()) { + // bad BSM index -- + // give a warning, but place the index in the arg anyway + env.traceln("Warning: (ClassData.relinkBootstrapMethods()): Bad bootstrapMethods index: " + bsmindex); + // env.error("const.bsmindex", bsmindex); + bsmdata.arg = bsmindex; + } else { + + BootstrapMethodData realbsmdata = bootstrapMethodsAttr.get(bsmindex); + // make the IndyPairs BSM Data point to the one from the attribute + refval.bsmData = realbsmdata; + } + } + } + } + } + } + + protected void numberBootstrapMethods() { + env.traceln("Numbering Bootstrap Methods"); + if (bootstrapMethodsAttr == null) { + return; + } + + int index = 0; + for (BootstrapMethodData data : bootstrapMethodsAttr) { + data.arg = index++; + } + } + + /*-------------------------------------------------------- API */ + public ConstantPool.ConstValue_Pair mkNape(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { + return new ConstantPool.ConstValue_Pair(ConstType.CONSTANT_NAMEANDTYPE, name, sig); + } + + public ConstantPool.ConstValue_Pair mkNape(String name, String sig) { + return mkNape(pool.FindCellAsciz(name), pool.FindCellAsciz(sig)); + } + + public void setSourceFileName(String name) { + } + + public FieldData addField(int access, ConstantPool.ConstValue_Pair nape) { + env.traceln(" [ClassData.addField]: #" + nape.left.arg + ":#" + nape.right.arg); + FieldData res = new FieldData(this, access, nape); + fields.add(res); + return res; + } + + public FieldData addField(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { + return addField(access, mkNape(name, sig)); + } + + public FieldData addField(int access, String name, String type) { + return addField(access, pool.FindCellAsciz(name), pool.FindCellAsciz(type)); + } + + public ConstantPool.ConstCell LocalFieldRef(FieldData field) { + return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(field.nape)); + } + + public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstValue nape) { + return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(nape)); + } + + public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { + return LocalFieldRef(mkNape(name, sig)); + } + + public ConstantPool.ConstCell LocalFieldRef(String name, String sig) { + return LocalFieldRef(pool.FindCellAsciz(name), pool.FindCellAsciz(sig)); + } + + MethodData curMethod; + + public MethodData StartMethod(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig, ArrayList exc_table) { + EndMethod(); + env.traceln(" [ClassData.StartMethod]: #" + name.arg + ":#" + sig.arg); + curMethod = new MethodData(this, access, name, sig, exc_table); + methods.add(curMethod); + return curMethod; + } + + public void EndMethod() { + curMethod = null; + } + + public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstValue nape) { + return pool.FindCell(ConstType.CONSTANT_METHOD, me, pool.FindCell(nape)); + } + + public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { + return LocalMethodRef(mkNape(name, sig)); + } + + void addLocVarData(int opc, Argument arg) { + } + + public void addInnerClass(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell innerClass, ConstantPool.ConstCell outerClass) { + env.traceln("addInnerClass (with indexes: Name (" + name.toString() + "), Inner (" + innerClass.toString() + "), Outer (" + outerClass.toString() + ")."); + if (innerClasses == null) { + innerClasses = new DataVectorAttr<>(this, AttrTag.ATT_InnerClasses.parsekey()); + } + innerClasses.add(new InnerClassData(access, name, innerClass, outerClass)); + } + + public void addBootstrapMethod(BootstrapMethodData bsmData) { + env.traceln("addBootstrapMethod"); + if (bootstrapMethodsAttr == null) { + bootstrapMethodsAttr = new DataVectorAttr<>(this, AttrTag.ATT_BootstrapMethods.parsekey()); + } + bootstrapMethodsAttr.add(bsmData); + } + + public void endClass() { + sourceFileNameAttr = new CPXAttr(this, + AttrTag.ATT_SourceFile.parsekey(), + pool.FindCellAsciz(env.getSourceName())); + pool.NumberizePool(); + pool.CheckGlobals(); + numberBootstrapMethods(); + try { + me = pool.uncheckedGetCell(me.arg); + env.traceln("me=" + me); + ConstantPool.ConstValue_Cell me_value = (ConstantPool.ConstValue_Cell) me.ref; + ConstantPool.ConstCell ascicell = me_value.cell; + env.traceln("ascicell=" + ascicell); + ConstantPool.ConstValue_String me_str = (ConstantPool.ConstValue_String) ascicell.ref; + myClassName = me_str.value; + env.traceln("--------------------------------------------"); + env.traceln("-- Constant Pool --"); + env.traceln("-------------------"); + pool.printPool(); + env.traceln("--------------------------------------------"); + env.traceln(" "); + env.traceln(" "); + env.traceln("--------------------------------------------"); + env.traceln("-- Inner Classes --"); + env.traceln("-------------------"); + printInnerClasses(); + + } catch (Throwable e) { + env.traceln("check name:" + e); + env.error("no.classname"); + e.printStackTrace(); + } + } + + private void printInnerClasses() { + if (innerClasses != null) { + int i = 1; + for (InnerClassData entry : innerClasses) { + env.trace(" InnerClass[" + i + "]: (" + Modifiers.toString(entry.access, CF_Context.CTX_INNERCLASS) + "]), "); + env.trace("Name: " + entry.name.toString() + " "); + env.trace("IC_info: " + entry.innerClass.toString() + " "); + env.trace("OC_info: " + entry.outerClass.toString() + " "); + env.traceln(" "); + i += 1; + } + } else { + env.traceln("<< NO INNER CLASSES >>"); + } + + } + + /*====================================================== write */ + public void write(CheckedDataOutputStream out) throws IOException { + + // Write the header + out.writeInt(JAVA_MAGIC); + out.writeShort(minor_version); + out.writeShort(major_version); + + pool.write(out); + out.writeShort(access); // & MM_CLASS; // Q + out.writeShort(me.arg); + out.writeShort(father.arg); + + // Write the interface names + if (interfaces != null) { + out.writeShort(interfaces.size()); + for (Argument intf : interfaces) { + out.writeShort(intf.arg); + } + } else { + out.writeShort(0); + } + + // Write the fields + if (fields != null) { + out.writeShort(fields.size()); + for (FieldData field : fields) { + field.write(out); + } + } else { + out.writeShort(0); + } + + // Write the methods + if (methods != null) { + out.writeShort(methods.size()); + for (MethodData method : methods) { + method.write(out); + } + } else { + out.writeShort(0); + } + + // Write the attributes + DataVector attrs = new DataVector(); + attrs.add(sourceFileNameAttr); + if (innerClasses != null) { + attrs.add(innerClasses); + } + if (syntheticAttr != null) { + attrs.add(syntheticAttr); + } + if (deprecatedAttr != null) { + attrs.add(deprecatedAttr); + } + if (annotAttrVis != null) { + attrs.add(annotAttrVis); + } + if (annotAttrInv != null) { + attrs.add(annotAttrInv); + } + if (type_annotAttrVis != null) { + attrs.add(type_annotAttrVis); + } + if (type_annotAttrInv != null) { + attrs.add(type_annotAttrInv); + } + if (bootstrapMethodsAttr != null) { + attrs.add(bootstrapMethodsAttr); + } + attrs.write(out); + } // end ClassData.write() + + static char fileSeparator; //=System.getProperty("file.separator"); + + /** + * write to the directory passed with -d option + */ + public void write(File destdir) throws IOException { + File outfile; + if (destdir == null) { + int startofname = myClassName.lastIndexOf("/"); + if (startofname != -1) { + myClassName = myClassName.substring(startofname + 1); + } + outfile = new File(myClassName + fileExtension); + } else { + env.traceln("writing -d " + destdir.getPath()); + if (fileSeparator == 0) { + fileSeparator = System.getProperty("file.separator").charAt(0); + } + if (fileSeparator != '/') { + myClassName = myClassName.replace('/', fileSeparator); + } + outfile = new File(destdir, myClassName + fileExtension); + File outdir = new File(outfile.getParent()); + if (!outdir.exists() && !outdir.mkdirs()) { + env.error("cannot.write", outdir.getPath()); + return; + } + } + + DataOutputStream dos = new DataOutputStream( + new BufferedOutputStream(new FileOutputStream(outfile))); + cdos.setDataOutputStream(dos); + try { + write(cdos); + } finally { + dos.close(); + } + } // end write() + + public void setByteLimit(int bytelimit) { + cdos.enable(); + cdos.setLimit(bytelimit); + } + + /** + * CDOutputStream + * + * This is a wrapper for DataOutputStream, used for debugging purposes. it allows + * writing the byte-stream of a class up to a given byte number. + */ + static private class CDOutputStream implements CheckedDataOutputStream { + + private int bytelimit; + private DataOutputStream dos; + public boolean enabled = false; + + public CDOutputStream() { + dos = null; + } + + public CDOutputStream(OutputStream out) { + setOutputStream(out); + } + + public final void setOutputStream(OutputStream out) { + dos = new DataOutputStream(out); + } + + public void setDataOutputStream(DataOutputStream dos) { + this.dos = dos; + } + + public void setLimit(int lim) { + bytelimit = lim; + } + + public void enable() { + enabled = true; + } + + private synchronized void check(String loc) throws IOException { + if (enabled && dos.size() >= bytelimit) { + throw new IOException(loc); + } + } + + @Override + public synchronized void write(int b) throws IOException { + dos.write(b); + check("Writing byte: " + b); + } + + @Override + public synchronized void write(byte b[], int off, int len) throws IOException { + dos.write(b, off, len); + check("Writing byte-array: " + b); + } + + @Override + public final void writeBoolean(boolean v) throws IOException { + dos.writeBoolean(v); + check("Writing writeBoolean: " + (v ? "true" : "false")); + } + + @Override + public final void writeByte(int v) throws IOException { + dos.writeByte(v); + check("Writing writeByte: " + v); + } + + @Override + public void writeShort(int v) throws IOException { + dos.writeShort(v); + check("Writing writeShort: " + v); + } + + @Override + public void writeChar(int v) throws IOException { + dos.writeChar(v); + check("Writing writeChar: " + v); + } + + @Override + public void writeInt(int v) throws IOException { + dos.writeInt(v); + check("Writing writeInt: " + v); + } + + @Override + public void writeLong(long v) throws IOException { + dos.writeLong(v); + check("Writing writeLong: " + v); + } + + @Override + public void writeFloat(float v) throws IOException { + dos.writeFloat(v); + check("Writing writeFloat: " + v); + } + + @Override + public void writeDouble(double v) throws IOException { + dos.writeDouble(v); + check("Writing writeDouble: " + v); + } + + @Override + public void writeBytes(String s) throws IOException { + dos.writeBytes(s); + check("Writing writeBytes: " + s); + } + + @Override + public void writeChars(String s) throws IOException { + dos.writeChars(s); + check("Writing writeChars: " + s); + } + + @Override + public void writeUTF(String s) throws IOException { + dos.writeUTF(s); + check("Writing writeUTF: " + s); + } + /* + public int writeUTF(String str, DataOutput out) throws IOException{ + int ret = dos.writeUTF(str, out); + check("Writing writeUTF: " + str); + return ret; + } + * */ + + } + +}// end class ClassData + diff --git a/src/org/openjdk/asmtools/jasm/CodeAttr.java b/src/org/openjdk/asmtools/jasm/CodeAttr.java new file mode 100644 index 0000000..c306b76 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/CodeAttr.java @@ -0,0 +1,522 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.OpcodeTables.Opcode; +import static org.openjdk.asmtools.jasm.RuntimeConstants.SPLIT_VERIFIER_CFV; +import org.openjdk.asmtools.jasm.Tables.AttrTag; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +class CodeAttr extends AttrData { + + /*-------------------------------------------------------- */ + /* CodeAttr inner classes */ + static public class Local extends Argument { + + String name; + boolean defd = false, refd = false; + + public Local(String name) { + this.name = name; + } + } + + /** + * + */ + static public class Label extends Local { + + public Label(String name) { + super(name); + } + } + + /** + * + */ + class LocVarData extends Local implements Data { + + // arg means slot + + short start_pc, length; + ConstantPool.ConstCell name_cpx, sig_cpx; + + public LocVarData(String name) { + super(name); + } + + @Override + public int getLength() { + return 10; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(start_pc); + out.writeShort(length); + out.writeShort(name_cpx.arg); + out.writeShort(sig_cpx.arg); + out.writeShort(arg); + } + } + + /** + * + */ + class LineNumData implements Data { + + int start_pc, line_number; + + public LineNumData(int start_pc, int line_number) { + this.start_pc = start_pc; + this.line_number = line_number; + } + + @Override + public int getLength() { + return 4; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(start_pc); + out.writeShort(line_number); + } + } + + /** + * + */ + class Trap extends Local { + + int start_pc = Argument.NotSet, end_pc = Argument.NotSet; + int pos; + + Trap(int pos, String name) { + super(name); + this.pos = pos; + } + } + + /** + * + */ + class TrapData implements Data { + + int pos; + Trap trap; + int handler_pc; + Argument catchType; + + public TrapData(int pos, Trap trap, int handler_pc, Argument catchType) { + this.pos = pos; + this.trap = trap; + this.handler_pc = handler_pc; + this.catchType = catchType; + } + + @Override + public int getLength() { + return 8; // add the length of number of elements + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(trap.start_pc); + out.writeShort(trap.end_pc); + out.writeShort(handler_pc); + if (catchType.isSet()) { + out.writeShort(catchType.arg); + } else { + out.writeShort(0); + } + } + } // end TrapData + + + /*-------------------------------------------------------- */ + /* CodeAttr Fields */ + protected MethodData mtd; + protected ClassData cls; + protected Environment env; + protected Argument max_stack, max_locals; + protected Instr zeroInstr, lastInstr; + protected int cur_pc = 0; + protected DataVector trap_table + = new DataVector<>(0); // TrapData + protected DataVectorAttr lin_num_tb; // LineNumData + protected int lastln = 0; + protected DataVectorAttr loc_var_tb; // LocVarData + protected DataVector> attrs; + // protected iVector slots; + protected ArrayList slots; + protected HashMap locvarsHash; + protected HashMap labelsHash; + protected HashMap trapsHash; + protected StackMapData curMapEntry = null; + protected DataVectorAttr stackMap; + /*-------------------------------------------------------- */ + + public CodeAttr(MethodData mtd, int pos, int paramcnt, Argument max_stack, Argument max_locals) { + super(mtd.cls, AttrTag.ATT_Code.parsekey()); + this.mtd = mtd; + this.cls = mtd.cls; + this.env = cls.env; + this.max_stack = max_stack; + this.max_locals = max_locals; + lastInstr = zeroInstr = new Instr(); + trap_table = new DataVector<>(0); // TrapData + attrs = new DataVector<>(); + if (env.debugInfoFlag) { + lin_num_tb = new DataVectorAttr<>(cls, AttrTag.ATT_LineNumberTable.parsekey()); + attrs.add(lin_num_tb); + } +// slots = new iVector(paramcnt); + slots = new ArrayList<>(paramcnt); + for (int k = 0; k < paramcnt; k++) { +// slots.setElement(1, k); + slots.add(k, 1); + } + } + + void endCode() { + checkTraps(); + checkLocVars(); + checkLabels(); + } + + /* -------------------------------------- Traps */ + Trap trapDecl(int pos, String name) { + Trap local; + if (trapsHash == null) { + trapsHash = new HashMap<>(10); + local = null; + } else { + local = trapsHash.get(name); + } + if (local == null) { + local = new Trap(pos, name); + trapsHash.put(name, local); + } + return local; + } + + void beginTrap(int pos, String name) { + Trap trap = trapDecl(pos, name); + if (trap.start_pc != Argument.NotSet) { + env.error("trap.tryredecl", name); + return; + } + trap.start_pc = cur_pc; + } + + void endTrap(int pos, String name) { + Trap trap = trapDecl(pos, name); + if (trap.end_pc != Argument.NotSet) { + env.error("trap.endtryredecl", name); + return; + } + trap.end_pc = cur_pc; + } + + void trapHandler(int pos, String name, Argument type) { + Trap trap = trapDecl(pos, name); + trap.refd = true; + TrapData trapData = new TrapData(pos, trap, cur_pc, type); + trap_table.addElement(trapData); + } + + void checkTraps() { + if (trapsHash == null) { + return; + } + for (Trap trap : trapsHash.values()) { + if (!trap.refd) { + env.error(trap.pos, "warn.trap.notref", trap.name); + } + } + + for (TrapData trapData : trap_table) { + Trap trapLabel = trapData.trap; + if (trapLabel.start_pc == Argument.NotSet) { + env.error(trapData.pos, "trap.notry", trapLabel.name); + } + if (trapLabel.end_pc == Argument.NotSet) { + env.error(trapData.pos, "trap.noendtry", trapLabel.name); + } + } + } + + /* -------------------------------------- Labels */ + Label labelDecl(String name) { + Label local; + if (labelsHash == null) { + labelsHash = new HashMap<>(10); + local = null; + } else { + local = labelsHash.get(name); + } + if (local == null) { + local = new Label(name); + labelsHash.put(name, local); + } + return local; + } + + public Label LabelDef(int pos, String name) { + Label label = labelDecl(name); + if (label.defd) { + env.error(pos, "label.redecl", name); + return null; + } + label.defd = true; + label.arg = cur_pc; + return label; + } + + public Label LabelRef(String name) { + Label label = labelDecl(name); + label.refd = true; + return label; + } + + void checkLabels() { + if (labelsHash == null) { + return; + } + + for (Label local : labelsHash.values()) { + // check that every label is defined + if (!local.defd) { + env.error("label.undecl", local.name); + } + } + } + + /* -------------------------------------- Variables */ + LocVarData locvarDecl(String name) { + LocVarData local; + if (locvarsHash == null) { + locvarsHash = new HashMap<>(10); + local = null; + } else { + local = locvarsHash.get(name); + } + if (local == null) { + local = new LocVarData(name); + locvarsHash.put(name, local); + } + return local; + } + + public void LocVarDataDef(int slot) { +// slots.setElement(1, slot); + slots.set(slot, 1); + if ((max_locals != null) && (max_locals.arg < slots.size())) { + +// if ((max_locals != null) && (max_locals.arg < slots.length)) { + env.error("warn.illslot", new Integer(slot).toString()); + } + } + + public void LocVarDataDef(String name, ConstantPool.ConstCell type) { + LocVarData locvar = locvarDecl(name); + if (locvar.defd) { + env.error("locvar.redecl", name); + return; + } + locvar.defd = true; + locvar.start_pc = (short) cur_pc; + locvar.name_cpx = cls.pool.FindCellAsciz(name); + locvar.sig_cpx = type; + int k; + findSlot: + { + /* + for (k = 0; k < slots.length; k++) { + if (slots.data[k] == 0) { + break findSlot; + } + } + k = slots.length; + * + */ + + for (k = 0; k < slots.size(); k++) { + if (slots.get(k) == 0) { + break findSlot; + } + } + k = slots.size(); + } + LocVarDataDef(k); + locvar.arg = k; + if (loc_var_tb == null) { + loc_var_tb = new DataVectorAttr<>(cls, AttrTag.ATT_LocalVariableTable.parsekey()); + attrs.add(loc_var_tb); + } + loc_var_tb.add(locvar); + } + + public Argument LocVarDataRef(String name) { + LocVarData locvar = locvarDecl(name); + if (!locvar.defd) { + env.error("locvar.undecl", name); + locvar.defd = true; // to avoid multiple error messages + } + locvar.refd = true; + return locvar; + } + + public void LocVarDataEnd(int slot) { +// slots.data[slot] = 0; + slots.set(slot, 0); + } + + public void LocVarDataEnd(String name) { + LocVarData locvar = locvarsHash.get(name); + if (locvar == null) { + env.error("locvar.undecl", name); + return; + } else if (!locvar.defd) { + env.error("locvar.undecl", name); + return; + } + locvar.length = (short) (cur_pc - locvar.start_pc); +// slots.data[locvar.arg] = 0; + + slots.set(locvar.arg, 0); +// locvarsHash.put(name, null); unfortunately, prohibited + locvarsHash.put(name, new LocVarData(name)); + } + + void checkLocVars() { + if (locvarsHash == null) { + return; + } + for (LocVarData locvar : locvarsHash.values()) { + if (!locvar.defd) { + continue; + } // this is false locvar + // set end of scope, if not set + if (slots.get(locvar.arg) == 1) { +// if (slots.data[locvar.arg] == 1) { + locvar.length = (short) (cur_pc - locvar.start_pc); +// slots.data[locvar.arg] = 0; + slots.set(locvar.arg, 0); + } + } + } + + /* -------------------------------------- StackMap */ + public StackMapData getStackMap() { + if (curMapEntry == null) { + curMapEntry = new StackMapData(env); + if (cls.major_version >= SPLIT_VERIFIER_CFV) { + curMapEntry.setIsStackMapTable(true); + } + } + return curMapEntry; + } + + /*====================================================== Instr */ + void addInstr(int mnenoc_pos, Opcode opcode, Argument arg, Object arg2) { + Instr newInstr = new Instr(cur_pc, cls.env.pos, opcode, arg, arg2); + lastInstr.next = newInstr; + lastInstr = newInstr; + int len = opcode.length(); + switch (opcode) { + case opc_tableswitch: + len = ((SwitchTable) arg2).recalcTableSwitch(cur_pc); + break; + case opc_lookupswitch: + len = ((SwitchTable) arg2).calcLookupSwitch(cur_pc); + break; + case opc_ldc: + ((ConstantPool.ConstCell) arg).setRank(0); + break; + default: + if (arg instanceof ConstantPool.ConstCell) { + ((ConstantPool.ConstCell) arg).setRank(1); + } + } + if (env.debugInfoFlag) { + int ln = env.lineNumber(mnenoc_pos); + if (ln != lastln) { // only one entry in lin_num_tb per line + lin_num_tb.add(new LineNumData(cur_pc, ln)); + lastln = ln; + } + } + if (curMapEntry != null) { + curMapEntry.pc = cur_pc; + StackMapData prevStackFrame = null; + if (stackMap == null) { + if (cls.major_version >= SPLIT_VERIFIER_CFV) { + stackMap = new DataVectorAttr<>(cls, AttrTag.ATT_StackMapTable.parsekey()); + } else { + stackMap = new DataVectorAttr<>(cls, AttrTag.ATT_StackMap.parsekey()); + } + attrs.add(stackMap); + } else if (stackMap.size() > 0) { + prevStackFrame = stackMap.get(stackMap.size() - 1); + } + curMapEntry.setOffset(prevStackFrame); + stackMap.add(curMapEntry); + curMapEntry = null; + } + + cur_pc += len; + } + + /*====================================================== Attr interface */ + // subclasses must redefine this + @Override + public int attrLength() { + return 2 + 2 + 4 // for max_stack, max_locals, and cur_pc + + cur_pc // + 2+trap_table.size()*8 + + trap_table.getLength() + attrs.getLength(); + + } + + @Override + public void write(CheckedDataOutputStream out) + throws IOException, Parser.CompilerError { + int mxstck = (max_stack != null) ? max_stack.arg : 0; +// int mxloc = (max_locals != null) ? max_locals.arg : slots.length; + int mxloc = (max_locals != null) ? max_locals.arg : slots.size(); + super.write(out); // attr name, attr len + out.writeShort(mxstck); + out.writeShort(mxloc); + out.writeInt(cur_pc); + for (Instr instr = zeroInstr.next; instr != null; instr = instr.next) { + instr.write(out, env); + } + + trap_table.write(out); + + attrs.write(out); + } +} // end CodeAttr + diff --git a/src/org/openjdk/asmtools/jasm/ConstantPool.java b/src/org/openjdk/asmtools/jasm/ConstantPool.java new file mode 100644 index 0000000..95d3cbe --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ConstantPool.java @@ -0,0 +1,1198 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.Tables.ConstType; +import java.io.IOException; +import java.util.*; + +/** + * ConstantPool + * + * ConstantPool is the class responsible for maintaining constants for a given class file. + * + */ +public class ConstantPool implements Iterable { + + + /*-------------------------------------------------------- */ + /* ConstantPool Inner Classes */ + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue { + + protected ConstType tag; + protected boolean isSet = false; + private boolean vizited = false; + + public ConstValue(ConstType tag) { + this.tag = tag; + } + + public int size() { + return 1; + } + + public boolean hasValue() { + return isSet; + } + + /** + * Compute the hash-code, based on the value of the native (_hashCode()) hashcode. + */ + @Override + public int hashCode() { + if (vizited) { + throw new Parser.CompilerError("CV hash:" + this); + } + vizited = true; + int res = _hashCode() + tag.value() * 1023; + vizited = false; + return res; + } + + // sub-classes override this. + // this is the default for getting a hash code. + protected int _hashCode() { + return 37; + } + + /** + * Compares this object to the specified object. + * + * Sub-classes must override this + * + * @param obj the object to compare with + * @return true if the objects are the same; false otherwise. + */ + @Override + public boolean equals(Object obj) { + return false; + } + + @Override + public String toString() { + String tagstr = tag.printval(); + String retval = ""; + if (tagstr == null) { + return "BOGUS_TAG:" + tag; + } + + String valueStr = _toString(); + if (valueStr != null) { + retval = "<" + tagstr + " " + valueStr + ">"; + } else { + retval = "<" + tagstr + ">"; + } + return retval; + } + + protected String _toString() { + return ""; + } + + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(tag.value()); + } + } // end ConstValue + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_Zero extends ConstValue { + + public ConstValue_Zero() { + super(ConstType.CONSTANT_ZERO); + isSet = false; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + throw new Parser.CompilerError("Trying to write Constant 0."); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_String extends ConstValue { + + String value; + + public ConstValue_String(String value) { + super(ConstType.CONSTANT_UTF8); + this.value = value; + isSet = (value != null); + } + + @Override + protected String _toString() { + return value; + } + + @Override + protected int _hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_String)) { + return false; + } + ConstValue_String dobj = (ConstValue_String) obj; + if (tag != dobj.tag) { + return false; + } + return value.equals(dobj.value); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + out.writeUTF(value); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_Integer extends ConstValue { + + Integer value; + + public ConstValue_Integer(ConstType tag, Integer value) { + super(tag); + this.value = value; + isSet = (value != null); + } + + @Override + protected String _toString() { + return value.toString(); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_Integer)) { + return false; + } + ConstValue_Integer dobj = (ConstValue_Integer) obj; + if (tag != dobj.tag) { + return false; + } + return value.equals(dobj.value); + } + + @Override + protected int _hashCode() { + return value.hashCode(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + out.writeInt(value.intValue()); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_Long extends ConstValue { + + Long value; + + public ConstValue_Long(ConstType tag, Long value) { + super(tag); + this.value = value; + isSet = (value != null); + } + + @Override + public int size() { + return 2; + } + + @Override + protected String _toString() { + return value.toString(); + } + +// @Override +// public Object value() { return value; } + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_Long)) { + return false; + } + ConstValue_Long dobj = (ConstValue_Long) obj; + if (tag != dobj.tag) { + return false; + } + return value.equals(dobj.value); + } + + @Override + protected int _hashCode() { + return value.hashCode(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + out.writeLong(value.longValue()); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_Cell extends ConstValue { + + ConstCell cell; + + public ConstValue_Cell(ConstType tag, ConstCell cell) { + super(tag); + this.cell = cell; + isSet = (cell != null); + } + + @Override + protected String _toString() { + return cell.toString(); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_Cell)) { + return false; + } + ConstValue_Cell dobj = (ConstValue_Cell) obj; + if (tag != dobj.tag) { + return false; + } + return cell.equals(dobj.cell); + } + + @Override + protected int _hashCode() { + return cell.hashCode(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + cell.write(out); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_Pair extends ConstValue { + + ConstCell left, right; + + public ConstValue_Pair(ConstType tag, ConstCell left, ConstCell right) { + super(tag); + this.left = left; + this.right = right; + isSet = (left != null && right != null); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_Pair)) { + return false; + } + ConstValue_Pair dobj = (ConstValue_Pair) obj; + if (tag != dobj.tag) { + return false; + } + return (dobj.left == left) && (dobj.right == right); + } + + @Override + public String toString() { + return super.toString() + "{" + left + "," + right + "}"; + } + + @Override + protected int _hashCode() { + return left.hashCode() * right.hashCode(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + if (tag == ConstType.CONSTANT_METHODHANDLE) { + out.writeByte(left.arg); // write subtag value + } else { + out.writeShort(left.arg); + } + out.writeShort(right.arg); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_IndyPair extends ConstValue { + + BootstrapMethodData bsmData; + ConstantPool.ConstCell napeCell; + + public ConstValue_IndyPair(BootstrapMethodData bsmdata, ConstCell napeCell) { + super(ConstType.CONSTANT_INVOKEDYNAMIC); + this.bsmData = bsmdata; + this.napeCell = napeCell; + isSet = (bsmdata != null && napeCell != null); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_IndyPair)) { + return false; + } + + ConstValue_IndyPair iobj = (ConstValue_IndyPair) obj; + return (iobj.bsmData == bsmData) && (iobj.napeCell == napeCell); + } + + @Override + public String toString() { + return super.toString() + "{" + bsmData + "," + napeCell + "}"; + } + + @Override + protected int _hashCode() { + if (bsmData.isPlaceholder()) { + return napeCell.hashCode(); + } + return bsmData.hashCode() * napeCell.hashCode(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + out.writeShort(bsmData.arg); + out.writeShort(napeCell.arg); + } + } + + /*-------------------------------------------------------- */ + /* ConstantPool Inner Classes */ + /** + * ConstantCell + * + * ConstantCell is a type of data that can be in a constant pool. + */ + static public class ConstCell extends Argument implements Data { + + ConstValue ref; + int rank = 2; // 0 - highest - ref from ldc, 1 - any ref, 2 - no ref + + ConstCell(int arg, ConstValue ref) { + this.arg = arg; + this.ref = ref; + } + + ConstCell(ConstValue ref) { + this(NotSet, ref); + } + + ConstCell(int arg) { + this(arg, null); + } + + @Override + public int getLength() { + return 2; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(arg); + } + + public void setRank(int rank) { + this.rank = rank; + } + + @Override + public int hashCode() { + if (arg == NotSet) { + if (ref != null) { + return ref.hashCode(); + } else { + throw new Parser.CompilerError("Can't generate Hash Code, Null ConstCell Reference."); + } + } + return arg; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + return obj == this; + } + + public boolean isUnset() { + return (arg == NotSet) && (ref == null); + } + + @Override + public String toString() { + return "#" + arg + "=" + ref; + } + } + + /** + * CPVisitor + * + * CPVisitor base class defining a visitor for decoding constants. + */ + public static class CPTagVisitor implements Constants { + + public CPTagVisitor() { + } + + public final R visit(ConstType tag) { + R retVal = null; + switch (tag) { + case CONSTANT_UTF8: + retVal = visitUTF8(tag); + break; + case CONSTANT_INTEGER: + retVal = visitInteger(tag); + break; + case CONSTANT_FLOAT: + retVal = visitFloat(tag); + break; + case CONSTANT_DOUBLE: + retVal = visitDouble(tag); + break; + case CONSTANT_LONG: + retVal = visitLong(tag); + break; + case CONSTANT_METHODTYPE: + retVal = visitMethodtype(tag); + break; + case CONSTANT_STRING: + retVal = visitString(tag); + break; + case CONSTANT_CLASS: + retVal = visitClass(tag); + break; + case CONSTANT_METHOD: + retVal = visitMethod(tag); + break; + case CONSTANT_FIELD: + retVal = visitField(tag); + break; + case CONSTANT_INTERFACEMETHOD: + retVal = visitInterfacemethod(tag); + break; + case CONSTANT_NAMEANDTYPE: + retVal = visitNameandtype(tag); + break; + case CONSTANT_METHODHANDLE: + retVal = visitMethodhandle(tag); + break; + case CONSTANT_INVOKEDYNAMIC: + retVal = visitInvokedynamic(tag); + break; +// case CONSTANT_INVOKEDYNAMIC_TRANS: +// retVal = visitInvokedynamicTrans(tag); +// break; + default: + visitDefault(tag); + } + return retVal; + } + + public R visitUTF8(ConstType tag) { + return null; + } + + ; + public R visitInteger(ConstType tag) { + return null; + } + + ; + public R visitFloat(ConstType tag) { + return null; + } + + ; + public R visitDouble(ConstType tag) { + return null; + } + + ; + public R visitLong(ConstType tag) { + return null; + } + + ; + public R visitMethodtype(ConstType tag) { + return null; + } + + ; + public R visitString(ConstType tag) { + return null; + } + + ; + public R visitClass(ConstType tag) { + return null; + } + + ; + public R visitMethod(ConstType tag) { + return null; + } + + ; + public R visitField(ConstType tag) { + return null; + } + + ; + public R visitInterfacemethod(ConstType tag) { + return null; + } + + ; + public R visitNameandtype(ConstType tag) { + return null; + } + + ; + public R visitMethodhandle(ConstType tag) { + return null; + } + + ; + public R visitInvokedynamic(ConstType tag) { + return null; + } + + ; +// public R visitInvokedynamicTrans() { return null; }; + public void visitDefault(ConstType tag) { + } + ; + + } + + /** + * CPVisitor + * + * CPVisitor base class defining a visitor for decoding constants. + */ + public static class CPVisitor implements Constants { + + public CPVisitor() { + } + + public final R visit(ConstValue val) { + R retVal = null; + ConstType tag = val.tag; + switch (tag) { + case CONSTANT_UTF8: + retVal = visitUTF8((ConstValue_String) val); + break; + case CONSTANT_INTEGER: + retVal = visitInteger((ConstValue_Integer) val); + break; + case CONSTANT_FLOAT: + retVal = visitFloat((ConstValue_Integer) val); + break; + case CONSTANT_DOUBLE: + retVal = visitDouble((ConstValue_Long) val); + break; + case CONSTANT_LONG: + retVal = visitLong((ConstValue_Long) val); + break; + case CONSTANT_METHODTYPE: + retVal = visitMethodtype((ConstValue_Cell) val); + break; + case CONSTANT_STRING: + retVal = visitString((ConstValue_Cell) val); + break; + case CONSTANT_CLASS: + retVal = visitClass((ConstValue_Cell) val); + break; + case CONSTANT_METHOD: + retVal = visitMethod((ConstValue_Pair) val); + break; + case CONSTANT_FIELD: + retVal = visitField((ConstValue_Pair) val); + break; + case CONSTANT_INTERFACEMETHOD: + retVal = visitInterfacemethod((ConstValue_Pair) val); + break; + case CONSTANT_NAMEANDTYPE: + retVal = visitNameandtype((ConstValue_Pair) val); + break; + case CONSTANT_METHODHANDLE: + retVal = visitMethodhandle((ConstValue_Pair) val); + break; + case CONSTANT_INVOKEDYNAMIC: + retVal = visitInvokedynamic((ConstValue_IndyPair) val); + break; +// case CONSTANT_INVOKEDYNAMIC_TRANS: +// retVal = visitInvokedynamicTrans((ConstValue_Pair) val); +// break; + default: + visitDefault(tag); + } + return retVal; + } + + public R visitUTF8(ConstValue_String p) { + return null; + } + + ; + public R visitInteger(ConstValue_Integer p) { + return null; + } + + ; + public R visitFloat(ConstValue_Integer p) { + return null; + } + + ; + public R visitDouble(ConstValue_Long p) { + return null; + } + + ; + public R visitLong(ConstValue_Long p) { + return null; + } + + ; + public R visitMethodtype(ConstValue_Cell p) { + return null; + } + + ; + public R visitString(ConstValue_Cell p) { + return null; + } + + ; + public R visitClass(ConstValue_Cell p) { + return null; + } + + ; + public R visitMethod(ConstValue_Pair p) { + return null; + } + + ; + public R visitField(ConstValue_Pair p) { + return null; + } + + ; + public R visitInterfacemethod(ConstValue_Pair p) { + return null; + } + + ; + public R visitNameandtype(ConstValue_Pair p) { + return null; + } + + ; + public R visitMethodhandle(ConstValue_Pair p) { + return null; + } + + ; + public R visitInvokedynamic(ConstValue_IndyPair p) { + return null; + } + + ; +// public R visitInvokedynamicTrans(ConstValue_Pair p) { return null; }; + public void visitDefault(ConstType tag) { + } + ; + + } + + + + /*-------------------------------------------------------- */ + /* Constant Pool Fields */ + + private ArrayList pool = new ArrayList<>(20); + + private final ConstValue ConstValue0 + = new ConstValue_String(""); +// private final ConstValue ConstValue0 = +// new ConstValue(CONSTANT_UTF8, ""); + private final ConstCell nullConst + = new ConstCell(null); + private final ConstCell constant_0 + = new ConstCell(new ConstValue_Zero()); +// private final ConstCell constant_0 = +// new ConstCell(new ConstValue(CONSTANT_ZERO, null)); + + // For hashing by value + Hashtable cpoolHashByValue + = new Hashtable<>(40); + + public Environment env; + + private static boolean debugCP = false; + + /*-------------------------------------------------------- */ + /** + * main constructor + * + * @param env The error reporting environment + */ + public ConstantPool(Environment env) { + this.env = env; + pool.add(constant_0); + + } + + public void debugStr(String s) { + if (debugCP) { + env.traceln(s); + } + } + + @Override + public Iterator iterator() { + return pool.iterator(); + } + + + /* + * Fix Refs in constant pool. + * + * This is used when scanning JASM files produced from JDis with the verbose + * option (eg. where the constant pool is declared in the jasm itself). In + * this scenario, we need two passes - the first pass to scan the entries + * (which creates constant references with indexes, but no reference values); + * and the second pass, which links references to existing constants. + * + */ + public void fixRefsInPool() { + // used to fix CP refs when a constant pool is constructed by refs alone. + env.traceln("Fixing CP for explicit Constant Entries."); + int i = 0; + // simply iterate through the pool. + for (ConstCell item : pool) { + i += 1; + // first item is always null + if (item == null) { + continue; + } + + checkAndFixCPRef(i, item); + } + } + + protected void CheckGlobals() { + env.traceln("Checking Globals"); + // + // This fn will put empty UTF8 string entries on any unset + // CP entries - before the last CP entry. + // + for (int cpx = 1; cpx < pool.size(); cpx++) { + ConstCell cell = pool.get(cpx); + if (cell == nullConst) { // gap + cell = new ConstCell(cpx, ConstValue0); + pool.set(cpx, cell); + } + ConstValue cval = cell.ref; + if ((cval == null) || !cval.hasValue()) { + String name = (new Integer(cpx)).toString(); + env.error("const.undecl", name); + } + } + } + + /* + * Helper function for "fixRefsInPool" + * + * Does recursive checking of references, + * using a locally-defined visitor. + */ + private void checkAndFixCPRef(int i, ConstCell item) { + ConstValue cv = item.ref; + if (cv != null) { + fixCPVstr.visit(cv); + } + } + + private CPVisitor fixCPVstr = new CPVisitor() { + @Override + public Void visitUTF8(ConstValue_String p) { + return null; + } + + ; + @Override + public Void visitInteger(ConstValue_Integer p) { + return null; + } + + ; + @Override + public Void visitFloat(ConstValue_Integer p) { + return null; + } + + ; + @Override + public Void visitDouble(ConstValue_Long p) { + return null; + } + + ; + @Override + public Void visitLong(ConstValue_Long p) { + return null; + } + + ; + @Override + public Void visitMethodtype(ConstValue_Cell p) { + handleClassRef(p); + return null; + } + + ; + @Override + public Void visitString(ConstValue_Cell p) { + handleClassRef(p); + return null; + } + + ; + @Override + public Void visitClass(ConstValue_Cell p) { + handleClassRef(p); + return null; + } + + ; + @Override + public Void visitMethod(ConstValue_Pair p) { + handleMemberRef(p); + return null; + } + + ; + @Override + public Void visitField(ConstValue_Pair p) { + handleMemberRef(p); + return null; + } + + ; + @Override + public Void visitInterfacemethod(ConstValue_Pair p) { + handleMemberRef(p); + return null; + } + + ; + @Override + public Void visitNameandtype(ConstValue_Pair p) { + handleMemberRef(p); + return null; + } + + ; + @Override + public Void visitMethodhandle(ConstValue_Pair p) { + handleMemberRef(p); + return null; + } + + ; + @Override + public Void visitInvokedynamic(ConstValue_IndyPair p) { + return null; + } + + ; + + public void handleClassRef(ConstValue_Cell cv) { + ConstCell clref = cv.cell; + if (clref.ref == null) { + ConstCell refval = cpool_get(clref.arg); + if (refval != null) { + checkAndFixCPRef(clref.arg, refval); + clref.ref = refval.ref; + } else { + clref.ref = null; + } + // env.traceln("FIXED ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value); + } + } + + public void handleMemberRef(ConstValue_Pair cv) { + // env.traceln("ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value); + ConstCell clref = cv.left; + ConstCell typref = cv.right; + if (clref.ref == null) { + ConstCell refval = cpool_get(clref.arg); + if (refval != null) { + checkAndFixCPRef(clref.arg, refval); + clref.ref = refval.ref; + } else { + clref.ref = null; + } + // env.traceln("FIXED ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value); + } + if (typref.ref == null) { + ConstCell refval = cpool_get(typref.arg); + if (refval != null) { + checkAndFixCPRef(typref.arg, refval); + typref.ref = refval.ref; + } else { + typref.ref = null; + } + // env.traceln("FIXED ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value); + } + } + + }; + + /* + * Help debug Constant Pools + */ + public void printPool() { + int i = 0; + for (ConstCell item : pool) { + env.traceln("^^^^^^^^^^^^^ const #" + i + ": " + item); + i += 1; + } + } + + private ConstCell cpool_get(int cpx) { + if (cpx >= pool.size()) { + return null; + } + return pool.get(cpx); + } + + private void cpool_set(int cpx, ConstCell cell, int sz) { + debugStr("cpool_set1: " + cpx + " " + cell); + debugStr("param_size: " + sz); + debugStr("pool_size: " + pool.size()); + cell.arg = cpx; + if (cpx + sz >= pool.size()) { + debugStr("calling ensureCapacity( " + (cpx + sz + 1) + ")"); + int low = pool.size(); + int high = cpx + sz; + for (int i = 0; i < high - low; i++) { + pool.add(nullConst); + } + } + pool.set(cpx, cell); + if (sz == 2) { + pool.set(cpx + 1, new ConstCell(cpx + 1, ConstValue0)); + } + debugStr(" cpool_set2: " + cpx + " " + cell); + } + + protected ConstCell uncheckedGetCell(int cpx) { // by index + return pool.get(cpx); + } + + public ConstCell getCell(int cpx) { // by index + ConstCell cell = cpool_get(cpx); + if (cell != null) { + return cell; + } + cell = new ConstCell(cpx, null); + return cell; + } + + public void setCell(int cpx, ConstCell cell) { + ConstValue value = cell.ref; + if (value == null) { + throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullvalset")); + } + int sz = value.size(); + + if (cpx == 0) { + // It is correct to warn about redeclaring constant zero, + // since this value is never written out to a class file. + env.error("warn.const0.redecl"); + } else { + if ((cpool_get(cpx) != null) || ((sz == 2) && (cpool_get(cpx + 1) != null))) { + String name = "#" + cpx; + env.error("const.redecl", name); + return; + } + if (cell.isSet() && (cell.arg != cpx)) { + env.traceln("setCell: new ConstCell"); + cell = new ConstCell(value); + } + } + cpool_set(cpx, cell, sz); + } + + protected void NumberizePool() { + env.traceln("NumberizePool"); + Enumeration e; + for (int rank = 0; rank < 3; rank++) { + for (ConstCell cell : cpoolHashByValue.values()) { + if (cell.rank != rank) { + continue; + } + if (cell.isSet()) { + continue; + } + ConstValue value = cell.ref; + if (value == null) { + throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullvalhash")); + } + int sz = value.size(), cpx; +find: + for (cpx = 1; cpx < pool.size(); cpx++) { + if ((pool.get(cpx) == nullConst) && ((sz == 1) || (pool.get(cpx + 1) == nullConst))) { + break find; + } + } + cpool_set(cpx, cell, sz); + } + } + + ConstCell firstCell = cpool_get(0); + firstCell.arg = 0; + } + + public ConstCell FindCell(ConstValue ref) { + if (ref == null) { + throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullval")); + } + ConstCell pconst = null; + try { + pconst = cpoolHashByValue.get(ref); + } catch (Parser.CompilerError e) { + throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullvalhash")); + } + // If we fund a cached ConstValue + if (pconst != null) { + ConstValue value = pconst.ref; + if (!value.equals(ref)) { + throw new Parser.CompilerError(env.errorStr("comperr.val.noteq")); + } + return pconst; + } + // If we didn't find a cached ConstValue + // Add it to the cache + pconst = new ConstCell(ref); + cpoolHashByValue.put(ref, pconst); + return pconst; + } + + public ConstCell FindCell(ConstType tag, String value) { + return FindCell(new ConstValue_String(value)); + } + + public ConstCell FindCell(ConstType tag, Integer value) { + return FindCell(new ConstValue_Integer(tag, value)); + } + + public ConstCell FindCell(ConstType tag, Long value) { + return FindCell(new ConstValue_Long(tag, value)); + } + + public ConstCell FindCell(ConstType tag, ConstCell value) { + return FindCell(new ConstValue_Cell(tag, value)); + } + + public ConstCell FindCell(ConstType tag, ConstCell left, ConstCell right) { + return FindCell(new ConstValue_Pair(tag, left, right)); + } + + public ConstCell FindCellAsciz(String str) { + return FindCell(ConstType.CONSTANT_UTF8, str); + } + + public ConstCell FindCellClassByName(String name) { + return FindCell(ConstType.CONSTANT_CLASS, FindCellAsciz(name)); + } + + public void write(CheckedDataOutputStream out) throws IOException { + // Write the constant pool + int length = pool.size(); + out.writeShort(length); + int i; + env.traceln("wr.pool:size=" + length); + for (i = 1; i < length;) { + ConstCell cell = pool.get(i); + ConstValue value = cell.ref; + if (cell.arg != i) { + throw new Parser.CompilerError(env.errorStr("comperr.constcell.invarg", Integer.toString(i), cell.arg)); + } +// System.out.print(" WRITE CONST[" + i + "]:"); + value.write(out); + i += value.size(); + } + } + + public static void setEnableDebug(boolean newState) { + debugCP = newState; + } +} diff --git a/src/org/openjdk/asmtools/jasm/Constants.java b/src/org/openjdk/asmtools/jasm/Constants.java new file mode 100644 index 0000000..ed60570 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Constants.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +/** + * This interface defines constant that are used throughout the compiler. It inherits from + * RuntimeConstants, which is an autogenerated class that contains constants defined in + * the interpreter. + */ +public interface Constants extends RuntimeConstants { + + /** + * Default version of class file + */ + public static final short DEFAULT_MAJOR_VERSION = 45; + public static final short DEFAULT_MINOR_VERSION = 3; + + /** + * End of input + */ + public static final int EOF = -1; + + /* + * Flags + */ + public static final int F_VERBOSE = 1 << 0; + public static final int F_DUMP = 1 << 1; + public static final int F_WARNINGS = 1 << 2; + public static final int F_DEBUG = 1 << 3; + public static final int F_OPTIMIZE = 1 << 4; + public static final int F_DEPENDENCIES = 1 << 5; + + /* + * Type codes + */ + public static final int TC_BOOLEAN = 0; + public static final int TC_BYTE = 1; + public static final int TC_CHAR = 2; + public static final int TC_SHORT = 3; + public static final int TC_INT = 4; + public static final int TC_LONG = 5; + public static final int TC_FLOAT = 6; + public static final int TC_DOUBLE = 7; + public static final int TC_NULL = 8; + public static final int TC_ARRAY = 9; + public static final int TC_CLASS = 10; + public static final int TC_VOID = 11; + public static final int TC_METHOD = 12; + public static final int TC_ERROR = 13; + + /* + * Type Masks + */ + public static final int TM_NULL = 1 << TC_NULL; + public static final int TM_VOID = 1 << TC_VOID; + public static final int TM_BOOLEAN = 1 << TC_BOOLEAN; + public static final int TM_BYTE = 1 << TC_BYTE; + public static final int TM_CHAR = 1 << TC_CHAR; + public static final int TM_SHORT = 1 << TC_SHORT; + public static final int TM_INT = 1 << TC_INT; + public static final int TM_LONG = 1 << TC_LONG; + public static final int TM_FLOAT = 1 << TC_FLOAT; + public static final int TM_DOUBLE = 1 << TC_DOUBLE; + public static final int TM_ARRAY = 1 << TC_ARRAY; + public static final int TM_CLASS = 1 << TC_CLASS; + public static final int TM_METHOD = 1 << TC_METHOD; + public static final int TM_ERROR = 1 << TC_ERROR; + + public static final int TM_INT32 = TM_BYTE | TM_SHORT | TM_CHAR | TM_INT; + public static final int TM_NUM32 = TM_INT32 | TM_FLOAT; + public static final int TM_NUM64 = TM_LONG | TM_DOUBLE; + public static final int TM_INTEGER = TM_INT32 | TM_LONG; + public static final int TM_REAL = TM_FLOAT | TM_DOUBLE; + public static final int TM_NUMBER = TM_INTEGER | TM_REAL; + public static final int TM_REFERENCE = TM_ARRAY | TM_CLASS | TM_NULL; + + /* + * Class status + */ + public static final int CS_UNDEFINED = 0; + public static final int CS_UNDECIDED = 1; + public static final int CS_BINARY = 2; + public static final int CS_SOURCE = 3; + public static final int CS_PARSED = 4; + public static final int CS_COMPILED = 5; + public static final int CS_NOTFOUND = 6; + + /* + * Attributes + */ + public static final int ATT_ALL = -1; + public static final int ATT_CODE = 1; + + /* + * Number of bits used in file offsets + */ + public static final int OFFSETBITS = 19; + public static final int MAXFILESIZE = (1 << OFFSETBITS) - 1; + public static final int MAXLINENUMBER = (1 << (32 - OFFSETBITS)) - 1; + + /* + * Operator precedence + */ + /* Who uses this???? + public static final int opPrecedence[] = { + 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 12, 13, 14, 15, 16, 17, 18, + 18, 19, 19, 19, 19, 19, 20, 20, 20, 21, + 21, 22, 22, 22, 23, 24, 24, 24, 24, 24, + 24, 25, 25, 26, 26, 26, 26, 26, 26 + }; + * */ +} diff --git a/src/org/openjdk/asmtools/jasm/Data.java b/src/org/openjdk/asmtools/jasm/Data.java new file mode 100644 index 0000000..830a441 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Data.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +/** + * + */ +interface Data { + + public void write(CheckedDataOutputStream out) throws IOException; + + public int getLength(); +} diff --git a/src/org/openjdk/asmtools/jasm/DataVector.java b/src/org/openjdk/asmtools/jasm/DataVector.java new file mode 100644 index 0000000..1b4ec45 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/DataVector.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * + */ +public class DataVector implements Iterable { + + ArrayList elements; + + public DataVector(int initSize) { + elements = new ArrayList<>(initSize); + } + + public DataVector() { + this(12); + } + + public Iterator iterator() { + return elements.iterator(); + } + + public void add(T element) { + elements.add(element); + } + + // full length of the attribute + // declared in Data + public int getLength() { + int length = 0; + // calculate overall size here rather than in add() + // because it may not be available at the time of invoking of add() + for (T element : elements) { + length += element.getLength(); + } + + return 2 + length; // add the length of number of elements + } + + public void write(CheckedDataOutputStream out) + throws IOException { + out.writeShort(elements.size()); + writeElements(out); + } + + public void writeElements(CheckedDataOutputStream out) + throws IOException { + for (Data element : elements) { + element.write(out); + } + } + + /* for compatibility with Vector */ + public void addElement(T element) { + elements.add(element); + } + + public int size() { + return elements.size(); + } + + public Data elementAt(int k) { + return elements.get(k); + } +}// end class DataVector + diff --git a/src/org/openjdk/asmtools/jasm/DataVectorAttr.java b/src/org/openjdk/asmtools/jasm/DataVectorAttr.java new file mode 100644 index 0000000..c3785a3 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/DataVectorAttr.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * + */ +// public class DataVectorAttr extends AttrData implements Constants { +// } +class DataVectorAttr extends AttrData implements Iterable { + + private ArrayList elements; + private boolean byteIndex; + + public DataVectorAttr(ClassData cls, String name, boolean byteIndex, ArrayList initialData) { + super(cls, name); + this.elements = initialData; + this.byteIndex = byteIndex; + } + + public DataVectorAttr(ClassData cls, String name, ArrayList initialData) { + this(cls, name, false, initialData); + } + + public DataVectorAttr(ClassData cls, String name) { + this(cls, name, false, new ArrayList()); + + } + + public DataVectorAttr(ClassData cls, String name, boolean byteIndex) { + this(cls, name, byteIndex, new ArrayList()); + + } + + public T get(int index) { + return elements.get(index); + } + + public void add(T element) { + elements.add(element); + } + + public void add(int i, T element) { + elements.add(i, element); + } + + public int size() { + return elements.size(); + } + + @Override + public Iterator iterator() { + return elements.iterator(); + } + + @Override + public int attrLength() { + int length = 0; + // calculate overall size here rather than in add() + // because it may not be available at the time of invoking of add() + for (T elem : elements) { + length += elem.getLength(); + } + + // add the length of number of elements + if (byteIndex) { + length += 1; + } else { + length += 2; + } + + return length; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); // attr name, attr len + if (byteIndex) { + out.writeByte(elements.size()); + } else { + out.writeShort(elements.size()); + } // number of elements + for (T elem : elements) { + elem.write(out); + } + } + +} diff --git a/src/org/openjdk/asmtools/jasm/DefaultAnnotationAttr.java b/src/org/openjdk/asmtools/jasm/DefaultAnnotationAttr.java new file mode 100644 index 0000000..af81687 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/DefaultAnnotationAttr.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +/** + * DefaultAnnotationAttr + * + * Used to represent Default Annotation Attributes + * + */ +public class DefaultAnnotationAttr extends AttrData { + + Data element; // Data + + public DefaultAnnotationAttr(ClassData cls, String name, Data element) { + super(cls, name); + this.element = element; + } + + public DefaultAnnotationAttr(ClassData cls, String name) { + super(cls, name); + this.element = null; + } + + public void add(Data element) { + this.element = element; + } + + @Override + public int attrLength() { + return element.getLength(); // add the length of number of elements + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); // attr name, attr len + element.write(out); + } +}// end class DataVectorAttr + diff --git a/src/org/openjdk/asmtools/jasm/Environment.java b/src/org/openjdk/asmtools/jasm/Environment.java new file mode 100644 index 0000000..e3d1f5e --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Environment.java @@ -0,0 +1,463 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.Constants.EOF; +import static org.openjdk.asmtools.jasm.Constants.OFFSETBITS; +import org.openjdk.asmtools.util.I18NResourceBundle; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.text.MessageFormat; + +/** + * An input stream for java programs. The stream treats either "\n", "\r" or "\r\n" as the + * end of a line, it always returns \n. It also parses UNICODE characters expressed as + * \uffff. However, if it sees "\\", the second slash cannot begin a unicode sequence. It + * keeps track of the current position in the input stream. + * + * An position consists of: ((linenr << OFFSETBITS) | offset) this means that both + * the line number and the exact offset into the file are encoded in each position + * value.

+ */ +public class Environment { + + /*-------------------------------------------------------- */ + /* Environment Inner Classes */ + /** + * A sorted list of error messages + */ + final class ErrorMessage { + + int where; + String message; + ErrorMessage next; + + /** + * Constructor + */ + ErrorMessage(int where, String message) { + this.where = where; + this.message = message; + } + } + + /*-------------------------------------------------------- */ + /* Environment Fields */ + static boolean traceFlag = false; + boolean debugInfoFlag = false; + + private File source; + public PrintStream out; + boolean nowarn; + private byte data[]; + private int bytepos; + private int linepos; + public int pos; + /*-------------------------------------------------------- */ + + public Environment(File source, PrintStream out, boolean nowarn) throws IOException { + this.out = out; + errorFileName = source.getPath(); + this.source = source; + this.nowarn = nowarn; + // Read the file + FileInputStream in = new FileInputStream(source); + data = new byte[in.available()]; + in.read(data); + in.close(); + bytepos = 0; + linepos = 1; + } + + public String getErrorFile() { + return errorFileName; + } + + public String getSourceName() { + return source.getName(); + } + + public int lookForward() { + try { + return data[bytepos]; + } catch (ArrayIndexOutOfBoundsException e) { + return EOF; + } + } + + public int convertUnicode() { + int c; + try { + while ((c = data[bytepos]) == 'u') { + bytepos++; + } + int d = 0; + for (int i = 0; i < 4; i++) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + d = (d << 4) + c - '0'; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + d = (d << 4) + 10 + c - 'a'; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + d = (d << 4) + 10 + c - 'A'; + break; + default: + error(pos, "invalid.escape.char"); + return d; + } + ++bytepos; + c = data[bytepos]; + } + return d; + } catch (ArrayIndexOutOfBoundsException e) { + error(pos, "invalid.escape.char"); + return EOF; + } + } + + public int read() { + int c; + pos = (linepos << OFFSETBITS) | bytepos; + try { + c = data[bytepos]; + } catch (ArrayIndexOutOfBoundsException e) { + return EOF; + } + bytepos++; + + // parse special characters + switch (c) { + /* case '\\': + if (lookForward() != 'u') { + return '\\'; + } + // we have a unicode sequence + return convertUnicode();*/ + case '\n': + linepos++; + return '\n'; + + case '\r': + if (lookForward() == '\n') { + bytepos++; + } + linepos++; + return '\n'; + + default: + return c; + } + } + + public int lineNumber(int lcpos) { + return lcpos >>> OFFSETBITS; + } + + public int lineNumber() { + return lineNumber(pos); + } + + public int lineOffset(int lcpos) { + return lcpos & ((1 << OFFSETBITS) - 1); + } + + public int lineOffset() { + return lineOffset(pos); + } + + /*============================================================== Environment */ + /** + * The number of errors and warnings + */ + public int nerrors; + public int nwarnings; + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + /** + * Error String + */ + String errorString(String err, Object arg1, Object arg2, Object arg3) { + String str = null; + + //str = getProperty(err); + str = i18n.getString(err); + if (str == null) { + return "error message '" + err + "' not found"; + } + + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if ((c == '%') && (i + 1 < str.length())) { + switch (str.charAt(++i)) { + case 's': + String arg = arg1.toString(); + for (int j = 0; j < arg.length(); j++) { + switch (c = arg.charAt(j)) { + case ' ': + case '\t': + case '\n': + case '\r': + buf.append((char) c); + break; + + default: + if ((c > ' ') && (c <= 255)) { + buf.append((char) c); + } else { + buf.append('\\'); + buf.append('u'); + buf.append(Integer.toString(c, 16)); + } + } + } + arg1 = arg2; + arg2 = arg3; + break; + + case '%': + buf.append('%'); + break; + + default: + buf.append('?'); + break; + } + } else { + buf.append((char) c); + } + } + // KTL + // Need to do message format to substitute args + String msg = buf.toString(); + MessageFormat form = new MessageFormat(msg); + Object args[] = {arg1, arg2, arg3}; + msg = form.format(args); + + return msg; + + } + + /** + * The filename where the last errors have occurred + */ + String errorFileName; + + /** + * List of outstanding error messages + */ + ErrorMessage errors; + + /** + * Insert an error message in the list of outstanding error messages. The list is + * sorted on input position. + */ + void insertError(int where, String message) { + //output("ERR = " + message); + ErrorMessage msg = new ErrorMessage(where, message); + if (errors == null) { + errors = msg; + } else if (errors.where > where) { + msg.next = errors; + errors = msg; + } else { + ErrorMessage m = errors; + for (; (m.next != null) && (m.next.where <= where); m = m.next); + msg.next = m.next; + m.next = msg; + } + } + + /** + * Flush outstanding errors + */ + public void flushErrors() { + if (errors == null) { + traceln("flushErrors: errors == null"); + return; + } + + // Report the errors + for (ErrorMessage msg = errors; msg != null; msg = msg.next) { + int ln = lineNumber(msg.where); + int off = lineOffset(msg.where); + + int i, j; + for (i = off; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r'); i--); + for (j = off; (j < data.length) && (data[j] != '\n') && (data[j] != '\r'); j++); + + String prefix = errorFileName + ":" + ln + ":"; + outputln(prefix + " " + msg.message); + outputln(new String(data, i, j - i)); + + char strdata[] = new char[(off - i) + 1]; + for (j = i; j < off; j++) { + strdata[j - i] = (data[j] == '\t') ? '\t' : ' '; + } + strdata[off - i] = '^'; + outputln(new String(strdata)); + } + errors = null; + } + + /** + * Output a string. This can either be an error message or something for debugging. + * This should be used instead of print. + */ + public void output(String msg) { +// try { + int len = msg.length(); + for (int i = 0; i < len; i++) { + out.write(msg.charAt(i)); + } +// } catch (IOException e) { +// } + } + + /** + * Output a string. This can either be an error message or something for debugging. + * This should be used instead of println. + */ + public void outputln(String msg) { + output(msg); + out.write('\n'); + } + + /** + * Issue an error. source - the input source, usually a file name string offset - the + * offset in the source of the error err - the error number (as defined in this + * interface) arg1 - an optional argument to the error (null if not applicable) arg2 - + * a second optional argument to the error (null if not applicable) arg3 - a third + * optional argument to the error (null if not applicable) + */ + /** + * Issue an error + */ + public void error(int where, String err, Object arg1, Object arg2, Object arg3) { + String msg; + if (err.startsWith("warn.")) { + if (nowarn) { + return; + } + nwarnings++; + msg = "Warning: "; + } else { + err = "err." + err; + nerrors++; + msg = "Error: "; + } + msg = msg + errorString(err, arg1, arg2, arg3); + traceln("error(" + lineNumber(where) + ":" + lineOffset(where) + "):" + msg); + insertError(where, msg); + } + + public final void error(int where, String err, Object arg1, Object arg2) { + error(where, err, arg1, arg2, null); + } + + public final void error(int where, String err, Object arg1) { + error(where, err, arg1, null, null); + } + + public final void error(int where, String err) { + error(where, err, null, null, null); + } + + public final void error(String err, Object arg1, Object arg2, Object arg3) { + error(pos, err, arg1, arg2, arg3); + } + + public final void error(String err, Object arg1, Object arg2) { + error(pos, err, arg1, arg2, null); + } + + public final void error(String err, Object arg1) { + error(pos, err, arg1, null, null); + } + + public final void error(String err) { + error(pos, err, null, null, null); + } + + public final String errorStr(String err, Object arg1, Object arg2, Object arg3) { + return errorString(err, arg1, arg2, arg3); + } + + public final String errorStr(String err, Object arg1, Object arg2) { + return errorStr(err, arg1, arg2, null); + } + + public final String errorStr(String err, Object arg1) { + return errorStr(err, arg1, null, null); + } + + public final String errorStr(String err) { + return errorStr(err, null, null, null); + } + + /*============================================================== trace */ + public boolean isTraceEnabled() { + return traceFlag; + } + + public boolean isDebugEnabled() { + return debugInfoFlag; + } + + void trace(String message) { + if (traceFlag) { + output(message); + } + } + + void traceln(String message) { + if (traceFlag) { + outputln(message); + } + } + +} // end Environment diff --git a/src/org/openjdk/asmtools/jasm/FieldData.java b/src/org/openjdk/asmtools/jasm/FieldData.java new file mode 100644 index 0000000..55c0758 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/FieldData.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.Tables.AttrTag; +import java.io.IOException; + +/** + * + */ +class FieldData extends MemberData { + + /*-------------------------------------------------------- */ + /* FieldData Fields */ + protected ConstantPool.ConstValue_Pair nape; + private AttrData initValue; + /*-------------------------------------------------------- */ + + public FieldData(ClassData cls, int acc, ConstantPool.ConstValue_Pair nape) { + super(cls, acc); + this.nape = nape; + if (Modifiers.hasPseudoMod(acc)) { + createPseudoMod(); + } + } + + public void SetValue(Argument value_cpx) { + initValue = new CPXAttr(cls, AttrTag.ATT_ConstantValue.parsekey(), + value_cpx); + } + + public void write(CheckedDataOutputStream out) throws IOException, Parser.CompilerError { + out.writeShort(access); + out.writeShort(nape.left.arg); + out.writeShort(nape.right.arg); + + DataVector attrs = new DataVector(); + if (initValue != null) { + attrs.add(initValue); + } + if (syntheticAttr != null) { + attrs.add(syntheticAttr); + } + if (deprecatedAttr != null) { + attrs.add(deprecatedAttr); + } + if (annotAttrVis != null) { + attrs.add(annotAttrVis); + } + if (annotAttrInv != null) { + attrs.add(annotAttrInv); + } + if (type_annotAttrVis != null) { + attrs.add(type_annotAttrVis); + } + if (type_annotAttrInv != null) { + attrs.add(type_annotAttrInv); + } + attrs.write(out); + } +} // end FieldData + diff --git a/src/org/openjdk/asmtools/jasm/InnerClassData.java b/src/org/openjdk/asmtools/jasm/InnerClassData.java new file mode 100644 index 0000000..0afef3e --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/InnerClassData.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +/** + * + */ +class InnerClassData implements Data { + + int access; + ConstantPool.ConstCell name, innerClass, outerClass; + + public InnerClassData(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell innerClass, ConstantPool.ConstCell outerClass) { + this.access = access; + this.name = name; + this.innerClass = innerClass; + this.outerClass = outerClass; + } + + @Override + public int getLength() { + return 8; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(innerClass.arg); + if (outerClass.isSet()) { + out.writeShort(outerClass.arg); + } else { + out.writeShort(0); + } + if (name.isSet()) { + out.writeShort(name.arg); + } else { + out.writeShort(0); + } + out.writeShort(access); + } +}// end class InnerClassData + diff --git a/src/org/openjdk/asmtools/jasm/Instr.java b/src/org/openjdk/asmtools/jasm/Instr.java new file mode 100644 index 0000000..0035308 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Instr.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +import static org.openjdk.asmtools.jasm.OpcodeTables.*; + +/** + * + */ +class Instr { + + Instr next = null; + int pc; + int pos; + Opcode opc; + Argument arg; + Object arg2; // second or unusual argument + + public Instr(int pc, int pos, Opcode opc, Argument arg, Object arg2) { + this.pc = pc; + this.pos = pos; + this.opc = opc; + this.arg = arg; + this.arg2 = arg2; + } + + public Instr() { + } + + public void write(CheckedDataOutputStream out, Environment env) throws IOException { + OpcodeType type = opc.type(); + switch (type) { + case NORMAL: { + if (opc == Opcode.opc_bytecode) { + out.writeByte(arg.arg); + return; + } + out.writeByte(opc.value()); + int opcLen = opc.length(); + if (opcLen == 1) { + return; + } + + switch (opc) { + case opc_tableswitch: + ((SwitchTable) arg2).writeTableSwitch(out); + return; + case opc_lookupswitch: + ((SwitchTable) arg2).writeLookupSwitch(out); + return; + } + + int iarg; + try { + iarg = arg.arg; + } catch (NullPointerException e) { + throw new Parser.CompilerError(env.errorStr("comperr.instr.nullarg", opc.parsekey())); + } +//env.traceln("instr:"+opcNamesTab[opc]+" len="+opcLen+" arg:"+iarg); + switch (opc) { + case opc_jsr: + case opc_goto: + case opc_ifeq: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_iflt: + case opc_ifne: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_icmplt: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_ifnull: + case opc_ifnonnull: + case opc_jsr_w: + case opc_goto_w: + iarg = iarg - pc; + break; + case opc_iinc: + iarg = (iarg << 8) | (((Argument) arg2).arg & 0xFF); + break; + case opc_invokeinterface: + iarg = ((iarg << 8) | (((Argument) arg2).arg & 0xFF)) << 8; + break; + case opc_invokedynamic: // JSR-292 + iarg = (iarg << 16); + break; + case opc_ldc: + if ((iarg & 0xFFFFFF00) != 0) { + throw new Parser.CompilerError( + env.errorStr("comperr.instr.arglong", opc.parsekey(), iarg)); + } + break; + } + switch (opcLen) { + case 1: + return; + case 2: + out.writeByte(iarg); + return; + case 3: + out.writeShort(iarg); + return; + case 4: // opc_multianewarray only + out.writeShort(iarg); + iarg = ((Argument) arg2).arg; + out.writeByte(iarg); + return; + case 5: + out.writeInt(iarg); + return; + default: + throw new Parser.CompilerError( + env.errorStr("comperr.instr.opclen", opc.parsekey())); + } + } + case WIDE: + out.writeByte(Opcode.opc_wide.value()); + out.writeByte(opc.value() & 0xFF); + out.writeShort(arg.arg); + if (opc == Opcode.opc_iinc_w) { + out.writeShort(((Argument) arg2).arg); + } + return; + case PRIVELEGED: + case NONPRIVELEGED: + out.writeByte(opc.value() >> 8); + out.writeByte(opc.value() & 0xFF); + return; + default: + throw new Parser.CompilerError( + env.errorStr("comperr.instr.opclen", opc.parsekey())); + } // end writeSpecCode + + } +} // end Instr + diff --git a/src/org/openjdk/asmtools/jasm/JasmTokens.java b/src/org/openjdk/asmtools/jasm/JasmTokens.java new file mode 100644 index 0000000..835fb67 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/JasmTokens.java @@ -0,0 +1,505 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.util.HashMap; + +/** + * + * JasmTokens + * + * This class contains tokens specific to parsing JASM syntax. + * + * The classes in JasmTokens are following a Singleton Pattern. These classes are Enums, + * and they are contained in private hash maps (lookup tables and reverse lookup tables). + * These hash maps all have public accessors, which clients use to look-up enums. + * + * Tokens in this table carry no external state, and are typically treated as constants. + * They do not need to be reset. + */ +public class JasmTokens { + + /*-------------------------------------------------------- */ + /* Marker: describes the type of Keyword */ + static public enum KeywordType { + TOKEN (0, "TOKEN"), + VALUE (1, "VALUE"), + JASMIDENTIFIER (2, "JASM"), + KEYWORD (3, "KEYWORD"); + + private final Integer value; + private final String printval; + + KeywordType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + } + + + /*-------------------------------------------------------- */ + /* Marker - describes the type of token */ + /* this is rather cosmetic, no function currently. */ + static public enum TokenType { + MODIFIER (0, "Modifier"), + OPERATOR (1, "Operator"), + VALUE (2, "Value"), + TYPE (3, "Type"), + EXPRESSION (4, "Expression"), + STATEMENT (5, "Statement"), + DECLARATION (6, "Declaration"), + PUNCTUATION (7, "Punctuation"), + SPECIAL (8, "Special"), + JASM (9, "Jasm"), + MISC (10, "Misc"); + + private final Integer value; + private final String printval; + + TokenType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + } + + /*-------------------------------------------------------- */ + /** + * Scanner Tokens (Definitive List) + */ + static public enum Token { + EOF (-1, "EOF", "EOF", TokenType.MISC), + COMMA (0, "COMMA", ",", TokenType.OPERATOR), + ASSIGN (1, "ASSIGN", "=", TokenType.OPERATOR), + + ASGMUL (2, "ASGMUL", "*=", TokenType.OPERATOR), + ASGDIV (3, "ASGDIV", "/=", TokenType.OPERATOR), + ASGREM (4, "ASGREM", "%=", TokenType.OPERATOR), + ASGADD (5, "ASGADD", "+=", TokenType.OPERATOR), + ASGSUB (6, "ASGSUB", "-=", TokenType.OPERATOR), + ASGLSHIFT (7, "ASGLSHIFT", "<<=", TokenType.OPERATOR), + ASGRSHIFT (8, "ASGRSHIFT", ">>=", TokenType.OPERATOR), + ASGURSHIFT (9, "ASGURSHIFT", "<<<=", TokenType.OPERATOR), + ASGBITAND (10, "ASGBITAND", "&=", TokenType.OPERATOR), + ASGBITOR (11, "ASGBITOR", "|=", TokenType.OPERATOR), + ASGBITXOR (12, "ASGBITXOR", "^=", TokenType.OPERATOR), + + COND (13, "COND", "?:", TokenType.OPERATOR), + OR (14, "OR", "||", TokenType.OPERATOR), + AND (15, "AND", "&&", TokenType.OPERATOR), + BITOR (16, "BITOR", "|", TokenType.OPERATOR), + BITXOR (17, "BITXOR", "^", TokenType.OPERATOR), + BITAND (18, "BITAND", "&", TokenType.OPERATOR), + NE (19, "NE", "!=", TokenType.OPERATOR), + EQ (20, "EQ", "==", TokenType.OPERATOR), + GE (21, "GE", ">=", TokenType.OPERATOR), + GT (22, "GT", ">", TokenType.OPERATOR), + LE (23, "LE", "<=", TokenType.OPERATOR), + LT (24, "LT", "<", TokenType.OPERATOR), + INSTANCEOF (25, "INSTANCEOF", "instanceof", TokenType.OPERATOR), + LSHIFT (26, "LSHIFT", "<<", TokenType.OPERATOR), + RSHIFT (27, "RSHIFT", ">>", TokenType.OPERATOR), + URSHIFT (28, "URSHIFT", "<<<", TokenType.OPERATOR), + ADD (29, "ADD", "+", TokenType.OPERATOR), + SUB (30, "SUB", "-", TokenType.OPERATOR), + DIV (31, "DIV", "/", TokenType.OPERATOR), + REM (32, "REM", "%", TokenType.OPERATOR), + MUL (33, "MUL", "*", TokenType.OPERATOR), + CAST (34, "CAST", "cast", TokenType.OPERATOR), + POS (35, "POS", "+", TokenType.OPERATOR), + NEG (36, "NEG", "-", TokenType.OPERATOR), + NOT (37, "NOT", "!", TokenType.OPERATOR), + BITNOT (38, "BITNOT", "~", TokenType.OPERATOR), + PREINC (39, "PREINC", "++", TokenType.OPERATOR), + PREDEC (40, "PREDEC", "--", TokenType.OPERATOR), + NEWARRAY (41, "NEWARRAY", "new", TokenType.OPERATOR), + NEWINSTANCE (42, "NEWINSTANCE", "new", TokenType.OPERATOR), + NEWFROMNAME (43, "NEWFROMNAME", "new", TokenType.OPERATOR), + POSTINC (44, "POSTINC", "++", TokenType.OPERATOR), + POSTDEC (45, "POSTDEC", "--", TokenType.OPERATOR), + FIELD (46, "FIELD", "field", TokenType.OPERATOR), + METHOD (47, "METHOD", "method", TokenType.OPERATOR), + ARRAYACCESS (48, "ARRAYACCESS", "[]", TokenType.OPERATOR), + NEW (49, "NEW", "new", TokenType.OPERATOR), + INC (50, "INC", "++", TokenType.OPERATOR), + DEC (51, "DEC", "--", TokenType.OPERATOR), + + CONVERT (55, "CONVERT", "convert", TokenType.OPERATOR), + EXPR (56, "EXPR", "expr", TokenType.OPERATOR), + ARRAY (57, "ARRAY", "array", TokenType.OPERATOR), + GOTO (58, "GOTO", "goto", TokenType.OPERATOR), + + + + /* + * Value tokens + */ + IDENT (60, "IDENT", "Identifier", TokenType.VALUE, KeywordType.VALUE, true), + BOOLEANVAL (61, "BOOLEANVAL", "Boolean", TokenType.VALUE, KeywordType.VALUE), + BYTEVAL (62, "BYTEVAL", "Byte", TokenType.VALUE), + CHARVAL (63, "CHARVAL", "Char", TokenType.VALUE), + SHORTVAL (64, "SHORTVAL", "Short", TokenType.VALUE), + INTVAL (65, "INTVAL", "Integer", TokenType.VALUE, KeywordType.VALUE), + LONGVAL (66, "LONGVAL", "Long", TokenType.VALUE, KeywordType.VALUE), + FLOATVAL (67, "FLOATVAL", "Float", TokenType.VALUE, KeywordType.VALUE), + DOUBLEVAL (68, "DOUBLEVAL", "Double", TokenType.VALUE, KeywordType.VALUE), + STRINGVAL (69, "STRINGVAL", "String", TokenType.VALUE, KeywordType.VALUE), + + /* + * Type keywords + */ + BYTE (70, "BYTE", "byte", TokenType.TYPE), + CHAR (71, "CHAR", "char", TokenType.TYPE), + SHORT (72, "SHORT", "short", TokenType.TYPE), + INT (73, "INT", "int", TokenType.TYPE), + LONG (74, "LONG", "long", TokenType.TYPE), + FLOAT (75, "FLOAT", "float", TokenType.TYPE), + DOUBLE (76, "DOUBLE", "double", TokenType.TYPE), + VOID (77, "VOID", "void", TokenType.TYPE), + BOOLEAN (78, "BOOLEAN", "boolean", TokenType.TYPE), + + /* + * Expression keywords + */ + TRUE (80, "TRUE", "true", TokenType.EXPRESSION), + FALSE (81, "FALSE", "false", TokenType.EXPRESSION), + THIS (82, "THIS", "this", TokenType.EXPRESSION), + SUPER (83, "SUPER", "super", TokenType.MODIFIER, KeywordType.KEYWORD), + NULL (84, "NULL", "null", TokenType.EXPRESSION), + + /* + * Statement keywords + */ + IF (90, "IF", "if", TokenType.STATEMENT), + ELSE (91, "ELSE", "else", TokenType.STATEMENT), + FOR (92, "FOR", "for", TokenType.STATEMENT), + WHILE (93, "WHILE", "while", TokenType.STATEMENT), + DO (94, "DO", "do", TokenType.STATEMENT), + SWITCH (95, "SWITCH", "switch", TokenType.STATEMENT), + CASE (96, "CASE", "case", TokenType.STATEMENT), + DEFAULT (97, "DEFAULT", "default", TokenType.STATEMENT, KeywordType.KEYWORD), + BREAK (98, "BREAK", "break", TokenType.STATEMENT), + CONTINUE (99, "CONTINUE", "continue", TokenType.STATEMENT), + RETURN (100, "RETURN", "return", TokenType.STATEMENT), + TRY (101, "TRY", "try", TokenType.STATEMENT), + CATCH (102, "CATCH", "catch", TokenType.STATEMENT), + FINALLY (103, "FINALLY", "finally", TokenType.STATEMENT), + THROW (104, "THROW", "throw", TokenType.STATEMENT), + STAT (105, "STAT", "stat", TokenType.STATEMENT), + EXPRESSION (106, "EXPRESSION", "expression", TokenType.STATEMENT), + DECLARATION (107, "DECLARATION", "declaration", TokenType.STATEMENT), + VARDECLARATION (108, "VARDECLARATION", "vdeclaration", TokenType.STATEMENT), + + /* + * Declaration keywords + */ + IMPORT (110, "IMPORT", "import", TokenType.DECLARATION), + CLASS (111, "CLASS", "class", TokenType.DECLARATION, KeywordType.KEYWORD), + EXTENDS (112, "EXTENDS", "extends", TokenType.DECLARATION, KeywordType.KEYWORD), + IMPLEMENTS (113, "IMPLEMENTS", "implements", TokenType.DECLARATION, KeywordType.KEYWORD), + INTERFACE (114, "INTERFACE", "interface", TokenType.DECLARATION, KeywordType.KEYWORD), + PACKAGE (115, "PACKAGE", "package", TokenType.DECLARATION, KeywordType.KEYWORD), + ENUM (116, "ENUM", "enum", TokenType.DECLARATION, KeywordType.KEYWORD), + MANDATED (117, "MANDATED", "mandated", TokenType.DECLARATION, KeywordType.KEYWORD), + /* + * Modifier keywords + */ + PRIVATE (120, "PRIVATE", "private", TokenType.MODIFIER, KeywordType.KEYWORD), + PUBLIC (121, "PUBLIC", "public", TokenType.MODIFIER, KeywordType.KEYWORD), + PROTECTED (122, "PROTECTED", "protected", TokenType.MODIFIER, KeywordType.KEYWORD), + CONST (123, "CONST", "const", TokenType.DECLARATION, KeywordType.KEYWORD), + STATIC (124, "STATIC", "static", TokenType.MODIFIER, KeywordType.KEYWORD), + TRANSIENT (125, "TRANSIENT", "transient", TokenType.MODIFIER, KeywordType.KEYWORD), + SYNCHRONIZED (126, "SYNCHRONIZED", "synchronized", TokenType.MODIFIER, KeywordType.KEYWORD), + NATIVE (127, "NATIVE", "native", TokenType.MODIFIER, KeywordType.KEYWORD), + FINAL (128, "FINAL", "final", TokenType.MODIFIER, KeywordType.KEYWORD), + VOLATILE (129, "VOLATILE", "volatile", TokenType.MODIFIER, KeywordType.KEYWORD), + ABSTRACT (130, "ABSTRACT", "abstract", TokenType.MODIFIER, KeywordType.KEYWORD), + + /* + * Punctuation + */ + SEMICOLON (135, "SEMICOLON", ";", TokenType.PUNCTUATION, KeywordType.VALUE), + COLON (136, "COLON", ":", TokenType.PUNCTUATION, KeywordType.VALUE), + QUESTIONMARK (137, "QUESTIONMARK", "?", TokenType.PUNCTUATION), + LBRACE (138, "LBRACE", "{", TokenType.PUNCTUATION, KeywordType.VALUE), + RBRACE (139, "RBRACE", "}", TokenType.PUNCTUATION, KeywordType.VALUE), + LPAREN (140, "LPAREN", "(", TokenType.PUNCTUATION), + RPAREN (141, "RPAREN", ")", TokenType.PUNCTUATION), + LSQBRACKET (142, "LSQBRACKET", "[", TokenType.PUNCTUATION), + RSQBRACKET (143, "RSQBRACKET", "]", TokenType.PUNCTUATION), + THROWS (144, "THROWS", "throws", TokenType.DECLARATION, KeywordType.KEYWORD), + /* + * Special tokens + */ + ERROR (145, "ERROR", "error", TokenType.MODIFIER), + COMMENT (146, "COMMENT", "comment", TokenType.MODIFIER), + TYPE (147, "TYPE", "type", TokenType.MODIFIER), + LENGTH (148, "LENGTH", "length", TokenType.DECLARATION), + INLINERETURN (149, "INLINERETURN", "inline-return", TokenType.MODIFIER), + INLINEMETHOD (150, "INLINEMETHOD", "inline-method", TokenType.MODIFIER), + INLINENEWINSTANCE (151, "INLINENEWINSTANCE", "inline-new", TokenType.MODIFIER), + + /* + * Added for jasm + */ + METHODREF (152, "METHODREF", "Method", TokenType.DECLARATION, KeywordType.KEYWORD, true), + FIELDREF (153, "FIELD", "Field", TokenType.DECLARATION, KeywordType.KEYWORD, true), + STACK (154, "STACK", "stack", TokenType.DECLARATION, KeywordType.KEYWORD, true), + LOCAL (155, "LOCAL", "locals", TokenType.DECLARATION, KeywordType.KEYWORD, true), + CPINDEX (156, "CPINDEX", "CPINDEX", TokenType.DECLARATION, true), + CPNAME (157, "CPNAME", "CPName", TokenType.DECLARATION, true), + SIGN (158, "SIGN", "SIGN", TokenType.DECLARATION, true), + BITS (159, "BITS", "bits", TokenType.MISC, KeywordType.KEYWORD, true), + INF (160, "INF", "Inf", "Infinity", TokenType.MISC, KeywordType.KEYWORD), + NAN (161, "NAN", "NaN", TokenType.MISC, KeywordType.KEYWORD, true), + INNERCLASS (162, "INNERCLASS", "InnerClass", TokenType.DECLARATION, KeywordType.KEYWORD, true), + OF (163, "OF", "of", TokenType.DECLARATION, KeywordType.KEYWORD, true), + SYNTHETIC (164, "SYNTHETIC", "synthetic", TokenType.MODIFIER, KeywordType.KEYWORD, true), + STRICT (165, "STRICT", "strict", TokenType.MODIFIER, KeywordType.KEYWORD, true), + DEPRECATED (166, "DEPRECATED", "deprecated", TokenType.MODIFIER, KeywordType.KEYWORD, true), + VERSION (167, "VERSION", "version", TokenType.DECLARATION, KeywordType.KEYWORD, true), + MODULE (168, "MODULE", "module", TokenType.DECLARATION, KeywordType.KEYWORD), + ANNOTATION (169, "ANNOTATION", "@", TokenType.MISC), + PARAM_NAME (173, "PARAM_NAME", "#", TokenType.MISC), + + VARARGS (170, "VARARGS", "varargs", TokenType.MODIFIER, KeywordType.KEYWORD), + BRIDGE (171, "BRIDGE", "bridge", TokenType.MODIFIER, KeywordType.KEYWORD), + + // Declaration keywords + BOOTSTRAPMETHOD (172, "BOOTSTRAPMETHOD", "BootstrapMethod", TokenType.DECLARATION, KeywordType.KEYWORD, true); + + // Misc Keywords + private Integer value; + private String printval; + private String alias; + private TokenType tok_type; + private KeywordType key_type; + private String parsekey; + private boolean possible_jasm_identifier; + + // By default, if a KeywordType is not specified, it has the value 'TOKEN' + Token(Integer val, String print, String op, TokenType ttype) { + init(val, print, op, null, ttype, KeywordType.TOKEN, false); + } + + Token(Integer val, String print, String op, TokenType ttype, boolean ident) { + init(val, print, op, null, ttype, KeywordType.TOKEN, ident); + } + + Token(Integer val, String print, String op, String als, TokenType ttype) { + init(val, print, op, als, ttype, KeywordType.TOKEN, false); + } + + Token(Integer val, String print, String op, TokenType ttype, KeywordType ktype) { + init(val, print, op, null, ttype, ktype, false); + } + + Token(Integer val, String print, String op, TokenType ttype, KeywordType ktype, boolean ident) { + init(val, print, op, null, ttype, ktype, ident); + } + + Token(Integer val, String print, String op, String als, TokenType ttype, KeywordType ktype) { + init(val, print, op, als, ttype, ktype, false); + } + + private void init(Integer val, String print, String op, String als, TokenType ttype, KeywordType ktype, boolean ident) { + value = val; + printval = print; + parsekey = op; + tok_type = ttype; + key_type = ktype; + alias = als; + possible_jasm_identifier = ident; + } + + public String printval() { + return printval; + } + + public String parsekey() { + return parsekey; + } + + public int value() { + return value; + } + + public boolean possibleJasmIdentifier() { + return possible_jasm_identifier; + } + + @Override + public String toString() { + return "<" + printval + "> [" + value + "]"; + } + + } + + /** + * Initialized keyword and token Hash Maps (and Reverse Tables) + */ + static protected final int MaxTokens = 172; + private static HashMap TagToTokens = new HashMap<>(MaxTokens); + private static HashMap SymbolToTokens = new HashMap<>(MaxTokens); + private static HashMap ParsekeyToTokens = new HashMap<>(MaxTokens); + + static protected final int MaxValTokens = 12; + private static HashMap TagToValTokens = new HashMap<>(MaxValTokens); + private static HashMap SymbolToValTokens = new HashMap<>(MaxValTokens); + private static HashMap ParsekeyToValTokens = new HashMap<>(MaxValTokens); + + private static HashMap PossibleJasmIdentifiers = new HashMap<>(MaxValTokens); + + static protected final int MaxKeywords = 40; + private static HashMap TagToKeywords = new HashMap<>(MaxKeywords); + private static HashMap SymbolToKeywords = new HashMap<>(MaxKeywords); + private static HashMap ParsekeyToKeywords = new HashMap<>(MaxKeywords); + + static { + + // register all of the tokens + for (Token tk : Token.values()) { + registerToken(tk); + } + } + + private static void registerToken(Token tk) { + // Tag is a keyword + if (tk.key_type == KeywordType.KEYWORD) { + TagToKeywords.put(tk.value, tk); + if (tk.alias != null) { + ParsekeyToKeywords.put(tk.alias, tk); + } + SymbolToKeywords.put(tk.printval, tk); + if (tk.parsekey != null) { + ParsekeyToKeywords.put(tk.parsekey, tk); + } + } + + // Values (and Keywords) go on the Val tokens list + if (tk.key_type == KeywordType.KEYWORD + || tk.key_type == KeywordType.VALUE) { + TagToValTokens.put(tk.value, tk); + SymbolToValTokens.put(tk.printval, tk); + if (tk.alias != null) { + SymbolToValTokens.put(tk.alias, tk); + } + if (tk.parsekey != null) { + ParsekeyToValTokens.put(tk.parsekey, tk); + } + } + + // make the list of 'possible jasm identifiers' + if (tk.possible_jasm_identifier) { + PossibleJasmIdentifiers.put(tk.value(), tk); + } + + // Finally, register all tokens + TagToTokens.put(tk.value, tk); + SymbolToTokens.put(tk.printval, tk); + ParsekeyToTokens.put(tk.printval, tk); + } + + /* Token accessors */ + public static Token token(int tk) { + return TagToTokens.get(tk); + } + + public static Token val_token(int tk) { + return TagToValTokens.get(tk); + } + + public static Token keyword_token(int tk) { + return TagToKeywords.get(tk); + } + + public static Token possibleJasmIdentifiers(int token) { + return PossibleJasmIdentifiers.get(token); + } + + /* Reverse lookup accessors */ + public static Token token(String parsekey) { + return ParsekeyToTokens.get(parsekey); + } + + public static Token val_token(String parsekey) { + return ParsekeyToValTokens.get(parsekey); + } + + public static Token keyword_token(String parsekey) { + return ParsekeyToKeywords.get(parsekey); + } + + /* Reverse lookup by ID accessors */ + public static Token token_ID(String ID) { + return ParsekeyToTokens.get(ID); + } + + public static Token val_token_ID(String ID) { + return ParsekeyToValTokens.get(ID); + } + + public static Token keyword_token_ID(String ID) { + return ParsekeyToKeywords.get(ID); + } + + public static String keywordName(int token) { + String retval = null; + Token tk = keyword_token(token); + if (tk != null) { + retval = tk.parsekey; + } + return retval; + } + + public static int val_token_int(String idValue) { + Token kwd = val_token(idValue); + int retval = Token.IDENT.value; + + if (kwd != null) { + retval = kwd.value; + } + return retval; + } + + public static Token keyword_token_ident(String idValue) { + Token kwd = keyword_token(idValue); + + if (kwd == null) { + kwd = Token.IDENT; + } + return kwd; + } + + public static int keyword_token_int(String idValue) { + return keyword_token_ident(idValue).value(); + } +} diff --git a/src/org/openjdk/asmtools/jasm/Main.java b/src/org/openjdk/asmtools/jasm/Main.java new file mode 100644 index 0000000..7c3fa69 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Main.java @@ -0,0 +1,343 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.Constants.DEFAULT_MAJOR_VERSION; +import static org.openjdk.asmtools.jasm.Constants.DEFAULT_MINOR_VERSION; +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; + +/** + * + * + */ +public class Main { + + /** + * Name of the program. + */ + String program; + /** + * The stream where error message are printed. + */ + PrintStream out; + int nerrors = 0; + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + private File destDir = null; + private boolean traceFlag = false; + private boolean debugInfoFlag = false; + private long tm = System.currentTimeMillis(); + private ArrayList v = new ArrayList<>(); + private boolean nowrite = false; + private boolean nowarn = false; + private boolean strict = false; + private String props = null; + private int nwarnings = 0; + private short major_version = DEFAULT_MAJOR_VERSION; + private short minor_version = DEFAULT_MINOR_VERSION; + private int bytelimit = 0; + private boolean debugScanner = false; + private boolean debugMembers = false; + private boolean debugCP = false; + private boolean debugAnnot = false; + private boolean debugInstr = false; + + /** + * Constructor. + */ + public Main(PrintStream out, String program) { + this.out = out; + this.program = program; + } + + /** + * Top level error message + */ + public void error(String msg) { + nerrors++; + out.println(program + ": " + msg); + } + + /** + * Usage + */ + public void usage() { + out.println(i18n.getString("jasm.usage")); + out.println(i18n.getString("jasm.opt.d")); + out.println(i18n.getString("jasm.opt.g")); + out.println(i18n.getString("jasm.opt.v")); + out.println(i18n.getString("jasm.opt.nowrite")); + out.println(i18n.getString("jasm.opt.nowarn")); + out.println(i18n.getString("jasm.opt.strict")); + out.println(i18n.getString("jasm.opt.cv", DEFAULT_MAJOR_VERSION, DEFAULT_MINOR_VERSION)); + out.println(i18n.getString("jasm.opt.version")); + } + + /** + * Run the compiler + */ + private synchronized boolean parseArgs(String argv[]) { + // Parse arguments + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + switch (arg) { + case "-v": + traceFlag = true; + break; + case "-g": + debugInfoFlag = true; + break; + case "-nowrite": + nowrite = true; + break; + case "-strict": + strict = true; + break; + case "-nowarn": + nowarn = true; + break; + case "-version": + out.println(ProductInfo.FULL_VERSION); + break; + case "-d": + if ((i + 1) >= argv.length) { + error(i18n.getString("jasm.error.d_requires_argument")); + usage(); + return false; + } + destDir = new File(argv[++i]); + if (!destDir.exists()) { + error(i18n.getString("jasm.error.does_not_exist", destDir.getPath())); + return false; + } + break; + // non-public options + case "-XdScanner": + debugScanner = true; + break; + case "-XdMember": + debugMembers = true; + break; + case "-XdCP": + debugCP = true; + break; + case "-XdInstr": + debugInstr = true; + break; + case "-XdAnnot": + debugAnnot = true; + break; + case "-XdAll": + debugScanner = true; + debugMembers = true; + debugCP = true; + debugInstr = true; + debugAnnot = true; + break; + case "-Xdlimit": + // parses file until the specified byte number + if (i + 1 > argv.length) { + out.println(" Error: Unspecified byte-limit"); + return false; + } else { + i++; + String bytelimstr = argv[i]; + bytelimit = 0; + try { + bytelimit = Integer.parseInt(bytelimstr); + } catch (NumberFormatException e) { + out.println(" Error: Unspecified byte-limit"); + return false; + } + } + break; + case "-cv": + if ((i + 1) >= argv.length) { + error(i18n.getString("jasm.error.cv_requires_arg")); + usage(); + return false; + } + String[] versions = {"", ""}; // workaround for String.split() + int index = argv[++i].indexOf("."); // + if (index != -1) { // + versions[0] = argv[i].substring(0, index); // + versions[1] = argv[i].substring(index + 1); // + } // + if (versions.length != 2) { + error(i18n.getString("jasm.error.invalid_major_minor_param")); + usage(); + return false; + } + try { + major_version = Short.parseShort(versions[0]); + minor_version = Short.parseShort(versions[1]); + } catch (NumberFormatException e) { + error(i18n.getString("jasm.error.invalid_major_minor_param")); + usage(); + return false; + } + break; + default: + if (arg.startsWith("-")) { + error(i18n.getString("jasm.error.invalid_option", arg)); + usage(); + return false; + } else { + v.add(argv[i]); + } + break; + } + } + if (v.size() == 0) { + usage(); + return false; + } + if (strict) { + nowarn = false; + } + return true; + } + + private void reset() { + destDir = null; + traceFlag = false; + debugInfoFlag = false; + System.currentTimeMillis(); + v = new ArrayList<>(); + nowrite = false; + nowarn = false; + strict = false; + props = null; + nwarnings = 0; + major_version = DEFAULT_MAJOR_VERSION; + minor_version = DEFAULT_MINOR_VERSION; + bytelimit = 0; + } + + /** + * Run the compiler + */ + public synchronized boolean compile(String argv[]) { + // Reset the state of all objs + reset(); + + boolean validArgs = parseArgs(argv); + if (!validArgs) { + return false; + } + // compile all input files + Environment sf = null; + try { + for (String inpname : v) { + Parser p; + try { + sf = new Environment(new File(inpname), out, nowarn); + sf.traceFlag = traceFlag; + sf.debugInfoFlag = debugInfoFlag; + p = new Parser(sf, major_version, minor_version); + p.setDebugFlags(debugScanner, debugMembers, debugCP, debugAnnot, debugInstr); + p.parseFile(); + } catch (FileNotFoundException ex) { + error(i18n.getString("jasm.error.cannot_read", inpname)); + continue; + } + nerrors += sf.nerrors; + nwarnings += sf.nwarnings; + if (nowrite || (nerrors > 0)) { + sf.flushErrors(); + continue; + } + try { + ClassData[] clsData = p.getClassesData(); + for (int i = 0; i < clsData.length; i++) { + ClassData cd = clsData[i]; + if (bytelimit > 0) { + cd.setByteLimit(bytelimit); + } + cd.write(destDir); + } + } catch (IOException ex) { + if (bytelimit > 0) { + // IO Error thrown from user-specified byte ount + ex.printStackTrace(); + error("UserSpecified byte-limit at byte[" + bytelimit + "]: " + ex.getMessage() + "\n" + sf.getErrorFile() + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]"); + } else { + String er = i18n.getString("jasm.error.cannot_write", ex.getMessage()); + error(er + "\n" + sf.getErrorFile() + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]"); + } + } + sf.flushErrors(); // possible errors from write() + } + } catch (Error ee) { + if (debugInfoFlag) { + ee.printStackTrace(); + } + String er = ee.getMessage() + "\n" + i18n.getString("jasm.error.fatal_error"); + error(er + "\n" + sf.getErrorFile() + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]"); + } catch (Exception ee) { + if (debugInfoFlag) { + ee.printStackTrace(); + } + String er = ee.getMessage() + "\n" + ee.getMessage() + "\n" + i18n.getString("jasm.error.fatal_exception"); + error(er + "\n" + sf.getErrorFile() + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]"); + } + + boolean errs = nerrors > 0; + boolean warns = (nwarnings > 0) && (!nowarn); + boolean errsOrWarns = errs || warns; + if (!errsOrWarns) { + return true; + } + if (errs) { + out.print(nerrors > 1 ? (nerrors + " errors") : "1 error"); + } + if (errs && warns) { + out.print(", "); + } + if (warns) { + out.print(nwarnings > 1 ? (nwarnings + " warnings") : "1 warning"); + } + out.println(); + if (strict) { + return !errsOrWarns; + } else { + return !errs; + } + } + + /** + * main program + */ + public static void main(String argv[]) { + Main compiler = new Main(System.out, "jasm"); + System.exit(compiler.compile(argv) ? 0 : 1); + } +} diff --git a/src/org/openjdk/asmtools/jasm/MemberData.java b/src/org/openjdk/asmtools/jasm/MemberData.java new file mode 100644 index 0000000..5db5720 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/MemberData.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.RuntimeConstants.DEPRECATED_ATTRIBUTE; +import static org.openjdk.asmtools.jasm.RuntimeConstants.SYNTHETIC_ATTRIBUTE; +import org.openjdk.asmtools.jasm.Tables.AttrTag; +import java.util.ArrayList; + +/** + * + * + */ +public class MemberData { + + protected int access; + protected AttrData syntheticAttr, + deprecatedAttr; + protected DataVectorAttr annotAttrVis = null; + protected DataVectorAttr annotAttrInv = null; + protected DataVectorAttr type_annotAttrVis = null; + protected DataVectorAttr type_annotAttrInv = null; + protected ClassData cls; + + public MemberData(ClassData cls, int access) { + this.cls = cls; + init(access); + } + + public void init(int access) { + this.access = access; + } + + public void createPseudoMod() { + // If a member has a Pseudo-modifier + + // create the appropriate marker attributes, + // and clear the PseudoModifiers from the access flags. + if (Modifiers.isSyntheticPseudoMod(access)) { + syntheticAttr = new AttrData(cls, + AttrTag.ATT_Synthetic.parsekey()); + + access &= ~SYNTHETIC_ATTRIBUTE; + } + if (Modifiers.isDeprecatedPseudoMod(access)) { + deprecatedAttr = new AttrData(cls, + AttrTag.ATT_Deprecated.parsekey()); + access &= ~DEPRECATED_ATTRIBUTE; + } + } + + public void addAnnotations(ArrayList annttns) { + for (AnnotationData annot : annttns) { + boolean invisible = annot.invisible; + if (annot instanceof TypeAnnotationData) { + // Type Annotations + TypeAnnotationData tannot = (TypeAnnotationData) annot; + if (invisible) { + if (type_annotAttrInv == null) { + type_annotAttrInv = new DataVectorAttr(cls, + AttrTag.ATT_RuntimeInvisibleTypeAnnotations.parsekey()); + + } + type_annotAttrInv.add(tannot); + } else { + if (type_annotAttrVis == null) { + type_annotAttrVis = new DataVectorAttr(cls, + AttrTag.ATT_RuntimeVisibleTypeAnnotations.parsekey()); + } + type_annotAttrVis.add(tannot); + } + } else { + // Regular Annotations + if (invisible) { + if (annotAttrInv == null) { + annotAttrInv = new DataVectorAttr(cls, + AttrTag.ATT_RuntimeInvisibleAnnotations.parsekey()); + + } + annotAttrInv.add(annot); + } else { + if (annotAttrVis == null) { + annotAttrVis = new DataVectorAttr(cls, + AttrTag.ATT_RuntimeVisibleAnnotations.parsekey()); + + } + annotAttrVis.add(annot); + } + } + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/MethodData.java b/src/org/openjdk/asmtools/jasm/MethodData.java new file mode 100644 index 0000000..c764c46 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/MethodData.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.Tables.AttrTag; +import org.openjdk.asmtools.jasm.ConstantPool.ConstCell; +import java.io.IOException; +import java.util.ArrayList; +import java.util.TreeMap; + +/** + * + */ +class MethodData extends MemberData { + + /** + * MethodParamData + */ + class ParamNameData implements Data { + + int access; + ConstCell name; + + public ParamNameData(int access, ConstCell name) { + this.access = access; + this.name = name; + } + + @Override + public int getLength() { + return 4; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + int nm = 0; + int ac = 0; + if (name != null) { + nm = name.arg; + ac = access; + } + +// short acc = (short) ac; +// env.traceln("ParamNameData.write() (name[" + nm + "], Flags: (" + access + "). [TESTING: short_access=" + acc + "]"); + out.writeShort(nm); + out.writeShort(ac); + } + }// end class MethodParamData + /*-------------------------------------------------------- */ + /* MethodParamData Inner Classes */ + + /** + * DataPArrayAttr + * + * Used to store Parameter Arrays (as attributes) + */ + static public class DataPArrayAttr extends AttrData implements Constants { + + TreeMap> elements; // Data + int paramsTotal; + + public DataPArrayAttr(ClassData cls, String name, int paramsTotal, TreeMap> elements) { + super(cls, name); + this.paramsTotal = paramsTotal; + this.elements = elements; + } + + public DataPArrayAttr(ClassData cls, String name, int paramsTotal) { + this(cls, name, paramsTotal, new TreeMap>()); + } + + public void put(int paramNum, T element) { + ArrayList v = get(paramNum); + if (v == null) { + v = new ArrayList<>(); + elements.put(paramNum, v); + } + + v.add(element); + } + + public ArrayList get(int paramNum) { + return elements.get(paramNum); + } + + @Override + public int attrLength() { + int length = 1; // One byte for the paramater count + + // calculate overall size here rather than in add() + // because it may not be available at the time of invoking of add() + for (int i = 0; i < paramsTotal; i++) { + ArrayList attrarray = get(i); + if (attrarray != null) { + for (Data item : attrarray) { + length += item.getLength(); + } + } + length += 2; // 2 bytes for the annotation count for each parameter + } + + return length; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); // attr name, attr len + out.writeByte(paramsTotal); // number of parameters total (in byte) + + for (int i = 0; i < paramsTotal; i++) { + ArrayList attrarray = get(i); + if (attrarray != null) { + // write out the number of annotations for the current param + out.writeShort(attrarray.size()); + for (T item : attrarray) { + item.write(out); // write the current annotation + } + } else { + out.writeShort(0); + // No annotations to write out + } + } + } + }// end class DataPArrayAttr + + + /*-------------------------------------------------------- */ + /* Method Data Fields */ + protected Environment env; + protected ConstCell nameCell, sigCell; + protected CodeAttr code; + protected DataVectorAttr exceptions = null; + protected DataVectorAttr paramNames = null; + protected DataPArrayAttr pannotAttrVis = null; + protected DataPArrayAttr pannotAttrInv = null; + protected DefaultAnnotationAttr defaultAnnot = null; + protected DataVector attrs = new DataVector(); + /*-------------------------------------------------------- */ + + public MethodData(ClassData cls, int acc, + ConstCell name, ConstCell sig, ArrayList exc_table) { + super(cls, acc); + this.env = cls.env; + nameCell = name; + sigCell = sig; + if ((exc_table != null) && (!exc_table.isEmpty())) { + exceptions = new DataVectorAttr<>(cls, + AttrTag.ATT_Exceptions.parsekey(), + exc_table); + } + // Normalize the modifiers to access flags + if (Modifiers.hasPseudoMod(acc)) { + createPseudoMod(); + } + } + + public void addMethodParameter(int totalParams, int paramNum, ConstCell name, int access) { + env.traceln("addMethodParameter Param[" + paramNum + "] (name: " + name.toString() + ", Flags (" + access + ")."); + if (paramNames == null) { + paramNames = new DataVectorAttr<>(cls, AttrTag.ATT_MethodParameters.parsekey(), true); + for (int i = 0; i < totalParams; i++) { + // initialize the paramName array (in case the name is not given in Jasm syntax) + paramNames.add(new ParamNameData(0, null)); + } + } + paramNames.add(paramNum, new ParamNameData(access, name)); + } + + public CodeAttr startCode(int pos, int paramcnt, Argument max_stack, Argument max_locals) { + code = new CodeAttr(this, pos, paramcnt, max_stack, max_locals); + return code; + } + + public void addDefaultAnnotation(DefaultAnnotationAttr data) { + defaultAnnot = data; + } + + public void addParamAnnotation(int totalParams, int paramNum, AnnotationData data) { + if (!data.invisible) { + if (pannotAttrVis == null) { + pannotAttrVis = new DataPArrayAttr<>(cls, + AttrTag.ATT_RuntimeVisibleParameterAnnotations.parsekey(), + totalParams); + } + pannotAttrVis.put(paramNum, data); + + } else { + if (pannotAttrInv == null) { + pannotAttrInv = new DataPArrayAttr<>(cls, + AttrTag.ATT_RuntimeInvisibleParameterAnnotations.parsekey(), + totalParams); + } + pannotAttrInv.put(paramNum, data); + } + } + + + /*====================================================== Write */ + public void write(CheckedDataOutputStream out) throws IOException, Parser.CompilerError { + out.writeShort(access); + out.writeShort(nameCell.arg); + out.writeShort(sigCell.arg); + if (exceptions != null) { + attrs.add(exceptions); + } + if (syntheticAttr != null) { + attrs.add(syntheticAttr); + } + if (deprecatedAttr != null) { + attrs.add(deprecatedAttr); + } + if (paramNames != null) { + attrs.add(paramNames); + } + if (code != null) { + attrs.add(code); + } + if (defaultAnnot != null) { + attrs.add(defaultAnnot); + } + if (annotAttrVis != null) { + attrs.add(annotAttrVis); + } + if (annotAttrInv != null) { + attrs.add(annotAttrInv); + } + if (type_annotAttrVis != null) { + attrs.add(type_annotAttrVis); + } + if (type_annotAttrInv != null) { + attrs.add(type_annotAttrInv); + } + if (pannotAttrVis != null) { + attrs.add(pannotAttrVis); + } + if (pannotAttrInv != null) { + attrs.add(pannotAttrInv); + } + + attrs.write(out); + } +} // end MethodData + diff --git a/src/org/openjdk/asmtools/jasm/Modifiers.java b/src/org/openjdk/asmtools/jasm/Modifiers.java new file mode 100644 index 0000000..e0e6b4c --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Modifiers.java @@ -0,0 +1,429 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.RuntimeConstants.*; +import static org.openjdk.asmtools.jasm.JasmTokens.*; +import static org.openjdk.asmtools.jasm.Tables.CF_Context; + +/** + * + * + */ +public class Modifiers { + + private static Modifiers ref; + + /* + * Modifier masks + */ + public static final int MM_ATTR = SYNTHETIC_ATTRIBUTE | DEPRECATED_ATTRIBUTE; + + public static final int MM_INTRF = ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | MM_ATTR ; // | ACC_MODULE ; + public static final int MM_CLASS = ACC_PUBLIC | ACC_FINAL| ACC_SUPER | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM | MM_ATTR ; // | ACC_MODULE ; + public static final int MM_ACCESS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED; // | ACC_MODULE; + public static final int MM_FIELD = MM_ACCESS | ACC_STATIC | ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM | MM_ATTR ; // | ACC_MODULE ; + public static final int MM_I_METHOD = ACC_ABSTRACT | ACC_PUBLIC | ACC_VARARGS | ACC_BRIDGE | ACC_SYNTHETIC ; // interface method + public static final int MM_A_METHOD = MM_ACCESS | ACC_ABSTRACT | MM_ATTR; + public static final int MM_N_METHOD = MM_ACCESS | ACC_STRICT | ACC_VARARGS | ACC_SYNTHETIC | MM_ATTR; // + public static final int MM_METHOD = MM_ACCESS | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | MM_ATTR ; // | ACC_MODULE ; + public static final int MM_INNERCLASS = MM_ACCESS | ACC_STATIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM | MM_ATTR ; // | ACC_MODULE ; + + private Modifiers() { + } + + public static Modifiers ModifiersObject() { + if (ref == null) { + ref = new Modifiers(); + } + return ref; + } + + public static boolean validClass(int mod) { + return (mod & ~MM_CLASS) == 0; + } + + public static boolean validInnerClass(int mod) { + return (mod & ~MM_INNERCLASS) == 0; + } + + public static boolean validField(int mod) { + return (mod & ~MM_FIELD) == 0; + } + + public static boolean validMethod(int mod) { + return (mod & ~MM_METHOD) == 0; + } + + public static boolean validInterface(int mod) { + return (mod & ~MM_INTRF) == 0; + } + + public static boolean validAbstractMethod(int mod) { + return (mod & ~MM_A_METHOD) == 0; + } + + public static boolean validInitMethod(int mod) { + return (mod & ~MM_N_METHOD) == 0; + } + + public static boolean validInterfaceMethod(int mod) { +// return (mod & ~MM_ATTR) == MM_I_METHOD; + return ((mod & ~MM_I_METHOD) == 0) && isPublic(mod) && isAbstract(mod); + } + + public static boolean validInterfaceField(int mod) { + return mod == (ACC_STATIC | ACC_PUBLIC | ACC_FINAL); + } + + public static boolean isPublic(int mod) { + return (mod & ACC_PUBLIC) != 0; + } + + public static boolean isPrivate(int mod) { + return (mod & ACC_PRIVATE) != 0; + } + + public static boolean isProtected(int mod) { + return (mod & ACC_PROTECTED) != 0; + } + + public static boolean isInterface(int mod) { + return (mod & ACC_INTERFACE) != 0; + } + + public static boolean isAbstract(int mod) { + return (mod & ACC_ABSTRACT) != 0; + } + + public static boolean isFinal(int mod) { + return (mod & ACC_FINAL) != 0; + } + + public static boolean isStatic(int mod) { + return (mod & ACC_STATIC) != 0; + } + + public static boolean isSynthetic(int mod) { + return (mod & ACC_SYNTHETIC) != 0; + } + + public static boolean isDeprecated(int mod) { + return (mod & DEPRECATED_ATTRIBUTE) != 0; + } + + public static boolean isTransient(int mod) { + return (mod & ACC_TRANSIENT) != 0; + } + + public static boolean isAnnotation(int mod) { + return (mod & ACC_ANNOTATION) != 0; + } + + public static boolean isNative(int mod) { + return (mod & ACC_NATIVE) != 0; + } + + public static boolean isStrict(int mod) { + return (mod & ACC_STRICT) != 0; + } + + public static boolean isEnum(int mod) { + return (mod & ACC_ENUM) != 0; + } + + public static boolean isSuper(int mod) { + return (mod & ACC_SUPER) != 0; + } + /* + public static boolean isModule(int mod) { + return (mod & ACC_MODULE)!=0; + } + * */ + + public static boolean isMandated(int mod) { + return (mod & ACC_MANDATED) != 0; + } + + public static boolean isSynchronized(int mod) { + return (mod & ACC_SYNCHRONIZED) != 0; + } + + public static boolean isBridge(int mod) { + return (mod & ACC_BRIDGE) != 0; + } + + public static boolean isVolatile(int mod) { + return (mod & ACC_VOLATILE) != 0; + } + + public static boolean isVarArgs(int mod) { + return (mod & ACC_VARARGS) != 0; + } + + public static boolean isSyntheticPseudoMod(int mod) { + return (mod & SYNTHETIC_ATTRIBUTE) != 0; + } + + public static boolean isDeprecatedPseudoMod(int mod) { + return (mod & DEPRECATED_ATTRIBUTE) != 0; + } + + public static boolean hasPseudoMod(int mod) { + return isSyntheticPseudoMod(mod) || isDeprecatedPseudoMod(mod); + } + + /* + * Checks that only one (or none) of the Access flags are set. + */ + public static boolean validAccess(int mod) { + boolean retval = true; + switch (mod & MM_ACCESS) { + case 0: + // case ACC_MODULE: + case ACC_PUBLIC: + case ACC_PRIVATE: + case ACC_PROTECTED: + break; + default: + retval = false; + } + + return retval; + + } + + /* + * Are both flags set + * + */ + public static boolean both(int mod, int flagA, int flagB) { + return (mod & (flagA | flagB)) == (flagA | flagB); + } + + /** + * Check the modifier flags for the class + * + * @param env The error reporting environment. + * @param mod The modifier flags being checked + * @param pos the position of the parser in the file + */ + public static void checkClassModifiers(Environment env, int mod, int pos) { + if (isInterface(mod)) { + if (!validInterface(mod)) { + env.error(pos, "warn.invalid.modifier.int"); + } + if (!isAbstract(mod)) { + env.error(pos, "warn.invalid.modifier.int.abs"); + } + } else { + if (!validClass(mod)) { + env.error(pos, "warn.invalid.modifier.class"); + } + if (isAbstract(mod) && Modifiers.isFinal(mod)) { + env.error(pos, "warn.invalid.modifier.class.finabs"); + } + } + } + + /** + * Check the modifier flags for the field + * + * @param cd The ClassData for the current class + * @param mod The modifier flags being checked + * @param pos the position of the parser in the file + */ + public static void checkFieldModifiers(ClassData cd, int mod, int pos) { + Environment env = cd.env; + if (cd.isInterface()) { + // For interfaces + if (!validInterfaceField(mod)) { + env.error(pos, "warn.invalid.modifier.intfield"); + } + } else { + // For non-interfaces + if (!validField(mod)) { + env.error(pos, "warn.invalid.modifier.field"); + } + if (both(mod, ACC_FINAL, ACC_VOLATILE)) { + env.error(pos, "warn.invalid.modifier.fiva"); + } + if (!validAccess(mod)) { + env.error(pos, "warn.invalid.modifier.acc"); + } + } + + } + + /** + * Check the modifier flags for the method + * + * @param cd The ClassData for the current class + * @param mod The modifier flags being checked + * @param pos the position of the parser in the file + */ + public static void checkMethodModifiers(ClassData cd, int mod, int pos, boolean is_init, boolean is_clinit) { + Environment env = cd.env; + if (!is_clinit) { + if (cd.isInterface()) { + if (is_init) { + env.error(pos, "warn.init.in_int"); + } else if (!validInterfaceMethod(mod)) { + int badflags = (mod & ~MM_I_METHOD); + env.error(pos, "warn.invalid.modifier.intmth", toString(badflags, CF_Context.CTX_METHOD) + + " *****" + toString(mod, CF_Context.CTX_METHOD) + "*****"); + } + } else { + if (is_init && !validInitMethod(mod)) { + int badflags = (mod & ~MM_N_METHOD); + env.error(pos, "warn.invalid.modifier.init", toString(badflags, CF_Context.CTX_METHOD) + + " *****" + toString(mod, CF_Context.CTX_METHOD) + "*****"); + } else if (isAbstract(mod)) { + if (!validAbstractMethod(mod)) { + int badflags = (mod & ~MM_A_METHOD); + env.error(pos, "warn.invalid.modifier.abst", toString(badflags, CF_Context.CTX_METHOD) + + " *****" + toString(mod, CF_Context.CTX_METHOD) + "*****"); + } + } else { + if (!validMethod(mod)) { + env.error(pos, "warn.invalid.modifier.mth"); + } + } + if (!validAccess(mod)) { + env.error(pos, "warn.invalid.modifier.acc"); + } + } + } + + } + + /** + * Check the modifier flags for the inner-class + * + * @param cd The ClassData for the current class + * @param mod The modifier flags being checked + * @param pos the position of the parser in the file + */ + public static void checkInnerClassModifiers(ClassData cd, int mod, int pos) { + Environment env = cd.env; + + if (!validInnerClass(mod)) { + int badflags = (mod & ~MM_INNERCLASS); + env.error(pos, "warn.invalid.modifier.innerclass", + toString(badflags, CF_Context.CTX_INNERCLASS) + + " *****" + toString(mod, CF_Context.CTX_INNERCLASS) + "*****"); + } + + } + + private static StringBuffer _accessString(int mod, CF_Context context) { + StringBuffer sb = new StringBuffer(); + // if (isModule(mod)) + // sb.append(Tables.keywordName(Tables.Module) + " "); + if (isPublic(mod)) { + sb.append(Token.PUBLIC.parsekey() + " "); + } + if (isPrivate(mod)) { + sb.append(Token.PRIVATE.parsekey() + " "); + } + if (isProtected(mod)) { + sb.append(Token.PROTECTED.parsekey() + " "); + } + if (isStatic(mod)) { + sb.append(Token.STATIC.parsekey() + " "); + } + if (context == CF_Context.CTX_METHOD && isFinal(mod)) { + sb.append(Token.FINAL.parsekey() + " "); + } + if (context == CF_Context.CTX_FIELD && isTransient(mod)) { + sb.append(Token.TRANSIENT.parsekey() + " "); + } + if (context == CF_Context.CTX_CLASS && isSuper(mod)) { + sb.append(Token.SUPER.parsekey() + " "); + } + if (context == CF_Context.CTX_METHOD && isSynchronized(mod)) { + sb.append(Token.SYNCHRONIZED.parsekey() + " "); + } + if (context == CF_Context.CTX_METHOD) { + if (isBridge(mod)) { + sb.append(Token.BRIDGE.parsekey() + " "); + } + if (isVarArgs(mod)) { + sb.append(Token.VARARGS.parsekey() + " "); + } + if (isNative(mod)) { + sb.append(Token.NATIVE.parsekey() + " "); + } + } + if (isAbstract(mod)) { + if ((context != CF_Context.CTX_CLASS) || !isInterface(mod)) { + sb.append(Token.ABSTRACT.parsekey() + " "); + } + } + if ((context == CF_Context.CTX_CLASS || context == CF_Context.CTX_INNERCLASS || context == CF_Context.CTX_FIELD) && isFinal(mod)) { + sb.append(Token.FINAL.parsekey() + " "); + } + if ((context == CF_Context.CTX_CLASS || context == CF_Context.CTX_INNERCLASS) && isInterface(mod)) { + sb.append(Token.INTERFACE.parsekey() + " "); + } + if (isStrict(mod)) { + sb.append(Token.STRICT.parsekey() + " "); + } + if (isSynthetic(mod)) { + sb.append(Token.SYNTHETIC.parsekey() + " "); + } + if (context == CF_Context.CTX_FIELD && isVolatile(mod)) { + sb.append(Token.VOLATILE.parsekey() + " "); + } + if (isEnum(mod)) { + sb.append(Token.ENUM.parsekey() + " "); + } + if (isMandated(mod)) { + sb.append(Token.MANDATED.parsekey() + " "); + } +// We don't have print identifiers for annotation flags +// if (isAnnotation(mod)) +// sb.append(Tables.keywordName(Tables.ANNOTATION) + " "); + + return sb; + } + + public static String toString(int mod, CF_Context context) { + StringBuffer sb = _accessString(mod, context); + + if (isSyntheticPseudoMod(mod)) { + sb.append("Synthetic(Pseudo) "); + } + if (isDeprecatedPseudoMod(mod)) { + sb.append("Deprecated(Pseudo) "); + } + + return sb.toString(); + } + + public static String accessString(int mod, CF_Context context) { + StringBuffer sb = _accessString(mod, context); + return sb.toString(); + } + +} diff --git a/src/org/openjdk/asmtools/jasm/OpcodeTables.java b/src/org/openjdk/asmtools/jasm/OpcodeTables.java new file mode 100644 index 0000000..c7cc522 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/OpcodeTables.java @@ -0,0 +1,567 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.util.HashMap; + +/** + * + * OpcodeTables + * + * The OpcodeTables class follows a Singleton Pattern. This class contains Enums, that are + * contained in private hash maps (lookup tables and reverse lookup tables). These hash + * maps all have public accessors, which clients use to look-up opcodes. + * + * Tokens in this table carry no external state, and are typically treated as constants. + * They do not need to be reset. + * + */ +public class OpcodeTables { + + /** + * Initialized keyword and token Hash Maps (and Reverse Tables) + */ + static private final int MaxOpcodes = 301; + static private HashMap IntToNormalOpcodes = new HashMap<>(MaxOpcodes); + static private HashMap IntToAllOpcodes = new HashMap<>(MaxOpcodes); + static private HashMap mnemocodes = new HashMap<>(MaxOpcodes); + + static private HashMap IntToPrivOpcode = new HashMap<>(MaxOpcodes); + static private HashMap PrivMnemocodes = new HashMap<>(MaxOpcodes); + + static private HashMap IntToNonPrivOpcode = new HashMap<>(MaxOpcodes); + static private HashMap NonPrivMnemocodes = new HashMap<>(MaxOpcodes); + + static { + // register all of the tokens + for (Opcode opc : Opcode.values()) { + registerOpcode(opc); + } + + } + + private static void registerOpcode(Opcode opc) { + IntToAllOpcodes.put(opc.value, opc); + mnemocodes.put(opc.parsekey, opc); + if (opc.alias != null) { + mnemocodes.put(opc.alias, opc); + } + + if (opc.type == OpcodeType.PRIVELEGED) { + PrivMnemocodes.put(opc.parsekey, opc); + IntToPrivOpcode.put(opc.baseVal, opc); + } else if (opc.type == OpcodeType.NONPRIVELEGED) { + NonPrivMnemocodes.put(opc.parsekey, opc); + IntToNonPrivOpcode.put(opc.baseVal, opc); + } + + } + + public static Opcode opcode(String mnemonic) { + return mnemocodes.get(mnemonic); + } + + public static Opcode opcode(Integer mnem_code) { + return IntToAllOpcodes.get(mnem_code); + } + + /*-------------------------------------------------------- */ + /** + * Marker: describes the type of Opcode. + * + * certain types of Opcodes will be added to specific lookup tables. + */ + static public enum OpcodeType { + NORMAL (0, "Normal"), + NONPRIVELEGED (1, "NonPriv"), + PRIVELEGED (2, "Priv"), + WIDE (3, "Wide"); + + private final Integer value; + private final String printval; + + OpcodeType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + + } + + /*-------------------------------------------------------- */ + /* Opcode Enums */ + static public enum Opcode { + /* Opcodes */ + opc_dead (-2, " opc_dead", 0), + opc_label (-1, "opc_label", 0), + opc_nop (0, "nop", 1), + opc_aconst_null (1, "aconst_null", 1), + opc_iconst_m1 (2, "iconst_m1", 1), + opc_iconst_0 (3, "iconst_0", 1), + opc_iconst_1 (4, "iconst_1", 1), + opc_iconst_2 (5, "iconst_2", 1), + opc_iconst_3 (6, "iconst_3", 1), + opc_iconst_4 (7, "iconst_4", 1), + opc_iconst_5 (8, "iconst_5", 1), + opc_lconst_0 (9, "lconst_0", 1), + opc_lconst_1 (10, "lconst_1", 1), + opc_fconst_0 (11, "fconst_0", 1), + opc_fconst_1 (12, "fconst_1", 1), + opc_fconst_2 (13, "fconst_2", 1), + opc_dconst_0 (14, "dconst_0", 1), + opc_dconst_1 (15, "dconst_1", 1), + opc_bipush (16, "bipush", 2), + opc_sipush (17, "sipush", 3), + opc_ldc (18, "ldc", 2), + opc_ldc_w (19, "ldc_w", 3), + opc_ldc2_w (20, "ldc2_w", 3), + opc_iload (21, "iload", 2), + opc_lload (22, "lload", 2), + opc_fload (23, "fload", 2), + opc_dload (24, "dload", 2), + opc_aload (25, "aload", 2), + opc_iload_0 (26, "iload_0", 1), + opc_iload_1 (27, "iload_1", 1), + opc_iload_2 (28, "iload_2", 1), + opc_iload_3 (29, "iload_3", 1), + opc_lload_0 (30, "lload_0", 1), + opc_lload_1 (31, "lload_1", 1), + opc_lload_2 (32, "lload_2", 1), + opc_lload_3 (33, "lload_3", 1), + opc_fload_0 (34, "fload_0", 1), + opc_fload_1 (35, "fload_1", 1), + opc_fload_2 (36, "fload_2", 1), + opc_fload_3 (37, "fload_3", 1), + opc_dload_0 (38, "dload_0", 1), + opc_dload_1 (39, "dload_1", 1), + opc_dload_2 (40, "dload_2", 1), + opc_dload_3 (41, "dload_3", 1), + opc_aload_0 (42, "aload_0", 1), + opc_aload_1 (43, "aload_1", 1), + opc_aload_2 (44, "aload_2", 1), + opc_aload_3 (45, "aload_3", 1), + opc_iaload (46, "iaload", 1), + opc_laload (47, "laload", 1), + opc_faload (48, "faload", 1), + opc_daload (49, "daload", 1), + opc_aaload (50, "aaload", 1), + opc_baload (51, "baload", 1), + opc_caload (52, "caload", 1), + opc_saload (53, "saload", 1), + opc_istore (54, "istore", 2), + opc_lstore (55, "lstore", 2), + opc_fstore (56, "fstore", 2), + opc_dstore (57, "dstore", 2), + opc_astore (58, "astore", 2), + opc_istore_0 (59, "istore_0", 1), + opc_istore_1 (60, "istore_1", 1), + opc_istore_2 (61, "istore_2", 1), + opc_istore_3 (62, "istore_3", 1), + opc_lstore_0 (63, "lstore_0", 1), + opc_lstore_1 (64, "lstore_1", 1), + opc_lstore_2 (65, "lstore_2", 1), + opc_lstore_3 (66, "lstore_3", 1), + opc_fstore_0 (67, "fstore_0", 1), + opc_fstore_1 (68, "fstore_1", 1), + opc_fstore_2 (69, "fstore_2", 1), + opc_fstore_3 (70, "fstore_3", 1), + opc_dstore_0 (71, "dstore_0", 1), + opc_dstore_1 (72, "dstore_1", 1), + opc_dstore_2 (73, "dstore_2", 1), + opc_dstore_3 (74, "dstore_3", 1), + opc_astore_0 (75, "astore_0", 1), + opc_astore_1 (76, "astore_1", 1), + opc_astore_2 (77, "astore_2", 1), + opc_astore_3 (78, "astore_3", 1), + opc_iastore (79, "iastore", 1), + opc_lastore (80, "lastore", 1), + opc_fastore (81, "fastore", 1), + opc_dastore (82, "dastore", 1), + opc_aastore (83, "aastore", 1), + opc_bastore (84, "bastore", 1), + opc_castore (85, "castore", 1), + opc_sastore (86, "sastore", 1), + opc_pop (87, "pop", 1), + opc_pop2 (88, "pop2", 1), + opc_dup (89, "dup", 1), + opc_dup_x1 (90, "dup_x1", 1), + opc_dup_x2 (91, "dup_x2", 1), + opc_dup2 (92, "dup2", 1), + opc_dup2_x1 (93, "dup2_x1", 1), + opc_dup2_x2 (94, "dup2_x2", 1), + opc_swap (95, "swap", 1), + opc_iadd (96, "iadd", 1), + opc_ladd (97, "ladd", 1), + opc_fadd (98, "fadd", 1), + opc_dadd (99, "dadd", 1), + opc_isub (100, "isub", 1), + opc_lsub (101, "lsub", 1), + opc_fsub (102, "fsub", 1), + opc_dsub (103, "dsub", 1), + opc_imul (104, "imul", 1), + opc_lmul (105, "lmul", 1), + opc_fmul (106, "fmul", 1), + opc_dmul (107, "dmul", 1), + opc_idiv (108, "idiv", 1), + opc_ldiv (109, "ldiv", 1), + opc_fdiv (110, "fdiv", 1), + opc_ddiv (111, "ddiv", 1), + opc_irem (112, "irem", 1), + opc_lrem (113, "lrem", 1), + opc_frem (114, "frem", 1), + opc_drem (115, "drem", 1), + opc_ineg (116, "ineg", 1), + opc_lneg (117, "lneg", 1), + opc_fneg (118, "fneg", 1), + opc_dneg (119, "dneg", 1), + opc_ishl (120, "ishl", 1), + opc_lshl (121, "lshl", 1), + opc_ishr (122, "ishr", 1), + opc_lshr (123, "lshr", 1), + opc_iushr (124, "iushr", 1), + opc_lushr (125, "lushr", 1), + opc_iand (126, "iand", 1), + opc_land (127, "land", 1), + opc_ior (128, "ior", 1), + opc_lor (129, "lor", 1), + opc_ixor (130, "ixor", 1), + opc_lxor (131, "lxor", 1), + opc_iinc (132, "iinc", 3), + opc_i2l (133, "i2l", 1), + opc_i2f (134, "i2f", 1), + opc_i2d (135, "i2d", 1), + opc_l2i (136, "l2i", 1), + opc_l2f (137, "l2f", 1), + opc_l2d (138, "l2d", 1), + opc_f2i (139, "f2i", 1), + opc_f2l (140, "f2l", 1), + opc_f2d (141, "f2d", 1), + opc_d2i (142, "d2i", 1), + opc_d2l (143, "d2l", 1), + opc_d2f (144, "d2f", 1), + opc_i2b (145, "i2b", 1), + opc_i2c (146, "i2c", 1), + opc_i2s (147, "i2s", 1), + opc_lcmp (148, "lcmp", 1), + opc_fcmpl (149, "fcmpl", 1), + opc_fcmpg (150, "fcmpg", 1), + opc_dcmpl (151, "dcmpl", 1), + opc_dcmpg (152, "dcmpg", 1), + opc_ifeq (153, "ifeq", 3), + opc_ifne (154, "ifne", 3), + opc_iflt (155, "iflt", 3), + opc_ifge (156, "ifge", 3), + opc_ifgt (157, "ifgt", 3), + opc_ifle (158, "ifle", 3), + opc_if_icmpeq (159, "if_icmpeq", 3), + opc_if_icmpne (160, "if_icmpne", 3), + opc_if_icmplt (161, "if_icmplt", 3), + opc_if_icmpge (162, "if_icmpge", 3), + opc_if_icmpgt (163, "if_icmpgt", 3), + opc_if_icmple (164, "if_icmple", 3), + opc_if_acmpeq (165, "if_acmpeq", 3), + opc_if_acmpne (166, "if_acmpne", 3), + opc_goto (167, "goto", 3), + opc_jsr (168, "jsr", 3), + opc_ret (169, "ret", 2), + opc_tableswitch (170, "tableswitch", 99), + opc_lookupswitch (171, "lookupswitch", 99), + opc_ireturn (172, "ireturn", 1), + opc_lreturn (173, "lreturn", 1), + opc_freturn (174, "freturn", 1), + opc_dreturn (175, "dreturn", 1), + opc_areturn (176, "areturn", 1), + opc_return (177, "return", 1), + opc_getstatic (178, "getstatic", 3), + opc_putstatic (179, "putstatic", 3), + opc_getfield (180, "getfield", 3), + opc_putfield (181, "putfield", 3), + opc_invokevirtual (182, "invokevirtual", 3), + opc_invokespecial (183, "invokespecial", "invokenonvirtual", 3), + opc_invokestatic (184, "invokestatic", 3), + opc_invokeinterface (185, "invokeinterface", 5), + opc_invokedynamic (186, "invokedynamic", 5), + opc_new (187, "new", 3), + opc_newarray (188, "newarray", 2), + opc_anewarray (189, "anewarray", 3), + opc_arraylength (190, "arraylength", 1), + opc_athrow (191, "athrow", 1), + opc_checkcast (192, "checkcast", 3), + opc_instanceof (193, "instanceof", 3), + opc_monitorenter (194, "monitorenter", 1), + opc_monitorexit (195, "monitorexit", 1), + + // Wide Marker (not really an opcode) + opc_wide (196, null, 0), + opc_multianewarray (197, "multianewarray", 4), + opc_ifnull (198, "ifnull", 3), + opc_ifnonnull (199, "ifnonnull", 3), + opc_goto_w (200, "goto_w", 5), + opc_jsr_w (201, "jsr_w", 5), +// opc_bytecode 202 (202, "bytecode 202", 1), + /* Pseudo-instructions */ + opc_bytecode (203, "bytecode", 1), + opc_try (204, "try", 0), + opc_endtry (205, "endtry", 0), + opc_catch (206, "catch", 0), + opc_var (207, "var", 0), + opc_endvar (208, "endvar", 0), + opc_locals_map (209, "locals_map", 0), + opc_stack_map (210, "stack_map", 0), + opc_stack_frame_type (211, "stack_frame_type", 0), + + + // Priv/NonPriv Marker (not really an opcode) + opc_nonpriv (254, "priv", 0), + opc_priv (255, "nonpriv", 0), + + + /* Wide instructions */ + opc_iload_w (opc_iload.value, "iload_w", 4, OpcodeType.WIDE), + opc_lload_w (opc_lload.value, "lload_w", 4, OpcodeType.WIDE), + opc_fload_w (opc_fload.value, "fload_w", 4, OpcodeType.WIDE), + opc_dload_w (opc_dload.value, "dload_w", 4, OpcodeType.WIDE), + opc_aload_w (opc_aload.value, "aload_w", 4, OpcodeType.WIDE), + opc_istore_w (opc_istore.value, "istore_w", 4, OpcodeType.WIDE), + opc_lstore_w (opc_lstore.value, "lstore_w", 4, OpcodeType.WIDE), + opc_fstore_w (opc_fstore.value, "fstore_w", 4, OpcodeType.WIDE), + opc_dstore_w (opc_dstore.value, "dstore_w", 4, OpcodeType.WIDE), + opc_astore_w (opc_astore.value, "astore_w", 4, OpcodeType.WIDE), + opc_ret_w (opc_ret.value, "ret_w", 4, OpcodeType.WIDE), + opc_iinc_w (opc_iinc.value, "iinc_w", 6, OpcodeType.WIDE), + + + /* Priveleged instructions */ + opc_load_ubyte (0, "load_ubyte", OpcodeType.NONPRIVELEGED), + opc_priv_load_ubyte (0, "priv_load_ubyte", OpcodeType.PRIVELEGED), + opc_load_byte (1, "load_byte", OpcodeType.NONPRIVELEGED), + opc_priv_load_byte (1, "priv_load_byte", OpcodeType.PRIVELEGED), + opc_load_char (2, "load_char", OpcodeType.NONPRIVELEGED), + opc_priv_load_char (2, "priv_load_char", OpcodeType.PRIVELEGED), + opc_load_short (3, "load_short", OpcodeType.NONPRIVELEGED), + opc_priv_load_short (3, "priv_load_short", OpcodeType.PRIVELEGED), + opc_load_word (4, "load_word", OpcodeType.NONPRIVELEGED), + opc_priv_load_word (4, "priv_load_word", OpcodeType.PRIVELEGED), + opc_load_char_oe (10, "load_char_oe", OpcodeType.NONPRIVELEGED), + opc_priv_load_char_oe (10, "priv_load_char_oe", OpcodeType.PRIVELEGED), + opc_load_short_oe (11, "load_short_oe", OpcodeType.NONPRIVELEGED), + opc_priv_load_short_oe (11, "priv_load_short_oe", OpcodeType.PRIVELEGED), + opc_load_word_oe (12, "load_word_oe", OpcodeType.NONPRIVELEGED), + opc_priv_load_word_oe (12, "priv_load_word_oe", OpcodeType.PRIVELEGED), + opc_ncload_ubyte (16, "ncload_ubyte", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_ubyte (16, "priv_ncload_ubyte", OpcodeType.PRIVELEGED), + opc_ncload_byte (17, "ncload_byte", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_byte (17, "priv_ncload_byte", OpcodeType.PRIVELEGED), + opc_ncload_char (18, "ncload_char", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_char (18, "priv_ncload_char", OpcodeType.PRIVELEGED), + opc_ncload_short (19, "ncload_short", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_short (19, "priv_ncload_short", OpcodeType.PRIVELEGED), + opc_ncload_word (20, "ncload_word", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_word (20, "priv_ncload_word", OpcodeType.PRIVELEGED), + opc_ncload_char_oe (26, "ncload_char_oe", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_char_oe (26, "priv_ncload_char_oe", OpcodeType.PRIVELEGED), + opc_ncload_short_oe (27, "ncload_short_oe", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_short_oe (27, "priv_ncload_short_oe", OpcodeType.PRIVELEGED), + opc_ncload_word_oe (28, "ncload_word_oe", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_word_oe (28, "priv_ncload_word_oe", OpcodeType.PRIVELEGED), + opc_cache_flush (30, "cache_flush", OpcodeType.NONPRIVELEGED), + opc_priv_cache_flush (30, "priv_cache_flush", OpcodeType.PRIVELEGED), + opc_store_byte (32, "store_byte", OpcodeType.NONPRIVELEGED), + opc_priv_store_byte (32, "priv_store_byte", OpcodeType.PRIVELEGED), + opc_store_short (34, "store_short", OpcodeType.NONPRIVELEGED), + opc_priv_store_short (34, "priv_store_short", OpcodeType.PRIVELEGED), + opc_store_word (36, "store_word", OpcodeType.NONPRIVELEGED), + opc_priv_store_word (36, "priv_store_word", OpcodeType.PRIVELEGED), + opc_store_short_oe (42, "store_short_oe", OpcodeType.NONPRIVELEGED), + opc_priv_store_short_oe (42, "priv_store_short_oe", OpcodeType.PRIVELEGED), + opc_store_word_oe (44, "store_word_oe", OpcodeType.NONPRIVELEGED), + opc_priv_store_word_oe (44, "priv_store_word_oe", OpcodeType.PRIVELEGED), + opc_ncstore_byte (48, "ncstore_byte", OpcodeType.NONPRIVELEGED), + opc_priv_ncstore_byte (48, "priv_ncstore_byte", OpcodeType.PRIVELEGED), + opc_ncstore_short (50, "ncstore_short", OpcodeType.NONPRIVELEGED), + opc_priv_ncstore_short (50, "priv_ncstore_short", OpcodeType.PRIVELEGED), + opc_ncstore_word (52, "ncstore_word", OpcodeType.NONPRIVELEGED), + opc_priv_ncstore_word (52, "priv_ncstore_word", OpcodeType.PRIVELEGED), + opc_ncstore_short_oe (58, "ncstore_short_oe", OpcodeType.NONPRIVELEGED), + opc_priv_ncstore_short_oe (58, "priv_ncstore_short_oe", OpcodeType.PRIVELEGED), + opc_ncstore_word_oe (60, "ncstore_word_oe", OpcodeType.NONPRIVELEGED), + opc_priv_ncstore_word_oe (60, "priv_ncstore_word_oe", OpcodeType.PRIVELEGED), + opc_zero_line (62, "zero_line", OpcodeType.NONPRIVELEGED), + opc_priv_zero_line (62, "priv_zero_line", OpcodeType.PRIVELEGED), + opc_ret_from_sub (5, "ret_from_sub", OpcodeType.NONPRIVELEGED), + opc_enter_sync_method (63, "enter_sync_method", OpcodeType.NONPRIVELEGED), + opc_priv_ret_from_trap (5, "priv_ret_from_trap", OpcodeType.PRIVELEGED), + opc_priv_read_dcache_tag (6, "priv_read_dcache_tag", OpcodeType.PRIVELEGED), + opc_priv_read_dcache_data (7, "priv_read_dcache_data", OpcodeType.PRIVELEGED), + opc_priv_read_icache_tag (14, "priv_read_icache_tag", OpcodeType.PRIVELEGED), + opc_priv_read_icache_data (15, "priv_read_icache_data", OpcodeType.PRIVELEGED), + opc_priv_powerdown (22, "priv_powerdown", OpcodeType.PRIVELEGED), + opc_priv_read_scache_data (23, "priv_read_scache_data", OpcodeType.PRIVELEGED), + opc_priv_cache_index_flush (31, "priv_cache_index_flush", OpcodeType.PRIVELEGED), + opc_priv_write_dcache_tag (38, "priv_write_dcache_tag", OpcodeType.PRIVELEGED), + opc_priv_write_dcache_data (39, "priv_write_dcache_data", OpcodeType.PRIVELEGED), + opc_priv_write_icache_tag (46, "priv_write_icache_tag", OpcodeType.PRIVELEGED), + opc_priv_write_icache_data (47, "priv_write_icache_data", OpcodeType.PRIVELEGED), + opc_priv_reset (54, "priv_reset", OpcodeType.PRIVELEGED), + opc_priv_write_scache_data (55, "priv_write_scache_data", OpcodeType.PRIVELEGED), + opc_priv_read_reg_0 (64, "priv_read_reg_0", OpcodeType.PRIVELEGED), + opc_priv_read_reg_1 (65, "priv_read_reg_1", OpcodeType.PRIVELEGED), + opc_priv_read_reg_2 (66, "priv_read_reg_2", OpcodeType.PRIVELEGED), + opc_priv_read_reg_3 (67, "priv_read_reg_3", OpcodeType.PRIVELEGED), + opc_priv_read_reg_4 (68, "priv_read_reg_4", OpcodeType.PRIVELEGED), + opc_priv_read_reg_5 (69, "priv_read_reg_5", OpcodeType.PRIVELEGED), + opc_priv_read_reg_6 (70, "priv_read_reg_6", OpcodeType.PRIVELEGED), + opc_priv_read_reg_7 (71, "priv_read_reg_7", OpcodeType.PRIVELEGED), + opc_priv_read_reg_8 (72, "priv_read_reg_8", OpcodeType.PRIVELEGED), + opc_priv_read_reg_9 (73, "priv_read_reg_9", OpcodeType.PRIVELEGED), + opc_priv_read_reg_10 (74, "priv_read_reg_10", OpcodeType.PRIVELEGED), + opc_priv_read_reg_11 (75, "priv_read_reg_11", OpcodeType.PRIVELEGED), + opc_priv_read_reg_12 (76, "priv_read_reg_12", OpcodeType.PRIVELEGED), + opc_priv_read_reg_13 (77, "priv_read_reg_13", OpcodeType.PRIVELEGED), + opc_priv_read_reg_14 (78, "priv_read_reg_14", OpcodeType.PRIVELEGED), + opc_priv_read_reg_15 (79, "priv_read_reg_15", OpcodeType.PRIVELEGED), + opc_priv_read_reg_16 (80, "priv_read_reg_16", OpcodeType.PRIVELEGED), + opc_priv_read_reg_17 (81, "priv_read_reg_17", OpcodeType.PRIVELEGED), + opc_priv_read_reg_18 (82, "priv_read_reg_18", OpcodeType.PRIVELEGED), + opc_priv_read_reg_19 (83, "priv_read_reg_19", OpcodeType.PRIVELEGED), + opc_priv_read_reg_20 (84, "priv_read_reg_20", OpcodeType.PRIVELEGED), + opc_priv_read_reg_21 (85, "priv_read_reg_21", OpcodeType.PRIVELEGED), + opc_priv_read_reg_22 (86, "priv_read_reg_22", OpcodeType.PRIVELEGED), + opc_priv_read_reg_23 (87, "priv_read_reg_23", OpcodeType.PRIVELEGED), + opc_priv_read_reg_24 (88, "priv_read_reg_24", OpcodeType.PRIVELEGED), + opc_priv_read_reg_25 (89, "priv_read_reg_25", OpcodeType.PRIVELEGED), + opc_priv_read_reg_26 (90, "priv_read_reg_26", OpcodeType.PRIVELEGED), + opc_priv_read_reg_27 (91, "priv_read_reg_27", OpcodeType.PRIVELEGED), + opc_priv_read_reg_28 (92, "priv_read_reg_28", OpcodeType.PRIVELEGED), + opc_priv_read_reg_29 (93, "priv_read_reg_29", OpcodeType.PRIVELEGED), + opc_priv_read_reg_30 (94, "priv_read_reg_30", OpcodeType.PRIVELEGED), + opc_priv_read_reg_31 (95, "priv_read_reg_31", OpcodeType.PRIVELEGED), + opc_priv_write_reg_0 (96, "priv_write_reg_0", OpcodeType.PRIVELEGED), + opc_priv_write_reg_1 (97, "priv_write_reg_1", OpcodeType.PRIVELEGED), + opc_priv_write_reg_2 (98, "priv_write_reg_2", OpcodeType.PRIVELEGED), + opc_priv_write_reg_3 (99, "priv_write_reg_3", OpcodeType.PRIVELEGED), + opc_priv_write_reg_4 (100, "priv_write_reg_4", OpcodeType.PRIVELEGED), + opc_priv_write_reg_5 (101, "priv_write_reg_5", OpcodeType.PRIVELEGED), + opc_priv_write_reg_6 (102, "priv_write_reg_6", OpcodeType.PRIVELEGED), + opc_priv_write_reg_7 (103, "priv_write_reg_7", OpcodeType.PRIVELEGED), + opc_priv_write_reg_8 (104, "priv_write_reg_8", OpcodeType.PRIVELEGED), + opc_priv_write_reg_9 (105, "priv_write_reg_9", OpcodeType.PRIVELEGED), + opc_priv_write_reg_10 (106, "priv_write_reg_10", OpcodeType.PRIVELEGED), + opc_priv_write_reg_11 (107, "priv_write_reg_11", OpcodeType.PRIVELEGED), + opc_priv_write_reg_12 (108, "priv_write_reg_12", OpcodeType.PRIVELEGED), + opc_priv_write_reg_13 (109, "priv_write_reg_13", OpcodeType.PRIVELEGED), + opc_priv_write_reg_14 (110, "priv_write_reg_14", OpcodeType.PRIVELEGED), + opc_priv_write_reg_15 (111, "priv_write_reg_15", OpcodeType.PRIVELEGED), + opc_priv_write_reg_16 (112, "priv_write_reg_16", OpcodeType.PRIVELEGED), + opc_priv_write_reg_17 (113, "priv_write_reg_17", OpcodeType.PRIVELEGED), + opc_priv_write_reg_18 (114, "priv_write_reg_18", OpcodeType.PRIVELEGED), + opc_priv_write_reg_19 (115, "priv_write_reg_19", OpcodeType.PRIVELEGED), + opc_priv_write_reg_20 (116, "priv_write_reg_20", OpcodeType.PRIVELEGED), + opc_priv_write_reg_21 (117, "priv_write_reg_21", OpcodeType.PRIVELEGED), + opc_priv_write_reg_22 (118, "priv_write_reg_22", OpcodeType.PRIVELEGED), + opc_priv_write_reg_23 (119, "priv_write_reg_23", OpcodeType.PRIVELEGED), + opc_priv_write_reg_24 (120, "priv_write_reg_24", OpcodeType.PRIVELEGED), + opc_priv_write_reg_25 (121, "priv_write_reg_25", OpcodeType.PRIVELEGED), + opc_priv_write_reg_26 (122, "priv_write_reg_26", OpcodeType.PRIVELEGED), + opc_priv_write_reg_27 (123, "priv_write_reg_27", OpcodeType.PRIVELEGED), + opc_priv_write_reg_28 (124, "priv_write_reg_28", OpcodeType.PRIVELEGED), + opc_priv_write_reg_29 (125, "priv_write_reg_29", OpcodeType.PRIVELEGED), + opc_priv_write_reg_30 (126, "priv_write_reg_30", OpcodeType.PRIVELEGED), + opc_priv_write_reg_31 (127, "priv_write_reg_31", OpcodeType.PRIVELEGED); + + private Integer value; + private String parsekey; + private String alias; + private Integer length; + private Integer baseVal; + private OpcodeType type; + + Opcode(Integer val, String parse, OpcodeType tp) { + init(val, parse, null, 2, tp); + } + + Opcode(Integer val, String parse, int len, OpcodeType tp) { + init(val, parse, null, len, tp); + } + + Opcode(Integer val, String parse) { + init(val, parse, null, 2, OpcodeType.NORMAL); + } + + Opcode(Integer val, String parse, int len) { + init(val, parse, null, len, OpcodeType.NORMAL); + } + + Opcode(Integer val, String parse, String als, int len) { + init(val, parse, als, len, OpcodeType.NORMAL); + } + + Opcode(Integer val, String parse, String als, int len, OpcodeType tp) { + init(val, parse, als, len, tp); + } + + private void init(Integer val, String parse, String als, int len, OpcodeType tp) { + type = tp; + baseVal = null; + switch (tp) { + case NORMAL: + value = val; + break; + case WIDE: + value = (opc_wide.value << 8) | val; + break; + case PRIVELEGED: + value = (opc_priv.value * 0xFF) + val; + baseVal = val; + break; + case NONPRIVELEGED: + value = (opc_nonpriv.value * 0xFF) + val; + baseVal = val; + break; + } + parsekey = parse; + alias = als; + length = len; + } + + public Integer value() { + return value; + } + + public int length() { + return length; + } + + public String parsekey() { + return parsekey; + } + + public OpcodeType type() { + return type; + } + } + +} diff --git a/src/org/openjdk/asmtools/jasm/ParseBase.java b/src/org/openjdk/asmtools/jasm/ParseBase.java new file mode 100644 index 0000000..5e7dc96 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ParseBase.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +/** + * + */ +public class ParseBase { + + protected boolean debugFlag; + protected Scanner scanner; + protected Parser parser; + protected Environment env; + + public ParseBase() { + init(null, null, null); + } + + public void init(Scanner scnr, Parser prsr, Environment envr) { + debugFlag = false; + scanner = scnr; + parser = prsr; + env = envr; + } + + public void enableDebug(boolean debState) { + debugFlag = debState; + } + + protected void debugStr(String str) { + if (debugFlag) { + env.traceln(str); + } + } + + protected void debugScan(String str) { + if (debugFlag) { + scanner.debugScan(str); + } + } + +} diff --git a/src/org/openjdk/asmtools/jasm/Parser.java b/src/org/openjdk/asmtools/jasm/Parser.java new file mode 100644 index 0000000..4d62dc4 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Parser.java @@ -0,0 +1,1457 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.Constants.DEFAULT_MAJOR_VERSION; +import static org.openjdk.asmtools.jasm.Constants.DEFAULT_MINOR_VERSION; +import static org.openjdk.asmtools.jasm.ConstantPool.*; +import static org.openjdk.asmtools.jasm.RuntimeConstants.*; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.JasmTokens.*; +import java.io.IOException; +import java.util.ArrayList; + +/** + * This class is used to parse Jasm statements and expressions. + * The result is a parse tree.

+ * + * This class implements an operator precedence parser. Errors are + * reported to the Environment object, if the error can't be + * resolved immediately, a SyntaxError exception is thrown.

+ * + * Error recovery is implemented by catching Scanner.SyntaxError exceptions + * and discarding input scanner.tokens until an input token is reached that + * is possibly a legal continuation.

+ * + * The parse tree that is constructed represents the input + * exactly (no rewrites to simpler forms). This is important + * if the resulting tree is to be used for code formatting in + * a programming environment. Currently only documentation comments + * are retained.

+ * + * A parser owns several components (scanner, constant-parser, + * instruction-parser, annotations-parser) to which it delegates certain + * parsing responsibilities. This parser contains functions to parse the + * overall form of a class, and any members (fields, methods, inner-classes). + *

+ * + * Syntax errors, should always be caught inside the + * parser for error recovery. + */ +class Parser extends ParseBase { + + /*-------------------------------------------------------- */ + /* Annotation Inner Classes */ + /** + * The main compile error for the parser + */ + public static class CompilerError extends Error { + + CompilerError(String message) { + super(message); + } + } + /*-------------------------------------------------------- */ + /* Parser Fields */ + + private ArrayList clsDataList = new ArrayList<>(); + protected ClassData cd = null; + protected ConstantPool pool = null; + private MethodData curMethod; + protected CodeAttr curCode; + private String pkg = null; + private String pkgPrefix = ""; + private ArrayList pkgAnnttns = null; + /* RemoveModules + private String mdl = null; + private ArrayList mdlAnnttns = null; + */ + private ArrayList clsAnnttns = null; + private ArrayList memberAnnttns = null; + private short major_version = DEFAULT_MAJOR_VERSION; + private short minor_version = DEFAULT_MINOR_VERSION; + private boolean explicitcp = false; + + /** other parser components */ + private ParserAnnotation annotParser = null; // For parsing Annotations + private ParserCP cpParser = null; // for parsing Constants + private ParserInstr instrParser = null; // for parsing Instructions + + + /*-------------------------------------------------------- */ + + + /** + * Create a parser + */ + /* + protected Parser(Environment sf) throws IOException { + this.scanner = new Scanner(sf); + } + * */ + + protected Parser(Environment sf, short major_version, short minor_version) throws IOException { + super.init(new Scanner(sf), this, sf); + this.major_version = major_version; + this.minor_version = minor_version; + this.annotParser = new ParserAnnotation(scanner, this, env); + this.cpParser = new ParserCP(scanner, this, env); + this.instrParser = new ParserInstr(scanner, this, cpParser, env); + } + + + protected void setDebugFlags(boolean debugScanner, boolean debugMembers, + boolean debugCP, boolean debugAnnot, boolean debugInstr) { + + enableDebug(debugMembers); + scanner.enableDebug(debugScanner); + cpParser.enableDebug(debugCP); + annotParser.enableDebug(debugAnnot); + instrParser.enableDebug(debugInstr); + } + + + + + /*---------------------------------------------*/ + + protected String encodeClassString(String classname) { + return "L" + classname + ";"; + } + + /** + * Parses version in package statements + */ + + protected final void parseVersionPkg() throws IOException { + if (scanner.token == Token.SEMICOLON) { + return; + } + parse_ver: { + if (scanner.token != Token.VERSION) { + break parse_ver; + } + scanner.scan(); + if (scanner.token != Token.INTVAL) { + break parse_ver; + } + major_version = (short)scanner.intValue; + scanner.scan(); + if (scanner.token != Token.COLON) { + break parse_ver; + } + scanner.scan(); + if (scanner.token != Token.INTVAL) { + break parse_ver; + } + minor_version = (short)scanner.intValue; + scanner.scan(); + debugScan(" [Parser.parseVersionPkg]: " + major_version + ":" + minor_version); + return; + } + env.error(scanner.pos, "version.expected"); + throw new Scanner.SyntaxError(); + } + + protected final void parseVersion() throws IOException { + if (scanner.token == Token.LBRACE) { + return; + } + parse_ver: { + if (scanner.token != Token.VERSION) { + break parse_ver; + } + scanner.scan(); + if (scanner.token != Token.INTVAL) { + break parse_ver; + } + cd.major_version = (short)scanner.intValue; + scanner.scan(); + if (scanner.token != Token.COLON) { + break parse_ver; + } + scanner.scan(); + if (scanner.token != Token.INTVAL) { + break parse_ver; + } + cd.minor_version = (short)scanner.intValue; + scanner.scan(); + debugStr("parseVersion: " + cd.major_version + ":" + cd.minor_version); + return; + } + env.error(scanner.pos, "version.expected"); + throw new Scanner.SyntaxError(); + } + + /** + * Parse an internal name: identifier. + */ + protected String parseIdent() throws Scanner.SyntaxError, IOException { + String v = scanner.idValue; + scanner.expect(Token.IDENT); + return v; + } + + /** + * Parse a local variable + */ + protected void parseLocVarDef() throws Scanner.SyntaxError, IOException { + if (scanner.token == Token.INTVAL) { + int v = scanner.intValue; + scanner.scan(); + curCode.LocVarDataDef(v); + } else { + String name = scanner.stringValue, type; + scanner.expect(Token.IDENT); + if (scanner.token == Token.COLON) { + scanner.scan(); + type = parseIdent(); + } else { + type = "I"; // TBD + } + curCode.LocVarDataDef(name, pool.FindCellAsciz(type)); + } + } + + protected Argument parseLocVarRef() throws Scanner.SyntaxError, IOException { + if (scanner.token == Token.INTVAL) { + int v = scanner.intValue; + scanner.scan(); + return new Argument(v); + } else { + String name = scanner.stringValue; + scanner.expect(Token.IDENT); + return curCode.LocVarDataRef(name); + } + } + + protected void parseLocVarEnd() throws Scanner.SyntaxError, IOException { + if (scanner.token == Token.INTVAL) { + int v = scanner.intValue; + scanner.scan(); + curCode.LocVarDataEnd(v); + } else { + String name = scanner.stringValue; + scanner.expect(Token.IDENT); + curCode.LocVarDataEnd(name); + } + } + + protected void parseMapItem(DataVector map) throws Scanner.SyntaxError, IOException { + StackMapType itemType = stackMapType(scanner.intValue); + ConstType tag = null; + Argument arg = null; + Token ptoken = scanner.token; + int iValue = scanner.intValue; + String sValue = scanner.stringValue; + scanner.scan(); + resolve: { + switch (ptoken) { + case INTVAL: + break resolve; + case CLASS: + itemType = StackMapType.ITEM_Object; + tag = ConstType.CONSTANT_CLASS; + break resolve; + case CPINDEX: + itemType = StackMapType.ITEM_Object; + arg = pool.getCell(iValue); + break resolve; + case IDENT: + itemType = stackMapType(sValue); + ConstType tg = Tables.tag(sValue); +// tag = (tg == null) ? 0 : tg.value; + tag = tg; + if (itemType != null) { // itemType OK + if ((tag != null) // ambiguity: "int," or "int 77,"? + && (scanner.token != Token.SEMICOLON) + && (scanner.token != Token.COMMA)) { + itemType=StackMapType.ITEM_Object; + } + break resolve; + } else if (tag != null) { // tag OK + itemType=StackMapType.ITEM_Object; + break resolve; + } + } + // resolution failed: + itemType = StackMapType.ITEM_Bogus; + env.error("itemtype.expected", "<" + ptoken.printval() + ">"); + } + switch (itemType) { + case ITEM_Object: // followed by CP index + if (arg == null) { + arg = pool.FindCell(cpParser.parseConstValue(tag)); + } + map.addElement(new StackMapData.StackMapItem2(itemType, arg)); + break; + case ITEM_NewObject: // followed by label + arg = instrParser.parseLabelRef(); + map.addElement(new StackMapData.StackMapItem2(itemType, arg)); + break; + default: + map.addElement(new StackMapData.StackMapItem1(itemType)); + } + } + + + /** + * Parse an external name: CPINDEX, string, or identifier. + */ + protected ConstCell parseName() throws Scanner.SyntaxError, IOException { + debugScan("------- [Parser.parseName]: "); + String v; + switch (scanner.token) { + case CPINDEX: { + int cpx = scanner.intValue; + scanner.scan(); + return pool.getCell(cpx); + } + case STRINGVAL: + v = scanner.stringValue; + scanner.scan(); + return pool.FindCellAsciz(v); + + // In many cases, Identifiers can correctly have the same + // names as keywords. We need to allow these. + case SYNTHETIC: + case DEPRECATED: + case VERSION: + case BITS: + case STACK: + case LOCAL: + case OF: + case NAN: + case INNERCLASS: + case STRICT: + case FIELDREF: + case METHODREF: + case IDENT: + case BRIDGE: + v = scanner.idValue; + scanner.scan(); + return pool.FindCellAsciz(v); + default: + env.error(scanner.pos, "name.expected", scanner.token); + throw new Scanner.SyntaxError(); + } + } + + /** + * Parses a field or method reference for method handle. + */ + protected ConstCell parseMethodHandle(SubTag subtag) throws Scanner.SyntaxError, IOException { + ConstCell refCell; + switch (subtag) { + case REF_GETFIELD: case REF_GETSTATIC: + case REF_PUTFIELD: case REF_PUTSTATIC: + refCell = pool.FindCell(cpParser.parseConstValue(ConstType.CONSTANT_FIELD)); + break; + case REF_INVOKEVIRTUAL: case REF_INVOKESTATIC: + case REF_INVOKESPECIAL: case REF_NEWINVOKESPECIAL: + refCell = pool.FindCell(cpParser.parseConstValue(ConstType.CONSTANT_METHOD)); + break; + case REF_INVOKEINTERFACE: + refCell = pool.FindCell(cpParser.parseConstValue(ConstType.CONSTANT_INTERFACEMETHOD)); + break; + default: + // should not reach + throw new Scanner.SyntaxError(); + } + return refCell; + } + + /** + * Parses a sub-tag value in method handle. + */ + protected SubTag parseSubtag() throws Scanner.SyntaxError, IOException { + SubTag subtag = null; + switch (scanner.token) { + case IDENT: + subtag = subtag(scanner.stringValue); + break; + case INTVAL: + subtag = subtag(scanner.intValue); + break; + } + if (subtag == null) { + env.error("subtag.expected"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + return subtag; + } + + protected ConstCell parseClassName(boolean uncond) throws Scanner.SyntaxError, IOException { + String v; + switch (scanner.token) { + case CPINDEX: { + int cpx = scanner.intValue; + scanner.scan(); + return pool.getCell(cpx); + } + case STRINGVAL: + v = scanner.stringValue; + scanner.scan(); + return pool.FindCellAsciz(v); + // Some identifiers might coincide with token names. + // these should be OK to use as identifier names. + case SYNTHETIC: + case DEPRECATED: + case VERSION: + case BITS: + case STACK: + case LOCAL: + case OF: + case NAN: + case INNERCLASS: + case STRICT: + case FIELDREF: + case METHODREF: + case BRIDGE: + case IDENT: + v = scanner.idValue; + scanner.scan(); + if (uncond || (scanner.token == Token.FIELD)) { //????????????????????????????????????? + if ((v.indexOf("/") == -1) // class identifier doesn't contain "/" + && (v.indexOf("[")==-1)){ // class identifier doesn't contain "[" + v = pkgPrefix + v; // add package + } + } + return pool.FindCellAsciz(v); + default: + ConstType key = Tables.tag(scanner.token.value()); + env.traceln("%%%%% Unrecognized token [" + scanner.token + "]: '" + (key == null? "null":key.parseKey()) + "'."); + env.error(scanner.prevPos, "name.expected"); + throw new Scanner.SyntaxError(); + } + } + + + /** + * Parse a signed integer of size bytes long. + * size = 1 or 2 + */ + protected Argument parseInt(int size) throws Scanner.SyntaxError, IOException { + if (scanner.token == Token.BITS) { + scanner.scan(); + } + if (scanner.token != Token.INTVAL) { + env.error(scanner.pos, "int.expected"); + throw new Scanner.SyntaxError(); + } + int arg = scanner.intValue * scanner.sign; + switch (size) { + case 1: +// if ((arg>127)||(arg<-128)) { // 0xFF not allowed + if ((arg > 255) || (arg < -128)) { // to allow 0xFF + env.error(scanner.pos, "value.large", "1 byte"); + throw new Scanner.SyntaxError(); + } + break; + case 2: +// if ((arg > 32767) || (arg < -32768)) { //this seems +// natural but is not backward compatible. Some tests contain +// expressions like: +// sipush 0x8765; + + if ((arg > 65535) || (arg < -32768)) { + env.error(scanner.pos, "value.large", "2 bytes"); + throw new Scanner.SyntaxError(); + } + break; + default: + throw new InternalError("parseInt("+size+")"); + } + scanner.scan(); + return new Argument(arg); + } + + /** + * Parse an unsigned integer of size bytes long. + * size = 1 or 2 + */ + protected Argument parseUInt(int size) throws Scanner.SyntaxError, IOException { + if (scanner.token != Token.INTVAL) { + env.error(scanner.pos, "int.expected"); + throw new Scanner.SyntaxError(); + } + if (scanner.sign == -1) { + env.error(scanner.pos, "neg.forbidden"); + throw new Scanner.SyntaxError(); + } + int arg = scanner.intValue; + switch (size) { + case 1: + if (arg > 255) { + env.error(scanner.pos, "value.large", "1 byte"); + throw new Scanner.SyntaxError(); + } + break; + case 2: + if (arg > 65535) { + env.error(scanner.pos, "value.large", "2 bytes"); + throw new Scanner.SyntaxError(); + } + break; + default: + throw new InternalError("parseUInt("+size+")"); + } + scanner.scan(); + return new Argument(arg); + } + + /** + * Parse constant declaration + */ + protected void parseConstDef() throws IOException { + for (;;) { + if (scanner.token == Token.CPINDEX) { + int cpx = scanner.intValue; + scanner.scan(); + scanner.expect(Token.ASSIGN); + env.traceln("parseConstDef:"+cpx); + pool.setCell(cpx, cpParser.parseConstRef(null)); + } else { + env.error("const.def.expected"); + throw new Scanner.SyntaxError(); + } + if (scanner.token != Token.COMMA) { + scanner.expect(Token.SEMICOLON); + return; + } + scanner.scan(); // COMMA + } + } + + /** + * Parse the modifiers + */ + private int scanModifier(int mod) throws IOException { + int nextmod, prevpos; + + while (true) { + nextmod = 0; + switch (scanner.token) { + case PUBLIC: nextmod = ACC_PUBLIC; break; + case PRIVATE: nextmod = ACC_PRIVATE; break; + case PROTECTED: nextmod = ACC_PROTECTED; break; + case STATIC: nextmod = ACC_STATIC; break; + case FINAL: nextmod = ACC_FINAL; break; + case SYNCHRONIZED: nextmod = ACC_SYNCHRONIZED; break; + case SUPER: nextmod = ACC_SUPER; break; + case VOLATILE: nextmod = ACC_VOLATILE; break; + case BRIDGE: nextmod = ACC_BRIDGE; break; + case TRANSIENT: nextmod = ACC_TRANSIENT; break; + case VARARGS: nextmod = ACC_VARARGS; break; + case NATIVE: nextmod = ACC_NATIVE; break; + case INTERFACE: nextmod = ACC_INTERFACE; break; + case ABSTRACT: nextmod = ACC_ABSTRACT; break; + case STRICT: nextmod = ACC_STRICT; break; + case ENUM: nextmod = ACC_ENUM; break; + case SYNTHETIC: nextmod = ACC_SYNTHETIC; break; + // case SYNTHETIC: nextmod = SYNTHETIC_ATTRIBUTE; break; + + case DEPRECATED: nextmod = DEPRECATED_ATTRIBUTE; break; + case MANDATED: nextmod = ACC_MANDATED; break; + default: +// env.traceln(" is not a mod"); + return nextmod; + } + prevpos = scanner.pos; + scanner.scan(); + if ((mod & nextmod) == 0) { + return nextmod; + } + env.error(prevpos, "warn.repeated.modifier"); + } + } + + protected int scanModifiers() throws IOException { + int mod = 0, nextmod; + + while (true) { + nextmod = scanModifier(mod); + if (nextmod == 0) { + return mod; + } + mod = mod | nextmod; + } + } + + /** + * Parse a field. + */ + protected void parseField(int mod) throws Scanner.SyntaxError, IOException { + debugStr(" [Parser.parseField]: <<>>"); + // check access modifiers: + Modifiers.checkFieldModifiers(cd, mod, scanner.pos); + + while (true) { + ConstCell nameCell = parseName(); + scanner.expect(Token.COLON); + ConstCell typeCell = parseName(); + + // Define the variable + FieldData fld = cd.addField(mod, nameCell, typeCell); + + if (memberAnnttns != null) { + fld.addAnnotations(memberAnnttns); + } + + // Parse the optional initializer + if (scanner.token == Token.ASSIGN) { + scanner.scan(); + fld.SetValue(cpParser.parseConstRef(null)); + } + + // If the next scanner.token is a comma, then there is more + debugScan(" [Parser.parseField]: Field: " + fld + " "); + + if (scanner.token != Token.COMMA) { + scanner.expect(Token.SEMICOLON); + return; + } + scanner.scan(); + } // end while + } // end parseField + + /** + * Scan method's signature to determine size of parameters. + */ + protected int countParams(int pos, ConstCell sigCell) throws Scanner.SyntaxError, IOException { + String sig; + try { + ConstValue_String strConst = (ConstValue_String) sigCell.ref; + sig = strConst.value; + } catch (NullPointerException | ClassCastException e) { + return 0; // ??? TBD + } + int siglen = sig.length(), k = 0, loccnt = 0, errparam = 0; + boolean arraytype = false; + scan: { + if (k >= siglen) { + break scan; + } + if (sig.charAt(k) != '(') { + errparam = 1; + break scan; + } + for (k = 1; k < siglen; k++) { + switch (sig.charAt(k)) { + case ')': + if (arraytype) { + errparam = 2; + break scan; + } + return loccnt; + case '[': + arraytype = true; + break; + case 'B': + case 'C': + case 'F': + case 'I': + case 'S': + case 'Z': + loccnt++; + arraytype = false; + break; + case 'D': + case 'J': + loccnt++; + if (arraytype) { + arraytype = false; + } + else { + loccnt++; + } + break; + case 'L': + for (;;k++) { + if (k >= siglen) { + errparam = 3; + break scan; + } + if (sig.charAt(k) == ';') { + break; + } + } + loccnt++; + arraytype = false; + break; + default: + errparam = 4; + break scan; + } + } + } + env.error(scanner.pos, "msig.malformed", new Integer(k).toString(), + new Integer(errparam).toString()); + return loccnt; + } + + /** + * Parse a method. + */ + protected void parseMethod(int mod) throws Scanner.SyntaxError, IOException { + + // The start of the method + int posa = scanner.pos; + debugStr(" [Parser.parseMethod]: <<>>"); + + ConstCell nameCell = parseName(); + ConstValue_String strConst = (ConstValue_String) nameCell.ref; + String name = strConst.value; + boolean is_clinit = name.equals(""); + boolean is_init = name.equals(""); + DefaultAnnotationAttr defAnnot = null; + + // check access modifiers: + Modifiers.checkMethodModifiers(cd, mod, posa, is_init, is_clinit); + + scanner.expect(Token.COLON); + ConstCell typeCell = parseName(); + int paramcnt = countParams(scanner.pos, typeCell); + if ((! Modifiers.isStatic(mod)) && ! is_clinit) { + paramcnt++; + } + if (paramcnt > 255) { + env.error(scanner.pos, "warn.msig.more255", new Integer(paramcnt).toString()); + } + // Parse throws clause + ArrayList exc_table = null; + if (scanner.token == Token.THROWS) { + scanner.scan(); + exc_table = new ArrayList<>(); + for (;;) { + posa = scanner.pos; + ConstCell exc = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + if (exc_table.contains(exc)) { + env.error(posa, "warn.exc.repeated"); + } else { + exc_table.add(exc); + env.traceln("THROWS:"+exc.arg); + } + if (scanner.token != Token.COMMA) { + break; + } + scanner.scan(); + } + } + if (scanner.token == Token.DEFAULT) { + // need to scan the annotation value + defAnnot = annotParser.parseDefaultAnnotation(); + } + + curMethod = cd.StartMethod(mod, nameCell, typeCell, exc_table); + Argument max_stack = null, max_locals = null; + + if (scanner.token == Token.STACK) { + scanner.scan(); + max_stack = parseUInt(2); + } + if (scanner.token == Token.LOCAL) { + scanner.scan(); + max_locals = parseUInt(2); + } + if (scanner.token == Token.INTVAL) { + annotParser.parseParamAnnots(paramcnt, curMethod); + } + + if (scanner.token == Token.SEMICOLON) { + if ((max_stack != null) || (max_locals != null)) { + env.error("token.expected", "{"); + } + scanner.scan(); + } else { + scanner.expect(Token.LBRACE); + curCode = curMethod.startCode(posa, paramcnt, max_stack, max_locals); + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + instrParser.parseInstr(); + if (scanner.token == Token.RBRACE) { + break; + } + scanner.expect(Token.SEMICOLON); + } + + curCode.endCode(); + scanner.expect(Token.RBRACE); + } + + if (defAnnot != null) { + curMethod.addDefaultAnnotation(defAnnot); + } + if (memberAnnttns != null) { + curMethod.addAnnotations(memberAnnttns); + } + cd.EndMethod(); + debugStr(" [Parser.parseMethod]: Method: " + curMethod ); + + } // end parseMethod + + + /** + * Parse a (CPX based) BootstrapMethod entry. + */ + protected void parseCPXBootstrapMethod(int mod) throws Scanner.SyntaxError, IOException { + // Parses in the form: + // BOOTSTRAPMETHOD CPX_MethodHandle (CPX_Arg)* ; + if (scanner.token == Token.CPINDEX) { + // CPX can be a CPX to an MethodHandle constant, + int cpx = scanner.intValue; + ConstCell MHCell = pool.getCell(cpx); + scanner.scan(); + ArrayList bsm_args = new ArrayList<>(256); + + while (scanner.token != Token.SEMICOLON) { + if (scanner.token == Token.CPINDEX) { + bsm_args.add(pool.getCell(scanner.intValue)); + + } else { + // throw error, bootstrap method is not recognizable + env.error(scanner.pos, "invalid.bootstrapmethod"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + } + BootstrapMethodData bsmData = new BootstrapMethodData(MHCell, bsm_args); + cd.addBootstrapMethod(bsmData); + } else { + // throw error, bootstrap method is not recognizable + env.error(scanner.pos, "invalid.bootstrapmethod"); + throw new Scanner.SyntaxError(); + } + } + + + + /** + * Parse an inner class. + */ + protected void parseInnerClass(int mod) throws Scanner.SyntaxError, IOException { + // Parses in the form: + // MODIFIERS (INNERCLASSNAME =)? (INNERCLASS) (OF OUTERCLASS)? ; + // + // where + // INNERCLASSNAME = (IDENT | CPX_IN-CL-NM) + // INNERCLASS = (CLASS IDENT | CPX_IN-CL) (S2) + // OUTERCLASS = (CLASS IDENT | CPX_OT-CL) (S3) + // + // Note: + // If a class reference cannot be identified using IDENT, CPX indexes must be used. + + // check access modifiers: + debugScan("[Parser.parseInnerClass]: Begin "); + Modifiers.checkInnerClassModifiers(cd, mod, scanner.pos); + + ConstCell nameCell; + ConstCell innerClass = null; + ConstCell outerClass = null; + + + if (scanner.token == Token.CLASS) { + nameCell = pool.getCell(0); // no NameIndex + parseInnerClass_s2(mod, nameCell, innerClass, outerClass); + } else { + if ((scanner.token == Token.IDENT) || scanner.checkTokenIdent()) { + // Got a Class Name + nameCell = parseName(); + parseInnerClass_s1(mod, nameCell, innerClass, outerClass); + } else if (scanner.token == Token.CPINDEX) { + // CPX can be either a CPX to an InnerClassName, + // or a CPX to an InnerClassInfo + int cpx = scanner.intValue; + nameCell = pool.getCell(cpx); + ConstValue nameCellValue = nameCell.ref; + + if (nameCellValue instanceof ConstValue_String) { + // got a name cell + scanner.scan(); + parseInnerClass_s1(mod, nameCell, innerClass, outerClass); + } else { + // got a CPRef cell + nameCell = pool.getCell(0); // no NameIndex + parseInnerClass_s2(mod, nameCell, innerClass, outerClass); + } + } else { + pic_error(); + } + + } + } + + private void parseInnerClass_s1(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException { + // next scanner.token must be '=' + if (scanner.token == Token.ASSIGN) { + scanner.scan(); + parseInnerClass_s2(mod, nameCell, innerClass, outerClass); + } else { + pic_error(); + } + + } + + + private void parseInnerClass_s2(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException { + // scanner.token is either "CLASS IDENT" or "CPX_Class" + if ((scanner.token == Token.CPINDEX) || (scanner.token == Token.CLASS)) { + if (scanner.token == Token.CPINDEX) { + int cpx = scanner.intValue; + innerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + } + + if (scanner.token == Token.CLASS) { + // next symbol needs to be InnerClass + scanner.scan(); + innerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + } + + // See if declaration is terminated + if (scanner.token == Token.SEMICOLON) { + // InnerClass is complete, no OUTERINFO; + outerClass = pool.getCell(0); + pic_tracecreate(mod, nameCell, innerClass, outerClass); + cd.addInnerClass(mod, nameCell, innerClass, outerClass); + } else if (scanner.token == Token.OF) { + // got an outer class reference + parseInnerClass_s3(mod, nameCell, innerClass, outerClass); + } else { + pic_error(); + } + + } else { + pic_error(); + } + + } + + + private void parseInnerClass_s3(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException { + scanner.scan(); + if ((scanner.token == Token.CLASS) || (scanner.token == Token.CPINDEX)) { + if (scanner.token == Token.CLASS) { + // next symbol needs to be InnerClass + scanner.scan(); + outerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + } + if (scanner.token == Token.CPINDEX) { + int cpx = scanner.intValue; + outerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + } + + if (scanner.token == Token.SEMICOLON) { + pic_tracecreate(mod, nameCell, innerClass, outerClass); + cd.addInnerClass(mod, nameCell, innerClass, outerClass); + } else { + pic_error(); + } + } else { + pic_error(); + } + } + + private void pic_tracecreate(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) { + // throw error, IC is not recognizable + env.trace(" Creating InnerClass: [" + Modifiers.toString(mod, CF_Context.CTX_INNERCLASS) + "], "); + + if (nameCell != pool.getCell(0)) { + ConstValue value = nameCell.ref; + if (value != null) { + env.trace(value.toString() + " = "); + } + } + + ConstValue_Cell ici_val = (ConstValue_Cell) innerClass.ref; + ConstCell ici_ascii = (ConstCell) ici_val.cell; + // Constant pool may not be numberized yet. + // + // check values before dereference on a trace. + if (ici_ascii.ref == null) { + env.trace("<#cpx-unresolved> "); + } else { + ConstValue_String cval = ( ConstValue_String) ici_ascii.ref; + if (cval.value == null){ + env.trace("<#cpx-0> "); + } else { + env.trace(cval.value + " "); + } + } + + if (outerClass != pool.getCell(0)) { + if (outerClass.arg != 0) { + ConstValue_Cell oci_val = (ConstValue_Cell) outerClass.ref; + ConstCell oci_ascii = (ConstCell) oci_val.cell; + if (oci_ascii.ref == null) { + env.trace(" of <#cpx-unresolved> "); + } else { + ConstValue_String cval = ( ConstValue_String) oci_ascii.ref; + if (cval.value == null) { + env.trace(" of <#cpx-0> "); + } else { + env.trace(" of " + cval.value); + } + } + } + } + + env.traceln(""); + } + + private void pic_error() { + // throw error, IC is not recognizable + env.error(scanner.pos, "invalid.innerclass"); + throw new Scanner.SyntaxError(); + } + + /** + * The match() method is used to quickly match opening + * brackets (ie: '(', '{', or '[') with their closing + * counter part. This is useful during error recovery.

+ * + * Scan to a matching '}', ']' or ')'. The current scanner.token must be + * a '{', '[' or '('; + */ + protected void match(Token open, Token close) throws IOException { + int depth = 1; + + while (true) { + scanner.scan(); + if (scanner.token == open) { + depth++; + } else if (scanner.token == close) { + if (--depth == 0) { + return; + } + } else if (scanner.token == Token.EOF) { + env.error(scanner.pos, "unbalanced.paren"); + return; + } + } + } + + /** + * Recover after a syntax error in a field. This involves + * discarding scanner.tokens until an EOF or a possible legal + * continuation is encountered. + */ + protected void recoverField() throws Scanner.SyntaxError, IOException { + while (true) { + switch (scanner.token) { + case EOF: + case STATIC: + case FINAL: + case PUBLIC: + case PRIVATE: + case SYNCHRONIZED: + case TRANSIENT: + case PROTECTED: + case VOLATILE: + case NATIVE: +// case INTERFACE: see below + case ABSTRACT: + + // possible begin of a field, continue + return; + + case LBRACE: + match(Token.LBRACE, Token.RBRACE); + scanner.scan(); + break; + + case LPAREN: + match(Token.LPAREN, Token.RPAREN); + scanner.scan(); + break; + + case LSQBRACKET: + match(Token.LSQBRACKET, Token.RSQBRACKET); + scanner.scan(); + break; + + case RBRACE: + case INTERFACE: + case CLASS: + case IMPORT: + case PACKAGE: + // begin of something outside a class, panic more + endClass(scanner.pos); + scanner.debugStr(" [Parser.recoverField]: pos: [" + scanner.pos + "]: "); + throw new Scanner.SyntaxError(); + + default: + // don't know what to do, skip + scanner.scan(); + break; + } + } + } + + /** + * Parse a class or interface declaration. + */ + protected void parseClass(int mod) throws IOException { + int posa = scanner.pos; + debugStr(" [Parser.parseClass]: Begin "); + // check access modifiers: + Modifiers.checkClassModifiers(env, mod, posa); + + if (cd == null) { + cd = new ClassData(env, major_version, minor_version); + pool = cd.pool; + } + + if (clsAnnttns != null) { + cd.addAnnotations(clsAnnttns); + } + + // move the tokenizer to the identifier: + if (scanner.token == Token.CLASS) { + scanner.scan(); + } + + // Parse the class name + ConstCell nm = cpParser.parseConstRef(ConstType.CONSTANT_CLASS, null, true); + + if (scanner.token == Token.FIELD) { // DOT + String fileExtension; + scanner.scan(); + switch (scanner.token) { + case STRINGVAL: + fileExtension = scanner.stringValue; + break; + case IDENT: + fileExtension = scanner.idValue; + break; + default: + env.error(scanner.pos, "name.expected"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + cd.fileExtension="."+fileExtension; + } else if (scanner.token == Token.SEMICOLON) { + // drop the semi-colon following a name + scanner.scan(); + } + + // Parse extends clause + ConstCell sup = null; + if (scanner.token == Token.EXTENDS) { + scanner.scan(); + sup = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + while (scanner.token == Token.COMMA) { + scanner.scan(); + env.error(posa, "multiple.inherit"); + cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + } + } + + // Parse implements clause + ArrayList impl = new ArrayList<>(); + if (scanner.token == Token.IMPLEMENTS) { + do { + scanner.scan(); + Argument intf = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + if (impl.contains(intf)) { + env.error(posa, "warn.intf.repeated", intf); + } else { + impl.add(intf); + } + } while (scanner.token == Token.COMMA); + } + parseVersion(); + scanner.expect(Token.LBRACE); + + // Begin a new class + cd.init(mod, nm, sup, impl); + + // Parse constant declarations + + // Parse class members + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + switch (scanner.token) { + case SEMICOLON: + // Empty fields are allowed + scanner.scan(); + break; + + case CONST: + scanner.scan(); + parseConstDef(); + explicitcp = true; + break; + + + default: // scanner.token is some member. + parseClassMembers(); + } // end switch + } // while + + scanner.expect(Token.RBRACE); + + // End the class + endClass(scanner.prevPos); + } // end parseClass + + private void parseClassMembers() throws IOException { + debugScan("[Parser.parseClassMembers]: Begin "); + // Parse annotations + if (scanner.token == Token.ANNOTATION) { + memberAnnttns = annotParser.scanAnnotations(); + } + + // Parse modifiers + int mod = scanModifiers(); + try { + switch (scanner.token) { + case FIELDREF: + scanner.scan(); + parseField(mod); + break; + case METHODREF: + scanner.scan(); + parseMethod(mod); + break; + case INNERCLASS: + scanner.scan(); + parseInnerClass(mod); + break; + case BOOTSTRAPMETHOD: + scanner.scan(); + parseCPXBootstrapMethod(mod); + break; + default: + env.error(scanner.pos, "field.expected"); + throw new Scanner.SyntaxError(); + } // end switch + } catch (Scanner.SyntaxError e) { + recoverField(); + } + memberAnnttns = null; + } + + /** + * Recover after a syntax error in the file. + * This involves discarding scanner.tokens until an EOF + * or a possible legal continuation is encountered. + */ + protected void recoverFile() throws IOException { + while (true) { + env.traceln("recoverFile: scanner.token=" + scanner.token); + switch (scanner.token) { + case CLASS: + case INTERFACE: + // Start of a new source file statement, continue + return; + + case LBRACE: + match(Token.LBRACE, Token.RBRACE); + scanner.scan(); + break; + + case LPAREN: + match(Token.LPAREN, Token.RPAREN); + scanner.scan(); + break; + + case LSQBRACKET: + match(Token.LSQBRACKET, Token.RSQBRACKET); + scanner.scan(); + break; + + case EOF: + return; + + default: + // Don't know what to do, skip + scanner.scan(); + break; + } + } + } + + /** + * End class + */ + protected void endClass(int where) { + if (explicitcp) { + // Fix references in the constant pool (for explicitly coded CPs) + pool.fixRefsInPool(); + // Fix any bootstrap Method references too + cd.relinkBootstrapMethods(); + } + + cd.endClass(); + clsDataList.add(cd); + cd = null; + } + + public final ClassData[] getClassesData() { + return ((ClassData[]) clsDataList.toArray(new ClassData[0])); + } + + /** + * Determines whether the JASM file is for a package-info class + * or a module-info class. + * + * creates the correct kind of ClassData accordingly. + * + * @throws IOException + */ + private void parseJasmPackages() throws IOException { + try { + // starting annotations could either be + // a package annotation, or a class annotation + if (scanner.token == Token.ANNOTATION) { + if (cd == null) { + cd = new ClassData(env, major_version, minor_version); + pool = cd.pool; + } + pkgAnnttns = annotParser.scanAnnotations(); + } + if (scanner.token == Token.PACKAGE) { + // Package statement + scanner.scan(); + int where = scanner.pos; + String id = parseIdent(); + parseVersionPkg(); + scanner.expect(Token.SEMICOLON); + + if (pkg == null) { + pkg = id; + pkgPrefix = id + "/"; + } else { + env.error(where, "package.repeated"); + } + debugScan("[Parser.parseJasmPackages] {PARSED} package-prefix: " + pkgPrefix + " "); + } + } catch (Scanner.SyntaxError e) { + recoverFile(); + } + // skip bogus semi colons + while (scanner.token == Token.SEMICOLON) { + scanner.scan(); + } + + // checks that we compile module or package compilation unit + if (scanner.token == Token.EOF) { + env.traceln("Scanner: EOF"); + String sourceName = env.getSourceName(); + int mod = ACC_INTERFACE | ACC_ABSTRACT; + + // package-info + if (sourceName.endsWith("package-info.jasm")) { + env.traceln("Creating \"package-info.jasm\": package: " + pkg + " " + major_version + ":" + minor_version); + + if (cd == null) { + cd = new ClassData(env, major_version, minor_version); + pool = cd.pool; + } else { + cd.major_version = major_version; + cd.minor_version = minor_version; + } + ConstCell me = pool.FindCellClassByName(pkgPrefix + "package-info"); + + if (major_version > 49) { + mod |= SYNTHETIC_ATTRIBUTE; + } + cd.init(mod, me, new ConstCell(0), null); + + if (pkgAnnttns != null) { + cd.addAnnotations(pkgAnnttns); + } + + endClass(scanner.prevPos); + } + return; + } + + if (pkg == null && pkgAnnttns != null) { // RemoveModules + clsAnnttns = pkgAnnttns; + pkgAnnttns = null; + } + } + + /** + * Parse an Jasm file. + */ + public void parseFile() { + try{ + // First, parse any package identifiers (and associated package annotations) + parseJasmPackages(); + + while (scanner.token != Token.EOF) { + // Second, parse any class identifiers (and associated class annotations) + try { + // Parse annotations + if (scanner.token == Token.ANNOTATION) { + if (cd == null) { + cd = new ClassData(env, major_version, minor_version); + pool = cd.pool; + } + clsAnnttns = annotParser.scanAnnotations(); + } + + // Parse class modifiers + int mod = scanModifiers(); + if (mod == 0) { + switch (scanner.token) { + case CLASS: + case CPINDEX: + case STRINGVAL: + case IDENT: + // this is a class declaration anyway + break; + case SEMICOLON: + // Bogus semi colon + scanner.scan(); + continue; + default: + // no class declaration found + debugScan(" [Parser.parseFile]: "); + env.error(scanner.pos, "toplevel.expected"); + throw new Scanner.SyntaxError(); + } + } else if (Modifiers.isInterface(mod) && (scanner.token != Token.CLASS)) { + // rare syntactic sugar: + // interface == abstract interface class + mod |= ACC_ABSTRACT; + } + parseClass(mod); + clsAnnttns = null; + } catch (Scanner.SyntaxError e) { + // KTL + env.traceln("^^^^^^^ Syntax Error ^^^^^^^^^^^^"); + if (scanner.debugFlag) + e.printStackTrace(); + recoverFile(); + } + } + } catch (IOException e) { + env.error(scanner.pos, "io.exception", env.getSourceName()); + } catch (Error er) { + er.printStackTrace(); + } + } //end parseFile +} //end Parser diff --git a/src/org/openjdk/asmtools/jasm/ParserAnnotation.java b/src/org/openjdk/asmtools/jasm/ParserAnnotation.java new file mode 100644 index 0000000..28f6083 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ParserAnnotation.java @@ -0,0 +1,1142 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.TypeAnnotationUtils.*; +import static org.openjdk.asmtools.jasm.JasmTokens.*; +import static org.openjdk.asmtools.jasm.ConstantPool.*; +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.TreeMap; + +/** + * ParserAnnotation + * + * ParserAnnotation is a parser class owned by Parser.java. It is primarily responsible + * for parsing Annotations (for classes, methods or fields). + * + * ParserAnnotation can parse the different types of Annotation Attributes: + * Runtime(In)Visible Annotations (JDK 6+) Default Annotations (JDK 6+) + * Runtime(In)VisibleParameter Annotations (JDK 7+) Runtime(In)VisibleType Annotations + * (JSR308, JDK8+) + */ +public class ParserAnnotation extends ParseBase { + + /*-------------------------------------------------------- */ + /* Annotation Inner Classes */ + /** + * AnnotationElemValue + * + * Used to store Annotation values + */ + class AnnotationElemValue implements Data { + + AnnotationData annotation; + + public AnnotationElemValue(AnnotationData annotation) { + this.annotation = annotation; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte('@'); + annotation.write(out); + } + + @Override + public int getLength() { + return 1 + annotation.getLength(); + } + } + + /** + * ClassElemValue + * + * Annotation Element value referring to a class + */ + class ClassElemValue implements Data { + + ConstCell indx; + + public ClassElemValue(ConstCell indx) { + this.indx = indx; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte('c'); + indx.write(out); + } + + @Override + public int getLength() { + return 3; + } + } + + /** + * ArrayElemValue + * + * Annotation Element value referring to an Array + */ + class ArrayElemValue implements Data { + + ArrayList elemValues; + int arrayLength = 0; + + public ArrayElemValue() { + this.elemValues = new ArrayList<>(); + } + + void add(Data elemValue) { + elemValues.add(elemValue); + arrayLength += elemValue.getLength(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte('['); + out.writeShort(elemValues.size()); + + for (Data eval : elemValues) { + eval.write(out); + } + } + + @Override + public int getLength() { + return 3 + arrayLength; + } + } + + /** + * ConstElemValue + * + * Annotation Element value referring to a Constant + */ + class ConstElemValue implements Data { + + char tag; + ConstCell indx; + + public ConstElemValue(char tag, ConstCell indx) { + this.tag = tag; + this.indx = indx; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(tag); + indx.write(out); + } + + @Override + public int getLength() { + return 3; + } + } + + /** + * EnumElemValue + * + * Element Value for Enums + */ + class EnumElemValue implements Data { + + ConstCell type; + ConstCell value; + + public EnumElemValue(ConstCell type, ConstCell value) { + this.type = type; + this.value = value; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte('e'); + type.write(out); + value.write(out); + } + + @Override + public int getLength() { + return 5; + } + } + + /*-------------------------------------------------------- */ + /* Annotation Parser Fields */ + /** + * local handles on the scanner, main parser, and the error reporting env + */ + private static TTVis ttVisitor; + /*-------------------------------------------------------- */ + + /** + * main constructor + * + * @param scanner + * @param parser + * @param env + */ + protected ParserAnnotation(Scanner scanner, Parser parser, Environment env) { + super.init(scanner, parser, env); + ttVisitor = new TTVis(); + ttVisitor.init(env, scanner); + } + + protected void scanParamName(int totalParams, int paramNum, MethodData curMethod) throws IOException { + debugScan(" - - - > [ParserAnnotation.scanParamName]: Begin "); + scanner.scan(); + scanner.expect(Token.LBRACE); + // First scan the Name (String, or CPX to name) + ConstCell nameCell = null; + if ((scanner.token == Token.IDENT) || scanner.checkTokenIdent()) { + // Got a Class Name + nameCell = parser.parseName(); + } else if (scanner.token == Token.CPINDEX) { + int cpx = scanner.intValue; + nameCell = parser.pool.getCell(cpx); + // check the constant + ConstValue nameCellValue = nameCell.ref; + if (!(nameCellValue instanceof ConstValue_String)) { + // throw an error + env.error(scanner.pos, "paramname.constnum.invaltype", cpx); + throw new Scanner.SyntaxError(); + } + + } else { + // throw scan error - unexpected token + env.error(scanner.pos, "paramname.token.unexpected", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + + // Got the name cell. Next, scan the access flags + int mod = parser.scanModifiers(); + + scanner.expect(Token.RBRACE); + + curMethod.addMethodParameter(totalParams, paramNum, nameCell, mod); + + debugScan(" - - - > [ParserAnnotation.scanParamName]: End "); + } + + /** + * scanAnnotations + * + * The main entry for parsing an annotation list. + * + * @return An ArrayList of parsed annotations + * @throws IOException + */ + protected ArrayList scanAnnotations() throws IOException { + ArrayList annttns = new ArrayList<>(); + + while (scanner.token == Token.ANNOTATION) { + if (isAnnotationToken(scanner.stringValue)) { + annttns.add(parseAnnotation()); + } else if (isTypeAnnotationToken(scanner.stringValue)) { + annttns.add(parseTypeAnnotation()); + } + } + + if (annttns.size() > 0) { + return annttns; + } else { + return null; + } + } + + /** + * isAnnotation + * + * examines the beginning of a string to see if it starts with an annotation character + * + * @param str + * @return True if the string starts with an annotation char. + */ + protected boolean isAnnotation(String str) { + return (str.startsWith("@")); + } + + /** + * isAnnotationToken + * + * examines the beginning of a string to see if it starts with an annotation + * characters ('@+' = visible annotation, '@-' = invisible). + * + * @param str + * @return True if the string starts with an annotation char. + */ + protected boolean isAnnotationToken(String str) { + return (str.startsWith("@+") || str.startsWith("@-")); + } + + /** + * isTypeAnnotationToken + * + * examines the beginning of a string to see if it starts with type annotation + * characters ('@T+' = visible type annotation, '@T-' = invisible). + * + * @param str + * @return True if the string starts with an annotation char. + */ + protected boolean isTypeAnnotationToken(String str) { + return (str.startsWith("@T+") || str.startsWith("@T-")); + } + + /** + * isInvisibleAnnotToken + * + * examines the end of an annotation token to determine visibility ('+' = visible + * annotation, '-' = invisible). + * + * @param str + * @return True if the token implies invisible annotation. + */ + protected boolean isInvisibleAnnotToken(String str) { + return (str.endsWith("-")); + } + + /** + * parseDefaultAnnotation + * + * parses a default Annotation attribute + * + * @return the parsed Annotation Attribute + * @throws org.openjdk.asmtools.jasm.Scanner.SyntaxError + * @throws IOException + */ + protected DefaultAnnotationAttr parseDefaultAnnotation() throws Scanner.SyntaxError, IOException { + scanner.scan(); + DefaultAnnotationAttr attr = null; + Data value = null; + scanner.expect(Token.LBRACE); + + if ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + value = scanAnnotationData("default"); + } + scanner.expect(Token.RBRACE); + attr = new DefaultAnnotationAttr(parser.cd, + AttrTag.ATT_AnnotationDefault.parsekey(), + value); + return attr; + } + + /** + * parseParamAnnots + * + * Parses Parameter Annotations attributes. + * + * @param _totalParams + * @param curMethod + * @throws org.openjdk.asmtools.jasm.Scanner.SyntaxError + * @throws IOException + */ + protected void parseParamAnnots(int _totalParams, MethodData curMethod) throws Scanner.SyntaxError, IOException { + debugScan(" - - - > [ParserAnnotation.parseParamAnnots]: Begin, totalParams = " + _totalParams + " "); + // _The method thinks there are N+1 params in the signature + // (N = total params in the call list) + 1 (return value) + int totalParams = _totalParams - 1; + TreeMap> pAnnots = new TreeMap<>(); + + while (scanner.token == Token.INTVAL) { + // Create the Parameter Array for Param Annotations + + // Do something with Parameter annotations + // -------------------- + // First - validate that the parameter number (integer) + // (eg >= 0, < numParams, and param num is not previously set) + int paramNum = scanner.intValue; + Integer iParamNum = new Integer(paramNum); + if (paramNum < 0 || paramNum >= totalParams) { + //invalid Parameter number. Throw an error. + env.error(scanner.pos, "invalid.paramnum", paramNum); + } + if (pAnnots.get(iParamNum) != null) { + // paramter is already populated with annotations/pnames, Throw an error. + env.error(scanner.pos, "duplicate.paramnum", paramNum); + } + // 2nd - Parse the COLON (invalid if not present) + scanner.scan(); + scanner.expect(Token.COLON); + + // 3rd - parse either an optional ParamName, or a list of annotations + if (scanner.token == Token.PARAM_NAME) { + //parse the ParamName + scanParamName(_totalParams, iParamNum, curMethod); + } + + // 4th - parse each Annotation (followed by comma, followed by annotation + // assign array of annotations to param array + if (scanner.token == Token.ANNOTATION) { + ArrayList pAnnot = scanAnnotations(); + pAnnots.put(iParamNum, pAnnot); + + for (AnnotationData data : pAnnot) { + curMethod.addParamAnnotation(totalParams, paramNum, data); + } + } + + } + } + + /* ************************* Private Members *************************** */ + /** + * parseTypeAnnotation + * + * parses an individual annotation. + * + * @return a parsed annotation. + * @throws IOException + */ + private AnnotationData parseTypeAnnotation() throws Scanner.SyntaxError, IOException { + boolean invisible = isInvisibleAnnotToken(scanner.stringValue); + scanner.scan(); + debugScan(" [ParserAnnotation.parseTypeAnnotation]: id = " + scanner.stringValue + " "); + String annoName = "L" + scanner.stringValue + ";"; + TypeAnnotationData anno = new TypeAnnotationData(parser.pool.FindCellAsciz(annoName), invisible); + scanner.scan(); + debugScan(" [ParserAnnotation.parseTypeAnnotation]:new type annotation: " + annoName + " "); + + scanner.expect(Token.LBRACE); + + // Scan the usual annotation data + _scanAnnotation(anno); + + // scan the Target + _scanTypeTarget(anno); + + // scan the Location + _scanTargetPath(anno); + + scanner.expect(Token.RBRACE); + return anno; + } + + /** + * scanAnnotation + * + * parses an individual annotation. + * + * @return a parsed annotation. + * @throws IOException + */ + private AnnotationData parseAnnotation() throws Scanner.SyntaxError, IOException { + debugScan(" - - - > [ParserAnnotation.parseAnnotation]: Begin "); + boolean invisible = isInvisibleAnnotToken(scanner.stringValue); + scanner.scan(); + String annoName = "L" + scanner.stringValue + ";"; + + AnnotationData anno = new AnnotationData(parser.pool.FindCellAsciz(annoName), invisible); + scanner.scan(); + debugScan("[ParserAnnotation.parseAnnotation]: new annotation: " + annoName); + _scanAnnotation(anno); + + return anno; + } + + /** + * _scanAnnotation + * + * parses an individual annotation-data. + * + * @return a parsed annotation. + * @throws IOException + */ + private void _scanAnnotation(AnnotationData annotData) throws Scanner.SyntaxError, IOException { + debugScan(" - - - > [ParserAnnotation._scanAnnotation]: Begin"); + scanner.expect(Token.LBRACE); + + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + ConstCell nameCell = parser.parseName(); + scanner.expect(Token.ASSIGN); + + ConstValue cellref = nameCell.ref; + if (cellref.tag != ConstType.CONSTANT_UTF8) { + throw new Scanner.SyntaxError(); + } + String name = ((ConstValue_String) cellref)._toString(); + debugScan(" - - - > [ParserAnnotation._scanAnnotation]: Annot - Field Name: " + name); + Data data = scanAnnotationData(name); + annotData.add(new AnnotationData.ElemValuePair(nameCell, data)); + + // consume tokens inbetween annotation fields + if (scanner.token == Token.COMMA) { + scanner.scan(); + } + } + scanner.expect(Token.RBRACE); + } + + /** + * _scanAnnotation + * + * parses an individual annotation-data. + * + * @return a parsed annotation. + * @throws IOException + */ + private void _scanTypeTarget(TypeAnnotationData annotData) throws Scanner.SyntaxError, IOException { + debugScan(" [ParserAnnotation._scanTypeTarget]: Begin "); + scanner.expect(Token.LBRACE); + + //Scan the target_type and the target_info + scanner.expect(Token.IDENT); + debugScan(" [ParserAnnotation._scanTypeTarget]: TargetType: " + scanner.idValue); + TypeAnnotationUtils.TargetType tt = TypeAnnotationUtils.getTargetType(scanner.idValue); + if (tt == null) { + env.error(scanner.pos, "incorrect.typeannot.target", scanner.idValue); + throw new Scanner.SyntaxError(); + } + + debugScan(" [ParserAnnotation._scanTypeTarget]: Got TargetType: " + tt); + + if (ttVisitor.scanner == null) { + ttVisitor.scanner = scanner; + } + ttVisitor.visitExcept(tt); + + annotData.targetInfo = ttVisitor.getTargetInfo(); + annotData.targetType = tt; + debugScan(" [ParserAnnotation._scanTypeTarget]: Got TargetInfo: " + annotData.targetInfo); + + scanner.expect(Token.RBRACE); + } + + /** + * TTVis + * + * Target Type visitor, used for constructing the target-info within a type + * annotation. visitExcept() is the entry point. ti is the constructed target info. + */ + private static class TTVis extends TypeAnnotationUtils.TypeAnnotationTargetVisitor { + + private TypeAnnotationUtils.TargetInfo ti; + private IOException IOProb; + private Scanner.SyntaxError SyProb; + private Scanner scanner; + private Environment env; + + public TTVis() { + super(); + reset(); + } + + public void init(Environment en, Scanner scn) { + if (scanner == null) { + scanner = scn; + } + if (env == null) { + env = en; + } + } + + public final void reset() { + ti = null; + IOProb = null; + SyProb = null; + } + + //This is the entry point for a visitor that tunnels exceptions + public void visitExcept(TypeAnnotationUtils.TargetType tt) throws IOException, Scanner.SyntaxError { + IOProb = null; + SyProb = null; + ti = null; + + visit(tt); + + if (IOProb != null) { + throw IOProb; + } + + if (SyProb != null) { + throw SyProb; + } + } + + public TypeAnnotationUtils.TargetInfo getTargetInfo() { + return ti; + } + + // this fn gathers intvals, and tunnels any exceptions thrown by + // the scanner + private int scanIntVal(TypeAnnotationUtils.TargetType tt) { + int ret = -1; + if (scanner.token == Token.INTVAL) { + ret = scanner.intValue; + try { + scanner.scan(); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + } else { + env.error(scanner.pos, "incorrect.typeannot.targtype.int", tt.parseKey(), scanner.token); + SyProb = new Scanner.SyntaxError(); + } + return ret; + } + + // this fn gathers intvals, and tunnels any exceptions thrown by + // the scanner + private String scanStringVal(TypeAnnotationUtils.TargetType tt) { + String ret = ""; + if (scanner.token == Token.STRINGVAL) { + ret = scanner.stringValue; + try { + scanner.scan(); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + } else { + env.error(scanner.pos, "incorrect.typeannot.targtype.string", tt.parseKey(), scanner.token); + SyProb = new Scanner.SyntaxError(); + } + return ret; + } + + // this fn gathers intvals, and tunnels any exceptions thrown by + // the scanner + private void scanBrace(boolean left) { + try { + scanner.expect(left ? Token.LBRACE : Token.RBRACE); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + } + + private boolean error() { + return IOProb != null || SyProb != null; + } + + @Override + public void visit_type_param_target(TypeAnnotationUtils.TargetType tt) { + env.traceln("Type Param Target: "); + int byteval = scanIntVal(tt); // param index + if (!error()) { + ti = new TypeAnnotationUtils.typeparam_target(tt, byteval); + } + } + + @Override + public void visit_supertype_target(TypeAnnotationUtils.TargetType tt) { + env.traceln("SuperType Target: "); + int shortval = scanIntVal(tt); // type index + if (!error()) { + ti = new TypeAnnotationUtils.supertype_target(tt, shortval); + } + } + + @Override + public void visit_typeparam_bound_target(TypeAnnotationUtils.TargetType tt) { + env.traceln("TypeParam Bound Target: "); + int byteval1 = scanIntVal(tt); // param index + if (error()) { + return; + } + int byteval2 = scanIntVal(tt); // bound index + if (error()) { + return; + } + ti = new TypeAnnotationUtils.typeparam_bound_target(tt, byteval1, byteval2); + } + + @Override + public void visit_empty_target(TypeAnnotationUtils.TargetType tt) { + env.traceln("Empty Target: "); + if (!error()) { + ti = new TypeAnnotationUtils.empty_target(tt); + } + } + + @Override + public void visit_methodformalparam_target(TypeAnnotationUtils.TargetType tt) { + env.traceln("MethodParam Target: "); + int byteval = scanIntVal(tt); // param index + if (!error()) { + ti = new TypeAnnotationUtils.methodformalparam_target(tt, byteval); + } + } + + @Override + public void visit_throws_target(TypeAnnotationUtils.TargetType tt) { + env.traceln("Throws Target: "); + int shortval = scanIntVal(tt); // exception index + if (!error()) { + ti = new TypeAnnotationUtils.throws_target(tt, shortval); + } + } + + @Override + public void visit_localvar_target(TypeAnnotationUtils.TargetType tt) { + env.traceln("LocalVar Target: "); + TypeAnnotationUtils.localvar_target locvartab = new TypeAnnotationUtils.localvar_target(tt, 0); + ti = locvartab; + + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + // consume the left brace + scanBrace(true); + if (error()) { + return; + } + // scan the local var triple + int shortval1 = scanIntVal(tt); // startPC + if (error()) { + return; + } + int shortval2 = scanIntVal(tt); // length + if (error()) { + return; + } + int shortval3 = scanIntVal(tt); // CPX + locvartab.addEntry(shortval1, shortval2, shortval3); + scanBrace(false); + if (error()) { + return; + } + } + } + + @Override + public void visit_catch_target(TypeAnnotationUtils.TargetType tt) { + env.traceln("Catch Target: "); + int shortval = scanIntVal(tt); // catch index + + ti = new TypeAnnotationUtils.catch_target(tt, shortval); + } + + @Override + public void visit_offset_target(TypeAnnotationUtils.TargetType tt) { + env.traceln("Offset Target: "); + int shortval = scanIntVal(tt); // offset index + if (!error()) { + ti = new TypeAnnotationUtils.offset_target(tt, shortval); + } + } + + @Override + public void visit_typearg_target(TypeAnnotationUtils.TargetType tt) { + env.traceln("TypeArg Target: "); + int shortval = scanIntVal(tt); // offset + if (error()) { + return; + } + int byteval = scanIntVal(tt); // type index + if (error()) { + return; + } + ti = new TypeAnnotationUtils.typearg_target(tt, shortval, byteval); + } + + } + + /** + * _scanTypeLocation + * + * parses an individual annotation-data. + * + * @return a parsed annotation. + * @throws IOException + */ + private void _scanTargetPath(TypeAnnotationData annotData) throws Scanner.SyntaxError, IOException { + + ArrayList targPath = new ArrayList<>(); + // parse the location info + scanner.expect(Token.LBRACE); + + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + TypePathEntry tpe = _scanTypePathEntry(); + + scanner.scan(); + // throw away comma + if (scanner.token == Token.COMMA) { + scanner.scan(); + } + } + scanner.expect(Token.RBRACE); + annotData.targetPath = targPath; + } + + /** + * _scanTypeLocation + * + * parses an individual annotation-data. + * + * @return a parsed annotation. + * @throws IOException + */ + private TypePathEntry _scanTypePathEntry() throws Scanner.SyntaxError, IOException { + TypePathEntry tpe; + + if ((scanner.token != Token.EOF) && (scanner.token == Token.STRINGVAL)) { + String pathEntryKey = scanner.stringValue; + PathKind pk = pathKind(pathEntryKey); + if (pk == null) { + // unexpected Type Path + env.error(scanner.pos, "incorrect.typeannot.pathentry", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + if (pk == PathKind.ITHARG_PARAMETERTYPE) { + // + // need to scan the index + // Take the form: INNER_TYPE(#) + scanner.expect(Token.LPAREN); + int index = 0; + if ((scanner.token != Token.EOF) && (scanner.token == Token.INTVAL)) { + index = scanner.intValue; + } else { + // incorrect Arg index + env.error(scanner.pos, "incorrect.typeannot.pathentry.argindex", scanner.token); + throw new Scanner.SyntaxError(); + } + + scanner.expect(Token.RPAREN); + tpe = new TypePathEntry(pk.key(), (char) index); + } else { + tpe = new TypePathEntry(pk.key(), (char) 0); + } + } else { + // unexpected Type Path + env.error(scanner.pos, "incorrect.typeannot.pathentry", scanner.token); + throw new Scanner.SyntaxError(); + } + + return tpe; + } + + /** + * scanAnnotationArray + * + * Scans an Array of annotations. + * + * @param name Name of the annotation + * @return Array Element + * @throws IOException if scanning errors exist + */ + private ArrayElemValue scanAnnotationArray(String name) throws IOException { + scanner.scan(); + ArrayElemValue arrayElem = new ArrayElemValue(); + + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + Data data = scanAnnotationData(name + " {}"); + arrayElem.add(data); + + // consume tokens inbetween annotation fields + if (scanner.token == Token.COMMA) { + scanner.scan(); + } + } + + scanner.expect(Token.RBRACE); + return arrayElem; + } + + /** + * scanAnnotationEnum + * + * Scans an annotation enum val. + * + * @param name Annotation Name + * @return Constant element value for the Class Annotation. + * @throws IOException + */ + private Data scanAnnotationClass(String name) throws IOException { + Data constVal = null; + // scan the next identifier. + // if it is an Ident, consume it as the class name. + scanner.scan(); + switch (scanner.token) { + case IDENT: + env.traceln("[AnnotationParser.scanAnnotationData]:: Constant Class Field: " + name + " = " + scanner.stringValue); + //need to encode the stringval as an (internal) descriptor. + String desc = parser.encodeClassString(scanner.stringValue); + + // note: for annotations, a class field points to a string with the class descriptor. + constVal = new ConstElemValue('c', parser.pool.FindCellAsciz(desc)); + scanner.scan(); + break; + case CPINDEX: + // could be a reference to a class name + env.traceln("[AnnotationParser.scanAnnotationData]:: Constant Class Field: " + name + " = " + scanner.stringValue); + Integer ConstNmCPX = new Integer(scanner.stringValue); + constVal = new ClassElemValue(parser.pool.getCell(ConstNmCPX)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.class", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + + return constVal; + } + + /** + * scanAnnotationEnum + * + * Scans an annotation enum val. + * + * @param name Annotation Name + * @return Enumeration Element Value + * @throws IOException for scanning errors. + */ + private EnumElemValue scanAnnotationEnum(String name) throws IOException { + scanner.scan(); + EnumElemValue enumval = null; + switch (scanner.token) { + case IDENT: + // could be a string identifying enum class and name + String enumClassName = scanner.stringValue; + scanner.scan(); + // could be a string identifying enum class and name + switch (scanner.token) { + case IDENT: + // could be a string identifying enum class and name + String enumTypeName = scanner.stringValue; + env.traceln("[AnnotationParser.scanAnnotationEnum]:: Constant Enum Field: " + name + " = " + enumClassName + " " + enumTypeName); + String encodedClass = parser.encodeClassString(enumClassName); + ConstElemValue classConst = new ConstElemValue('s', parser.pool.FindCellAsciz(encodedClass)); + ConstElemValue typeConst = new ConstElemValue('s', parser.pool.FindCellAsciz(enumTypeName)); + enumval = new EnumElemValue(classConst.indx, typeConst.indx); + scanner.scan(); + break; + + default: + env.error(scanner.pos, "incorrect.annot.enum", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + break; + case CPINDEX: + Integer typeNmCPX = new Integer(scanner.stringValue); + scanner.scan(); + //need two indexes to form a proper enum + switch (scanner.token) { + case CPINDEX: + Integer ConstNmCPX = new Integer(scanner.stringValue); + env.traceln("[AnnotationParser.scanAnnotationEnum]:: Enumeration Field: " + name + " = #" + typeNmCPX + " #" + ConstNmCPX); + enumval = new EnumElemValue(parser.pool.getCell(typeNmCPX), parser.pool.getCell(ConstNmCPX)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.enum.cpx"); + throw new Scanner.SyntaxError(); + } + break; + } + + return enumval; + } + + /** + * scanAnnotationData + * + * parses the internals of an annotation. + * + * @param name Annotation Name + * @return a Data data structure containing the annotation data. + * @throws IOException for scanning errors. + */ + private Data scanAnnotationData(String name) throws IOException { + Data data = null; + switch (scanner.token) { + // This handles the Annotation types (as normalized in the constant pool) + // Some primitive types (Boolean, char, short, byte) are identified by a keyword. + case INTVAL: + env.traceln("[AnnotationParser.scanAnnotationData]:: Integer Field: " + name + " = " + scanner.intValue); + data = new ConstElemValue('I', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, new Integer(scanner.intValue))); + scanner.scan(); + break; + case DOUBLEVAL: + env.traceln("[AnnotationParser.scanAnnotationData]:: Double Field: " + name + " = " + scanner.doubleValue); + double dval = new Double(scanner.doubleValue); + long ivdal = Double.doubleToLongBits(dval); + Long val = new Long(ivdal); + data = new ConstElemValue('D', parser.pool.FindCell(ConstType.CONSTANT_DOUBLE, val)); + scanner.scan(); + break; + case FLOATVAL: + env.traceln("[AnnotationParser.scanAnnotationData]:: Float Field: " + name + " = " + scanner.floatValue); + float fval = new Float(scanner.floatValue); + int ifval = Float.floatToIntBits(fval); + Integer val1 = new Integer(ifval); + data = new ConstElemValue('F', parser.pool.FindCell(ConstType.CONSTANT_FLOAT, val1)); + scanner.scan(); + break; + case LONGVAL: + env.traceln("[AnnotationParser.scanAnnotationData]:: Long Field: " + name + " = " + scanner.longValue); + data = new ConstElemValue('J', parser.pool.FindCell(ConstType.CONSTANT_LONG, new Long(scanner.longValue))); + scanner.scan(); + break; + case STRINGVAL: + env.traceln("[AnnotationParser.scanAnnotationData]:: String Field: " + name + " = " + scanner.stringValue); + data = new ConstElemValue('s', parser.pool.FindCellAsciz(scanner.stringValue)); + scanner.scan(); + break; + case CLASS: + env.traceln("[AnnotationParser.scanAnnotationData]:: Class) keyword: " + scanner.stringValue); + data = scanAnnotationClass(name); + break; + case ENUM: + // scan the next two identifiers (eg ident.ident), or 2 CPRefs. + // if it is an Ident, use consume it as the class name. + env.traceln("[AnnotationParser.scanAnnotationData]:: Enum) keyword: " + scanner.stringValue); + data = scanAnnotationEnum(name); + break; + case IDENT: + env.traceln("[AnnotationParser.scanAnnotationData]:: JASM Keyword: (annotation field name: " + name + ") keyword: " + scanner.stringValue); + data = scanAnnotationIdent(scanner.stringValue, name); + break; + case ANNOTATION: + env.traceln("[AnnotationParser.scanAnnotationData]:: Annotation Field: " + name + " = " + scanner.stringValue); + data = new AnnotationElemValue(parseAnnotation()); + break; + case LBRACE: + env.traceln("[AnnotationParser.scanAnnotationData]:: Annotation Array Field: " + name); + data = scanAnnotationArray(name); + break; + default: + env.error(scanner.pos, "incorrect.annot.token", scanner.token); + throw new Scanner.SyntaxError(); + } + + return data; + } + + /** + * scanAnnotationIdent + * + * parses the identifier of an annotation. + * + * @param ident Basic Type identifier + * @param name Annotation Name + * @return Basic Type Annotation data + * @throws IOException if scanning errors occur + */ + private Data scanAnnotationIdent(String ident, String name) throws IOException { + // Handle JASM annotation Keyword Identifiers + Data data = null; + BasicType type = basictype(ident); + switch (type) { + + case T_BOOLEAN: + // consume the keyword, get the value + scanner.scan(); + switch (scanner.token) { + case INTVAL: + // Handle Boolean value in integer form + env.traceln("Boolean Field: " + name + " = " + scanner.intValue); + Integer val = new Integer(scanner.intValue); + if (val > 1 || val < 0) { + env.traceln("Warning: Boolean Field: " + name + " value is not 0 or 1, value = " + scanner.intValue); + } + data = new ConstElemValue('Z', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); + scanner.scan(); + break; + case IDENT: + // handle boolean value with true/false keywords + int val1 = 0; + switch (scanner.stringValue) { + case "true": + val1 = 1; + break; + case "false": + val1 = 0; + break; + default: + throw new IOException("Incorrect Annotation (boolean), expected true/false), got \"" + scanner.stringValue + "\"."); + } + env.traceln("Boolean Field: " + name + " = " + scanner.stringValue); + data = new ConstElemValue('Z', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, new Integer(val1))); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.bool", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + break; + case T_BYTE: + // consume the keyword, get the value + scanner.scan(); + switch (scanner.token) { + case INTVAL: + env.traceln("Byte Field: " + name + " = " + scanner.intValue); + Integer val = new Integer(scanner.intValue); + if (val > 0xFF) { + env.traceln("Warning: Byte Field: " + name + " value is greater than 0xFF, value = " + scanner.intValue); + } + data = new ConstElemValue('B', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.byte", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + break; + case T_CHAR: + // consume the keyword, get the value + scanner.scan(); + switch (scanner.token) { + case INTVAL: + env.traceln("Char Field: " + name + " = " + scanner.intValue); + Integer val = new Integer(scanner.intValue); + // Bounds check? + data = new ConstElemValue('C', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.char", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + break; + case T_SHORT: + // consume the keyword, get the value + scanner.scan(); + switch (scanner.token) { + case INTVAL: + env.traceln("Short Field: " + name + " = " + scanner.intValue); + Integer val = new Integer(scanner.intValue); + if (val > 0xFFFF) { + env.traceln("Warning: Short Field: " + name + " value is greater than 0xFFFF, value = " + scanner.intValue); + } + data = new ConstElemValue('S', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.short", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + break; + default: + env.error(scanner.pos, "incorrect.annot.keyword", ident); + throw new Scanner.SyntaxError(); + } + + return data; + } +} diff --git a/src/org/openjdk/asmtools/jasm/ParserCP.java b/src/org/openjdk/asmtools/jasm/ParserCP.java new file mode 100644 index 0000000..9aca1c7 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ParserCP.java @@ -0,0 +1,587 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.JasmTokens.*; +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.IOException; +import java.util.ArrayList; + +/** + * ParserCP + * + * ParseCP is a parser class owned by Parser.java. It is primarily responsible for parsing + * the constant pool and constant declarations. + */ +public class ParserCP extends ParseBase { + + /** + * local handles on the scanner, main parser, and the error reporting env + */ + /** + * Visitor object + */ + private ParserCPVisitor pConstVstr; + + /** + * main constructor + * + * @param scanner + * @param parser + * @param env + */ + protected ParserCP(Scanner scanner, Parser parser, Environment env) { + super.init(scanner, parser, env); + pConstVstr = new ParserCPVisitor(); + } + + /** + * ParserCPVisitor + * + * This inner class overrides a constant pool visitor to provide specific parsing + * instructions (per method) for each type of Constant. + * + * Note: since the generic visitor throws no exceptions, this derived class tunnels + * the exceptions, rethrown in the visitEcept method. + */ + class ParserCPVisitor extends ConstantPool.CPTagVisitor { + + private IOException IOProb; + private Scanner.SyntaxError SyProb; + + public ParserCPVisitor() { + IOProb = null; + SyProb = null; + } + + //This is the entry point for a visitor that tunnels exceptions + public ConstantPool.ConstValue visitExcept(ConstType tag) throws IOException, Scanner.SyntaxError { + IOProb = null; + SyProb = null; + debugStr("------- [ParserCPVisitor.visitExcept]: "); + ConstantPool.ConstValue ret = visit(tag); + + if (IOProb != null) { + throw IOProb; + } + + if (SyProb != null) { + throw SyProb; + } + + return ret; + } + + @Override + public ConstantPool.ConstValue visitUTF8(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitUTF8]: "); + try { + scanner.expect(Token.STRINGVAL); + } catch (IOException e) { + IOProb = e; + } + ConstantPool.ConstValue_String obj + = new ConstantPool.ConstValue_String(scanner.stringValue); + return obj; + } + + @Override + public ConstantPool.ConstValue visitInteger(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitInteger]: "); + ConstantPool.ConstValue_Integer obj; + int v = 0; + try { + if (scanner.token == Token.BITS) { + scanner.scan(); + scanner.inBits = true; + } + v = scanner.intValue * scanner.sign; + scanner.expect(Token.INTVAL); + } catch (IOException e) { + IOProb = e; + } + obj = new ConstantPool.ConstValue_Integer(tag, new Integer(v)); + return obj; + } + + @Override + public ConstantPool.ConstValue visitLong(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitLong]: "); + ConstantPool.ConstValue_Long obj = null; + try { + long v; + if (scanner.token == Token.BITS) { + scanner.scan(); + scanner.inBits = true; + } + switch (scanner.token) { + case INTVAL: + v = scanner.intValue; + break; + case LONGVAL: + v = scanner.longValue; + break; + default: + env.error(scanner.prevPos, "token.expected", "Integer"); + throw new Scanner.SyntaxError(); + } + obj = new ConstantPool.ConstValue_Long(tag, new Long(v * scanner.sign)); + scanner.scan(); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitFloat(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitFloat]: "); + ConstantPool.ConstValue_Integer obj = null; + try { + int v; + float f; + scanner.inBits = false; // this needs to be initialized for each float! + if (scanner.token == Token.BITS) { + scanner.scan(); + scanner.inBits = true; + } +i2f: { + switch (scanner.token) { + case INTVAL: + if (scanner.inBits) { + v = scanner.intValue; + break i2f; + } else { + f = (float) scanner.intValue; + break; + } + case FLOATVAL: + f = scanner.floatValue; + break; + case DOUBLEVAL: + f = (float) scanner.doubleValue; // to be excluded? + break; + case INF: + f = Float.POSITIVE_INFINITY; + break; + case NAN: + f = Float.NaN; + break; + default: + env.traceln("token=" + scanner.token); + env.error(scanner.pos, "token.expected", ""); + throw new Scanner.SyntaxError(); + } + v = Float.floatToIntBits(f); + } + if (scanner.sign == -1) { + v = v ^ 0x80000000; + } + obj = new ConstantPool.ConstValue_Integer(tag, new Integer(v)); + scanner.scan(); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitDouble(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitDouble]: "); + ConstantPool.ConstValue_Long obj = null; + try { + long v; + double d; + if (scanner.token == Token.BITS) { + scanner.scan(); + scanner.inBits = true; + } +d2l: { + switch (scanner.token) { + case INTVAL: + if (scanner.inBits) { + v = scanner.intValue; + break d2l; + } else { + d = (double) scanner.intValue; + break; + } + case LONGVAL: + if (scanner.inBits) { + v = scanner.longValue; + break d2l; + } else { + d = (double) scanner.longValue; + break; + } + case FLOATVAL: + d = scanner.floatValue; + break; + case DOUBLEVAL: + d = scanner.doubleValue; + break; + case INF: + d = Double.POSITIVE_INFINITY; + break; + case NAN: + d = Double.NaN; + break; + default: + env.error(scanner.pos, "token.expected", "Double"); + throw new Scanner.SyntaxError(); + } + v = Double.doubleToLongBits(d); + } + if (scanner.sign == -1) { + v = v ^ 0x8000000000000000L; + } + obj = new ConstantPool.ConstValue_Long(tag, new Long(v)); + scanner.scan(); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + return obj; + } + + private ConstantPool.ConstCell visitName(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitName]: "); + ConstantPool.ConstCell obj = null; + try { + obj = parser.parseName(); + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitMethodtype(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitMethodtype]: "); + ConstantPool.ConstValue_Cell obj = null; + ConstantPool.ConstCell cell = visitName(tag); + if (IOProb == null) { + obj = new ConstantPool.ConstValue_Cell(tag, cell); + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitString(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitString]: "); + ConstantPool.ConstValue_Cell obj = null; + ConstantPool.ConstCell cell = visitName(tag); + if (IOProb == null) { + obj = new ConstantPool.ConstValue_Cell(tag, cell); + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitClass(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitClass]: "); + ConstantPool.ConstValue_Cell obj = null; + try { + ConstantPool.ConstCell cell = parser.parseClassName(true); + obj = new ConstantPool.ConstValue_Cell(tag, cell); + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitMethodhandle(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitMethodHandle]: "); + ConstantPool.ConstValue_Pair obj = null; + try { + ConstantPool.ConstCell refCell; + ConstantPool.ConstCell subtagCell; + SubTag subtag; + if (scanner.token == Token.INTVAL) { + // Handle explicit constant pool form + subtag = subtag(scanner.intValue); + subtagCell = new ConstantPool.ConstCell(subtag.value()); + scanner.scan(); + scanner.expect(Token.COLON); + if (scanner.token != Token.CPINDEX) { + env.traceln("token=" + scanner.token); + env.error(scanner.pos, "token.expected", ""); + throw new Scanner.SyntaxError(); + } + int cpx = scanner.intValue; + refCell = parser.pool.getCell(cpx); + scanner.scan(); + + } else { + // normal JASM + subtag = parser.parseSubtag(); + subtagCell = new ConstantPool.ConstCell(subtag.value()); + scanner.expect(Token.COLON); + refCell = parser.parseMethodHandle(subtag); + } + obj = new ConstantPool.ConstValue_Pair(tag, subtagCell, refCell); + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + private ConstantPool.ConstValue_Pair visitMember(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitMember]: "); + ConstantPool.ConstValue_Pair obj = null; + try { + Token prevtoken = scanner.token; + ConstantPool.ConstCell firstName, ClassCell, NameCell, NapeCell; + firstName = parser.parseClassName(false); + if (scanner.token == Token.FIELD) { // DOT + scanner.scan(); + if (prevtoken == Token.CPINDEX) { + ClassCell = firstName; + } else { + ClassCell = parser.pool.FindCell(ConstType.CONSTANT_CLASS, firstName); + } + NameCell = parser.parseName(); + } else { + // no class provided - assume current class + ClassCell = parser.cd.me; + NameCell = firstName; + } + if (scanner.token == Token.COLON) { + // name and type separately + scanner.scan(); + NapeCell = parser.pool.FindCell(ConstType.CONSTANT_NAMEANDTYPE, NameCell, parser.parseName()); + } else { + // name and type as single name + NapeCell = NameCell; + } + obj = new ConstantPool.ConstValue_Pair(tag, ClassCell, NapeCell); + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitField(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitField]: "); + return visitMember(tag); + } + + @Override + public ConstantPool.ConstValue visitMethod(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitMethod]: "); + return visitMember(tag); + } + + @Override + public ConstantPool.ConstValue visitInterfacemethod(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitInterfacemethod]: "); + return visitMember(tag); + } + + @Override + public ConstantPool.ConstValue visitNameandtype(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitNameandtype]: "); + ConstantPool.ConstValue_Pair obj = null; + try { + ConstantPool.ConstCell NameCell = parser.parseName(), TypeCell; + scanner.expect(Token.COLON); + TypeCell = parser.parseName(); + obj = new ConstantPool.ConstValue_Pair(tag, NameCell, TypeCell); + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue_IndyPair visitInvokedynamic(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitInvokeDynamic]: "); + ConstantPool.ConstValue_IndyPair obj = null; + try { + if (scanner.token == Token.INTVAL) { + // Handle explicit constant pool form + int bsmIndex = scanner.intValue; + scanner.scan(); + scanner.expect(Token.COLON); + if (scanner.token != Token.CPINDEX) { + env.traceln("token=" + scanner.token); + env.error(scanner.pos, "token.expected", ""); + throw new Scanner.SyntaxError(); + } + int cpx = scanner.intValue; + scanner.scan(); + // Put a placeholder in place of BSM. + // resolve placeholder after the attributes are scanned. + BootstrapMethodData bsmData = new BootstrapMethodData(bsmIndex); + obj = new ConstantPool.ConstValue_IndyPair(bsmData, parser.pool.getCell(cpx)); + + } else { + // Handle full form + ConstantPool.ConstCell MHCell = parser.pool.FindCell(parseConstValue(ConstType.CONSTANT_METHODHANDLE)); + scanner.expect(Token.COLON); + ConstantPool.ConstCell NapeCell = parser.pool.FindCell(parseConstValue(ConstType.CONSTANT_NAMEANDTYPE)); + + ArrayList bsm_args = new ArrayList<>(256); + while (scanner.token != Token.SEMICOLON) { + if (scanner.token == Token.COMMA) { + scanner.scan(); + } + bsm_args.add(parseConstRef(null)); + } + BootstrapMethodData bsmData = new BootstrapMethodData(MHCell, bsm_args); + parser.cd.addBootstrapMethod(bsmData); + obj = new ConstantPool.ConstValue_IndyPair(bsmData, NapeCell); + } + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + } // End Visitor + + /** + * Parse CONSTVALUE + */ + protected ConstantPool.ConstValue parseConstValue(ConstType tag) throws IOException, Scanner.SyntaxError { + return pConstVstr.visitExcept(tag); + } + + /** + * Parse [TAG] CONSTVALUE + */ + protected ConstantPool.ConstValue parseTagConstValue(ConstType defaultTag) throws Scanner.SyntaxError, IOException { + return parseTagConstValue(defaultTag, null, false); + } + + private ConstType scanConstByID(boolean ignoreKeywords) { + ConstType tag = null; + if (!ignoreKeywords) { + ConstType tg = Tables.tag(scanner.idValue); + if (tg != null) { + tag = tg; + } + debugStr(" *^*^*^*^ [ParserCP.scanConst]: {TAG = " + (tg == null ? "null" : tg.toString()) + " "); + } + return tag; + } + + private ConstType scanConstPrimVal() throws Scanner.SyntaxError, IOException { + ConstType tag = null; + switch (scanner.token) { + case INTVAL: + tag = ConstType.CONSTANT_INTEGER; + break; + case LONGVAL: + tag = ConstType.CONSTANT_LONG; + break; + case FLOATVAL: + tag = ConstType.CONSTANT_FLOAT; + break; + case DOUBLEVAL: + tag = ConstType.CONSTANT_DOUBLE; + break; + case STRINGVAL: + case BITS: + case IDENT: + tag = ConstType.CONSTANT_STRING; + break; + default: + // problem - no constant value + System.err.println("NEAR: " + scanner.token.printval()); + env.error(scanner.pos, "value.expected"); + throw new Scanner.SyntaxError(); + } + return tag; + } + + private void checkWrongTag(ConstType tag, ConstType defaultTag, ConstType default2Tag) throws Scanner.SyntaxError, IOException { + if (defaultTag != null) { + if (tag != defaultTag) { + if (default2Tag == null) { + env.error("warn.wrong.tag", defaultTag.parseKey()); + } else if (tag != default2Tag) { + env.error("warn.wrong.tag2", defaultTag.parseKey(), default2Tag.parseKey()); + } + } + } + } + + protected ConstantPool.ConstValue parseTagConstValue(ConstType defaultTag, ConstType default2Tag, boolean ignoreKeywords) throws Scanner.SyntaxError, IOException { + debugScan(" *^*^*^*^ [ParserCP.parseTagConstValue]: Begin default_tag: ignoreKeywords: " + (ignoreKeywords ? "true" : "false")); + // Lookup the Tag from the scanner + ConstType tag = scanConstByID(ignoreKeywords); + debugStr(" *^*^*^*^ [ParserCP.parseTagConstValue]: {tag = " + tag + ", defaulttag = " + defaultTag + "} "); + + // If the scanned tag is null + if (tag == null) { + // and, if the expected tag is null + if (defaultTag == null) { + // return some other type of constant as the tag + tag = scanConstPrimVal(); + } else { + // otherwise, make the scanned-tag the same constant-type + // as the expected tag. + tag = defaultTag; + } + } else { + // If the scanned tag is some constant type + // and the scanned type does not equal the expected type + checkWrongTag(tag, defaultTag, default2Tag); + scanner.scan(); + } + return parseConstValue(tag); + } // end parseTagConstValue + + protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag) throws Scanner.SyntaxError, IOException { + return parseConstRef(defaultTag, null, false); + } + + protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag, ConstType default2Tag) throws Scanner.SyntaxError, IOException { + return parseConstRef(defaultTag, default2Tag, false); + } + + /** + * Parse an instruction argument, one of: * #NUMBER, #NAME, [TAG] CONSTVALUE + */ + protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag, + ConstType default2Tag, + boolean ignoreKeywords) throws Scanner.SyntaxError, IOException { + if (scanner.token == Token.CPINDEX) { + int cpx = scanner.intValue; + scanner.scan(); + return parser.pool.getCell(cpx); + } else { + ConstantPool.ConstValue ref = null; + ref = parseTagConstValue(defaultTag, default2Tag, ignoreKeywords); + return parser.pool.FindCell(ref); + } + } // end parseConstRef +} diff --git a/src/org/openjdk/asmtools/jasm/ParserInstr.java b/src/org/openjdk/asmtools/jasm/ParserInstr.java new file mode 100644 index 0000000..76c9ef0 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ParserInstr.java @@ -0,0 +1,405 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.JasmTokens.*; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.OpcodeTables.*; +import java.io.IOException; + +/** + * ParserInstr + * + * ParserInstr is a parser class owned by Parser.java. It is primarily responsible for + * parsing instruction byte codes. + */ +public class ParserInstr extends ParseBase { + + /** + * local handle for the constant parser - needed for parsing constants during + * instruction construction. + */ + private ParserCP cpParser = null; + + /** + * main constructor + * + * @param scanner + * @param parser + * @param env + */ + protected ParserInstr(Scanner scanner, Parser parser, ParserCP cpParser, Environment env) { + super.init(scanner, parser, env); + this.cpParser = cpParser; + } + + /** + * Parse an instruction. + */ + protected void parseInstr() throws Scanner.SyntaxError, IOException { + // ignore possible line numbers after java disassembler + if (scanner.token == Token.INTVAL) { + scanner.scan(); + } + // ignore possible numeric labels after java disassembler + if (scanner.token == Token.INTVAL) { + scanner.scan(); + } + if (scanner.token == Token.COLON) { + scanner.scan(); + } + + String mnemocode; + int mnenoc_pos; + for (;;) { // read labels + if (scanner.token != Token.IDENT) { + return; + } + mnemocode = scanner.idValue; + mnenoc_pos = scanner.pos; + scanner.scan(); + if (scanner.token != Token.COLON) { + break; + } + // actually it was a label + scanner.scan(); + parser.curCode.LabelDef(mnenoc_pos, mnemocode); + } + + Opcode opcode = OpcodeTables.opcode(mnemocode); + if (opcode == null) { + debugScan(" %%error%%error%%% $$$$$$$$$$$$$$$ mnemocode = '" + mnemocode + "'. "); + } + OpcodeType optype = opcode.type(); + + Argument arg = null; + Object arg2 = null; + StackMapData sMap = null; + + debugScan(" --IIIII---[ParserInstr:[parseInstr]: (Pos: " + mnenoc_pos + ") mnemocode: '" + opcode.parsekey() + "' "); + + switch (optype) { + case NORMAL: + switch (opcode) { + + // pseudo-instructions: + case opc_bytecode: + for (;;) { + parser.curCode.addInstr(mnenoc_pos, Opcode.opc_bytecode, parser.parseUInt(1), null); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_try: + for (;;) { + parser.curCode.beginTrap(scanner.pos, parser.parseIdent()); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_endtry: + for (;;) { + parser.curCode.endTrap(scanner.pos, parser.parseIdent()); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_catch: + parser.curCode.trapHandler(scanner.pos, parser.parseIdent(), + cpParser.parseConstRef(ConstType.CONSTANT_CLASS)); + return; + case opc_var: + for (;;) { + parser.parseLocVarDef(); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_endvar: + for (;;) { + parser.parseLocVarEnd(); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_locals_map: + sMap = parser.curCode.getStackMap(); + if (sMap.localsMap != null) { + env.error(scanner.pos, "localsmap.repeated"); + } + ; + DataVector localsMap = new DataVector(); + sMap.localsMap = localsMap; + if (scanner.token == Token.SEMICOLON) { + return; // empty locals_map allowed + } + for (;;) { + parser.parseMapItem(localsMap); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_stack_map: + sMap = parser.curCode.getStackMap(); + if (sMap.stackMap != null) { + env.error(scanner.pos, "stackmap.repeated"); + } + ; + DataVector stackMap = new DataVector(); + sMap.stackMap = stackMap; + if (scanner.token == Token.SEMICOLON) { + return; // empty stack_map allowed + } + for (;;) { + parser.parseMapItem(stackMap); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_stack_frame_type: + sMap = parser.curCode.getStackMap(); + if (sMap.stackFrameType != null) { + env.error(scanner.pos, "frametype.repeated"); + } + ; + sMap.setStackFrameType(parser.parseIdent()); + return; + + // normal instructions: + case opc_aload: + case opc_astore: + case opc_fload: + case opc_fstore: + case opc_iload: + case opc_istore: + case opc_lload: + case opc_lstore: + case opc_dload: + case opc_dstore: + case opc_ret: + case opc_aload_w: + case opc_astore_w: + case opc_fload_w: + case opc_fstore_w: + case opc_iload_w: + case opc_istore_w: + case opc_lload_w: + case opc_lstore_w: + case opc_dload_w: + case opc_dstore_w: + case opc_ret_w: + // loc var + arg = parser.parseLocVarRef(); + break; + case opc_iinc: // loc var, const + arg = parser.parseLocVarRef(); + scanner.expect(Token.COMMA); + arg2 = parser.parseInt(1); + break; + case opc_tableswitch: + case opc_lookupswitch: + arg2 = parseSwitchTable(); + break; + case opc_newarray: { + int type; + if (scanner.token == Token.INTVAL) { + type = scanner.intValue; + } else if ((type = Tables.basictypeValue(scanner.idValue)) == -1) { + env.error(scanner.pos, "type.expected"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + arg = new Argument(type); + break; + } + case opc_new: + case opc_anewarray: + case opc_instanceof: + case opc_checkcast: + arg = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + break; + case opc_bipush: + arg = parser.parseInt(1); + break; + case opc_sipush: + arg = parser.parseInt(2); + break; + case opc_ldc: + case opc_ldc_w: + case opc_ldc2_w: + arg = cpParser.parseConstRef(null); + break; + case opc_putstatic: + case opc_getstatic: + case opc_putfield: + case opc_getfield: + arg = cpParser.parseConstRef(ConstType.CONSTANT_FIELD); + break; + case opc_invokevirtual: + arg = cpParser.parseConstRef(ConstType.CONSTANT_METHOD); + break; + case opc_invokestatic: + case opc_invokespecial: + arg = cpParser.parseConstRef(ConstType.CONSTANT_METHOD, ConstType.CONSTANT_INTERFACEMETHOD); + break; + case opc_jsr: + case opc_goto: + case opc_ifeq: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_iflt: + case opc_ifne: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_icmplt: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_ifnull: + case opc_ifnonnull: + case opc_jsr_w: + case opc_goto_w: + arg = parseLabelRef(); + break; + + case opc_invokeinterface: + arg = cpParser.parseConstRef(ConstType.CONSTANT_INTERFACEMETHOD); + scanner.expect(Token.COMMA); + arg2 = parser.parseUInt(1); + break; + case opc_invokedynamic: + arg = cpParser.parseConstRef(ConstType.CONSTANT_INVOKEDYNAMIC); + break; + + case opc_multianewarray: + arg = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + scanner.expect(Token.COMMA); + arg2 = parser.parseUInt(1); + break; + case opc_wide: + case opc_nonpriv: + case opc_priv: + int opc2 = (opcode.value() << 8) | parser.parseUInt(1).arg; + opcode = opcode(opc2); + break; + } + break; + case WIDE: + arg = parser.parseLocVarRef(); + if (opcode == Opcode.opc_iinc_w) { // loc var, const + scanner.expect(Token.COMMA); + arg2 = parser.parseInt(2); + } + break; + case NONPRIVELEGED: + case PRIVELEGED: + break; + default: + env.error(scanner.prevPos, "wrong.mnemocode", mnemocode); + throw new Scanner.SyntaxError(); + } + // env.traceln(" [ParserInstr.parseInstr] ===============> Adding Instruction: [" + mnenoc_pos + "]: instr: "+ mnemocode /* opcNamesTab[opc] */); + parser.curCode.addInstr(mnenoc_pos, opcode, arg, arg2); + } //end parseInstr + + /** + * Parse a Switch Table. return value: SwitchTable. + */ + protected SwitchTable parseSwitchTable() throws Scanner.SyntaxError, IOException { + scanner.expect(Token.LBRACE); + Argument label; + int numpairs = 0, key; + SwitchTable table = new SwitchTable(env); +tableScan: + { + while (numpairs < 1000) { +// env.traceln("start tableScan:" + token); + switch (scanner.token) { + case INTVAL: +// env.traceln("enter tableScan:" + token); + key = scanner.intValue * scanner.sign; + scanner.scan(); + scanner.expect(Token.COLON); + table.addEntry(key, parseLabelRef()); + numpairs++; + if (scanner.token != Token.SEMICOLON) { +// env.traceln("break tableScan1:" + token); + break tableScan; + } + scanner.scan(); + break; + case DEFAULT: + scanner.scan(); + scanner.expect(Token.COLON); + if (table.deflabel != null) { + env.error("default.redecl"); + } + table.deflabel = parseLabelRef(); + if (scanner.token != Token.SEMICOLON) { +// env.traceln("break tableScan2:" + token); + break tableScan; + } + scanner.scan(); + break; + default: +// env.traceln("break tableScan3:" + token + "val=" + intValue); + break tableScan; + } // end switch + } // while (numpairs<1000) + env.error("long.switchtable", "1000"); + } // end tableScan + scanner.expect(Token.RBRACE); + return table; + } // end parseSwitchTable + + /** + * Parse a label instruction argument + */ + protected Argument parseLabelRef() throws Scanner.SyntaxError, IOException { + switch (scanner.token) { + case INTVAL: { + int v = scanner.intValue * scanner.sign; + scanner.scan(); + return new Argument(v); + } + case IDENT: { + String label = scanner.stringValue; + scanner.scan(); + return parser.curCode.LabelRef(label); + } + } + env.error("label.expected"); + throw new Scanner.SyntaxError(); + } + +} diff --git a/src/org/openjdk/asmtools/jasm/RuntimeConstants.java b/src/org/openjdk/asmtools/jasm/RuntimeConstants.java new file mode 100644 index 0000000..767c056 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/RuntimeConstants.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +/** + * + */ +public interface RuntimeConstants { + + /* Signature Characters */ + public static final char SIGC_VOID = 'V'; + public static final String SIG_VOID = "V"; + public static final char SIGC_BOOLEAN = 'Z'; + public static final String SIG_BOOLEAN = "Z"; + public static final char SIGC_BYTE = 'B'; + public static final String SIG_BYTE = "B"; + public static final char SIGC_CHAR = 'C'; + public static final String SIG_CHAR = "C"; + public static final char SIGC_SHORT = 'S'; + public static final String SIG_SHORT = "S"; + public static final char SIGC_INT = 'I'; + public static final String SIG_INT = "I"; + public static final char SIGC_LONG = 'J'; + public static final String SIG_LONG = "J"; + public static final char SIGC_FLOAT = 'F'; + public static final String SIG_FLOAT = "F"; + public static final char SIGC_DOUBLE = 'D'; + public static final String SIG_DOUBLE = "D"; + public static final char SIGC_ARRAY = '['; + public static final String SIG_ARRAY = "["; + public static final char SIGC_CLASS = 'L'; + public static final String SIG_CLASS = "L"; + public static final char SIGC_METHOD = '('; + public static final String SIG_METHOD = "("; + public static final char SIGC_ENDCLASS = ';'; + public static final String SIG_ENDCLASS = ";"; + public static final char SIGC_ENDMETHOD = ')'; + public static final String SIG_ENDMETHOD = ")"; + public static final char SIGC_PACKAGE = '/'; + public static final String SIG_PACKAGE = "/"; + + /* Class File Constants */ +// public static final int JAVA_MAGIC = 0xcafebabe; + public static final int JAVA_VERSION = 45; + public static final int JAVA_MINOR_VERSION = 3; + /* Access Flags */ + + public static final int ACC_PUBLIC = 0x0001; // class, inner, field, method + public static final int ACC_PRIVATE = 0x0002; // inner, field, method + public static final int ACC_PROTECTED = 0x0004; // inner, field, method + public static final int ACC_STATIC = 0x0008; // inner, field, method + public static final int ACC_FINAL = 0x0010; // class, inner, field, method + public static final int ACC_SUPER = 0x0020; // class + public static final int ACC_SYNCHRONIZED = 0x0020; // method + public static final int ACC_VOLATILE = 0x0040; // field + public static final int ACC_BRIDGE = 0x0040; // method + public static final int ACC_TRANSIENT = 0x0080; // field + public static final int ACC_VARARGS = 0x0080; // method + public static final int ACC_NATIVE = 0x0100; // method + public static final int ACC_INTERFACE = 0x0200; // class, inner + public static final int ACC_ABSTRACT = 0x0400; // class, inner, method + public static final int ACC_STRICT = 0x0800; // method + public static final int ACC_SYNTHETIC = 0x1000; // class, inner, field, method + public static final int ACC_ANNOTATION = 0x2000; // class, inner + public static final int ACC_ENUM = 0x4000; // class, inner, field +// public static final int ACC_MODULE = 0x8000; // class, inner, field, method + public static final int ACC_MANDATED = 0x8000; // method + + /* Attribute codes */ + public static final int SYNTHETIC_ATTRIBUTE = 0x00010000; // actually, this is an attribute + public static final int DEPRECATED_ATTRIBUTE = 0x00020000; // actually, this is an attribute + /* The version of a class file since which the compact format of stack map + * is necessary */ + public final int SPLIT_VERIFIER_CFV = 50; + +} diff --git a/src/org/openjdk/asmtools/jasm/Scanner.java b/src/org/openjdk/asmtools/jasm/Scanner.java new file mode 100644 index 0000000..f7b20ae --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Scanner.java @@ -0,0 +1,1164 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.JasmTokens.*; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.Constants.EOF; +import static org.openjdk.asmtools.jasm.Constants.OFFSETBITS; +import java.io.IOException; + +/** + * A Scanner for Jasm tokens. Errors are reported to the environment object.

+ * + * The scanner keeps track of the current token, the value of the current token (if any), + * and the start position of the current token.

+ * + * The scan() method advances the scanner to the next token in the input.

+ * + * The match() method is used to quickly match opening brackets (ie: '(', '{', or '[') + * with their closing counter part. This is useful during error recovery.

+ * + * The compiler treats either "\n", "\r" or "\r\n" as the end of a line.

+ */ +public class Scanner extends ParseBase { + + /** + * SyntaxError is the generic error thrown for parsing problems. + */ + protected static class SyntaxError extends Error { + } + + /** + * Input stream + */ + protected Environment in; + + /** + * The current character + */ + protected int ch; + + /** + * Current token + */ +// protected int token; + protected Token token; + + /** + * The position of the current token + */ + protected int pos; + + /* + * Token values. + */ + protected char charValue; + protected int intValue; + protected long longValue; + protected float floatValue; + protected double doubleValue; + protected String stringValue; + protected String idValue; + protected int radix; // Radix, when reading int or long + + /* doc comment preceding the most recent token */ + protected String docComment; + + /* A growable character buffer. */ + private int count; + private char buffer[] = new char[32]; + + /** + * The position of the previous token + */ + protected int prevPos; + protected int sign; // sign, when reading number + protected boolean inBits; // inBits prefix, when reading number + + /** + * main constructor. + * + * Create a scanner to scan an input stream. + */ + protected Scanner(Environment env) throws IOException { + super.init(this, null, env); + this.in = env; + ch = env.read(); + xscan(); + } + + /** + * scan + * + * Scan the next token. + * + * @throws IOException + */ + protected void scan() throws IOException { + int signloc = 1, cnt = 0; + prevPos = pos; +prefix: + for (;;) { + xscan(); + switch (token) { + case SIGN: + signloc = signloc * intValue; + break; + default: + break prefix; + } + cnt++; + } + switch (token) { + case INTVAL: + case LONGVAL: + case FLOATVAL: + case DOUBLEVAL: + case INF: + case NAN: + sign = signloc; + break; + default: + } + } + + /** + * Check the token may be identifier + */ + protected final boolean checkTokenIdent() { + return token.possibleJasmIdentifier(); + } + + static String readableConstant(int t) { + return "<" + Tables.tag(t) + "> [" + t + "]"; + } + + /** + * Expect a token, return its value, scan the next token or throw an exception. + */ + protected final void expect(Token t) throws SyntaxError, IOException { + if (token != t) { + if ((t != Token.IDENT) || !checkTokenIdent()) { + env.traceln("expect:" + t + " instead of " + token); + switch (t) { + case IDENT: + env.error(pos, "identifier.expected"); + break; + default: + env.error(pos, "token.expected", "<" + t.printval() + ">"); + break; + } + + if (debugFlag) { + debugStr("<<<<>>>>>>: "); + throw new Error("<<<<>>>>>>"); + } else { + throw new SyntaxError(); + } + } + } + scan(); + } + + private void putc(int ch) { + if (count == buffer.length) { + char newBuffer[] = new char[buffer.length * 2]; + System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); + buffer = newBuffer; + } + buffer[count++] = (char) ch; + } + + private String bufferString() { + char buf[] = new char[count]; + System.arraycopy(buffer, 0, buf, 0, count); + return new String(buf); + } + + /** + * Returns true if the character is a unicode digit. + * + * @param ch the character to be checked + */ + public static boolean isUCDigit(int ch) { + if ((ch >= '0') && (ch <= '9')) { + return true; + } + switch (ch >> 8) { + case 0x06: + return ((ch >= 0x0660) && (ch <= 0x0669)) || // Arabic-Indic + ((ch >= 0x06f0) && (ch <= 0x06f9)); // Eastern Arabic-Indic + case 0x07: + case 0x08: + default: + return false; + case 0x09: + return ((ch >= 0x0966) && (ch <= 0x096f)) || // Devanagari + ((ch >= 0x09e6) && (ch <= 0x09ef)); // Bengali + case 0x0a: + return ((ch >= 0x0a66) && (ch <= 0x0a6f)) || // Gurmukhi + ((ch >= 0x0ae6) && (ch <= 0x0aef)); // Gujarati + case 0x0b: + return ((ch >= 0x0b66) && (ch <= 0x0b6f)) || // Oriya + ((ch >= 0x0be7) && (ch <= 0x0bef)); // Tamil + case 0x0c: + return ((ch >= 0x0c66) && (ch <= 0x0c6f)) || // Telugu + ((ch >= 0x0ce6) && (ch <= 0x0cef)); // Kannada + case 0x0d: + return ((ch >= 0x0d66) && (ch <= 0x0d6f)); // Malayalam + case 0x0e: + return ((ch >= 0x0e50) && (ch <= 0x0e59)) || // Thai + ((ch >= 0x0ed0) && (ch <= 0x0ed9)); // Lao + case 0x0f: + return false; + case 0x10: + return ((ch >= 0x1040) && (ch <= 0x1049)); // Tibetan + } + } + + /** + * Returns true if the character is a Unicode letter. + * + * @param ch the character to be checked + */ + public static boolean isUCLetter(int ch) { + // fast check for Latin capitals and small letters + if (((ch >= 'A') && (ch <= 'Z')) + || ((ch >= 'a') && (ch <= 'z'))) { + return true; + } + // rest of ISO-LATIN-1 + if (ch < 0x0100) { + // fast check + if (ch < 0x00c0) { + return (ch == '_') || (ch == '$'); + } + // various latin letters and diacritics, + // but *not* the multiplication and division symbols + return ((ch >= 0x00c0) && (ch <= 0x00d6)) + || ((ch >= 0x00d8) && (ch <= 0x00f6)) + || ((ch >= 0x00f8) && (ch <= 0x00ff)); + } + // other non CJK alphabets and symbols, but not digits + if (ch <= 0x1fff) { + return !isUCDigit(ch); + } + // rest are letters only in five ranges: + // Hiragana, Katakana, Bopomofo and Hangul + // CJK Squared Words + // Korean Hangul Symbols + // Han (Chinese, Japanese, Korean) + // Han compatibility + return ((ch >= 0x3040) && (ch <= 0x318f)) + || ((ch >= 0x3300) && (ch <= 0x337f)) + || ((ch >= 0x3400) && (ch <= 0x3d2d)) + || ((ch >= 0x4e00) && (ch <= 0x9fff)) + || ((ch >= 0xf900) && (ch <= 0xfaff)); + } + + /** + * Scan a comment. This method should be called once the initial /, * and the next + * character have been read. + */ + private void skipComment() throws IOException { + while (true) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.comment"); + return; + case '*': + if ((ch = in.read()) == '/') { + ch = in.read(); + return; + } + break; + default: + ch = in.read(); + break; + } + } + } + + /** + * Scan a doc comment. This method should be called once the initial /, * and * have + * been read. It gathers the content of the comment (without leading spaces and '*'s) + * in the string buffer. + */ + @SuppressWarnings("empty-statement") + private String scanDocComment() throws IOException { + count = 0; + + if (ch == '*') { + do { + ch = in.read(); + } while (ch == '*'); + if (ch == '/') { + ch = in.read(); + return ""; + } + } + switch (ch) { + case '\n': + case ' ': + ch = in.read(); + break; + } + + boolean seenstar = false; + int c = count; + while (true) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.comment"); + return bufferString(); + case '\n': + putc('\n'); + ch = in.read(); + seenstar = false; + c = count; + break; + case ' ': + case '\t': + putc(ch); + ch = in.read(); + break; + case '*': + if (seenstar) { + if ((ch = in.read()) == '/') { + ch = in.read(); + count = c; + return bufferString(); + } + putc('*'); + } else { + seenstar = true; + count = c; + while ((ch = in.read()) == '*'); + switch (ch) { + case ' ': + ch = in.read(); + break; + case '/': + ch = in.read(); + count = c; + return bufferString(); + } + } + break; + default: + if (!seenstar) { + seenstar = true; + } + putc(ch); + ch = in.read(); + c = count; + break; + } + } + } + + /** + * Scan a decimal at this point + */ + private void scanCPRef() throws IOException { + switch (ch = in.read()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + boolean overflow = false; + long value = ch - '0'; + count = 0; + putc(ch); // save character in buffer +numberLoop: + for (;;) { + switch (ch = in.read()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + putc(ch); + if (overflow) { + break; + } + value = (value * 10) + (ch - '0'); + overflow = (value > 0xFFFF); + break; + default: + break numberLoop; + } + } // while true + intValue = (int) value; + stringValue = bufferString(); + token = Token.CPINDEX; + if (overflow) { + env.error(pos, "overflow"); + } + break; + } + default: + stringValue = (new Character((char) ch)).toString(); + env.error(in.pos, "invalid.number", stringValue); + intValue = 0; + token = Token.CPINDEX; + ch = in.read(); + } + } // scanCPRef() + + /** + * Scan a number. The first digit of the number should be the current character. We + * may be scanning hex, decimal, or octal at this point + */ + private void scanNumber() throws IOException { + boolean seenNonOctal = false; + boolean overflow = false; + radix = (ch == '0' ? 8 : 10); + long value = ch - '0'; + count = 0; + putc(ch); // save character in buffer +numberLoop: + for (;;) { + switch (ch = in.read()) { + case '.': + if (radix == 16) { + break numberLoop; // an illegal character + } + scanReal(); + return; + + case '8': + case '9': + // We can't yet throw an error if reading an octal. We might + // discover we're really reading a real. + seenNonOctal = true; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + putc(ch); + if (radix == 10) { + overflow = overflow || (value * 10) / 10 != value; + value = (value * 10) + (ch - '0'); + overflow = overflow || (value - 1 < -1); + } else if (radix == 8) { + overflow = overflow || (value >>> 61) != 0; + value = (value << 3) + (ch - '0'); + } else { + overflow = overflow || (value >>> 60) != 0; + value = (value << 4) + (ch - '0'); + } + break; + case 'd': + case 'D': + case 'e': + case 'E': + case 'f': + case 'F': + if (radix != 16) { + scanReal(); + return; + } + // fall through + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + putc(ch); + if (radix != 16) { + break numberLoop; // an illegal character + } + overflow = overflow || (value >>> 60) != 0; + value = (value << 4) + 10 + + Character.toLowerCase((char) ch) - 'a'; + break; + case 'l': + case 'L': + ch = in.read(); // skip over 'l' + longValue = value; + token = Token.LONGVAL; + break numberLoop; + case 'x': + case 'X': + // if the first character is a '0' and this is the second + // letter, then read in a hexadecimal number. Otherwise, error. + if (count == 1 && radix == 8) { + radix = 16; + break; + } else { + // we'll get an illegal character error + break numberLoop; + } + default: + intValue = (int) value; + token = Token.INTVAL; + break numberLoop; + } + } // while true + // we have just finished reading the number. The next thing better + // not be a letter or digit. + if (isUCDigit(ch) || isUCLetter(ch) || ch == '.') { + env.error(in.pos, "invalid.number", (new Character((char) ch)).toString()); + do { + ch = in.read(); + } while (isUCDigit(ch) || isUCLetter(ch) || ch == '.'); + intValue = 0; + token = Token.INTVAL; + } else if (radix == 8 && seenNonOctal) { + intValue = 0; + token = Token.INTVAL; + env.error(in.pos, "invalid.octal.number"); + } else if (overflow + || (token == Token.INTVAL + && ((radix == 10) ? (intValue - 1 < -1) + : ((value & 0xFFFFFFFF00000000L) != 0)))) { + intValue = 0; // so we don't get second overflow in Parser + longValue = 0; + env.error(pos, "overflow"); + } + } // scanNumber() + + /** + * Scan a float. We are either looking at the decimal, or we have already seen it and + * put it into the buffer. We haven't seen an exponent. Scan a float. Should be called + * with the current character is either the 'e', 'E' or '.' + */ + private void scanReal() throws IOException { + boolean seenExponent = false; + boolean isSingleFloat = false; + char lastChar; + if (ch == '.') { + putc(ch); + ch = in.read(); + } + +numberLoop: + for (;; ch = in.read()) { + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + putc(ch); + break; + case 'e': + case 'E': + if (seenExponent) { + break numberLoop; // we'll get a format error + } + putc(ch); + seenExponent = true; + break; + case '+': + case '-': + lastChar = buffer[count - 1]; + if (lastChar != 'e' && lastChar != 'E') { + break numberLoop; // this isn't an error, though! + } + putc(ch); + break; + case 'f': + case 'F': + ch = in.read(); // skip over 'f' + isSingleFloat = true; + break numberLoop; + case 'd': + case 'D': + ch = in.read(); // skip over 'd' + // fall through + default: + break numberLoop; + } // sswitch + } // loop + + // we have just finished reading the number. The next thing better + // not be a letter or digit. + if (isUCDigit(ch) || isUCLetter(ch) || ch == '.') { + env.error(in.pos, "invalid.number", (new Character((char) ch)).toString()); + do { + ch = in.read(); + } while (isUCDigit(ch) || isUCLetter(ch) || ch == '.'); + doubleValue = 0; + token = Token.DOUBLEVAL; + } else { + token = isSingleFloat ? Token.FLOATVAL : Token.DOUBLEVAL; + try { + lastChar = buffer[count - 1]; + if (lastChar == 'e' || lastChar == 'E' + || lastChar == '+' || lastChar == '-') { + env.error(in.pos - 1, "float.format"); + } else if (isSingleFloat) { + floatValue = Float.valueOf(bufferString()).floatValue(); + if (Float.isInfinite(floatValue)) { + env.error(pos, "overflow"); + } + } else { + doubleValue = Double.valueOf(bufferString()).doubleValue(); + if (Double.isInfinite(doubleValue)) { + env.error(pos, "overflow"); + env.error(pos, "overflow"); + } + } + } catch (NumberFormatException ee) { + env.error(pos, "float.format"); + doubleValue = 0; + floatValue = 0; + } + } + } // scanReal + + /** + * Scan an escape character. + * + * @return the character or '\\' + */ + private int scanEscapeChar() throws IOException { + int p = in.pos; + + switch (ch = in.read()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + int n = ch - '0'; + for (int i = 2; i > 0; i--) { + switch (ch = in.read()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + n = (n << 3) + ch - '0'; + break; + default: + if (n > 0xFF) { + env.error(p, "invalid.escape.char"); + } + return n; + } + } + ch = in.read(); + if (n > 0xFF) { + env.error(p, "invalid.escape.char"); + } + return n; + } + case 'r': + ch = in.read(); + return '\r'; + case 'n': + ch = in.read(); + return '\n'; + case 'f': + ch = in.read(); + return '\f'; + case 'b': + ch = in.read(); + return '\b'; + case 't': + ch = in.read(); + return '\t'; + case '\\': + ch = in.read(); + return '\\'; + case '\"': + ch = in.read(); + return '\"'; + case '\'': + ch = in.read(); + return '\''; + case 'u': + int unich = in.convertUnicode(); + ch = in.read(); + return unich; + } + return '\\'; + } + + /** + * Scan a string. The current character should be the opening " of the string. + */ + private void scanString() throws IOException { + token = Token.STRINGVAL; + count = 0; + ch = in.read(); + + // Scan a String + while (true) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.string"); + stringValue = bufferString(); + return; + case '\n': + ch = in.read(); + env.error(pos, "newline.in.string"); + stringValue = bufferString(); + return; + case '"': + ch = in.read(); + stringValue = bufferString(); + return; + case '\\': { + int c = scanEscapeChar(); + if (c >= 0) { + putc((char) c); + } + break; + } + default: + putc(ch); + ch = in.read(); + break; + } + } + } + + /** + * Scan an Identifier. The current character should be the first character of the + * identifier. + */ + private void scanIdentifier() throws IOException { + count = 0; + int firstChar = ch; + boolean firstIteration = true; +scanloop: + while (true) { + putc(ch); + ch = in.read(); + + // Check to see if the annotation marker is at + // the front of the identifier. + if (firstIteration && firstChar == '@') { + // May be a type annotation + if (ch == 'T') { // type annotation + putc(ch); + ch = in.read(); + } + + // is either a runtime visible or invisible annotation + if (ch == '+' || ch == '-') { // regular annotation + // possible annotation - + // need to eat up the '@+' or '@-' + putc(ch); + ch = in.read(); + } + idValue = bufferString(); + stringValue = idValue; + token = Token.ANNOTATION; + return; + } + + firstIteration = false; + switch (ch) { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '$': + case '_': + case '-': + case '[': + case ']': + case '(': + case ')': + case '<': + case '>': + break; + case '/': {// may be comment right after identifier + int c = in.lookForward(); + if ((c == '*') || (c == '/')) { + break scanloop; // yes, comment + } + break; // no, continue to parse identifier + } + case '\\': + if ((ch = in.read()) == 'u') { + ch = in.convertUnicode(); + if (isUCLetter(ch) || isUCDigit(ch)) { + break; + } + } else { + int p = in.pos; + env.error(p, "invalid.escape.char"); + } + default: +// if ((!isUCDigit(ch)) && (!isUCLetter(ch))) { + break scanloop; +// } + } // end switch + } // end scanloop + idValue = bufferString(); + stringValue = idValue; + token = keyword_token_ident(idValue); + debugStr("##### SCANNER (scanIdent) ######## token = " + token + " ##### "); + } // end scanIdentifier + +//============================== + @SuppressWarnings("empty-statement") + protected final void xscan() throws IOException { + docComment = null; +loop: + for (;;) { + pos = in.pos; + switch (ch) { + case EOF: + token = Token.EOF; + break loop; + case '\n': + case ' ': + case '\t': + case '\f': + ch = in.read(); + break; + case '/': + switch (ch = in.read()) { + case '/': + // Parse a // comment + while (((ch = in.read()) != EOF) && (ch != '\n')); + break; + case '*': + ch = in.read(); + if (ch == '*') { + docComment = scanDocComment(); + } else { + skipComment(); + } + break; + default: + token = Token.DIV; + break loop; + } + break; + case '"': + scanString(); + break loop; + case '-': + intValue = -1; + token = Token.SIGN; + ch = in.read(); + break loop; + case '+': + intValue = +1; + ch = in.read(); + token = Token.SIGN; + break loop; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + scanNumber(); + break loop; + case '.': + switch (ch = in.read()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + count = 0; + putc('.'); + scanReal(); + break; + default: + token = Token.FIELD; + } + break loop; + case '{': + ch = in.read(); + token = Token.LBRACE; + break loop; + case '}': + ch = in.read(); + token = Token.RBRACE; + break loop; + case ',': + ch = in.read(); + token = Token.COMMA; + break loop; + case ';': + ch = in.read(); + token = Token.SEMICOLON; + break loop; + case ':': + ch = in.read(); + token = Token.COLON; + break loop; + case '=': + if ((ch = in.read()) == '=') { + ch = in.read(); + token = Token.EQ; + break loop; + } + token = Token.ASSIGN; + break loop; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '$': + case '_': + case '@': + case '[': + case ']': + case '(': + case ')': + case '<': + case '>': + scanIdentifier(); + break loop; + case '\u001a': + // Our one concession to DOS. + if ((ch = in.read()) == EOF) { + token = Token.EOF; +// t0ken = Token.EOF; + break loop; + } + env.error(pos, "funny.char"); + ch = in.read(); + break; + case '#': + int c = in.lookForward(); + if (c == '{') { + // '#' char denotes a "paramMethod name" token + ch = in.read(); + token = Token.PARAM_NAME; + break loop; + } + // otherwise, it is a normal cpref + scanCPRef(); + break loop; + case '\\': + if ((ch = in.read()) == 'u') { + ch = in.convertUnicode(); + if (isUCLetter(ch)) { + scanIdentifier(); + break loop; + } + } + default: + env.out.println("funny.char:" + env.lineNumber(pos) + "/" + (pos & ((1 << OFFSETBITS) - 1))); + env.error(pos, "funny.char"); + ch = in.read(); + } + } + } + + /** + * Scan to a matching '}', ']' or ')'. The current token must be a '{', '[' or '('; + */ + protected void match(Token open, Token close) throws IOException { + int depth = 1; + + while (true) { + xscan(); + if (token == open) { + depth++; + } else if (token == close) { + if (--depth == 0) { + return; + } + } else if (token == Token.EOF) { + env.error(pos, "unbalanced.paren"); + return; + } + } + } + + @Override + protected void debugScan(String dbstr) { + if (token == null) { + env.traceln(dbstr + "<<>>"); + return; + } + env.trace(dbstr + token); + switch (token) { + case IDENT: + env.traceln(" = '" + stringValue + "' {idValue = '" + idValue + "'}"); + break; + case STRINGVAL: + env.traceln(" = {stringValue}: \"" + stringValue + "\""); + break; + case INTVAL: + env.traceln(" = {intValue}: " + intValue + "}"); + break; + case FLOATVAL: + env.traceln(" = {floatValue}: " + floatValue); + break; + case DOUBLEVAL: + env.traceln(" = {doubleValue}: " + doubleValue); + break; + default: + env.traceln(""); + } + } + +} diff --git a/src/org/openjdk/asmtools/jasm/StackMapData.java b/src/org/openjdk/asmtools/jasm/StackMapData.java new file mode 100644 index 0000000..68ea32b --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/StackMapData.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.IOException; + +/** + * + */ +public class StackMapData implements Data { + + /** + * + */ + static public class StackMapItem1 implements Data { + + StackMapType itemType; + + StackMapItem1(StackMapType itemType) { + this.itemType = itemType; + } + + @Override + public int getLength() { + return 1; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(itemType.value()); + } + } + + /** + * + */ + static public class StackMapItem2 implements Data { + + StackMapType itemType; + Argument arg; + + StackMapItem2(StackMapType itemType, Argument arg) { + this.itemType = itemType; + this.arg = arg; + } + + @Override + public int getLength() { + return 3; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(itemType.value()); + out.writeShort(arg.arg); + } + } + + int pc; + int offset; + int type; + String stackFrameType = null; + boolean isStackMapTable = false; + DataVector localsMap, stackMap; + Environment env; + + StackMapData(Environment env) { + this.env = env; + } + + void setPC(int pc) { + this.pc = pc; + } + + void setOffset(int offset) { + this.offset = offset; + } + + void setOffset(StackMapData prevFrame) { + offset = (prevFrame == null) ? pc : (pc - prevFrame.pc - 1); + } + + void setStackFrameType(String stackFrameType) { + this.stackFrameType = stackFrameType; + + if (stackFrameType != null) { + type = stackMapFrameTypeValue(stackFrameType); + } + + if (stackFrameType == null || type == -1) { + env.error(pc, "invalid.stack.frame.type", stackFrameType, "" + type); + } + } + + void setIsStackMapTable(boolean isStackMapTable) { + this.isStackMapTable = isStackMapTable; + } + + void setLocalsMap(DataVector localsMap) { + this.localsMap = localsMap; + } + + void setStackMap(DataVector stackMap) { + this.stackMap = stackMap; + } + + @Override + public int getLength() { + int res = 0; + StackMapFrameType frame_type = StackMapFrameType.FULL_FRAME; + // int frame_type = FULL_FRAME; + + if (isStackMapTable) { + if (stackFrameType != null) { + frame_type = stackMapFrameType(type); + } + res += 1; + } + + switch (frame_type) { + case SAME_FRAME: + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + res += stackMap.getLength() - 2; + break; + case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME: + res += stackMap.getLength(); + break; + case CHOP_1_FRAME: + case CHOP_2_FRAME: + case CHOP_3_FRAME: + res += 2; + break; + case SAME_FRAME_EX: + res += 2; + break; + case APPEND_FRAME: + res += 2 + (localsMap == null ? 0 : (localsMap.getLength() - 2)); + break; + case FULL_FRAME: + res += 2; + res += (localsMap == null ? 2 : localsMap.getLength()); + res += (stackMap == null ? 2 : stackMap.getLength()); + break; + default: + ; + } + return res; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + StackMapFrameType frame_type = StackMapFrameType.FULL_FRAME; + + if (isStackMapTable) { + if (stackFrameType != null) { + frame_type = stackMapFrameType(type); + } + } + + switch (frame_type) { + case SAME_FRAME: + if (offset >= 64) { + env.error(pc, "invalid.offset.same.frame", "" + offset); + } + out.writeByte(offset); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + if (stackMap == null) { + env.error(pc, "no.stack.map.same.locals"); + break; + } + + if (stackMap.elements.size() != 1) { + env.error(pc, "should.be.only.one.stack.map.element"); + break; + } + + if (offset >= 64) { + env.error(pc, "invalid.offset.same.locals", "" + offset); + break; + } + out.writeByte(frame_type.value() + offset); + stackMap.writeElements(out); + break; + case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME: + if (stackMap == null) { + env.error(pc, "no.stack.map.same.locals"); + break; + } + + if (stackMap.elements.size() != 1) { + env.error(pc, "should.be.only.one.stack.map.element"); + break; + } + out.writeByte(frame_type.value()); + out.writeShort(offset); + stackMap.writeElements(out); + break; + case CHOP_1_FRAME: + case CHOP_2_FRAME: + case CHOP_3_FRAME: + case SAME_FRAME_EX: + boolean error = false; + + if (stackMap != null) { + env.error(pc, "unexpected.stack.maps"); + error = true; + } + + if (localsMap != null) { + env.error(pc, "unexpected.locals.maps"); + error = true; + } + + if (error) { + break; + } + out.writeByte(frame_type.value()); + out.writeShort(offset); + break; + case APPEND_FRAME: + if (localsMap == null) { + env.error(pc, "no.locals.map.append"); + break; + } + + if (localsMap.elements.size() > 3) { + env.error(pc, "more.locals.map.elements"); + break; + } + out.writeByte(frame_type.value() + localsMap.elements.size() - 1); + out.writeShort(offset); + localsMap.writeElements(out); + break; + case FULL_FRAME: + if (isStackMapTable) { + out.writeByte(frame_type.value()); + out.writeShort(offset); + } else { + out.writeShort(pc); + } + + if (localsMap == null) { + out.writeShort(0); + } else { + localsMap.write(out); + } + + if (stackMap == null) { + out.writeShort(0); + } else { + stackMap.write(out); + } + break; + default: + env.error(pc, "invalid.stack.frame.type", "" + frame_type); + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/SwitchTable.java b/src/org/openjdk/asmtools/jasm/SwitchTable.java new file mode 100644 index 0000000..402ee60 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/SwitchTable.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * + */ +class SwitchTable { + + Argument deflabel = null; + ArrayList labels = new ArrayList<>(); + ArrayList keys = new ArrayList<>(); + +// for tableswitch: + Argument[] resLabels; + int high, low; + + int pc, pad; + Environment env; + + SwitchTable(Environment env) { + this.env = env; + } + + void addEntry(int key, Argument label) { + keys.add(key); + labels.add(label); + } + +// for lookupswitch: + int calcLookupSwitch(int pc) { + this.pc = pc; + pad = ((3 - pc) & 0x3); + int len = 1 + pad + (keys.size() + 1) * 8; + if (deflabel == null) { + deflabel = new Argument(pc + len); + } + return len; + } + + void writeLookupSwitch(CheckedDataOutputStream out) throws IOException { + env.traceln(" writeLookupSwitch: pc=" + pc + " pad=" + pad + " deflabel=" + deflabel.arg); + int k; + for (k = 0; k < pad; k++) { + out.writeByte(0); + } + out.writeInt(deflabel.arg - pc); + out.writeInt(keys.size()); + for (k = 0; k < keys.size(); k++) { + out.writeInt(keys.get(k)); + out.writeInt((labels.get(k)).arg - pc); + } + } + + int recalcTableSwitch(int pc) { + int k; + int numpairs = keys.size(); + int high1 = Integer.MIN_VALUE, low1 = Integer.MAX_VALUE; + int numslots = 0; + if (numpairs > 0) { + for (k = 0; k < numpairs; k++) { + int key = keys.get(k); + if (key > high1) { + high1 = key; + } + if (key < low1) { + low1 = key; + } + } + numslots = high1 - low1 + 1; + } +// if (numslots>2000) env.error("long.switchtable", "2000"); + env.traceln(" recalcTableSwitch: low=" + low1 + " high=" + high1); + this.pc = pc; + pad = ((3 - pc) & 0x3); + int len = 1 + pad + (numslots + 3) * 4; + if (deflabel == null) { + deflabel = new Argument(pc + len); + } + Argument[] resLabels1 = new Argument[numslots]; + for (k = 0; k < numslots; k++) { + resLabels1[k] = deflabel; + } + for (k = 0; k < numpairs; k++) { + env.traceln(" keys.data[" + k + "]=" + keys.get(k)); + resLabels1[keys.get(k) - low1] = labels.get(k); + } + this.resLabels = resLabels1; + this.labels = null; + this.keys = null; + this.high = high1; + this.low = low1; + return len; + } + + void writeTableSwitch(CheckedDataOutputStream out) throws IOException { + int k; + for (k = 0; k < pad; k++) { + out.writeByte(0); + } + out.writeInt(deflabel.arg - pc); + out.writeInt(low); + out.writeInt(high); + for (k = 0; k < resLabels.length; k++) { + out.writeInt(resLabels[k].arg - pc); + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/Tables.java b/src/org/openjdk/asmtools/jasm/Tables.java new file mode 100644 index 0000000..7e6565b --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Tables.java @@ -0,0 +1,653 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.PrintWriter; +import java.util.HashMap; + +/** + * + * Tables + * + * The classes in Tables are following a Singleton Pattern. These classes are Enums, and + * they are contained in private hash maps (lookup tables and reverse lookup tables). + * These hash maps all have public accessors, which clients use to look-up enums. + * + * Tokens in this table carry no external state, and are typically treated as constants. + * They do not need to be reset. + * + */ +public class Tables { + + public static final int JAVA_MAGIC = 0xCAFEBABE; + /** + * Lookup-tables for various types. + */ + private static HashMap NameToAttrTag = new HashMap<>(9); + private static HashMap AttrTags = new HashMap<>(9); + + private static HashMap NameToSubTag = new HashMap<>(9); + private static HashMap SubTags = new HashMap<>(9); + + private static HashMap NameToBasicType = new HashMap<>(10); + private static HashMap BasicTypes = new HashMap<>(10); + + private static HashMap NameToAnnotElemType = new HashMap<>(10); + private static HashMap AnnotElemTypes = new HashMap<>(10); + + private static HashMap KeyToStackMapType = new HashMap<>(10); + private static HashMap NameToStackMapType = new HashMap<>(10); + private static HashMap StackMapTypes = new HashMap<>(10); + + private static HashMap NameToStackMapFrameType = new HashMap<>(10); + private static HashMap StackMapFrameTypes = new HashMap<>(10); + + private static HashMap NameToConstantType = new HashMap<>(ConstType.maxTag); + private static HashMap ConstantTypes = new HashMap<>(ConstType.maxTag); + + static { + // register all of the tokens + for (ConstType ct : ConstType.values()) { + registerConstantType(ct); + } + + /* Type codes for SubTags */ + for (AttrTag at : AttrTag.values()) { + registerAttrtag(at); + } + + /* Type codes for SubTags */ + for (SubTag st : SubTag.values()) { + registerSubtag(st); + } + + /* Type codes for BasicTypes */ + for (BasicType bt : BasicType.values()) { + registerBasicType(bt); + } + + /* Type codes for BasicTypes */ + for (AnnotElemType aet : AnnotElemType.values()) { + registerAnnotElemType(aet); + } + + /* Type codes for StackMapTypes */ + for (StackMapType smt : StackMapType.values()) { + registerStackMapType(smt); + } + + /* Type codes for StackMapFrame attribute */ + for (StackMapFrameType smft : StackMapFrameType.values()) { + registerStackMapFrameType(smft); + } + + } + + /** + * ConstType + * + * A (typed) tag (constant) representing the type of Constant in the Constant Pool. + */ + static public enum ConstType { + CONSTANT_ZERO (-3, "CONSTANT_ZERO", ""), + CONSTANT_UTF8 (1, "CONSTANT_UTF8", "Asciz"), + CONSTANT_UNICODE (2, "CONSTANT_UNICODE", ""), + CONSTANT_INTEGER (3, "CONSTANT_INTEGER", "int"), + CONSTANT_FLOAT (4, "CONSTANT_FLOAT", "float"), + CONSTANT_LONG (5, "CONSTANT_LONG", "long"), + CONSTANT_DOUBLE (6, "CONSTANT_DOUBLE", "double"), + CONSTANT_CLASS (7, "CONSTANT_CLASS", "class"), + CONSTANT_STRING (8, "CONSTANT_STRING", "String"), + CONSTANT_FIELD (9, "CONSTANT_FIELD", "Field"), + CONSTANT_METHOD (10, "CONSTANT_METHOD", "Method"), + CONSTANT_INTERFACEMETHOD (11, "CONSTANT_INTERFACEMETHOD", "InterfaceMethod"), + CONSTANT_NAMEANDTYPE (12, "CONSTANT_NAMEANDTYPE", "NameAndType"), + // Constant 13 reserved + // Constant 14 reserved + CONSTANT_METHODHANDLE (15, "CONSTANT_METHODHANDLE", "MethodHandle"), + CONSTANT_METHODTYPE (16, "CONSTANT_METHODTYPE", "MethodType"), + CONSTANT_INVOKEDYNAMIC_TRANS (17, "CONSTANT_INVOKEDYNAMIC_TRANS", "InvokeDynamicTrans"), + CONSTANT_INVOKEDYNAMIC (18, "CONSTANT_INVOKEDYNAMIC", "InvokeDynamic"); + + static final public int maxTag = 18; + + private final int value; + private final String parseKey; + private final String printval; + + ConstType(int val, String print, String parse) { + value = val; + parseKey = parse; + printval = print; + } + + public int value() { + return value; + } + + public String parseKey() { + return parseKey; + } + + public String printval() { + return printval; + } + + public void print(PrintWriter out) { + out.print(parseKey); + } + + @Override + public String toString() { + return "<" + printval + "> [" + Integer.toString(value) + "]"; + } + }; + + static public ConstType tag(int i) { + return ConstantTypes.get(i); + } + + static public ConstType tag(String parsekey) { + return NameToConstantType.get(parsekey); + } + + private static void registerConstantType(ConstType tt) { + NameToConstantType.put(tt.parseKey, tt); + ConstantTypes.put(tt.value, tt); + } + + /*-------------------------------------------------------- */ + /** + * Attribute descriptor enums + */ + static public enum AttrTag { + + // Constant for ME Spec (StackMap does not appear in SE VM Spec) + ATT_Unrecognized (0, "ATT_Unrecognized", ""), + ATT_StackMap (1, "ATT_StackMap", "StackMap"), + // Numbers corespond to VM spec (chapter 4.7.X) + ATT_ConstantValue (2, "ATT_ConstantValue", "ConstantValue"), + ATT_Code (3, "ATT_Code", "Code"), + ATT_StackMapTable (4, "ATT_StackMapTable", "StackMapTable"), + ATT_Exceptions (5, "ATT_Exceptions", "Exceptions"), + ATT_InnerClasses (6, "ATT_InnerClasses", "InnerClasses"), + ATT_EnclosingMethod (7, "ATT_EnclosingMethod", "EnclosingMethod"), + ATT_Synthetic (8, "ATT_Synthetic", "Synthetic"), + ATT_Signature (9, "ATT_Signature", "Signature"), + ATT_SourceFile (10, "ATT_SourceFile", "SourceFile"), + ATT_SourceDebugExtension (11, "ATT_SourceDebugExtension", "SourceDebugExtension"), + ATT_LineNumberTable (12, "ATT_LineNumberTable", "LineNumberTable"), + ATT_LocalVariableTable (13, "ATT_LocalVariableTable", "LocalVariableTable"), + ATT_LocalVariableTypeTable (14, "ATT_LocalVariableTypeTable", "LocalVariableTypeTable"), + ATT_Deprecated (15, "ATT_Deprecated", "Deprecated"), + ATT_RuntimeVisibleAnnotations (16, "ATT_RuntimeVisibleAnnotations", "RuntimeVisibleAnnotations"), + ATT_RuntimeInvisibleAnnotations (17, "ATT_RuntimeInvisibleAnnotations", "RuntimeInvisibleAnnotations"), + ATT_RuntimeVisibleParameterAnnotations (18, "ATT_RuntimeVisibleParameterAnnotations", "RuntimeVisibleParameterAnnotations"), + ATT_RuntimeInvisibleParameterAnnotations (19, "ATT_RuntimeInvisibleParameterAnnotations", "RuntimeInvisibleParameterAnnotations"), + ATT_AnnotationDefault (20, "ATT_AnnotationDefault", "AnnotationDefault"), + ATT_BootstrapMethods (21, "ATT_BootstrapMethods", "BootstrapMethods"), + ATT_RuntimeVisibleTypeAnnotations (22, "ATT_RuntimeVisibleTypeAnnotations", "RuntimeVisibleTypeAnnotations"), + ATT_RuntimeInvisibleTypeAnnotations (23, "ATT_RuntimeInvisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations"), + ATT_MethodParameters (24, "ATT_MethodParameters", "MethodParameters"); + + private final Integer value; + private final String printval; + private final String parsekey; + + AttrTag(Integer val, String print, String parse) { + value = val; + printval = print; + parsekey = parse; + } + + public String printval() { + return printval; + } + + public String parsekey() { + return parsekey; + } + } + + private static void registerAttrtag(AttrTag tg) { + NameToAttrTag.put(tg.parsekey, tg); + AttrTags.put(tg.value, tg); + } + + public static AttrTag attrtag(int val) { + AttrTag tg = AttrTags.get(val); + if (tg == null) { + tg = AttrTag.ATT_Unrecognized; + } + return tg; + } + + public static AttrTag attrtag(String idValue) { + AttrTag tg = NameToAttrTag.get(idValue); + if (tg == null) { + tg = AttrTag.ATT_Unrecognized; + } + return tg; + } + + public static String attrtagName(int subtag) { + AttrTag tg = AttrTags.get(subtag); + return tg.parsekey; + } + + public static int attrtagValue(String idValue) { + AttrTag tg = attrtag(idValue); + return tg.value; + } + + + /*-------------------------------------------------------- */ + /** + * SubTag enums + */ + static public enum SubTag { + REF_GETFIELD (1, "REF_getField"), + REF_GETSTATIC (2, "REF_getStatic"), + REF_PUTFIELD (3, "REF_putField"), + REF_PUTSTATIC (4, "REF_putStatic"), + REF_INVOKEVIRTUAL (5, "REF_invokeVirtual"), + REF_INVOKESTATIC (6, "REF_invokeStatic"), + REF_INVOKESPECIAL (7, "REF_invokeSpecial"), + REF_NEWINVOKESPECIAL (8, "REF_newInvokeSpecial"), + REF_INVOKEINTERFACE (9, "REF_invokeInterface"); + + private final Integer value; + private final String printval; + + SubTag(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + + public Integer value() { + return value; + } + } + + private static void registerSubtag(SubTag tg) { + NameToSubTag.put(tg.printval, tg); + SubTags.put(tg.value, tg); + } + + public static SubTag subtag(String subtag) { + return NameToSubTag.get(subtag); + } + + public static SubTag subtag(int subtag) { + return SubTags.get(subtag); + } + + public static String subtagName(int subtag) { + String retval = null; + SubTag tg = SubTags.get(subtag); + if (tg != null) { + retval = tg.printval; + } + return retval; + } + + public static int subtagValue(String idValue) { + int retval = 0; + SubTag tg = NameToSubTag.get(idValue); + if (tg != null) { + retval = tg.value; + } + return retval; + } + + /*-------------------------------------------------------- */ + /** + * BasicType enums + */ + static public enum BasicType { + T_INT (0x0000000a, "int"), + T_LONG (0x0000000b, "long"), + T_FLOAT (0x00000006, "float"), + T_DOUBLE (0x00000007, "double"), + T_CLASS (0x00000002, "class"), + T_BOOLEAN (0x00000004, "boolean"), + T_CHAR (0x00000005, "char"), + T_BYTE (0x00000008, "byte"), + T_SHORT (0x00000009, "short"); + + private final Integer value; + private final String printval; + + BasicType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + } + + private static void registerBasicType(BasicType typ) { + NameToBasicType.put(typ.printval, typ); + BasicTypes.put(typ.value, typ); + } + + public static BasicType basictype(String idValue) { + return NameToBasicType.get(idValue); + } + + public static BasicType basictype(int subtag) { + return BasicTypes.get(subtag); + } + + public static String basictypeName(int subtag) { + String retval = null; + BasicType tg = BasicTypes.get(subtag); + if (tg != null) { + retval = tg.printval; + } + return retval; + } + + public static int basictypeValue(String idValue) { + int retval = -1; + BasicType tg = NameToBasicType.get(idValue); + if (tg != null) { + retval = tg.value; + } + return retval; + } + + /*-------------------------------------------------------- */ + /** + * AnnotElemType enums + */ + static public enum AnnotElemType { + + AE_BYTE ('B', "byte"), + AE_CHAR ('C', "char"), + AE_SHORT ('S', "short"), + AE_INT ('I', "int"), + AE_LONG ('J', "long"), + AE_FLOAT ('F', "float"), + AE_DOUBLE ('D', "double"), + AE_BOOLEAN ('Z', "boolean"), + AE_STRING ('s', "string"), + AE_ENUM ('e', "enum"), + AE_CLASS ('c', "class"), + AE_ANNOTATION ('@', "annotation"), + AE_ARRAY ('[', "array"); + + private final char value; + private final String printval; + + AnnotElemType(char val, String print) { + value = val; + printval = print; + } + + public char val() { + return value; + } + + public String printval() { + return printval; + } + } + + private static void registerAnnotElemType(AnnotElemType typ) { + NameToAnnotElemType.put(typ.printval, typ); + AnnotElemTypes.put(typ.value, typ); + } + + public static AnnotElemType annotElemType(String idValue) { + return NameToAnnotElemType.get(idValue); + } + + public static AnnotElemType annotElemType(char subtag) { + return AnnotElemTypes.get(subtag); + } + + public static String annotElemTypeName(char subtag) { + String retval = null; + AnnotElemType tg = AnnotElemTypes.get(subtag); + if (tg != null) { + retval = tg.printval; + } + return retval; + } + + public static char annotElemTypeVal(String idValue) { + char retval = 0; + AnnotElemType tg = NameToAnnotElemType.get(idValue); + if (tg != null) { + retval = tg.value; + } + return retval; + } + + + /*-------------------------------------------------------- */ + /** + * MapTypes table. These constants are used in stackmap pseudo-instructions only. + */ + static public enum StackMapType { + /* Type codes for StackMap attribute */ + ITEM_Bogus (0, "bogus", "B"), // an unknown or uninitialized value + ITEM_Integer (1, "int", "I"), // a 32-bit integer + ITEM_Float (2, "float", "F"), // not used + ITEM_Double (3, "double", "D"), // not used + ITEM_Long (4, "long", "L"), // a 64-bit integer + ITEM_Null (5, "null", "N"), // the type of null + ITEM_InitObject (6, "this", "IO"), // "this" in constructor + ITEM_Object (7, "CP", "O"), // followed by 2-byte index of class name + ITEM_NewObject (8, "at", "NO"); // followed by 2-byte ref to "new" + + private final Integer value; + private final String printval; + private final String parsekey; + + StackMapType(Integer val, String print, String parse) { + value = val; + printval = print; + parsekey = parse; + } + + public String parsekey() { + return parsekey; + } + + public String printval() { + return printval; + } + + public Integer value() { + return value; + } + } + + private static void registerStackMapType(StackMapType typ) { + KeyToStackMapType.put(typ.parsekey, typ); + NameToStackMapType.put(typ.printval, typ); + StackMapTypes.put(typ.value, typ); + } + + public static StackMapType stackMapType(int subtag) { + return StackMapTypes.get(subtag); + } + + public static StackMapType stackMapType(String subtag) { + return NameToStackMapType.get(subtag); + } + + public static StackMapType stackMapTypeKey(String subtag) { + return KeyToStackMapType.get(subtag); + } + + public static String stackMapTypeName(int subtag) { + String retval = null; + StackMapType tg = StackMapTypes.get(subtag); + if (tg != null) { + retval = tg.printval; + } + return retval; + } + + public static int stackMapTypeValue(String idValue) { + int retval = 0; + StackMapType tg = NameToStackMapType.get(idValue); + if (tg != null) { + retval = tg.value; + } + return retval; + } + + + /*-------------------------------------------------------- */ + /** + * StackMap-FrameType table. These constants are used in stackmap pseudo-instructions + * only. + */ + static public enum StackMapFrameType { + /* Type codes for StackMapFrame attribute */ + SAME_FRAME (0, "same"), + SAME_LOCALS_1_STACK_ITEM_FRAME (64, "stack1"), + SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME (247, "stack1_ex"), + CHOP_1_FRAME (250, "chop1"), + CHOP_2_FRAME (249, "chop2"), + CHOP_3_FRAME (248, "chop3"), + SAME_FRAME_EX (251, "same_ex"), + APPEND_FRAME (252, "append"), + FULL_FRAME (255, "full"); + + private final Integer value; + private final String parsekey; + + StackMapFrameType(Integer val, String print) { + value = val; + parsekey = print; + } + + public String parsekey() { + return parsekey; + } + + public Integer value() { + return value; + } + } + + private static void registerStackMapFrameType(StackMapFrameType typ) { + NameToStackMapFrameType.put(typ.parsekey, typ); + StackMapFrameTypes.put(typ.value, typ); + } + + public static StackMapFrameType stackMapFrameTypeVal(int subtag) { + return StackMapFrameTypes.get(subtag); + } + + public static String stackMapFrameTypeName(int subtag) { + String retval = null; + StackMapFrameType tg = StackMapFrameTypes.get(subtag); + if (tg != null) { + retval = tg.parsekey; + } + return retval; + } + + public static StackMapFrameType stackMapFrameType(int subtag) { + StackMapFrameType frametype; + if (subtag < StackMapFrameType.SAME_LOCALS_1_STACK_ITEM_FRAME.value()) { + // type is same_frame; + frametype = StackMapFrameType.SAME_FRAME; + } else if (subtag >= StackMapFrameType.SAME_LOCALS_1_STACK_ITEM_FRAME.value() + && subtag <= 127) { + // type is same_locals_1_stack_item_frame + frametype = StackMapFrameType.SAME_LOCALS_1_STACK_ITEM_FRAME; + + } else if (subtag >= StackMapFrameType.APPEND_FRAME.value() + && subtag < StackMapFrameType.FULL_FRAME.value()) { + // type is append_frame + frametype = StackMapFrameType.APPEND_FRAME; + } else { + frametype = StackMapFrameTypes.get(subtag); + } + return frametype; + } + + public static int stackMapFrameTypeValue(String idValue) { + int retval = 0; + StackMapFrameType tg = NameToStackMapFrameType.get(idValue); + if (tg != null) { + retval = tg.value; + } + return retval; + } + + /*-------------------------------------------------------- */ + /** + * CF_Context enums + */ + static public enum CF_Context { + + CTX_CLASS (0, "class"), + CTX_FIELD (1, "field"), + CTX_METHOD (2, "method"), + CTX_INNERCLASS (3, "inner-class"); + + private final int value; + private final String printval; + + CF_Context(int val, String print) { + value = val; + printval = print; + } + + public int val() { + return value; + } + + public String printval() { + return printval; + } + } + + private static void registerAnnotElemType(CF_Context ctx) { + // NameToAnnotElemType.put(typ.printval, typ); +// AnnotElemTypes.put(typ.value, typ); + } + /* + public static CF_Context annotElemType(String idValue) { + return NameToAnnotElemType.get(idValue); + } + public static CF_Context annotElemType(int subtag) { + return AnnotElemTypes.get(subtag); + } + * */ + +} diff --git a/src/org/openjdk/asmtools/jasm/TypeAnnotationData.java b/src/org/openjdk/asmtools/jasm/TypeAnnotationData.java new file mode 100644 index 0000000..0b4b53f --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/TypeAnnotationData.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; +import org.openjdk.asmtools.jasm.TypeAnnotationUtils.*; + +/** + * + */ +public class TypeAnnotationData extends AnnotationData { + + protected TargetType targetType; + protected TargetInfo targetInfo; + protected ArrayList targetPath; + + + /*-------------------------------------------------------- */ + + /*-------------------------------------------------------- */ + /* TypeAnnotationData Methods */ + public TypeAnnotationData(Argument typeCPX, boolean invisible) { + super(typeCPX, invisible); + } + + @Override + public int getLength() { + return super.getLength() + 2 + targetInfo.getLength(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); +// KTL: (1/10/13) Spec changed: char -> byte +// out.writeShort(targetType.value); + out.writeByte(targetType.value); + targetInfo.write(out); + } + + @Override + public String toString() { + return toString(0); + } + + public String toString(int tabLevel) { + StringBuilder sb = new StringBuilder(); + String tabStr = tabString(tabLevel); + + sb.append(tabStr + "Target Type: "); + sb.append(targetType.toString()); + sb.append('\n'); + + sb.append(tabStr + "Target Info: "); + sb.append(targetInfo.toString(tabLevel)); + sb.append('\n'); + + sb.append(tabStr + "Target Path: ["); + boolean first = true; + for (TypePathEntry tpe : targetPath) { + if (!first) { + sb.append(", "); + } + first = false; + sb.append(tpe); + + } + sb.append("]"); + sb.append('\n'); + + return sb.toString(); + } + + protected static String tabString(int tabLevel) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < tabLevel; i++) { + sb.append('\t'); + } + + return sb.toString(); + } + +} diff --git a/src/org/openjdk/asmtools/jasm/TypeAnnotationUtils.java b/src/org/openjdk/asmtools/jasm/TypeAnnotationUtils.java new file mode 100644 index 0000000..6ea1baa --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/TypeAnnotationUtils.java @@ -0,0 +1,891 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * + */ +public class TypeAnnotationUtils { + + + /*-------------------------------------------------------- */ + /* TypeAnnotationData Inner Classes */ + static public enum PathKind { + DEEPER_ARRAY (0, "DEEPER_ARRAY", "ARRAY"), + DEEPER_NESTEDTYPE (1, "DEEPER_NESTEDTYPE", "INNER_TYPE"), + BOUND_WILDCARDTYPE (2, "BOUND_WILDCARDTYPE", "WILDCARD"), + ITHARG_PARAMETERTYPE (3, "ITHARG_PARAMETERTYPE", "TYPE_ARGUMENT"); + + private final int key; + private final String type; + private final String parseKey; + public static final int maxLen = 3; + + PathKind(int key, String type, String parseval) { + this.key = key; + this.type = type; + this.parseKey = parseval; + } + + public int key() { + return key; + } + + public String parsekey() { + return parseKey; + } + + } + + static private HashMap PathTypeHash = new HashMap<>(PathKind.maxLen); + + private static void initPathTypes(PathKind pk) { + PathTypeHash.put(pk.parseKey, pk); + } + + static { + for (PathKind pk : PathKind.values()) { + initPathTypes(pk); + } + } + + public static PathKind pathKind(String parseKey) { + return PathTypeHash.get(parseKey); + } + + // will throw ArrayIndexOutOfBounds if i < 0 or i > 3 + static public PathKind getPathKind(int i) { + return PathKind.values()[i]; + } + + /*-------------------------------------------------------- */ + /* TypeAnnotationData Inner Classes */ + static public class TypePathEntry { + + private final PathKind kind; + private final char index; + + public TypePathEntry(int kind, char index) { + this.kind = getPathKind(kind); + this.index = index; + } + + public PathKind kind() { + return kind; + } + + public char index() { + return index; + } + + public String toString() { + return kind.parsekey() + "(" + index + ")"; + } + + } + + /*-------------------------------------------------------- */ + /* TypeAnnotationData Inner Classes */ + static public enum InfoType { + TYPEPARAM ("TYPEPARAM", "TYPEPARAM"), + SUPERTYPE ("SUPERTYPE", "SUPERTYPE"), + TYPEPARAM_BOUND ("TYPEPARAM_BOUND", "TYPEPARAM_BOUND"), + EMPTY ("EMPTY", "EMPTY"), + METHODPARAM ("METHODPARAM", "METHODPARAM"), + EXCEPTION ("EXCEPTION", "EXCEPTION"), + LOCALVAR ("LOCALVAR", "LOCALVAR"), + CATCH ("CATCH", "CATCH"), + OFFSET ("OFFSET", "OFFSET"), + TYPEARG ("TYPEARG", "TYPEARG"); + + private final String parseKey; + private final String printval; + + InfoType(String parse, String print) { + parseKey = parse; + printval = print; + } + + public String parseKey() { + return parseKey; + } + + } + + /** + * TargetType + * + * A (typed) tag (constant) representing the type of Annotation Target. + */ + static public enum TargetType { + class_type_param (0x00, "CLASS_TYPE_PARAMETER", InfoType.TYPEPARAM, "class type parameter"), + meth_type_param (0x01, "METHOD_TYPE_PARAMETER", InfoType.TYPEPARAM, "method type parameter"), + class_exts_impls (0x10, "CLASS_EXTENDS", InfoType.SUPERTYPE, "class extends/implements"), + class_type_param_bnds (0x11, "CLASS_TYPE_PARAMETER_BOUND", InfoType.TYPEPARAM_BOUND, "class type parameter bounds"), + meth_type_param_bnds (0x12, "METHOD_TYPE_PARAMETER_BOUND", InfoType.TYPEPARAM_BOUND, "method type parameter bounds"), + field (0x13, "FIELD", InfoType.EMPTY, "field"), + meth_ret_type (0x14, "METHOD_RETURN", InfoType.EMPTY, "method return type"), + meth_reciever (0x15, "METHOD_RECEIVER", InfoType.EMPTY, "method reciever"), + meth_formal_param (0x16, "METHOD_FORMAL_PARAMETER", InfoType.METHODPARAM, "method formal parameter type"), + throws_type (0x17, "THROWS", InfoType.EXCEPTION, "exception type in throws"), + local_var (0x40, "LOCAL_VARIABLE", InfoType.LOCALVAR, "local variable"), + resource_var (0x41, "RESOURCE_VARIABLE", InfoType.LOCALVAR, "resource variable"), // TODO + exception_param (0x42, "EXCEPTION_PARAM", InfoType.CATCH, "exception parameter"), // TODO + type_test (0x43, "INSTANCEOF", InfoType.OFFSET, "type test (instanceof)"), + obj_creat (0x44, "NEW", InfoType.OFFSET, "object creation (new)"), + constr_ref_receiver (0x45, "CONSTRUCTOR_REFERENCE_RECEIVER", InfoType.OFFSET, "constructor reference receiver"), + meth_ref_receiver (0x46, "METHOD_REFERENCE_RECEIVER", InfoType.OFFSET, "method reference receiver"), + cast (0x47, "CAST", InfoType.TYPEARG, "cast"), + constr_invoc_typearg (0x48, "CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT", InfoType.TYPEARG, "type argument in constructor call"), + meth_invoc_typearg (0x49, "METHOD_INVOCATION_TYPE_ARGUMENT", InfoType.TYPEARG, "type argument in method call"), + constr_ref_typearg (0x4A, "CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT", InfoType.TYPEARG, "type argument in constructor reference"), + meth_ref_typearg (0x4B, "METHOD_REFERENCE_TYPE_ARGUMENT", InfoType.TYPEARG, "type argument in method reference"); + + public static final int maxTag = 0x9A; + public static final int maxLen = 36; + + public final int value; + private final String parseKey; + private final InfoType infoKey; + private final String printval; + + TargetType(int val, String parse, InfoType info, String print) { + value = val; + parseKey = parse; + infoKey = info; + printval = print; + } + + public String parseKey() { + return parseKey; + } + + public String infoKey() { + return infoKey.parseKey(); + } + + public InfoType infoType() { + return infoKey; + } + + public void print(PrintWriter out) { + out.print(parseKey); + } + + @Override + public String toString() { + return parseKey + " (" + infoKey() + ") <" + printval + "> [" + Integer.toHexString(value) + "]"; + } + }; + + static private HashMap TargetTypeHash = new HashMap<>(TargetType.maxLen); + static private HashMap TargetTypeList = new HashMap<>(TargetType.maxLen); + + private static void initTargetTypes(TypeAnnotationUtils.TargetType tt) { + TargetTypeList.put(tt.value, tt); + TargetTypeHash.put(tt.parseKey, tt); + } + + static { + for (TargetType type : TargetType.values()) { + initTargetTypes(type); + } + } + + public static TargetType targetType(String parseKey) { + return TargetTypeHash.get(parseKey); + } + + public static TargetType targetTypeEnum(Integer typeCode) { + return TargetTypeList.get(typeCode); + } + + /** + * TargetInfo + * + * BaseClass for any Type Annotation Target-Info. + */ + public static class TargetInfo { + + protected TargetType targettype = null; + + public TargetInfo(TargetType tt) { + targettype = tt; + } + + public TargetType getTargetType() { + return targettype; + } + + public void print(PrintWriter out, String tab) { + // print the TargetType and TargetInfo + out.print(tab + " {"); + targettype.print(out); + _print(out, tab); + out.print(tab + "}"); + } + + public void _print(PrintWriter out, String tab) { + // sub-classes override + } + + public void write(CheckedDataOutputStream out) throws IOException { + // placeholder + } + + public int getLength() { + return 0; + } + + @Override + public String toString() { + return toString(0); + } + + protected void _toString(StringBuilder sb, int tabLevel) { + // sub-classes override + } + + public String toString(int tabLevel) { + StringBuilder sb = new StringBuilder(); + String tabStr = tabString(tabLevel); + + // first print the name/target-type + sb.append(tabStr); + sb.append(targettype.infoKey() + " \n"); + sb.append(tabStr); + sb.append(targettype + " \n"); + + // get the sub-classes parts + _toString(sb, tabLevel); + + return sb.toString(); + } + + protected static String tabString(int tabLevel) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < tabLevel; i++) { + sb.append('\t'); + } + + return sb.toString(); + } + + } + + /** + * TypeParams_Target (3.3.1 Type parameters) + * + * BaseClass for any Type Annotation Target-Info that is in a parameterized type, + * array, or nested type. + * + * These need location information to identify the annotated element. + */ + public static class typeparam_target extends TargetInfo { + + int pindex; + + public typeparam_target(TargetType tt, int indx) { + super(tt); + pindex = indx; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(pindex); + super.write(out); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(pindex); + } + + @Override + public int getLength() { + return 1 + super.getLength(); + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + String tabStr = tabString(tabLevel); + sb.append(tabStr); + sb.append("param_index: "); + sb.append(pindex); + sb.append('\n'); + } + } + + /** + * supertype_target (3.3.2 Class extends and implements) + * + * BaseClass for any Type Annotation Target-Info that is in a parameterized type, + * array, or nested type. + * + * These need location information to identify the annotated element. + */ + public static class supertype_target extends TargetInfo { + + int type_index; + + public supertype_target(TargetType tt, int tindex) { + super(tt); + type_index = tindex; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(type_index); + super.write(out); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(type_index); + } + + @Override + public int getLength() { + return 2 + super.getLength(); + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + String tabStr = tabString(tabLevel); + sb.append(tabStr); + sb.append("type_index: "); + sb.append(type_index); + sb.append('\n'); + } + } + + /** + * typeparam_bound_target (3.3.3 Type parameter bounds) + * + * BaseClass for any Type Annotation Target-Info that is in a parameterized type, + * array, or nested type. + * + * These need location information to identify the annotated element. + */ + public static class typeparam_bound_target extends TargetInfo { + + int pindex; + int bindex; + + public typeparam_bound_target(TargetType tt, int pindx, int bindx) { + super(tt); + pindex = pindx; + bindex = bindx; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(pindex); + out.writeByte(bindex); + super.write(out); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(pindex); + out.print(" "); + out.print(bindex); + } + + @Override + public int getLength() { + return 2 + super.getLength(); + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + String tabStr = tabString(tabLevel); + sb.append(tabStr); + sb.append("param_index: "); + sb.append(pindex); + sb.append('\n'); + + sb.append(tabStr); + sb.append("bound_index: "); + sb.append(bindex); + sb.append('\n'); + } + } + + /** + * empty_target (3.3.4 ) + * + * Types without arguments. + * + * Basic types without arguments, like field, method return, or method receiver. + */ + public static class empty_target extends TargetInfo { + + public empty_target(TargetType tt) { + super(tt); + } + } + + /** + * methodformalparam_target (3.3.5 Method parameters) + * + * BaseClass for any Type Annotation Target-Info that is in a parameterized type, + * array, or nested type. + * + * These need location information to identify the annotated element. + */ + public static class methodformalparam_target extends TargetInfo { + + int index; + + public methodformalparam_target(TargetType tt, int indx) { + super(tt); + index = indx; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(index); + super.write(out); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(index); + } + + @Override + public int getLength() { + return 1 + super.getLength(); + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + String tabStr = tabString(tabLevel); + sb.append(tabStr); + sb.append("index: "); + sb.append(index); + sb.append('\n'); + } + } + + /** + * throws_target (3.3.6 throws clauses) + * + * BaseClass for any Type Annotation Target-Info that is in a parameterized type, + * array, or nested type. + * + * These need location information to identify the annotated element. + */ + public static class throws_target extends TargetInfo { + + int type_index; + + public throws_target(TargetType tt, int tindex) { + super(tt); + type_index = tindex; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(type_index); + super.write(out); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(type_index); + } + + @Override + public int getLength() { + return 2 + super.getLength(); + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + String tabStr = tabString(tabLevel); + sb.append(tabStr); + sb.append("type_index: "); + sb.append(type_index); + sb.append('\n'); + } + } + + /** + * localvar_target (3.3.7 Local variables) + * + * BaseClass for any Type Annotation Target-Info that is in a parameterized type, + * array, or nested type. + * + * These need location information to identify the annotated element. + */ + public static class localvar_target extends TargetInfo { + + public static class LocalVar_Entry { + + public int startPC; + public int length; + public int cpx; + + public LocalVar_Entry(int st, int len, int index) { + startPC = st; + length = len; + cpx = index; + } + + void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(startPC); + out.writeShort(length); + out.writeShort(cpx); + } + + public void _print(PrintWriter out, String tab) { + out.print(tab + "{"); + out.print(startPC); + out.print(" "); + out.print(length); + out.print(" "); + out.print(cpx); + out.println("}"); + } + + public String toString() { + return new String("startPC: " + startPC + + " length: " + length + + " cpx: " + cpx); + } + } + + ArrayList table = null; + + public localvar_target(TargetType tt, int size) { + super(tt); + table = new ArrayList<>(size); + } + + public void addEntry(int startPC, int length, int cpx) { + LocalVar_Entry entry = new LocalVar_Entry(startPC, length, cpx); + table.add(entry); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(table.size()); + for (LocalVar_Entry entry : table) { + entry.write(out); + } + super.write(out); + } + + @Override + public void _print(PrintWriter out, String tab) { + String innerTab = tab + " "; + out.println(); + for (LocalVar_Entry entry : table) { + entry._print(out, innerTab); + } + out.print(innerTab); + } + + @Override + public int getLength() { + return 2 + // U2 for table size + (6 * table.size()) + // (3 * U2) for each table entry + super.getLength(); + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + String tabStr1 = tabString(tabLevel + 1); + int i = 1; + for (LocalVar_Entry entry : table) { + sb.append(tabStr1); + sb.append("[" + i + "]: "); + sb.append(entry.toString()); + sb.append('\n'); + i += 1; + } + } + } + + /** + * catch_target (3.3.8 Exception parameters (catch clauses)) + * + * Index to the exception type (the type that shows up in a catch clause). + * + * These need location information to identify the annotated element. + */ + public static class catch_target extends TargetInfo { + + int exception_table_index; + + public catch_target(TargetType tt, int indx) { + super(tt); + exception_table_index = indx; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(exception_table_index); + super.write(out); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(exception_table_index); + } + + @Override + public int getLength() { + return 2 + super.getLength(); + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + String tabStr = tabString(tabLevel); + sb.append(tabStr); + sb.append("exception_table_index: "); + sb.append(exception_table_index); + sb.append('\n'); + } + } + + /** + * offset_target (3.3.9 Typecasts, type tests, and object creation) + * + * BaseClass for any Type Annotation Target-Info that is in a parameterized type, + * array, or nested type. + * + * These need location information to identify the annotated element. + */ + public static class offset_target extends TargetInfo { + + int offset; + + public offset_target(TargetType tt, int ofst) { + super(tt); + offset = ofst; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(offset); + super.write(out); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(offset); + } + + @Override + public int getLength() { + return 2 + super.getLength(); + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + String tabStr = tabString(tabLevel); + sb.append(tabStr); + sb.append("offset: "); + sb.append(offset); + sb.append('\n'); + } + } + + /** + * typearg_target (3.3.10 Constructor and method call type arguments) + * + * BaseClass for any Type Annotation Target-Info that is in a parameterized type, + * array, or nested type. + * + * These need location information to identify the annotated element. + */ + public static class typearg_target extends TargetInfo { + + int offset; + int typeIndex; + + public typearg_target(TargetType tt, int ofst, int tindx) { + super(tt); + offset = ofst; + typeIndex = tindx; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(offset); + out.writeByte(typeIndex); + super.write(out); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(offset); + out.print(" "); + out.print(typeIndex); + } + + @Override + public int getLength() { + return 3 + super.getLength(); + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + String tabStr = tabString(tabLevel); + sb.append(tabStr); + sb.append("offset: "); + sb.append(offset); + sb.append('\n'); + + sb.append(tabStr); + sb.append("type_index: "); + sb.append(typeIndex); + sb.append('\n'); + } + } + + /*-------------------------------------------------------- */ + /* TypeAnnotationVisitor Methods */ + public static class TypeAnnotationTargetVisitor { + + public final void visit(TargetType tt) { + switch (tt) { + case class_type_param: + case meth_type_param: + visit_type_param_target(tt); + break; + case class_exts_impls: + visit_supertype_target(tt); + break; + case class_type_param_bnds: + case meth_type_param_bnds: + visit_typeparam_bound_target(tt); + break; + case field: + case meth_ret_type: + case meth_reciever: + visit_empty_target(tt); + break; + case meth_formal_param: + visit_methodformalparam_target(tt); + break; + case throws_type: + visit_throws_target(tt); + break; + case local_var: + case resource_var: + visit_localvar_target(tt); + break; + case exception_param: + visit_catch_target(tt); + break; + case type_test: + case obj_creat: + case constr_ref_receiver: + case meth_ref_receiver: + visit_offset_target(tt); + break; + + case cast: + case constr_invoc_typearg: + case meth_invoc_typearg: + case constr_ref_typearg: + case meth_ref_typearg: + + visit_typearg_target(tt); + break; + } + } + + public void visit_type_param_target(TargetType tt) { + } + + public void visit_supertype_target(TargetType tt) { + } + + public void visit_typeparam_bound_target(TargetType tt) { + } + + public void visit_empty_target(TargetType tt) { + } + + public void visit_methodformalparam_target(TargetType tt) { + } + + public void visit_throws_target(TargetType tt) { + } + + public void visit_localvar_target(TargetType tt) { + } + + public void visit_catch_target(TargetType tt) { + } + + public void visit_offset_target(TargetType tt) { + } + + public void visit_typearg_target(TargetType tt) { + } + } + + /*-------------------------------------------------------- */ + + /*-------------------------------------------------------- */ + /* TypeAnnotationData Methods */ + public TypeAnnotationUtils() { + } + + public static TargetType getTargetType(int tt_index) { + if (tt_index < 0 || tt_index > TargetType.maxTag) { + return null; + } + return TargetTypeList.get(tt_index); + } + + static public TargetType getTargetType(String tt) { + TargetType retval = TargetTypeHash.get(tt); + + if (retval.name().equals("UNUSED")) { + retval = null; + } + + return retval; + } + +} diff --git a/src/org/openjdk/asmtools/jasm/i18n.properties b/src/org/openjdk/asmtools/jasm/i18n.properties new file mode 100644 index 0000000..b0b68c4 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/i18n.properties @@ -0,0 +1,186 @@ +# Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +jasm.usage=\ +Usage: java -jar asmtools.jar jasm [options] file.jasm...\n\ +where possible options include: + +jasm.opt.d=\ +\ -d destdir directory to place resulting .class files +jasm.opt.v=\ +\ -v add trace information +jasm.opt.g=\ +\ -g add debug information +jasm.opt.version=\ +\ -version prints the program version +jasm.opt.nowrite=\ +\ -nowrite do not write resulting .class files +jasm.opt.strict=\ +\ -strict consider warnings as errors +jasm.opt.nowarn=\ +\ -nowarn do not print warnings +jasm.opt.cv=\ +\ -cv major.minor set operating class file version (by default {0}.{1}) + + +jasm.error.d_requires_argument=-d requires argument +jasm.error.does_not_exist={0} does not exist +jasm.error.cv_requires_arg=-cv requires argument +jasm.error.invalid_major_minor_param=invalid parameter major.minor +jasm.error.invalid_option=invalid option: {0} +jasm.error.cannot_read=cannot read {0} +jasm.error.cannot_write=cannot write {0} +jasm.error.fatal_error=fatal error +jasm.error.fatal_exception=fatal exception + + +#err.not.implemented=Sorry, {0} not implemented. +# Scanner: +err.invalid.escape.char=Invalid escape character. +err.eof.in.comment=Comment not terminated at end of input. +err.invalid.number=Invalid character "{0}" in number. +err.invalid.octal.number=Invalid character in octal number. +err.overflow=Numeric overflow. +err.float.format=Invalid floating point format. +err.eof.in.string=String not terminated at end of input. +err.newline.in.string=String not terminated at end of line. +#err.invalid.char.constant=Invalid character constant. +err.funny.char=Invalid character in input. +err.unbalanced.paren=Unbalanced parentheses. +# Parser: +err.package.repeated=Package statement repeated. +#err.module.repeated=Module statement repeated. +warn.intf.repeated=Interface {0} repeated. +warn.exc.repeated=Exception repeated in throws clause. +err.multiple.inherit=Multiple inheritance is not supported. +err.toplevel.expected=Class or interface declaration expected. +err.const.def.expected=Constant declaration expected. +err.const.undecl=Constant #{0} not declared. +err.const.redecl=Constant {0} redeclared. +warn.const0.redecl=Re-declaration of Constant #0 cannot be written to the class file. +#err.const.bsmindex=Bad Bootstrap Methods index {0} specified. +#warn.const.misused=Constant {0} was assumed to have another tag. +err.field.expected=Field or method declaration expected. +err.token.expected={0} expected. +err.identifier.expected=Identifier expected. +#err.missing.term=Missing term. +#err.tag.expected=Tag expected. +err.name.expected=Name expected, got {0}. +err.int.expected=Integer expected. +err.neg.forbidden=Negative integer is not allowed here. +err.value.large=Value doesn't fit in {0}. +err.value.expected=Value expected. +err.wrong.mnemocode=Invalid mnemocode ({0}). +#err.class.expected='class' or 'interface' keyword expected. +err.default.redecl=Default statement already declared in this table. +err.long.switchtable=Switchtable too long: > {0}. +err.io.exception=I/O error in {0}. +warn.wrong.tag=Wrong tag: {0} expected. +warn.wrong.tag2=Wrong tags: {0} or {1} expected. +# Code Gen: +err.locvar.redecl=Local variable {0} redeclared. +err.locvar.undecl=Local variable {0} not declared. +#err.locvar.expected=Local variable expected. +err.label.redecl=Label {0} redeclared. +err.label.undecl=Label {0} not declared. +err.label.expected=Label expected. +err.subtag.expected=Subtag expected. +err.type.expected=Type expected. +err.trap.tryredecl= redeclared. +err.trap.endtryredecl= redeclared. +err.trap.notry=No found. +err.trap.noendtry=No found. +warn.trap.notref=No declared. +err.cannot.write=Cannot write to {0}. +err.msig.malformed=Malformed method signature at char {0}. [err={1}] +err.no.classname=Class name not defined. +warn.msig.more255=Number of parameters too large ({0}>255). +#warn.msig.large=Number of parameters ({0}) exceeds max_locals value ({1}). +warn.illslot=Local variable at Illegal slot {0}. +#warn.invalid.modifier=This modifier is not allowed here +warn.repeated.modifier=Repeated modifier. +warn.invalid.modifier.init=invalid modifier for method \"{0}\". +warn.invalid.modifier.fiva=at most one of final and volatile modifiers can be used for a field. +warn.invalid.modifier.intfield=interface field must be public static final only +warn.invalid.modifier.field=invalid modifier for a field. +warn.init.in_int= method cannot be placed in an interface. +warn.invalid.modifier.intmth=interface method must be abstract public only \"{0}\". +warn.invalid.modifier.abst=invalid modifier for abstract method. +warn.invalid.modifier.mth=invalid modifier for a method. +warn.invalid.modifier.acc=at most one of public, protected, and private modifiers can be used. +warn.invalid.modifier.int=invalid modifier for an interface. +warn.invalid.modifier.int.abs=interface class must have abstract modifier. +warn.invalid.modifier.class=invalid modifier for a class. +warn.invalid.modifier.class.finabs=class cannot be both abstract and final. +warn.invalid.modifier.innerclass=invalid modifier for an inner class \"{0}\". +err.itemtype.expected=StackMap item type expected instead of {0}. +err.localsmap.repeated=locals_map redeclared. +err.invalid.stack.frame.type=invalid stack frame type. +err.invalid.offset.same.frame=offset value more than 64 for the 'same_frame' type frame. +err.no.stack.map.same.locals=stack map element for the 'same_locals_1_stack_item_frame' type frame is absent. +err.should.be.only.one.stack.map.element=should be only one stack map element for the 'same_locals_1_stack_item_frame' type frame. +err.invalid.offset.same.locals=offset value more than 64 for the 'same_locals_1_stack_item_frame' type frame. +err.unexpected.stack.maps=there are unexpected stack maps. +err.unexpected.locals.maps=there are unexpected locals maps. +err.no.locals.map.append=locals map element for the 'append_frame' type frame is absent. +err.more.locals.map.elements=there are more than 3 locals map element for the 'append_frame' type frame. +err.stackmap.repeated=stack_map redeclared. +err.version.expected=class file version expected +err.invalid.innerclass=Invalid declaration of Inner Class +err.invalid.bootstrapmethod=Invalid declaration of BootstrapMethod Entry +#err.table.expected=Table expected +#err.package.module.expected=package or module expected +err.frametype.repeated=Frametype repeated +#err.module.expected=module expected +err.invalid.paramnum=Invalid Parameter Number: {0}. +err.duplicate.paramnum=Duplicate Parameter Number: {0}. +err.paramname.constnum.invaltype=ParameterName CPX at {0} is not a ConstantString. +err.paramname.token.unexpected=Incorrect ParamName, unrecognized token: \"{0}\". +# +# annotations Errors +# +err.incorrect.annot.class=Incorrect Annotation (class), expected class name or CPX), got \"{0}\". +err.incorrect.annot.enum=Incorrect Annotation (enum), expected type field IDENT, \"{0}\". +err.incorrect.annot.enum.cpx==Incorrect Annotation (enum), expexted type field CPX. +err.incorrect.annot.token=Incorrect Annotation, unrecognized token: \"{0}\". +err.incorrect.annot.bool=Incorrect Annotation (boolean), expected Integer), got \"{0}\". +err.incorrect.annot.byte=Incorrect Annotation (byte), expected Integer), got \"{0}\". +err.incorrect.annot.char=Incorrect Annotation (char), expected Integer), got \"{0}\". +err.incorrect.annot.short=Incorrect Annotation (short), expected Integer), got \"{0}\". +err.incorrect.annot.keyword=Incorrect Annotation keyword \"{0}\". +err.incorrect.typeannot.target=Incorrect TypeAnnotation target \"{0}\". +err.incorrect.typeannot.targtype.string=Incorrect TypeAnnotation \"{0}\" argument: (expected String), \"{1}\". +err.incorrect.typeannot.targtype.int=Incorrect TypeAnnotation \"{0}\" argument: (expected Integer), \"{1}\". +err.incorrect.typeannot.pathentry=Incorrect TypeAnnotation TargetPath PathEntry \"{0}\". +err.incorrect.typeannot.pathentry.argindex=Incorrect TypeAnnotation TargetPath PathEntry ArgIndex (expected Integer), \"{0}\". + +# +# Compiler Errors +# +# comperr.hashcode.err="CV hash:{0}" +comperr.constcell.nullvalset="Cell without value in setCell" +comperr.constcell.nullvalhash="Cell without value in cpoolHashByValue" +comperr.constcell.invarg="Cell[{0}] has #{1}" +comperr.constcell.nullval="ConstCell.value=null??" +comperr.val.noteq="Values not eq" +comperr.instr.nullarg="null arg for {0}" +comperr.instr.arglong="Too long argument of {0}: {1}" +comperr.instr.opclen="Wrong opcLength({0})" diff --git a/src/org/openjdk/asmtools/jcdec/Main.java b/src/org/openjdk/asmtools/jcdec/Main.java new file mode 100644 index 0000000..f907e6b --- /dev/null +++ b/src/org/openjdk/asmtools/jcdec/Main.java @@ -0,0 +1,957 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcdec; + +import static org.openjdk.asmtools.jcoder.JcodTokens.*; +import org.openjdk.asmtools.jdis.uEscWriter; +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Main program of the JavaCard DeCoder + * + */ +public class Main { + + /*-------------------------------------------------------- */ + /* Main Fields */ + /** + * Name of the program. + */ + String program; + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + /** + * The stream where error message are printed. + */ + PrintWriter out; + boolean DebugFlag = false; + boolean printDetails = false; + int shift = 0; + private static final char hexTable[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + /*-------------------------------------------------------- */ + + static String toHex(long val, int width) { + StringBuffer s = new StringBuffer(); + for (int i = width * 2 - 1; i >= 0; i--) { + s.append(hexTable[((int) (val >> (4 * i))) & 0xF]); + } + return "0x" + s.toString(); + } + + static String toHex(long val) { + int width; + for (width = 8; width > 0; width--) { + if ((val >> (width - 1) * 8) != 0) { + break; + } + } + return toHex(val, width); + } + + void printByteHex(PrintWriter out, int b) { + out.print(hexTable[(b >> 4) & 0xF]); + out.print(hexTable[b & 0xF]); + } + + /*========================================================*/ + void out_begin(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + shift++; + } + + void out_print(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.print(s); + } + + void out_println(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + } + + void out_end(String s) { + shift--; + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + } + + String startArray(int length) { + return "[" + (printDetails ? Integer.toString(length) : "") + "]"; + } + + void printBytes(DataInputStream in, int len) throws IOException { + try { + for (int i = 0; i < len; i++) { + if (i % 8 == 0) { + out_print("0x"); + } + printByteHex(out, in.readByte()); + if (i % 8 == 7) { + out.println(";"); + } + } + } finally { + if (len % 8 != 0) { + out.println(";"); + } + } + } + + /*========================================================*/ + static final int EXPORT_MAGIC = 0x00FACADE; + static final int HEADER_MAGIC = 0xDECAFFED; + static String[] compNames = { + "Header", + "Directory", + "Applet", + "Import", + "ConstantPool", + "Class", + "Method", + "StaticField", + "RefLocation", + "Export", + "Descriptor" + }; + + static String compName(int compNum) { + try { + return compNames[compNum - 1]; + } catch (ArrayIndexOutOfBoundsException e) { + return "tag " + compNum + "???"; + } + } + String[] cPoolStrings; + + void decodeAttr(DataInputStream in) throws IOException { + int name_cpx = in.readUnsignedShort(), len = in.readInt(); + String AttrName = null; + String endingComment = "Attr(#" + name_cpx + ")"; + try { + endingComment = AttrName = cPoolStrings[name_cpx]; + } catch (ArrayIndexOutOfBoundsException e) { + } + if (printDetails) { + out_begin("Attr(#" + name_cpx + ", " + len + ") { // " + AttrName); + } else { + out_begin("Attr(#" + name_cpx + ") { // " + AttrName); + } + if (AttrName == null) { + printBytes(in, len); + } else if (AttrName.equals("ConstantValue")) { + if (len != 2) { + out_println("// invalid length of ConstantValue attr: " + len + " (should be 2)"); + printBytes(in, len); + } else { + out_println("#" + in.readUnsignedShort() + ";"); + } + } else { + printBytes(in, len); + } + out_end("} // end " + endingComment); + } + + void decodeExp(String inpName) throws IOException { + DataInputStream in = new DataInputStream(new FileInputStream(inpName)); + out_println("file " + inpName); + out_begin("{ // export file"); + + int magic = in.readInt(); + out_print(toHex(magic, 4) + "; // "); + if (magic != EXPORT_MAGIC) { + out.print("wrong magic: 0x" + Integer.toString(EXPORT_MAGIC, 16) + " expected"); + } else { + out_print("magic"); + } + out.println(); + out_println(in.readUnsignedByte() + "b; // minor version"); + out_println(in.readUnsignedByte() + "b; // major version"); + + int cp_count = in.readUnsignedShort(); + cPoolStrings = new String[cp_count]; + out_begin(startArray(cp_count) + " { // Constant Pool"); + for (int i = 0; i < cp_count; i++) { + int tag = in.readUnsignedByte(); + ConstType tg = constType(tag); + switch (tg) { + case CONSTANT_UTF8: + out_print("Utf8 \""); + + StringBuffer sb = new StringBuffer(); + String s = in.readUTF(); + cPoolStrings[i] = s; + for (int k = 0; k < s.length(); k++) { + char c = s.charAt(k); + switch (c) { + case '\t': + sb.append('\\').append('t'); + break; + case '\n': + sb.append('\\').append('n'); + break; + case '\r': + sb.append('\\').append('r'); + break; + case '\"': + sb.append('\\').append('\"'); + break; + default: + sb.append(c); + } + } + out.println(sb.append("\"; // #").append(i).toString()); + break; + + case CONSTANT_INTEGER: + out_println("int " + toHex(in.readInt(), 4) + "; // #" + i); + break; + + case CONSTANT_CLASS: + out_println("class #" + in.readUnsignedShort() + "; // #" + i); + break; + + case CONSTANT_PACKAGE: + out_begin("package { // #" + i); + out_println(toHex(in.readUnsignedByte(), 1) + "; // flags"); + out_println("#" + in.readUnsignedShort() + "; // name"); + out_println(in.readUnsignedByte() + "b; // minor version"); + out_println(in.readUnsignedByte() + "b; // major version"); + int aid_len = in.readUnsignedByte(); + out_begin("Bytes" + startArray(aid_len) + "b {"); + printBytes(in, aid_len); + out_end("};"); // Bytes[] + out_end("};"); // package info + break; + + default: + throw new Error("invalid constant type: " + (int) tag); + } + } + ; + out_end("} // Constant pool"); + out_println("#" + in.readUnsignedShort() + "; // this package"); + int class_count = in.readUnsignedByte(); + out_begin(startArray(class_count) + "b { // classes"); + for (int i = 0; i < class_count; i++) { + out_begin("{ // class " + i); + + out_println(in.readUnsignedByte() + "b; // token"); + + int flags = in.readUnsignedShort(); + out_print("0x"); + printByteHex(out, flags >> 8); + printByteHex(out, flags); + out.println("; // flags"); + + out_println("#" + in.readUnsignedShort() + "; // this class"); + + int sup_count = in.readUnsignedShort(); + out_begin(startArray(sup_count) + " { // supers"); + for (int k = 0; k < sup_count; k++) { + out_println("#" + in.readUnsignedShort() + ";"); + } + out_end("} // supers"); + + int int_count = in.readUnsignedByte(); + out_begin(startArray(int_count) + "b { // interfaces"); + for (int k = 0; k < int_count; k++) { + out_println("#" + in.readUnsignedShort() + ";"); + } + out_end("} // interfaces"); + + int field_count = in.readUnsignedShort(); + out_begin(startArray(field_count) + " { // fields"); + for (int k = 0; k < field_count; k++) { + out_begin("{ // field " + k); + out_println(in.readUnsignedByte() + "b; // token"); + + int f_flags = in.readUnsignedShort(); + out_print("0x"); + printByteHex(out, f_flags >> 8); + printByteHex(out, f_flags); + out.println("; // flags"); + + out_println("#" + in.readUnsignedShort() + "; // this field name"); + out_println("#" + in.readUnsignedShort() + "; // this field descriptor"); + + int attr_count = in.readUnsignedShort(); + out_begin(startArray(attr_count) + " { // Attributes"); + for (int ai = 0; ai < attr_count; ai++) { + decodeAttr(in); + } + out_end("} // Attributes"); + out_end("};"); + } + out_end("} // fields"); + + int mth_count = in.readUnsignedShort(); + out_begin(startArray(mth_count) + " { // methods"); + for (int k = 0; k < mth_count; k++) { + out_begin("{ // method " + k); + out_println(in.readUnsignedByte() + "b; // token"); + + int mth_flags = in.readUnsignedShort(); + out_print("0x"); + printByteHex(out, mth_flags >> 8); + printByteHex(out, mth_flags); + out.println("; // flags"); + + out_println("#" + in.readUnsignedShort() + "; // this method name"); + out_println("#" + in.readUnsignedShort() + "; // this method descriptor"); + out_end("};"); + } + out_end("} // methods"); + out_end("};"); + } + out_end("} // classes"); + endComponent(in); + } + + DataInputStream beginComponent(String inpName) throws IOException { + DataInputStream in = new DataInputStream(new FileInputStream(inpName)); + out_println("file " + inpName); + + int tag = in.readUnsignedByte(); + out_print("Component(" + tag); + int size = in.readUnsignedShort(); + if (printDetails) { + out.print(", " + size); + } + out_begin(") { // " + compName(tag)); + return in; + } + + void endComponent(DataInputStream in) throws IOException { + out_end("};"); // Component + int avail = in.available(); + if (avail > 0) { + out.println("=========== extra bytes:"); + for (int k = 0; k < 8; k++) { + printBytes(in, avail >= 8 ? 8 : avail); + avail = in.available(); + if (avail == 0) { + break; + } + } + if (avail > 0) { + out.println(" there is also " + avail + " bytes available"); + } + } + in.close(); + } + + ArrayList methodsLengths = null; + ArrayList methodsOffsets = null; + + void decodeHeader(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int magic = in.readInt(); + out_print(toHex(magic, 4) + "; // "); + if (magic != HEADER_MAGIC) { + out.print("wrong magic: 0x" + Integer.toString(HEADER_MAGIC, 16) + " expected"); + } else { + out_print("magic"); + } + out.println(); + out_println(in.readUnsignedByte() + "b; // minor version"); + out_println(in.readUnsignedByte() + "b; // major version"); + out_println(toHex(in.readUnsignedByte(), 1) + "; // flags"); + + out_begin("{ // package info"); + out_println(in.readUnsignedByte() + "b; // minor version"); + out_println(in.readUnsignedByte() + "b; // major version"); + int aid_len = in.readUnsignedByte(); + out_begin("Bytes" + startArray(aid_len) + "b {"); + printBytes(in, aid_len); + out_end("};"); // Bytes[] + out_end("};"); // package info + endComponent(in); + } + + void decodeDirectory(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int i; + out_begin("{ // component sizes"); + for (i = 0; i < 11; i++) { + out_println(in.readUnsignedShort() + "; // " + (i + 1)); + } + out_end("};"); + + out_begin("{ // static field size"); + out_println(in.readUnsignedShort() + "; // image size"); + out_println(in.readUnsignedShort() + "; // array init count"); + out_println(in.readUnsignedShort() + "; // array init size"); + out_end("};"); + + out_println(in.readUnsignedByte() + "b; // import count"); + out_println(in.readUnsignedByte() + "b; // applet count"); + + int custom_count = in.readUnsignedByte(); + out_begin(startArray(custom_count) + "b { // custom components"); + for (i = 0; i < custom_count; i++) { + out_print("Comp(" + in.readUnsignedByte()); // tag; + int size2 = in.readUnsignedShort(); + if (printDetails) { + out_print(", " + size2); + } + out_begin(") {"); + int aid_len = in.readUnsignedByte(); + out_begin("Bytes" + startArray(aid_len) + "b {"); + printBytes(in, aid_len); + out_end("};"); + out_end("};"); + } + out_end("};"); + + endComponent(in); + } + + void decodeApplet(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int applet_count = in.readUnsignedByte(); + out_begin(startArray(applet_count) + "b { // applets"); + for (int i = 0; i < applet_count; i++) { + out_begin("{ // applet " + i); + int aid_len = in.readUnsignedByte(); + out_begin("Bytes" + startArray(aid_len) + "b {"); + printBytes(in, aid_len); + out_end("};"); // Bytes[] + out_println(in.readUnsignedShort() + "; // install method offset"); + out_end("};"); // applet + } + out_end("};"); // applets + endComponent(in); + } + + void decodeImport(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int package_count = in.readUnsignedByte(); + out_begin(startArray(package_count) + "b { // packages"); + for (int i = 0; i < package_count; i++) { + out_begin("{ // package " + i); + out_println(in.readUnsignedByte() + "b; // minor version"); + out_println(in.readUnsignedByte() + "b; // major version"); + int aid_len = in.readUnsignedByte(); + out_begin("Bytes" + startArray(aid_len) + "b {"); + printBytes(in, aid_len); + out_end("};"); // Bytes[] + out_end("};"); // package info + } + out_end("};"); // package info + endComponent(in); + } + + static String[] refNames = { + "Classref", + "InstanceFieldref", + "VirtualMethodref", + "SuperMethodref", + "StaticFieldref", + "StaticMethodref" + }; + + void decodeConstantPool(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int items_count = in.readUnsignedShort(); + out_begin(startArray(items_count) + " { // items"); + for (int i = 0; i < items_count; i++) { + int tag = in.readUnsignedByte(); + int info1 = in.readUnsignedByte(), + info2 = in.readUnsignedByte(), + info3 = in.readUnsignedByte(); + out_print(tag + "b "); + if ((tag > 0) && (tag <= 6)) { + if ((info1 & 0x80) == 0) { + if (tag <= 4) { + out_print(((info1 << 8) | info2) + " " + info3 + "b;"); + } else { + out_print(info1 + "b " + ((info2 << 8) | info3) + ";"); + } + out.print(" // internal "); + } else { + out.print(info1 + "b " + info2 + "b " + info3 + "b;"); + out.print(" // external "); + } + out.println(refNames[tag - 1]); + } else { + out.print(info1 + "b " + info2 + "b " + info3 + "b;"); + out.println(" // unknown tag "); + } + } + out_end("};"); // CP array + endComponent(in); + } + + void printClassref(DataInputStream in) throws IOException { + int info1 = in.readUnsignedByte(), + info2 = in.readUnsignedByte(); + if ((info1 & 0x80) == 0) { + out_print(((info1 << 8) | info2) + ";"); + out_print(" // internal "); + } else { + out_print(info1 + "b " + info2 + "b;"); + out_print(" // external "); + } + out_println(" Classref "); + } + + void decodeClass(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + for (int i = 0; in.available() > 0; i++) { + out_begin("{ // class " + i); + int bitfield = in.readUnsignedByte(); + int interface_count = bitfield & 0x0F; + out_print("0x"); + printByteHex(out, bitfield); + out.println("; // bitfield"); + if ((bitfield & 0x80) != 0) { + // interface + for (int k = 0; k < interface_count; k++) { + printClassref(in); + } + } else { + // class + printClassref(in); + out_println(in.readUnsignedByte() + "b; // declared instance size"); + out_println(in.readUnsignedByte() + "b; // first reference token"); + out_println(in.readUnsignedByte() + "b; // reference count"); + out_println(in.readUnsignedByte() + "b; // public method table base"); + int pumrc = in.readUnsignedByte(); + out_println(pumrc + "b; // public method table count"); + out_println(in.readUnsignedByte() + "b; // package method table base"); + int pamrc = in.readUnsignedByte(); + out_println(pamrc + "b; // package method table count"); + out_begin("{ // public method table"); + for (int k = 0; k < pumrc; k++) { + out_println(in.readUnsignedShort() + ";"); + } + out_end("};"); + out_begin("{ // package method table"); + for (int k = 0; k < pamrc; k++) { + out_println(in.readUnsignedShort() + ";"); + } + out_end("};"); + out_begin("{ // implemented interfaces"); + for (int k = 0; k < interface_count; k++) { + out_begin("{ // interface " + k); + printClassref(in); + int count = in.readUnsignedByte(); + out_begin("Bytes" + startArray(count) + "b {"); + printBytes(in, count); + out_end("};"); // Bytes[] + out_end("};"); + } + out_end("};"); + } + out_end("};"); + } + endComponent(in); + } + + void decodeDescriptor(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + methodsLengths = new ArrayList<>(); + methodsOffsets = new ArrayList<>(); + int class_count = in.readUnsignedByte(); + out_begin(startArray(class_count) + "b { // classes"); + for (int c = 0; c < class_count; c++) { + out_begin("{ // class " + c); + out_println(in.readUnsignedByte() + "b; // token"); + out_print("0x"); + printByteHex(out, in.readUnsignedByte()); + out.println("; // flags"); + printClassref(in); + int icount = in.readUnsignedByte(); + out_println(icount + "b; // interface count"); + int fcount = in.readUnsignedShort(); + out_println(fcount + "; // field count"); + int mcount = in.readUnsignedShort(); + out_println(mcount + "; // method count"); + if (icount != 0) { + out_begin("{ // interfaces"); + for (int i = 0; i < icount; i++) { + printClassref(in); + } + out_end("};"); + } + for (int i = 0; i < fcount; i++) { + out_begin("{ // field " + i); + out_println(in.readUnsignedByte() + "b; // token"); + int flags = in.readUnsignedByte(); + out_print("0x"); + printByteHex(out, flags); + out.println("; // flags"); + if ((flags & 0x08) == 0) { + printClassref(in); + out_println(in.readUnsignedByte() + "b; // token"); + } else { // static field + int info1 = in.readUnsignedByte(), + info2 = in.readUnsignedByte(), + info3 = in.readUnsignedByte(); + if ((info1 & 0x80) == 0) { + out_print(info1 + "b " + ((info2 << 8) | info3) + ";"); + out.println(" // internal field"); + } else { + out.print(info1 + "b " + info2 + "b " + info3 + "b;"); + out.println(" // external field"); + } + } + int type = in.readUnsignedShort(); + if ((type & 0x8000) == 0) { + out_println(type + "; // reference type"); + } else { + out_print("0x"); + printByteHex(out, type >> 8); + printByteHex(out, type); + out.println("; // primitive type"); + } + out_end("};"); + } + for (int i = 0; i < mcount; i++) { + int token = in.readUnsignedByte(); + int flags = in.readUnsignedByte(); + int m_offset = in.readUnsignedShort(); + int t_offset = in.readUnsignedShort(); + int bytecode_count = in.readUnsignedShort(); + if (m_offset != 0) { + out_begin("{ // method " + i + " (" + methodsLengths.size() + ")"); + methodsLengths.add(bytecode_count); + methodsOffsets.add(m_offset); + } else { + out_begin("{ // method " + i); + } + out_println(token + "b; // token"); + out_print("0x"); + printByteHex(out, flags); + out.println("; // flags"); + out_println(m_offset + "; // method offset"); + out_println(t_offset + "; // type offset"); + out_println(bytecode_count + "; // bytecode count"); + out_println(in.readUnsignedShort() + "; // exception handler count"); + out_println(in.readUnsignedShort() + "; // exception handler index"); + out_end("};"); + } + out_end("};"); // class i + } + out_end("}; // classes"); + + int cp_count = in.readUnsignedShort(); + out_begin(startArray(cp_count) + " { // constant pool types"); + for (int i = 0; i < cp_count; i++) { + int type = in.readUnsignedShort(); + if (type == 0xFFFF) { + out_println("0xFFFF;"); + } else { + out_println(type + "; "); + } + } + out_end("}; // constant pool types"); + + out_begin("{ // type descriptors"); + for (int i = 0; in.available() > 0; i++) { + int nibble_count = in.readUnsignedByte(); + out_print(nibble_count + "b; "); + printBytes(in, (nibble_count + 1) / 2); + } + out_end("}; // type descriptors"); + endComponent(in); + } + + void decodeMethod(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int handler_count = in.readUnsignedByte(); + out_begin(startArray(handler_count) + "b { // exception handlers"); + for (int i = 0; i < handler_count; i++) { + out_print(in.readUnsignedShort() + ", "); + int bitfield = in.readUnsignedShort(); + out.print("0x"); + printByteHex(out, bitfield >> 8); + printByteHex(out, bitfield); + out.print(", " + in.readUnsignedShort() + ", "); + out.println(in.readUnsignedShort() + "; // handler " + i); + } + out_end("};"); // handlers + + if (methodsLengths == null) { + out.println("// Descriptor.cap absent - methods not printed"); + } else { + int f_offset = 1 + handler_count * 8; + for (int i = 0; i < methodsLengths.size(); i++) { + out_begin("{ // method " + i); + int m_offset = methodsOffsets.get(i); + if (m_offset != f_offset) { + out.println("file offset=" + f_offset + " but m_offset=" + m_offset); + break; + } + int bitfield = in.readUnsignedByte(); + if ((bitfield & 0x80) == 0) { + out_print("0x"); + printByteHex(out, bitfield); + out.println("; // flags, max_stack"); + out_print("0x"); + printByteHex(out, in.readUnsignedByte()); + out.println("; // nargs, max_locals"); + f_offset += 2; + } else { + out_print("0x"); + printByteHex(out, bitfield); + out.println("; // flags, padding"); + out_println(in.readUnsignedByte() + "b; // max_stack"); + out_println(in.readUnsignedByte() + "b; // nargs"); + out_println(in.readUnsignedByte() + "b; // max_locals"); + f_offset += 4; + } + int bytecode_count = methodsLengths.get(i); + out_begin("{ // bytecodes"); + printBytes(in, bytecode_count); + f_offset += bytecode_count; + out_end("};"); + out_end("};"); + } + } + + endComponent(in); + } + + void decodeStaticField(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int image_size = in.readUnsignedShort(); + out_println(image_size + "; // image size"); + int reference_count = in.readUnsignedShort(); + out_println(reference_count + "; // reference count"); + int array_init_count = in.readUnsignedShort(); + out_begin(startArray(array_init_count) + " { // array_init_info"); + for (int i = 0; i < array_init_count; i++) { + out_println(in.readUnsignedByte() + "b // type "); + int count = in.readUnsignedShort(); + out_begin("Bytes" + startArray(count) + "s { // values"); + printBytes(in, count); + out_end("};"); // Bytes[] + } + out_end("};"); // array_init_info + int default_value_count = in.readUnsignedShort(); + out_println(default_value_count + "; // default value count"); + int non_default_value_count = in.readUnsignedShort(); + out_begin("Bytes" + startArray(non_default_value_count) + "s { // non default values"); + printBytes(in, non_default_value_count); + out_end("};"); // Bytes[] + + endComponent(in); + } + + void decodeRefLocation(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int byte_index_count = in.readUnsignedShort(); + out_begin("Bytes" + startArray(byte_index_count) + "s { // offsets to byte indices"); + printBytes(in, byte_index_count); + out_end("};"); // Bytes[] + + byte_index_count = in.readUnsignedShort(); + out_begin("Bytes" + startArray(byte_index_count) + "s { // offsets to byte2 indices"); + printBytes(in, byte_index_count); + out_end("};"); // Bytes[] + + endComponent(in); + } + + void decodeExport(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + int class_count = in.readUnsignedByte(); + out_begin(startArray(class_count) + "b { // classes"); + for (int i = 0; i < class_count; i++) { + out_begin("{ // class " + i); + out_println(in.readUnsignedShort() + "; // class offset"); + int fcount = in.readUnsignedByte(); + out_println(fcount + "b; // static field count"); + int mcount = in.readUnsignedByte(); + out_println(mcount + "b; // static method count"); + out_begin("{ // static field offsets"); + for (int j = 0; j < fcount; j++) { + out_println(in.readUnsignedShort() + "; // field " + j + " offset"); + } + out_end("};"); + out_begin("{ // static method offsets"); + for (int j = 0; j < mcount; j++) { + out_println(in.readUnsignedShort() + "; // method " + j + " offset"); + } + out_end("};"); + out_end("};"); // class i + } + out_end("};"); // classes + endComponent(in); + } + /*========================================================*/ + + /** + * Constructor. + */ + public Main(PrintWriter out, String program) { + this.out = out; + this.program = program; + } + + public void error(String msg) { + out.println(program + ": " + msg); + } + + /** + * Usage + */ + public void usage() { + out.println(i18n.getString("jcdec.usage")); + out.println(i18n.getString("jcdec.opt.g")); + out.println(i18n.getString("jcdec.opt.version")); + } + + /** + * Run the decoder + */ + public synchronized boolean decode(String argv[]) { +// int flags = F_WARNINGS; + long tm = System.currentTimeMillis(); + ArrayList vargs = new ArrayList<>(); + ArrayList vj = new ArrayList<>(); + boolean nowrite = false; + int addOptions = 0; + + // Parse arguments + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + if (arg.equals("-g")) { + printDetails = true; + vargs.add(arg); + } else if (arg.equals("-v")) { + DebugFlag = true; + vargs.add(arg); + out.println("arg[" + i + "]=" + argv[i] + "/verbose"); + } else if (arg.equals("-version")) { + out.println(ProductInfo.FULL_VERSION); + } else if (arg.startsWith("-")) { +//out.println("arg["+i+"]="+argv[i]+"/invalid flag"); + error(i18n.getString("jcdec.error.invalid_flag", arg)); + usage(); + return false; + } else { + vargs.add(arg); + vj.add(arg); + } + } + + if (vj.isEmpty()) { + usage(); + return false; + } + +// String[] names = new String[vj.size()]; +// vj.copyInto(names); + String[] names = null; + names = vj.toArray(names); +decode: + for (int k = 0; k < names.length; k++) { + String inpname = names[k]; + try { + if (inpname.endsWith(".cap")) { + String shortName = inpname.substring(0, inpname.length() - 4); + if (shortName.endsWith("Header")) { + decodeHeader(inpname); + } else if (shortName.endsWith("Directory")) { + decodeDirectory(inpname); + } else if (shortName.endsWith("Applet")) { + decodeApplet(inpname); + } else if (shortName.endsWith("Import")) { + decodeImport(inpname); + } else if (shortName.endsWith("ConstantPool")) { + decodeConstantPool(inpname); + } else if (shortName.endsWith("Class")) { + decodeClass(inpname); + } else if (shortName.endsWith("Descriptor")) { + decodeDescriptor(inpname); + } else if (shortName.endsWith("Method")) { + decodeMethod(inpname); + } else if (shortName.endsWith("StaticField")) { + decodeStaticField(inpname); + } else if (shortName.endsWith("RefLocation")) { + decodeRefLocation(inpname); + } else if (shortName.endsWith("Export")) { + decodeExport(inpname); + } else { + continue decode; + } + out.println(""); + } else if (inpname.endsWith(".exp")) { + decodeExp(inpname); + out.println(""); + } + continue decode; + } catch (FileNotFoundException ee) { + error(i18n.getString("jcdec.error.cannot_read", inpname)); + } catch (Error ee) { + ee.printStackTrace(); + error(i18n.getString("jcdec.error.fatal_error")); + } catch (Exception ee) { + ee.printStackTrace(); + error(i18n.getString("jcdec.error.fatal_exception")); + } + return false; + } + return true; + } + + /** + * Main program + */ + public static void main(String argv[]) { + Main decoder = new Main(new PrintWriter(new uEscWriter(System.out)), "jcdec"); + System.exit(decoder.decode(argv) ? 0 : 1); + } +} diff --git a/src/org/openjdk/asmtools/jcdec/i18n.properties b/src/org/openjdk/asmtools/jcdec/i18n.properties new file mode 100644 index 0000000..88560b2 --- /dev/null +++ b/src/org/openjdk/asmtools/jcdec/i18n.properties @@ -0,0 +1,34 @@ +# Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +jcdec.error.invalid_flag=invalid flag: " {0} +jcdec.error.fatal_error=fatal error +jcdec.error.fatal_exception=fatal exception +jcdec.error.cannot_read=cannot read {0} +jcdec.usage=\ +Usage: java -jar asmtools.jar jcdec [options] FILE.class... > FILE.jcod\n\ +where possible options include: + +jcdec.opt.g=\ +\ -g: detailed output format +jcdec.opt.version=\ +\ -version: print version number and date + diff --git a/src/org/openjdk/asmtools/jcoder/ByteBuffer.java b/src/org/openjdk/asmtools/jcoder/ByteBuffer.java new file mode 100644 index 0000000..cce3913 --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/ByteBuffer.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +/** + * Compiles just 1 source file + */ +class ByteBuffer extends java.io.OutputStream { + + String myname; + /** + * The buffer where elements are stored. + */ + byte data[]; + /** + * The number of elements in the buffer. + */ + int length; + /** + * The size of the increment. If it is 0 the size of the the buffer is doubled + * everytime it needs to grow. + */ + protected int capacityIncrement; + + /** + * Constructs an empty vector with the specified storage capacity and the specified + * capacityIncrement. + * + * @param initialCapacity the initial storage capacity of the vector + * @param capacityIncrement how much to increase the element's size by. + */ + public ByteBuffer(int initialCapacity, int capacityIncrement) { +// super(); + this.data = new byte[initialCapacity]; + this.capacityIncrement = capacityIncrement; + } + + /** + * Constructs an empty vector with the specified storage capacity. + * + * @param initialCapacity the initial storage capacity of the vector + */ + public ByteBuffer(int initialCapacity) { + this(initialCapacity, 0); + } + + /** + * Constructs an empty vector. + */ + public ByteBuffer() { + this(30); + } + + /** + * Constructs a full vector. + */ + public ByteBuffer(byte data[], int capacityIncrement) { + this.length = data.length; + this.data = data; + this.capacityIncrement = capacityIncrement; + } + + /** + * Constructs a full vector. + */ + public ByteBuffer(byte data[]) { + this(data, 0); + } + + /** + * Returns the number of elements in the vector. Note that this is not the same as the + * vector's capacity. + */ + public final int size() { + return length; + } + + /** + * Ensures that the vector has at least the specified capacity. + * + * @param minCapacity the desired minimum capacity + */ + public final synchronized void ensureCapacity(int minCapacity) { + int oldCapacity = data.length; + if (minCapacity <= oldCapacity) { + return; + } + byte oldData[] = data; + int newCapacity = (capacityIncrement > 0) ? (oldCapacity + capacityIncrement) : (oldCapacity * 2); + if (newCapacity < minCapacity) { + newCapacity = minCapacity; + } + data = new byte[newCapacity]; + System.arraycopy(oldData, 0, data, 0, length); + } + + /*======================================*/ + /** + * Adds the specified object as the last element of the vector. + * + * @param obj the element to be added + */ + public void write(int val) { + ensureCapacity(length + 1); + data[length++] = (byte) val; + } + + public void writeAt(int index, long val, int width) { + for (int i = 0; i < width; i++) { + data[index + i] = (byte) (val >> (width - 1 - i) * 8); + } + } + + public void append(long val, int width) { + ensureCapacity(length + width); + writeAt(length, val, width); + length += width; + } + + /*======================================================*/ +} // end ByteBuffer + diff --git a/src/org/openjdk/asmtools/jcoder/ErrorMessage.java b/src/org/openjdk/asmtools/jcoder/ErrorMessage.java new file mode 100644 index 0000000..019ef8e --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/ErrorMessage.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +/** + * A sorted list of error messages + */ +final class ErrorMessage { + + int where; + String message; + ErrorMessage next; + + /** + * Constructor + */ + ErrorMessage(int where, String message) { + this.where = where; + this.message = message; + } +} diff --git a/src/org/openjdk/asmtools/jcoder/JcodTokens.java b/src/org/openjdk/asmtools/jcoder/JcodTokens.java new file mode 100644 index 0000000..ad3a27f --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/JcodTokens.java @@ -0,0 +1,386 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +import static org.openjdk.asmtools.jasm.Tables.*; + +import java.io.PrintWriter; +import java.util.HashMap; + +/** + * + * JcodTokens + * + * This class contains tokens specific to parsing JCOD syntax. + * + * The classes in JcodTokens are following a Singleton Pattern. These classes are Enums, + * and they are contained in private hash maps (lookup tables and reverse lookup tables). + * These hash maps all have public accessors, which clients use to look-up enums. + * + * Tokens in this table carry no external state, and are typically treated as constants. + * They do not need to be reset. + */ +public class JcodTokens { + + /*-------------------------------------------------------- */ + /* Marker: describes the type of Keyword */ + static public enum KeywordType { + + TOKEN(0, "TOKEN"), + KEYWORD(3, "KEYWORD"); + + private final Integer value; + private final String printval; + + KeywordType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + } + + /*-------------------------------------------------------- */ + /* Marker - describes the type of token */ + /* this is rather cosmetic, no function currently. */ + static public enum TokenType { + VALUE (0, "Value"), + KEYWORDS (1, "Keywords"), + PUNCTUATION (2, "Punctuation"), + JDEC (3, "JDec"), + STACKMAP (4, "StackMap"), + MISC (5, "Misc"); + + private final Integer value; + private final String printval; + + TokenType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + } + + /*-------------------------------------------------------- */ + /** Scanner Tokens (Definitive List) */ + static public enum Token { + EOF (-1, "EOF", "EOF", TokenType.MISC), + IDENT (60, "IDENT", "IDENT", TokenType.VALUE), + LONGSTRINGVAL (61, "LONGSTRINGVAL", "LONGSTRING", TokenType.VALUE), + INTVAL (65, "INTVAL", "INT", TokenType.VALUE), + LONGVAL (66, "LONGVAL", "LONG", TokenType.VALUE), + STRINGVAL (69, "STRINGVAL", "STRING", TokenType.VALUE), + + CLASS (70, "CLASS", "class", TokenType.KEYWORDS, KeywordType.KEYWORD), + INTERFACE (71, "INTERFACE", "interface", TokenType.KEYWORDS, KeywordType.KEYWORD), + DIV (72, "DIV", "div", TokenType.KEYWORDS), + EQ (73, "EQ", "eq", TokenType.KEYWORDS), + ASSIGN (74, "ASSIGN", "assign", TokenType.KEYWORDS), + + COLON (134, "COLON", ":", TokenType.PUNCTUATION), + SEMICOLON (135, "SEMICOLON", ";", TokenType.PUNCTUATION, KeywordType.KEYWORD), + COMMA (0, "COMMA", ",", TokenType.PUNCTUATION, KeywordType.KEYWORD), + LBRACE (138, "LBRACE", "{", TokenType.PUNCTUATION, KeywordType.KEYWORD), + RBRACE (139, "RBRACE", "}", TokenType.PUNCTUATION, KeywordType.KEYWORD), + LPAREN (140, "LPAREN", "(", TokenType.PUNCTUATION, KeywordType.KEYWORD), + RPAREN (141, "RPAREN", ")", TokenType.PUNCTUATION, KeywordType.KEYWORD), + LSQBRACKET (142, "LSQBRACKET", "[", TokenType.PUNCTUATION, KeywordType.KEYWORD), + RSQBRACKET (143, "RSQBRACKET", "]", TokenType.PUNCTUATION, KeywordType.KEYWORD), + + + BYTEINDEX (156, "BYTEINDEX", "b", TokenType.JDEC, KeywordType.KEYWORD), + SHORTINDEX (157, "SHORTINDEX", "s", TokenType.JDEC, KeywordType.KEYWORD), + ATTR (158, "ATTR", "Attr", TokenType.JDEC, KeywordType.KEYWORD), + BYTES (159, "BYTES", "Bytes", TokenType.JDEC, KeywordType.KEYWORD), + MACRO (160, "MACRO", "Attr", TokenType.JDEC), + COMP (161, "COMP", "Component", TokenType.JDEC, KeywordType.KEYWORD), + FILE (162, "FILE", "file", TokenType.JDEC, KeywordType.KEYWORD), + + ZEROINDEX (163, "ZEROINDEX", "z", TokenType.STACKMAP, KeywordType.KEYWORD); + + private Integer value; + private String printval; + private String parsekey; + private TokenType tk_type; + private KeywordType key_type; + + // By default, if a KeywordType is not specified, it has the value 'TOKEN' + Token(Integer val, String print, String op) { + init(val, print, op, TokenType.VALUE, KeywordType.TOKEN); + } + + Token(Integer val, String print, String op, TokenType tt) { + init(val, print, op, tt, KeywordType.TOKEN); + } + + Token(Integer val, String print, String op, TokenType tt, KeywordType kt) { + init(val, print, op, tt, kt); + } + + private void init(Integer val, String print, String op, TokenType tt, KeywordType kt) { + value = val; + printval = print; + parsekey = op; + tk_type = tt; + key_type = kt; + } + + public String printval() { + return printval; + } + + public String parsekey() { + return parsekey; + } + + public int value() { + return value; + } + + @Override + public String toString() { + return "<" + printval + "> [" + value + "]"; + } + + } + + /** + * Initialized keyword and token Hash Maps (and Reverse Tables) + */ + protected static final int MaxTokens = 172; + private static HashMap TagToTokens = new HashMap<>(MaxTokens); + private static HashMap SymbolToTokens = new HashMap<>(MaxTokens); + private static HashMap ParsekeyToTokens = new HashMap<>(MaxTokens); + + protected static final int MaxKeywords = 40; + private static HashMap TagToKeywords = new HashMap<>(MaxKeywords); + private static HashMap SymbolToKeywords = new HashMap<>(MaxKeywords); + private static HashMap ParsekeyToKeywords = new HashMap<>(MaxKeywords); + + static { + + // register all of the tokens + for (Token tk : Token.values()) { + registerToken(tk); + } + + SymbolToKeywords.put(Token.INTVAL.printval(), Token.INTVAL); + ParsekeyToKeywords.put(Token.INTVAL.parsekey(), Token.INTVAL); + SymbolToKeywords.put(Token.STRINGVAL.printval(), Token.STRINGVAL); + ParsekeyToKeywords.put(Token.STRINGVAL.parsekey(), Token.STRINGVAL); + } + + private static void registerToken(Token tk) { + // Tag is a keyword + if (tk.key_type == KeywordType.KEYWORD) { + TagToKeywords.put(tk.value, tk); + SymbolToKeywords.put(tk.printval, tk); + if (tk.parsekey != null) { + ParsekeyToKeywords.put(tk.parsekey, tk); + } + } + + // Finally, register all tokens + TagToTokens.put(tk.value, tk); + SymbolToTokens.put(tk.printval, tk); + ParsekeyToTokens.put(tk.printval, tk); + } + + /* Token accessors */ + public static Token token(int tk) { + return TagToTokens.get(tk); + } + + public static Token keyword_token(int tk) { + return TagToKeywords.get(tk); + } + + /* Reverse lookup accessors */ + public static Token token(String parsekey) { + return ParsekeyToTokens.get(parsekey); + } + + public static Token keyword_token(String parsekey) { + return ParsekeyToKeywords.get(parsekey); + } + + /* Reverse lookup by ID accessors */ + public static Token token_ID(String ID) { + return ParsekeyToTokens.get(ID); + } + + public static Token keyword_token_ID(String ID) { + return ParsekeyToKeywords.get(ID); + } + + public static String keywordName(int token) { + String retval = ""; + if (token > TagToTokens.size()) { + retval = null; + } else { + Token tk = keyword_token(token); + if (tk != null) { + retval = tk.parsekey; + } + } + return retval; + } + + public static Token keyword_token_ident(String idValue) { + Token kwd = keyword_token(idValue); + + if (kwd == null) { + kwd = Token.IDENT; + } + return kwd; + } + + public static int keyword_token_int(String idValue) { + return keyword_token_ident(idValue).value(); + } + + private static HashMap NameToConstantType = new HashMap<>(ConstType.maxTag); + private static HashMap ConstantTypes = new HashMap<>(ConstType.maxTag); + + static { + // register all of the tokens + for (ConstType ct : ConstType.values()) { + registerConstantType(ct); + } + } + + /** + * ConstType + * + * A (typed) tag (constant) representing the type of Constant in the Constant Pool. + * + * This is more-or-less a copy of jasm.ConstType. Unfortunately, there's no way to + * sub-class (or slightly alter) the members of an enum. This enum set is slightly + * modified from the Jasm one. + */ + static public enum ConstType { +// CONSTANT_ZERO (-3, "CONSTANT_ZERO", ""), + CONSTANT_UTF8 (1, "CONSTANT_UTF8", "Asciz", "Utf8"), + CONSTANT_UNICODE (2, "CONSTANT_UNICODE", ""), + CONSTANT_INTEGER (3, "CONSTANT_INTEGER", "int", "u4"), + CONSTANT_FLOAT (4, "CONSTANT_FLOAT", "float"), + CONSTANT_LONG (5, "CONSTANT_LONG", "long"), + CONSTANT_DOUBLE (6, "CONSTANT_DOUBLE", "double"), + + // Class is removed for JavaCard (???) + CONSTANT_CLASS (7, "CONSTANT_CLASS", "class"), + CONSTANT_STRING (8, "CONSTANT_STRING", "String"), + CONSTANT_FIELD (9, "CONSTANT_FIELD", "Field"), + CONSTANT_METHOD (10, "CONSTANT_METHOD", "Method"), + CONSTANT_INTERFACEMETHOD (11, "CONSTANT_INTERFACEMETHOD", "InterfaceMethod"), + CONSTANT_NAMEANDTYPE (12, "CONSTANT_NAMEANDTYPE", "NameAndType"), + + // added for JavaCard + CONSTANT_PACKAGE (13, "CONSTANT_PACKAGE", "package"), // in javacard export file + // Constant 14 reserved + CONSTANT_METHODHANDLE (15, "CONSTANT_METHODHANDLE", "MethodHandle"), + CONSTANT_METHODTYPE (16, "CONSTANT_METHODTYPE", "MethodType"), + CONSTANT_INVOKEDYNAMIC_TRANS (17, "CONSTANT_INVOKEDYNAMIC_TRANS", "InvokeDynamicTrans"), + CONSTANT_INVOKEDYNAMIC (18, "CONSTANT_INVOKEDYNAMIC", "InvokeDynamic"); + + public static final int maxTag = 18; + + private final int value; + private final String parseKey; + private final String printval; + private final String alias; + + ConstType(int val, String print, String parse) { + value = val; + parseKey = parse; + printval = print; + alias = null; + } + + ConstType(int val, String print, String parse, String als) { + value = val; + parseKey = parse; + printval = print; + alias = als; + } + + public int value() { + return value; + } + + public String parseKey() { + return parseKey; + } + + public String printval() { + return printval; + } + + public void print(PrintWriter out) { + out.print(parseKey); + } + + @Override + public String toString() { + return "<" + printval + "> [" + Integer.toString(value) + "]"; + } + }; + + static public ConstType constType(int i) { + return ConstantTypes.get(i); + } + + static public ConstType constType(String parsekey) { + return NameToConstantType.get(parsekey); + } + + private static void registerConstantType(ConstType tt) { + NameToConstantType.put(tt.parseKey, tt); + if (tt.alias != null) { + NameToConstantType.put(tt.alias, tt); + } + ConstantTypes.put(tt.value, tt); + } + + static int constValue(String stringValue) { + ConstType Val = constType(stringValue); + int val = -1; + + if (Val != null) { + val = Val.value(); + } else { + StackMapType smt = stackMapTypeKey(stringValue); + + if (smt != null) { + val = smt.value(); + } + } + return val; + } + +} diff --git a/src/org/openjdk/asmtools/jcoder/Jcoder.java b/src/org/openjdk/asmtools/jcoder/Jcoder.java new file mode 100644 index 0000000..449d21b --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/Jcoder.java @@ -0,0 +1,761 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +import static org.openjdk.asmtools.jcoder.JcodTokens.*; +import java.io.BufferedOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Stack; +import java.util.ArrayList; + +/** + * Compiles just 1 source file + */ +class Jcoder { + + /*-------------------------------------------------------- */ + /* Jcoder Fields */ + ArrayList Classes = new ArrayList<>(); + ByteBuffer buf; + DataOutputStream bufstream; + String pkg = null, pkgPrefix = ""; + int depth = 0; + String tabStr = ""; + Context context = null; + protected SourceFile env; + protected Scanner scanner; + + /*-------------------------------------------------------- */ + /* Jcoder inner classes */ + + /*-------------------------------------------------------- */ + /* ContextTag (marker) - describes the type of token */ + /* this is rather cosmetic, no function currently. */ + static public enum ContextTag { + NULL (0, ""), + CLASS (1, "Class"), + CONSTANTPOOL (2, "Constant-Pool"), + INTERFACES (3, "Interfaces"), + INTERFACE (4, "Interface"), + METHODS (5, "Methods"), + METHOD (6, "Method"), + FIELDS (7, "Fields"), + FIELD (8, "Field"), + ATTRIBUTE (9, "Attribute"); + + private final Integer value; + private final String printval; + + ContextTag(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + } + + /*-------------------------------------------------------- */ + /* ContextVal (marker) - Specific value on a context stack */ + public class ContextVal { + + public ContextTag tag; + public int compCount; + public ContextVal owner; + + public ContextVal(ContextTag tg) { + tag = tg; + compCount = 0; + owner = null; + } + + public ContextVal(ContextTag tg, ContextVal ownr) { + tag = tg; + compCount = 0; + owner = ownr; + } + } + + + /*-------------------------------------------------------- */ + /* Context - Context stack */ + public class Context { + + Stack stack; + + private boolean hasCP; + private boolean hasMethods; + private boolean hasInterfaces; + private boolean hasFields; + + Context() { + stack = new Stack<>(); + init(); + } + + public boolean isConstantPool() { + if (stack.empty()) { + return false; + } + return (stack.peek().tag == ContextTag.CONSTANTPOOL); + } + + public void init() { + stack.removeAllElements(); + hasCP = false; + hasMethods = false; + hasInterfaces = false; + hasFields = false; + } + + public void update() { + if (stack.empty()) { + stack.push(new ContextVal(ContextTag.CLASS)); + return; + } + + ContextVal currentCtx = stack.peek(); + switch (currentCtx.tag) { + case CLASS: + if (!hasCP) { + stack.push(new ContextVal(ContextTag.CONSTANTPOOL)); + hasCP = true; + } else if (!hasInterfaces) { + stack.push(new ContextVal(ContextTag.INTERFACES)); + hasInterfaces = true; + } else if (!hasFields) { + stack.push(new ContextVal(ContextTag.FIELDS)); + hasFields = true; + } else if (!hasMethods) { + stack.push(new ContextVal(ContextTag.METHODS)); + hasMethods = true; + } else { + // must be class attributes + currentCtx.compCount += 1; + stack.push(new ContextVal(ContextTag.ATTRIBUTE, currentCtx)); + } + break; + case INTERFACES: + currentCtx.compCount += 1; + stack.push(new ContextVal(ContextTag.INTERFACE, currentCtx)); + break; + // case INTERFACE: +// break; + case FIELDS: + currentCtx.compCount += 1; + stack.push(new ContextVal(ContextTag.FIELD, currentCtx)); + break; + case METHODS: + currentCtx.compCount += 1; + stack.push(new ContextVal(ContextTag.METHOD, currentCtx)); + break; + case FIELD: + case METHOD: + case ATTRIBUTE: + currentCtx.compCount += 1; + stack.push(new ContextVal(ContextTag.ATTRIBUTE, currentCtx)); + break; + default: + break; + } + } + + public void exit() { + if (!stack.isEmpty()) { + stack.pop(); + } + } + + public String toString() { + if (stack.isEmpty()) { + return ""; + } + ContextVal currentCtx = stack.peek(); + String retval = currentCtx.tag.printval(); + switch (currentCtx.tag) { + case INTERFACE: + case METHOD: + case FIELD: + case ATTRIBUTE: + if (currentCtx.owner != null) { + retval += "[" + currentCtx.owner.compCount + "]"; + } + } + + return retval; + } + } + + + /*-------------------------------------------------------- */ + /* Jcoder */ + /** + * Create a parser + */ + protected Jcoder(SourceFile sf, HashMap macros) throws IOException { + scanner = new Scanner(sf, macros); + env = sf; + context = new Context(); + + } + /*-------------------------------------------------------- */ + + /** + * Expect a token, return its value, scan the next token or throw an exception. + */ + protected final void expect(Token t) throws SyntaxError, IOException { + if (scanner.token != t) { + env.traceln("expect:" + t + " instead of " + scanner.token); + switch (t) { + case IDENT: +// env.error(prevPos, "identifier.expected"); + env.error(scanner.pos, "identifier.expected"); + break; + default: +// env.error(prevPos, "token.expected", keywordName(t)]); + env.error(scanner.pos, "token.expected", t.toString()); + break; + } + throw new SyntaxError(); + } + scanner.scan(); + } + + protected void recoverField() throws SyntaxError, IOException { + while (true) { + switch (scanner.token) { + case LBRACE: + scanner.match(Token.LBRACE, Token.RBRACE); + scanner.scan(); + break; + + case LPAREN: + scanner.match(Token.LPAREN, Token.RPAREN); + scanner.scan(); + break; + + case LSQBRACKET: + scanner.match(Token.LSQBRACKET, Token.RSQBRACKET); + scanner.scan(); + break; + + case RBRACE: + case EOF: + case INTERFACE: + case CLASS: + // begin of something outside a class, panic more +// endClass(pos); + throw new SyntaxError(); + + default: + // don't know what to do, skip + scanner.scan(); + break; + } + } + } + + /** + * Parse an array of struct. + */ + protected void parseArray() throws IOException { + scanner.scan(); + int length0 = buf.length, pos0 = scanner.pos; + int num_expected; + if (scanner.token == Token.INTVAL) { + num_expected = scanner.intValue; + scanner.scan(); + } else { + num_expected = -1; + } + expect(Token.RSQBRACKET); + int numSize; + switch (scanner.token) { + case BYTEINDEX: + scanner.scan(); + numSize = 1; + break; + case SHORTINDEX: + scanner.scan(); + numSize = 2; + break; + case ZEROINDEX: + scanner.scan(); + numSize = 0; + break; + default: + numSize = 2; + } + + // skip array size + if (numSize > 0) { + buf.append(num_expected, numSize); + } + int num_present = parseStruct(); + if (num_expected == -1) { + env.trace(" buf.writeAt(" + length0 + ", " + num_present + ", " + numSize + "); "); + // skip array size + if (numSize > 0) { + buf.writeAt(length0, num_present, numSize); + } + } else if (num_expected != num_present) { + env.error(pos0, "warn.array.wronglength", new Integer(num_expected), new Integer(num_present)); + } + } + + /** + * Parse a byte array. + */ + protected void parseByteArray() throws IOException { + scanner.scan(); + expect(Token.LSQBRACKET); + int length0 = buf.length, pos0 = scanner.pos; + int len_expected; + if (scanner.token == Token.INTVAL) { + len_expected = scanner.intValue; + scanner.scan(); + } else { + len_expected = -1; + } + expect(Token.RSQBRACKET); + int lenSize; + switch (scanner.token) { + case BYTEINDEX: + scanner.scan(); + lenSize = 1; + break; + case SHORTINDEX: + scanner.scan(); + lenSize = 2; + break; + case ZEROINDEX: + scanner.scan(); + lenSize = 0; + break; + default: + lenSize = 4; + } + + // skip array size + if (lenSize > 0) { + buf.append(len_expected, lenSize); + } + int length1 = buf.length; + parseStruct(); + int len_present = buf.length - length1; + if (len_expected == -1) { + env.trace(" buf.writeAt(" + length0 + ", " + len_present + ", " + lenSize + "); "); + // skip array size + if (lenSize > 0) { + buf.writeAt(length0, len_present, lenSize); + } + } else if (len_expected != len_present) { + env.error(pos0, "warn.array.wronglength", new Integer(len_expected), new Integer(len_present)); + } + } + + /** + * Parse an Attribute. + */ + protected void parseAttr() throws IOException { + scanner.scan(); + expect(Token.LPAREN); + int cpx; // index int const. pool + if (scanner.token == Token.INTVAL) { + cpx = scanner.intValue; + scanner.scan(); + + /* } else if (token==STRINGVAL) { + Integer Val=(Integer)(CP_Strings.get(stringValue)); + if (Val == null) { + env.error(pos, "attrname.notfound", stringValue); + throw new SyntaxError(); + } + cpx=Val.intValue(); + */ } else { + env.error(scanner.pos, "attrname.expected"); + throw new SyntaxError(); + } + buf.append(cpx, 2); + int pos0 = scanner.pos, length0 = buf.length; + int len_expected; + if (scanner.token == Token.COMMA) { + scanner.scan(); + len_expected = scanner.intValue; + expect(Token.INTVAL); + } else { + len_expected = -1; + } + buf.append(len_expected, 4); + expect(Token.RPAREN); + parseStruct(); + int len_present = buf.length - (length0 + 4); + if (len_expected == -1) { + buf.writeAt(length0, len_present, 4); + } else if (len_expected != len_present) { + env.error(pos0, "warn.attr.wronglength", new Integer(len_expected), new Integer(len_present)); + } + } // end parseAttr + + /** + * Parse a Component of JavaCard .cap file. + */ + protected void parseComp() throws IOException { + scanner.scan(); + expect(Token.LPAREN); + int tag = scanner.intValue; // index int const. pool + expect(Token.INTVAL); + buf.append(tag, 1); + int pos0 = scanner.pos, length0 = buf.length; + int len_expected; + if (scanner.token == Token.COMMA) { + scanner.scan(); + len_expected = scanner.intValue; + expect(Token.INTVAL); + } else { + len_expected = -1; + } + buf.append(len_expected, 2); + expect(Token.RPAREN); + parseStruct(); + int len_present = buf.length - (length0 + 2); + if (len_expected == -1) { + buf.writeAt(length0, len_present, 2); + } else if (len_expected != len_present) { + env.error(pos0, "warn.attr.wronglength", new Integer(len_expected), new Integer(len_present)); + } + } // end parseComp + + private void adjustDepth(boolean up) { + if (up) { + depth += 1; + context.update(); + scanner.setDebugCP(context.isConstantPool()); + } else { + depth -= 1; + context.exit(); + } + StringBuilder bldr = new StringBuilder(); + int tabAmt = 4; + int len = depth * tabAmt; + for (int i = 0; i < len; i++) { + bldr.append(" "); + } + tabStr = bldr.toString(); + } + + /** + * Parse a structure. + */ + protected int parseStruct() throws IOException { + adjustDepth(true); + env.traceln(" "); + env.traceln(tabStr + "Struct { <" + context + "> "); + expect(Token.LBRACE); + int num = 0; + int addElem = 0; + while (true) { + try { + switch (scanner.token) { + case COMMA: // ignored + scanner.scan(); + break; + case SEMICOLON: + num++; + addElem = 0; + scanner.scan(); + break; + case CLASS: + scanner.addConstDebug(ConstType.CONSTANT_CLASS); + env.trace("class "); + scanner.longValue = ConstType.CONSTANT_CLASS.value(); + scanner.intSize = 1; + case INTVAL: + env.trace("int [" + scanner.longValue + "] "); + buf.append(scanner.longValue, scanner.intSize); + scanner.scan(); + addElem = 1; + break; + case STRINGVAL: + scanner.scan(); + scanner.addConstDebug(ConstType.CONSTANT_UTF8); + env.trace("UTF8 [\"" + scanner.stringValue + "\"] "); + bufstream.writeUTF(scanner.stringValue); + addElem = 1; + break; + case LONGSTRINGVAL: + scanner.scan(); + env.traceln("LongString [\"" + scanner.longStringValue.data + "\"] "); + buf.write(scanner.longStringValue.data, 0, scanner.longStringValue.length); + addElem = 1; + break; + case LBRACE: + parseStruct(); + addElem = 1; + break; + case LSQBRACKET: + parseArray(); + addElem = 1; + break; + case BYTES: + env.trace("bytes "); + parseByteArray(); + addElem = 1; + break; + case ATTR: + env.trace("attr "); + parseAttr(); + addElem = 1; + break; + case COMP: + env.trace("comp "); + parseComp(); + addElem = 1; + break; + case RBRACE: + scanner.scan(); + env.traceln(" "); + env.traceln(tabStr + "} // Struct <" + context + "> ["); + adjustDepth(false); + return num + addElem; + default: + env.traceln("unexp token=" + scanner.token); + env.traceln(" scanner.stringval = \"" + scanner.stringValue + "\""); + env.error(scanner.pos, "element.expected"); + throw new SyntaxError(); + } + } catch (SyntaxError e) { + recoverField(); + } + } + } // end parseStruct + + /** + * Recover after a syntax error in the file. This involves discarding tokens until an + * EOF or a possible legal continuation is encountered. + */ + protected void recoverFile() throws IOException { + while (true) { + switch (scanner.token) { + case CLASS: + case INTERFACE: + // Start of a new source file statement, continue + return; + + case LBRACE: + scanner.match(Token.LBRACE, Token.RBRACE); + scanner.scan(); + break; + + case LPAREN: + scanner.match(Token.LPAREN, Token.RPAREN); + scanner.scan(); + break; + + case LSQBRACKET: + scanner.match(Token.LSQBRACKET, Token.RSQBRACKET); + scanner.scan(); + break; + + case EOF: + return; + + default: + // Don't know what to do, skip + scanner.scan(); + break; + } + } + } + + /** + * Parse a class or interface declaration. + */ + protected void parseClass() throws IOException { + // Begin a new class + Token prev = scanner.token; + scanner.scan(); + buf = new ByteBuffer(); + bufstream = new DataOutputStream(buf); + + String doc = scanner.docComment; + + // Parse the class name + int p = scanner.pos; + switch (scanner.token) { + case STRINGVAL: + buf.myname = scanner.stringValue; + break; + case BYTEINDEX: + case SHORTINDEX: + case ATTR: + case BYTES: + case MACRO: + case COMP: + case FILE: + case IDENT: + if (prev == Token.FILE) { + buf.myname = scanner.stringValue; + } else { + buf.myname = scanner.stringValue + ".class"; + } + break; + default: + env.error(scanner.prevPos, "name.expected"); + throw new SyntaxError(); + } + scanner.scan(); + + env.traceln("starting class " + buf.myname); + // Parse the clause + switch (scanner.token) { + case LBRACE: + parseStruct(); // ?? + break; + case LSQBRACKET: + parseArray(); + break; + case BYTES: + parseByteArray(); + break; + case ATTR: + parseAttr(); + break; + case COMP: + parseComp(); + break; + default: + env.error(scanner.pos, "struct.expected"); + } + + env.traceln("ending class " + buf.myname); + + // End the class + env.flushErrors(); + Classes.add(buf); + } // end parseClass + + /** + * Parse an Jcoder file. + */ + public ArrayList parseFile() { + env.traceln("PARSER"); + context.init(); + try { + while (scanner.token != Token.EOF) { + try { + switch (scanner.token) { + case CLASS: + case INTERFACE: + case FILE: + // Start of a class +// scan(); + parseClass(); + break; + + case SEMICOLON: + // Bogus semi colon + scanner.scan(); + break; + + case EOF: + // The end + return Classes; + + default: + env.traceln("unexpected token=" + scanner.token.toString()); + env.error(scanner.pos, "toplevel.expected"); + throw new SyntaxError(); + } + } catch (SyntaxError e) { + env.traceln("SyntaxError " + e.getMessage()); + e.printStackTrace(); + recoverFile(); + } + } + } catch (IOException e) { + env.error(scanner.pos, "io.exception", env.getSource()); + return Classes; + } + return Classes; + } //end parseFile + + /*---------------------------------------------*/ + static char fileSeparator; //=System.getProperty("file.separator"); + + /** + * write to the directory passed with -d option + */ + public void write(ByteBuffer cls, File destdir) throws IOException { + String myname = cls.myname; + if (myname == null) { + env.error("cannot.write", null); + return; + } + + env.traceln("writing class " + myname); + File outfile; + if (destdir == null) { + int startofname = myname.lastIndexOf('/'); + if (startofname != -1) { + myname = myname.substring(startofname + 1); + } + outfile = new File(myname); + } else { + env.traceln("writing -d " + destdir.getPath()); + if (fileSeparator == 0) { + fileSeparator = System.getProperty("file.separator").charAt(0); + } + if (fileSeparator != '/') { + myname = myname.replace('/', fileSeparator); + } + outfile = new File(destdir, myname); + File outdir = new File(outfile.getParent()); + if (!outdir.exists() && !outdir.mkdirs()) { + env.error("cannot.write", outdir.getPath()); + return; + } + } + + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outfile)); + out.write(cls.data, 0, cls.length); + try { + out.close(); + } catch (IOException e) { + } + } + + /** + * Writes the classes + */ + public void write(File destdir) throws IOException { + for (ByteBuffer cls : Classes) { + write(cls, destdir); + } + } // end write() +} // end Jcoder diff --git a/src/org/openjdk/asmtools/jcoder/Main.java b/src/org/openjdk/asmtools/jcoder/Main.java new file mode 100644 index 0000000..1423402 --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/Main.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * + * + */ +public class Main { + + /*-------------------------------------------------------- */ + /* Main Fields */ + /** + * Name of the program. + */ + String program; + /** + * The stream where error message are printed. + */ + PrintStream out; + int nerrors = 0; + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + /*-------------------------------------------------------- */ + + /** + * Constructor. + */ + public Main(PrintStream out, String program) { + this.out = out; + this.program = program; + } + /*-------------------------------------------------------- */ + + /** + * Top level error message + */ + public void error(String msg) { + nerrors++; + out.println(program + ": " + msg); + } + + /** + * Usage + */ + public void usage() { + out.println(i18n.getString("jcoder.usage")); + out.println(i18n.getString("jcoder.opt.nowrite")); + out.println(i18n.getString("jcoder.opt.d")); + out.println(i18n.getString("jcoder.opt.version")); + +// out.println(" -D= macro declaration"); + } + + /** + * Run the compiler + */ + public synchronized boolean compile(String argv[]) { + File destDir = null; + boolean traceFlag = false; + boolean debugInfoFlag = false; + long tm = System.currentTimeMillis(); + ArrayList v = new ArrayList<>(); + boolean nowrite = false; + String props = null; + int nwarnings = 0; + HashMap macros = new HashMap<>(); + macros.put("VERSION", "3;45"); + + // Parse arguments + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + if (!arg.startsWith("-")) { + v.add(arg); + } else if (arg.startsWith("-D")) { + int argLength = arg.length(); + if (argLength == 2) { + error(i18n.getString("jcoder.error.D_needs_marco")); + return false; + } + int index = arg.indexOf('='); + if (index == -1) { + error(i18n.getString("jcoder.error.D_needs_marco")); + return false; + } + String macroId = arg.substring(2, index); + index++; + if (argLength == index) { + error(i18n.getString("jcoder.error.D_needs_marco")); + return false; + } + String macro; + if (arg.charAt(index) == '"') { + index++; + if (argLength == index || arg.charAt(argLength - 1) != '"') { + error(i18n.getString("jcoder.error.no_closing_quota")); + return false; + } + macro = arg.substring(index, argLength - 1); + } else { + macro = arg.substring(index, argLength); + } + macros.put(macroId, macro); + } else if (arg.equals("-v")) { + traceFlag = true; + } else if (arg.equals("-nowrite")) { + nowrite = true; + } else if (arg.equals("-d")) { + if ((i + 1) == argv.length) { + error(i18n.getString("jcoder.error.d_requires_argument")); + usage(); + return false; + } + destDir = new File(argv[++i]); + if (!destDir.exists()) { + error(i18n.getString("jcoder.error.does_not_exist", destDir)); + return false; + } + } else if (arg.equals("-version")) { + out.println(ProductInfo.FULL_VERSION); + } else { + error(i18n.getString("jcoder.error.invalid_option", arg)); + usage(); + return false; + } + } + if (v.isEmpty()) { + usage(); + return false; + } + // compile all input files + try { + for (String inpname : v) { + SourceFile env; + Jcoder p; + try { + env = new SourceFile(new File(inpname), out); + env.traceFlag = traceFlag; + env.debugInfoFlag = debugInfoFlag; + p = new Jcoder(env, macros); + p.parseFile(); + env.traceln("END PARSER"); + env.closeInp(); + } catch (FileNotFoundException ex) { + error(i18n.getString("jcoder.error.cannot_read", inpname)); + continue; + } + nerrors += env.nerrors; + nwarnings += env.nwarnings; + if (nowrite || (nerrors > 0)) { + continue; + } + try { + env.traceln("WRITE"); + p.write(destDir); + } catch (FileNotFoundException ex) { + error(i18n.getString("jcoder.error.cannot_write", ex.getMessage())); + } + } + } catch (Error ee) { + ee.printStackTrace(); + error(i18n.getString("jcoder.error.fatal_error")); + } catch (Exception ee) { + ee.printStackTrace(); + error(i18n.getString("jcoder.error.fatal_exception")); + } + + boolean errs = nerrors > 0; + boolean warns = nwarnings > 0; + if (!errs && !warns) { + return true; + } + out.println(errs ? (nerrors > 1 ? (nerrors + " errors") : "1 error") : "" + ((errs && warns) ? ", " : "") + (warns ? (nwarnings > 1 ? (nwarnings + " warnings") : "1 warning") : "")); + return !errs; + } + + /** + * main program + */ + public static void main(String argv[]) { + Main compiler = new Main(System.out, "jcoder"); + System.exit(compiler.compile(argv) ? 0 : 1); + } +} diff --git a/src/org/openjdk/asmtools/jcoder/Scanner.java b/src/org/openjdk/asmtools/jcoder/Scanner.java new file mode 100644 index 0000000..4909b43 --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/Scanner.java @@ -0,0 +1,898 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +import static org.openjdk.asmtools.jcoder.JcodTokens.*; + +import java.io.IOException; +import java.util.HashMap; + +/** + * A Scanner for Jcoder tokens. Errors are reported to the environment object.

+ * + * The scanner keeps track of the current token, the value of the current token (if any), + * and the start position of the current token.

+ * + * The scan() method advances the scanner to the next token in the input.

+ * + * The match() method is used to quickly match opening brackets (ie: '(', '{', or '[') + * with their closing counter part. This is useful during error recovery.

+ * + * The compiler treats either "\n", "\r" or "\r\n" as the end of a line.

+ */ +public class Scanner { + /*-------------------------------------------------------- */ + /* Scanner Fields */ + + /** + * End of input + */ + public static final int EOF = -1; + private boolean debugCP = false; + private int numCPentrs = 0; + + /** + * Where errors are reported + */ + protected SourceFile env; + + /** + * Input stream + */ + protected SourceFile in; + HashMap macros; + + /** + * The current character + */ + protected int ch, prevCh = -1; + protected String macro; + protected int indexMacro; + + /** + * Current token + */ + protected Token token; + + /** + * The position of the current token + */ + protected int pos; + + /** + * The position of the previous token + */ + protected int prevPos; + + /* Token values. */ + protected long longValue; + protected int intValue; + protected int intSize; + protected String stringValue; + protected ByteBuffer longStringValue; + protected int sign; // sign, when reading number + + /* A doc comment preceding the most recent token */ + protected String docComment; + + /** + * A growable character buffer. + */ + private int count; + private char buffer[] = new char[32]; + + /*-------------------------------------------------------- */ + /** + * Create a scanner to scan an input stream. + */ + protected Scanner(SourceFile sf, HashMap macros) + throws IOException { + this.env = sf; + this.in = sf; + this.macros = macros; + + ch = sf.read(); + prevPos = sf.pos; + + scan(); + } + + /** + * for use in jcfront. + */ + protected Scanner(SourceFile sf) + throws IOException { + this.env = sf; + this.in = sf; + this.macros = new HashMap<>(); + + ch = sf.read(); + prevPos = sf.pos; + + scan(); + } + + /* *********************************************** */ + void setDebugCP(boolean enable) { + if (enable) { + numCPentrs = 0; + } + debugCP = enable; + + } + + void addConstDebug(ConstType ct) { + numCPentrs += 1; + env.traceln("\n Const[" + numCPentrs + "] = " + ct.printval()); + } + + void setMacro(String macro) { + this.macro = macro; + indexMacro = 0; + prevCh = ch; + } + + void readCh() throws IOException { + if (macro != null) { + if (indexMacro < macro.length()) { + ch = macro.charAt(indexMacro); + } + macro = null; + } + if (prevCh >= 0) { + ch = prevCh; + prevCh = -1; + } else { + ch = in.read(); + } + } + + private void putc(int ch) { + if (count == buffer.length) { + char newBuffer[] = new char[buffer.length * 2]; + System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); + buffer = newBuffer; + } + buffer[count++] = (char) ch; + } + + private String bufferString() { + char buf[] = new char[count]; + System.arraycopy(buffer, 0, buf, 0, count); + return new String(buf); + } + + /** + * Scan a comment. This method should be called once the initial /, * and the next + * character have been read. + */ + private void skipComment() throws IOException { + while (true) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.comment"); + return; + + case '*': + readCh(); + if (ch == '/') { + readCh(); + return; + } + break; + + default: + readCh(); + break; + } + } + } + + /** + * Scan a doc comment. This method should be called once the initial /, * and * have + * been read. It gathers the content of the comment (witout leading spaces and '*'s) + * in the string buffer. + */ + private String scanDocComment() throws IOException { + count = 0; + + if (ch == '*') { + do { + readCh(); + } while (ch == '*'); + if (ch == '/') { + readCh(); + return ""; + } + } + switch (ch) { + case '\n': + case ' ': + readCh(); + break; + } + + boolean seenstar = false; + int c = count; + while (true) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.comment"); + return bufferString(); + + case '\n': + putc('\n'); + readCh(); + seenstar = false; + c = count; + break; + + case ' ': + case '\t': + putc(ch); + readCh(); + break; + + case '*': + if (seenstar) { + readCh(); + if (ch == '/') { + readCh(); + count = c; + return bufferString(); + } + putc('*'); + } else { + seenstar = true; + count = c; + do { + readCh(); + } while (ch == '*'); + switch (ch) { + case ' ': + readCh(); + break; + + case '/': + readCh(); + count = c; + return bufferString(); + } + } + break; + + default: + if (!seenstar) { + seenstar = true; + } + putc(ch); + readCh(); + c = count; + break; + } + } + } + + /** + * Scan a decimal number + */ + private void scanDecNumber() throws IOException { + boolean overflow = false; + long value = ch - '0'; + count = 0; + token = Token.INTVAL; + intSize = 2; // default + putc(ch); // save character in buffer +numberLoop: + for (;;) { + readCh(); + switch (ch) { + case '8': + case '9': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + putc(ch); + overflow = overflow || (value * 10) / 10 != value; + value = (value * 10) + (ch - '0'); + overflow = overflow || (value - 1 < -1); + break; + case 'b': + readCh(); + intSize = 1; + break numberLoop; + case 's': + readCh(); + intSize = 2; + break numberLoop; + case 'i': + readCh(); + intSize = 4; + break numberLoop; + case 'l': + readCh(); + intSize = 8; + break numberLoop; + default: + break numberLoop; + } + } + longValue = value; + intValue = (int) value; + // we have just finished reading the number. The next thing better + // not be a letter or digit. + if (Character.isJavaIdentifierPart((char) ch) || ch == '.') { + env.error(in.pos, "invalid.number", (new Character((char) ch)).toString()); + do { + readCh(); + } while (Character.isJavaIdentifierPart((char) ch) || ch == '.'); + return; + } + if (overflow) { + env.error(pos, "overflow"); + } + } // scanNumber() + + /** + * Scan a hex number. + */ + private void scanHexNumber() throws IOException { + boolean overflow = false; + long value = 0; + int cypher; + count = 0; + token = Token.INTVAL; + intSize = 2; // default + putc(ch); // save character in buffer +numberLoop: + for (int k = 0;; k++) { + readCh(); + switch (ch) { + case '8': + case '9': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + cypher = (char) ch - '0'; + break; + case 'd': + case 'D': + case 'e': + case 'E': + case 'f': + case 'F': + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + cypher = 10 + Character.toLowerCase((char) ch) - 'a'; + break; + + default: + break numberLoop; + } + putc(ch); + overflow = overflow || ((value >>> 60) != 0); + value = (value << 4) + cypher; + intSize = (k + 1) / 2; + } + longValue = value; + intValue = (int) value; + // we have just finished reading the number. The next thing better + // not be a letter or digit. + if (Character.isJavaIdentifierPart((char) ch) || ch == '.') { + env.error(in.pos, "invalid.number", (new Character((char) ch)).toString()); + do { + readCh(); + } while (Character.isJavaIdentifierPart((char) ch) || ch == '.'); + intValue = 0; +// } else if ( overflow || (intValue - 1 < -1) ) { + } else if (overflow) { + intValue = 0; // so we don't get second overflow in Parser + env.error(pos, "overflow"); + } + } // scanNumber() + + /** + * Scan an escape character. + * + * @return the character or -1 if it escaped an end-of-line. + */ + private int scanEscapeChar() throws IOException { + int p = in.pos; + + readCh(); + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + int n = ch - '0'; + for (int i = 2; i > 0; i--) { + readCh(); + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + n = (n << 3) + ch - '0'; + break; + + default: + if (n > 0xFF) { + env.error(p, "invalid.escape.char"); + } + return n; + } + } + readCh(); + if (n > 0xFF) { + env.error(p, "invalid.escape.char"); + } + return n; + } + + case 'r': + readCh(); + return '\r'; + case 'n': + readCh(); + return '\n'; + case 'f': + readCh(); + return '\f'; + case 'b': + readCh(); + return '\b'; + case 't': + readCh(); + return '\t'; + case '\\': + readCh(); + return '\\'; + case '\"': + readCh(); + return '\"'; + case '\'': + readCh(); + return '\''; + } + + env.error(p, "invalid.escape.char"); + readCh(); + return -1; + } + + /** + * Scan a string. The current character should be the opening " of the string. + */ + private void scanString() throws IOException { + token = Token.STRINGVAL; + count = 0; + readCh(); + +loop: + for (;;) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.string"); + break loop; + + case '\n': + readCh(); + env.error(pos, "newline.in.string"); + break loop; + + case '"': + readCh(); + break loop; + + case '\\': { + int c = scanEscapeChar(); + if (c >= 0) { + putc((char) c); + } + break; + } + + default: + putc(ch); + readCh(); + break; + } + } + stringValue = bufferString(); + } + + /** + * Scan a character array. The current character should be the opening ' of the array. + */ + private void scanCharArray() throws IOException { + token = Token.LONGSTRINGVAL; + ByteBuffer buf = new ByteBuffer(); + count = 0; + readCh(); + +loop: + for (;;) { + int c = ch; + switch (ch) { + case EOF: + env.error(pos, "eof.in.string"); + break loop; + + case '\n': + readCh(); + env.error(pos, "newline.in.string"); + break loop; + + case '\'': + readCh(); + break loop; + + case '\\': + c = scanEscapeChar(); + if (c < 0) { + break; + } + // no break - continue + default: + // see description of java.io.DataOutput.writeUTF() + if ((c > 0) && (c <= 0x7F)) { + buf.write(c); + } else if ((c == 0) || ((c >= 0x80) && (c <= 0x7FF))) { + buf.write(0xC0 | (0x1F & (c >> 6))); + buf.write(0x80 | (0x3f & c)); + } else { + buf.write(0xc0 | (0x0f & (c >> 12))); + buf.write(0x80 | (0x3f & (c >> 6))); + buf.write(0x80 | (0x3f & c)); + } + readCh(); + } + } + longStringValue = buf; + } + + /** + * Scan an Identifier. The current character should be the first character of the + * identifier. + */ + private void scanIdentifier() throws IOException { + count = 0; + boolean compound = false; + for (;;) { + putc(ch); + readCh(); +//env.traceln(" read:"+(char)ch); + if ((ch == '/') || (ch == '.') || (ch == '-')) { +//env.traceln(" =>compound"); + compound = true; + continue; + } else if (!Character.isJavaIdentifierPart((char) ch)) { + break; + } + } +//env.traceln(" end:"+(char)ch); + stringValue = bufferString(); + if (compound) { + token = Token.IDENT; + } else { + token = keyword_token_ident(stringValue); + if (token == Token.IDENT) { + intValue = constValue(stringValue); + if (intValue != -1) { + // this is a constant + if (debugCP) { + ConstType ct = constType(stringValue); + if (ct != null) { + addConstDebug(ct); + } + } + token = Token.INTVAL; + intSize = 1; + longValue = intValue; + } else { + //env.traceln(" ^^^^^^^^ Massive Weirdness here: Can't locate IDENT '" + stringValue + "'. ^^^^^^^^^^"); + } + } + } + } // end scanIdentifier + + protected int xscan() throws IOException { + int retPos = pos; + prevPos = in.pos; + docComment = null; + sign = 1; + for (;;) { + pos = in.pos; + + switch (ch) { + case EOF: + token = Token.EOF; + return retPos; + + case '\n': + case ' ': + case '\t': + case '\f': + readCh(); + break; + + case '/': + readCh(); + switch (ch) { + case '/': + // Parse a // comment + do { + readCh(); + } while ((ch != EOF) && (ch != '\n')); + break; + + case '*': + readCh(); + if (ch == '*') { + docComment = scanDocComment(); + } else { + skipComment(); + } + break; + + default: + token = Token.DIV; + return retPos; + } + break; + + case '"': + scanString(); + return retPos; + + case '\'': + scanCharArray(); + return retPos; + + case '-': + sign = -sign; // hack: no check that numbers only are allowed after + case '+': + readCh(); + break; + + case '0': + readCh(); + token = Token.INTVAL; + longValue = intValue = 0; + switch (ch) { + case 'x': + case 'X': + scanHexNumber(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + scanDecNumber(); + break; + case 'b': + readCh(); + intSize = 1; + break; + case 's': + readCh(); + intSize = 2; + break; + case 'i': + readCh(); + intSize = 4; + break; + case 'l': + readCh(); + intSize = 8; + break; + default: + intSize = 2; + } + return retPos; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + scanDecNumber(); + return retPos; + + case '{': + readCh(); + token = Token.LBRACE; + return retPos; + + case '}': + readCh(); + token = Token.RBRACE; + return retPos; + + case '(': + readCh(); + token = Token.LPAREN; + return retPos; + + case ')': + readCh(); + token = Token.RPAREN; + return retPos; + + case '[': + readCh(); + token = Token.LSQBRACKET; + return retPos; + + case ']': + readCh(); + token = Token.RSQBRACKET; + return retPos; + + case ',': + readCh(); + token = Token.COMMA; + return retPos; + + case ';': + readCh(); + token = Token.SEMICOLON; + return retPos; + + case ':': + readCh(); + token = Token.COLON; + return retPos; + + case '=': + readCh(); + if (ch == '=') { + readCh(); + token = Token.EQ; + return retPos; + } + token = Token.ASSIGN; + return retPos; + + case '\u001a': + // Our one concession to DOS. + readCh(); + if (ch == EOF) { + token = Token.EOF; + return retPos; + } + env.error(pos, "funny.char"); + readCh(); + break; + + case '#': + readCh(); + scanDecNumber(); + return retPos; + + case '&': { + readCh(); + retPos = pos; + if (!Character.isJavaIdentifierStart((char) ch)) { + env.error(pos, "identifier.expected"); + } + scanIdentifier(); + String macroId = stringValue; + String macro = (String) macros.get(macroId); + if (macro == null) { + env.error(pos, "macro.undecl", macroId); + throw new SyntaxError(); + } + setMacro(macro); + readCh(); + } + break; + + default: + if (Character.isJavaIdentifierStart((char) ch)) { + scanIdentifier(); + return retPos; + } + env.error(pos, "funny.char"); + readCh(); + break; + } + } + } + + /** + * Scan to a matching '}', ']' or ')'. The current token must be a '{', '[' or '('; + */ + protected void match(Token open, Token close) throws IOException { + int depth = 1; + + while (true) { + scan(); + if (token == open) { + depth++; + } else if (token == close) { + if (--depth == 0) { + return; + } + } else if (token == Token.EOF) { + env.error(pos, "unbalanced.paren"); + return; + } + } + } + + /** + * Scan the next token. + * + * @return the position of the previous token. + */ + protected int scan() throws IOException { + int retPos = xscan(); +//env.traceln("scanned:"+token+" ("+keywordName(token)+")"); + return retPos; + } + + /** + * Scan the next token. + * + * @return the position of the previous token. + */ + protected int scanMacro() throws IOException { + int retPos = xscan(); +//env.traceln("scanned:"+token+" ("+keywordName(token)+")"); + return retPos; + } +} diff --git a/src/org/openjdk/asmtools/jcoder/SourceFile.java b/src/org/openjdk/asmtools/jcoder/SourceFile.java new file mode 100644 index 0000000..ae08692 --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/SourceFile.java @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Hashtable; + +import org.openjdk.asmtools.util.I18NResourceBundle; + +/** + * An input stream for java programs. The stream treats either "\n", "\r" or "\r\n" as the + * end of a line, it always returns \n. It also parses UNICODE characters expressed as + * \uffff. However, if it sees "\\", the second slash cannot begin a unicode sequence. It + * keeps track of the current position in the input stream. + * + * An position consists of: ((linenr << OFFSETBITS) | offset) this means that both + * the line number and the exact offset into the file are encoded in each postion + * value.

+ */ +public class SourceFile implements org.openjdk.asmtools.jasm.Constants { + + public boolean traceFlag = false; + public boolean debugInfoFlag = false; + /** + * The increment for each character. + */ + protected static final int OFFSETINC = 1; + /** + * The increment for each line. + */ + protected static final int LINEINC = 1 << OFFSETBITS; + File source; + InputStream in; + PrintStream out; + int pos; + private int chpos; + private int pushBack = -1; + + public SourceFile(File source, PrintStream out) throws IOException { + this.source = source; + this.in = new BufferedInputStream(new FileInputStream(source)); + chpos = LINEINC; + this.out = out; + errorFileName = source.getPath(); + } + + public String getSource() { + return source.getPath(); + } + + public void closeInp() { + try { + in.close(); + } catch (IOException e) { + } + flushErrors(); + } + + public int read() throws IOException { + pos = chpos; + chpos += OFFSETINC; + + int c = pushBack; + if (c == -1) { + c = in.read(); + } else { + pushBack = -1; + } + + // parse special characters + switch (c) { + case -2: + // -2 is a special code indicating a pushback of a backslash that + // definitely isn't the start of a unicode sequence. + return '\\'; + + case '\\': + if ((c = in.read()) != 'u') { + pushBack = (c == '\\' ? -2 : c); + return '\\'; + } + // we have a unicode sequence + chpos += OFFSETINC; + while ((c = in.read()) == 'u') { + chpos += OFFSETINC; + } + + // unicode escape sequence + int d = 0; + for (int i = 0; i < 4; i++, chpos += OFFSETINC, c = in.read()) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + d = (d << 4) + c - '0'; + break; + + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + d = (d << 4) + 10 + c - 'a'; + break; + + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + d = (d << 4) + 10 + c - 'A'; + break; + + default: + error(pos, "invalid.escape.char"); + pushBack = c; + return d; + } + } + pushBack = c; + return d; + + case '\n': + chpos += LINEINC; + return '\n'; + + case '\r': + if ((c = in.read()) != '\n') { + pushBack = c; + } else { + chpos += OFFSETINC; + } + chpos += LINEINC; + return '\n'; + + default: + return c; + } + } + + public int lineNumber(int pos) { + return pos >>> OFFSETBITS; + } + + public int lineNumber() { + return pos >>> OFFSETBITS; + } + + /*============================================================== Environment */ + /** + * The number of errors and warnings + */ + public int nerrors; + public int nwarnings; + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + /* + * Until place for jasm.properties is defind, + * I have to keep them right here + * + static Hashtable properties = new Hashtable(40); + + static { + // Scanner: + properties.put("err.eof.in.comment", "Comment not terminated at end of input."); + properties.put("err.invalid.number", "Invalid character \'%s\' in number."); + properties.put("err.invalid.octal.number", "Invalid character in octal number."); + properties.put("err.overflow", "Numeric overflow."); + properties.put("err.float.format", "Invalid floating point format."); + properties.put("err.eof.in.string", "String not terminated at end of input."); + properties.put("err.newline.in.string", "String not terminated at end of line."); + properties.put("err.funny.char", "Invalid character in input."); + properties.put("err.unbalanced.paren", "Unbalanced parentheses."); + // Parser: + properties.put("err.toplevel.expected", "Class or interface declaration expected."); + properties.put("err.token.expected", "'%s' expected."); + properties.put("err.identifier.expected", "Identifier expected."); + properties.put("err.name.expected", "Name expected."); + properties.put("err.io.exception", "I/O error in %s."); + properties.put("err.cannot.write", "Cannot write to %s."); + properties.put("warn.array.wronglength", "expected array length %s do not match real length %s; expected length written"); + properties.put("warn.attr.wronglength", "expected attribute length %s do not match real length %s; expected length written"); + properties.put("attrname.notfound", "Cannot find \"%s\" in constant pool"); + properties.put("err.attrname.expected", "Attribute's name or index expected."); + properties.put("err.element.expected", "Primary data item expected."); + properties.put("err.struct.expected", "Structured data item expected."); + properties.put("err.macro.undecl", "Macro %s undefined."); + } + static String getProperty(String nm) { + return (String) properties.get(nm); + } + */ + /** + * Error String + */ + String errorString(String err, Object arg1, Object arg2, Object arg3) { + String str = null; + + if (!err.startsWith("warn.")) { + err = "err." + err; + } + //str = getProperty(err); + str = i18n.getString(err); + + if (str == null) { + return "error message '" + err + "' not found"; + } + + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if ((c == '%') && (i + 1 < str.length())) { + switch (str.charAt(++i)) { + case 's': + String arg = arg1.toString(); + for (int j = 0; j < arg.length(); j++) { + switch (c = arg.charAt(j)) { + case ' ': + case '\t': + case '\n': + case '\r': + buf.append((char) c); + break; + + default: + if ((c > ' ') && (c <= 255)) { + buf.append((char) c); + } else { + buf.append('\\'); + buf.append('u'); + buf.append(Integer.toString(c, 16)); + } + } + } + arg1 = arg2; + arg2 = arg3; + break; + + case '%': + buf.append('%'); + break; + + default: + buf.append('?'); + break; + } + } else { + buf.append((char) c); + } + } + return buf.toString(); + } + /** + * The filename where the last errors have occurred + */ + String errorFileName; + /** + * List of outstanding error messages + */ + ErrorMessage errors; + + /** + * Insert an error message in the list of outstanding error messages. The list is + * sorted on input position. + */ + void insertError(int where, String message) { + //output("ERR = " + message); + ErrorMessage msg = new ErrorMessage(where, message); + if (errors == null) { + errors = msg; + } else if (errors.where > where) { + msg.next = errors; + errors = msg; + } else { + ErrorMessage m = errors; + for (; (m.next != null) && (m.next.where <= where); m = m.next) { + ; + } + msg.next = m.next; + m.next = msg; + } + } + + /** + * Flush outstanding errors + */ + public void flushErrors() { + if (errors == null) { + return; + } + + try { + // Read the file + FileInputStream in = new FileInputStream(errorFileName); + byte data[] = new byte[in.available()]; + in.read(data); + in.close(); + + // Report the errors + for (ErrorMessage msg = errors; msg != null; msg = msg.next) { + int ln = msg.where >>> OFFSETBITS; + int off = msg.where & ((1 << OFFSETBITS) - 1); + + int i, j; + for (i = off; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r'); i--) { + ; + } + for (j = off; (j < data.length) && (data[j] != '\n') && (data[j] != '\r'); j++) { + ; + } + + String prefix = errorFileName + ":" + ln + ":"; + outputln(prefix + " " + msg.message); + outputln(new String(data, i, j - i)); + + char strdata[] = new char[(off - i) + 1]; + for (j = i; j < off; j++) { + strdata[j - i] = (data[j] == '\t') ? '\t' : ' '; + } + strdata[off - i] = '^'; + outputln(new String(strdata)); + } + } catch (IOException e) { + outputln("I/O exception"); + } + errors = null; + } + + /** + * Output a string. This can either be an error message or something for debugging. + * This should be used instead of print. + */ + public void output(String msg) { +// try { + int len = msg.length(); + for (int i = 0; i < len; i++) { + out.write(msg.charAt(i)); + } +// } catch (IOException e) { +// } + } + + /** + * Output a string. This can either be an error message or something for debugging. + * This should be used instead of println. + */ + public void outputln(String msg) { + output(msg); + out.write('\n'); + } + + /** + * Issue an error. + * @param where Offset in the source for the error + * @param err Error number (as defined in this interface) + * @param arg1 Optional argument to the error (null if not applicable) + * @param arg2 Optional argument to the error (null if not applicable) + * @param arg3 Optional argument to the error (null if not applicable) + */ + /** + * Issue an error + */ + public void error(int where, String err, Object arg1, Object arg2, Object arg3) { + String msg = errorString(err, arg1, arg2, arg3); + if (err.startsWith("warn.")) { + nwarnings++; + } else { + nerrors++; + } + traceln("error:" + msg); + insertError(where, msg); + } + + public final void error(int where, String err, Object arg1, Object arg2) { + error(where, err, arg1, arg2, null); + } + + public final void error(int where, String err, Object arg1) { + error(where, err, arg1, null, null); + } + + public final void error(int where, String err) { + error(where, err, null, null, null); + } + + public final void error(String err) { + error(pos, err, null, null, null); + } + + public final void error(String err, Object arg1) { + error(pos, err, arg1, null, null); + } + + /*============================================================== trace */ + public void trace(String message) { + if (traceFlag) { + output(message); + } + } + + public void traceln(String message) { + if (traceFlag) { + outputln(message); + } + } +} // end SourceFile + diff --git a/src/org/openjdk/asmtools/jcoder/SyntaxError.java b/src/org/openjdk/asmtools/jcoder/SyntaxError.java new file mode 100644 index 0000000..6d8784e --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/SyntaxError.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +/** + * Syntax errors, should always be caught inside the parser for error recovery. + */ +class SyntaxError extends Error { +} diff --git a/src/org/openjdk/asmtools/jcoder/i18n.properties b/src/org/openjdk/asmtools/jcoder/i18n.properties new file mode 100644 index 0000000..3f7e27c --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/i18n.properties @@ -0,0 +1,65 @@ +# Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +jcoder.usage=\ +Usage: java -jar asmtools.jar jcoder [options] file.jcod...\n\ +where possible options include: + +jcoder.opt.nowrite=\ +\ -nowrite do not write resulting .class files +jcoder.opt.d=\ +\ -d destdir directory to place resulting .class files +jcoder.opt.version=\ +\ -version prints the program version + +jcoder.error.D_needs_marco=-D needs macro declaration +jcoder.error.no_closing_quota=no closing quota in macro definition +jcoder.error.d_requires_argument=-d requires argument +jcoder.error.does_not_exist={0} does not exist +jcoder.error.invalid_option=invalid option: {0} +jcoder.error.cannot_read=cannot read {0} +jcoder.error.cannot_write=cannot write {0} +jcoder.error.fatal_error=fatal error +jcoder.error.fatal_exception=fatal exception + +err.eof.in.comment=Comment not terminated at end of input. +err.invalid.number=Invalid character '{0}' in number. +#err.invalid.octal.number=Invalid character in octal number. +err.overflow=Numeric overflow. +#err.float.format=Invalid floating point format. +err.eof.in.string=String not terminated at end of input. +err.newline.in.string=String not terminated at end of line. +err.funny.char=Invalid character in input. +err.unbalanced.paren=Unbalanced parentheses. +err.toplevel.expected=Class or interface declaration expected. +err.token.expected='{0}' expected. +err.identifier.expected=Identifier expected. +err.name.expected=Name expected. +err.io.exception=I/O error in {0}. +err.cannot.write=Cannot write to {0}. +warn.array.wronglength=expected array length {0} do not match real length {1}; expected length written +warn.attr.wronglength=expected attribute length {0} do not match real length {1}; expected length written +#attrname.notfound=Cannot find "{0}" in constant pool +err.attrname.expected=Attribute's name or index expected. +err.element.expected=Primary data item expected. +err.struct.expected=Structured data item expected. +err.macro.undecl=Macro {0} undefined. +err.invalid.escape.char=Invalid escape char diff --git a/src/org/openjdk/asmtools/jdec/ClassData.java b/src/org/openjdk/asmtools/jdec/ClassData.java new file mode 100644 index 0000000..a07eb32 --- /dev/null +++ b/src/org/openjdk/asmtools/jdec/ClassData.java @@ -0,0 +1,969 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdec; + +import org.openjdk.asmtools.util.I18NResourceBundle; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.TypeAnnotationUtils.*; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * + * + */ +class ClassData { + + byte types[]; + Object cpool[]; + int CPlen; + NestedByteArrayInputStream countedin; + DataInputStream in; + PrintWriter out; + String inpname; + int[] cpe_pos; + boolean printDetails; + + public static I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + public ClassData(String inpname, int printFlags, PrintWriter out) throws IOException { + FileInputStream filein = new FileInputStream(inpname); + byte buf[] = new byte[filein.available()]; + filein.read(buf); + countedin = new NestedByteArrayInputStream(buf); + in = new DataInputStream(countedin); + this.out = out; + this.inpname = inpname; + printDetails = ((printFlags & 1) == 1); + } + + /*========================================================*/ + public static final char hexTable[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + String toHex(long val, int width) { + StringBuilder s = new StringBuilder(); + for (int i = width * 2 - 1; i >= 0; i--) { + s.append(hexTable[((int) (val >> (4 * i))) & 0xF]); + } + return "0x" + s.toString(); + } + + String toHex(long val) { + int width; + for (width = 8; width > 0; width--) { + if ((val >> (width - 1) * 8) != 0) { + break; + } + } + return toHex(val, width); + } + + void printByteHex(PrintWriter out, int b) { + out.print(hexTable[(b >> 4) & 0xF]); + out.print(hexTable[b & 0xF]); + } + + void printBytes(PrintWriter out, DataInputStream in, int len) + throws IOException { + try { + for (int i = 0; i < len; i++) { + if (i % 8 == 0) { + out_print("0x"); + } + printByteHex(out, in.readByte()); + if (i % 8 == 7) { + out.println(";"); + } + } + } finally { + if (len % 8 != 0) { + out.println(";"); + } + } + } + + void printRestOfBytes() throws IOException { + for (int i = 0;; i++) { + try { + if (i % 8 == 0) { + out_print("0x"); + } + printByteHex(out, in.readByte()); + if (i % 8 == 7) { + out.print(";\n"); + } + } catch (IOException e) { + out.println(); + return; + } + } + } + + /*========================================================*/ + int shift = 0; + + void out_begin(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + shift++; + } + + void out_print(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.print(s); + } + + void out_println(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + } + + void out_end(String s) { + shift--; + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + } + + String startArray(int length) { + return "[" + (printDetails ? Integer.toString(length) : "") + "]"; + } + + void startArrayCmt(int length, String comment) { + out_begin(startArray(length) + " { // " + comment); + } + + void startArrayCmtB(int length, String comment) { + out_begin(startArray(length) + "b { // " + comment); + } + + /*========================================================*/ + void readCP(DataInputStream in) throws IOException { + int length = in.readUnsignedShort(); + CPlen = length; + traceln(i18n.getString("jdec.trace.CP_len", length)); + types = new byte[length]; + cpool = new Object[length]; + cpe_pos = new int[length]; + for (int i = 1; i < length; i++) { + byte btag; + int v1, v2, n = i; + long lv; + cpe_pos[i] = countedin.getPos(); + btag = in.readByte(); + traceln(i18n.getString("jdec.trace.CP_entry", i, btag)); + types[i] = btag; + ConstType tg = tag(btag); + switch (tg) { + case CONSTANT_UTF8: + cpool[i] = in.readUTF(); + break; + case CONSTANT_INTEGER: + v1 = in.readInt(); + cpool[i] = new Integer(v1); + break; + case CONSTANT_FLOAT: + v1 = Float.floatToIntBits(in.readFloat()); + cpool[i] = new Integer(v1); + break; + case CONSTANT_LONG: + lv = in.readLong(); + cpool[i] = new Long(lv); + i++; + break; + case CONSTANT_DOUBLE: + lv = Double.doubleToLongBits(in.readDouble()); + cpool[i] = new Long(lv); + i++; + break; + case CONSTANT_CLASS: + case CONSTANT_STRING: + v1 = in.readUnsignedShort(); + cpool[i] = new Integer(v1); + break; + case CONSTANT_INTERFACEMETHOD: + case CONSTANT_FIELD: + case CONSTANT_METHOD: + case CONSTANT_NAMEANDTYPE: +// case CONSTANT_INVOKEDYNAMIC_TRANS: + cpool[i] = "#" + in.readUnsignedShort() + " #" + in.readUnsignedShort(); + break; + case CONSTANT_INVOKEDYNAMIC: + cpool[i] = in.readUnsignedShort() + "s #" + in.readUnsignedShort(); + break; + case CONSTANT_METHODHANDLE: + cpool[i] = in.readUnsignedByte() + "b #" + in.readUnsignedShort(); + break; + case CONSTANT_METHODTYPE: + cpool[i] = "#" + in.readUnsignedShort(); + break; + default: + CPlen = i; + printCP(out); + out_println(toHex(btag, 1) + "; // invalid constant type: " + (int) btag + " for element " + i); + throw new ClassFormatError(); + } + } + } + + void printCP(PrintWriter out) throws IOException { + int length = CPlen; + startArrayCmt(length, "Constant Pool"); + out_println("; // first element is empty"); + int size; + for (int i = 1; i < length; i = i + size) { + size = 1; + byte btag = types[i]; + ConstType tg = tag(btag); + int pos = cpe_pos[i]; + String tagstr = ""; + String valstr; + int v1, v2; + long lv; + if (tg != null) { + tagstr = tg.parseKey(); + } + switch (tg) { + case CONSTANT_UTF8: { + tagstr = "Utf8"; + StringBuilder sb = new StringBuilder(); + String s = (String) cpool[i]; + sb.append('\"'); + for (int k = 0; k < s.length(); k++) { + char c = s.charAt(k); + switch (c) { + case '\t': + sb.append('\\').append('t'); + break; + case '\n': + sb.append('\\').append('n'); + break; + case '\r': + sb.append('\\').append('r'); + break; + case '\"': + sb.append('\\').append('\"'); + break; + default: + sb.append(c); + } + } + valstr = sb.append('\"').toString(); + } + break; + case CONSTANT_FLOAT: + v1 = ((Integer) cpool[i]).intValue(); + valstr = toHex(v1, 4); + break; + case CONSTANT_INTEGER: + v1 = ((Integer) cpool[i]).intValue(); + valstr = toHex(v1, 4); + break; + case CONSTANT_DOUBLE: + lv = ((Long) cpool[i]).longValue(); + valstr = toHex(lv, 8) + ";"; + size = 2; + break; + case CONSTANT_LONG: + lv = ((Long) cpool[i]).longValue(); + valstr = toHex(lv, 8) + ";"; + size = 2; + break; + case CONSTANT_CLASS: + v1 = ((Integer) cpool[i]).intValue(); + valstr = "#" + v1; + break; + case CONSTANT_STRING: + v1 = ((Integer) cpool[i]).intValue(); + valstr = "#" + v1; + break; + case CONSTANT_INTERFACEMETHOD: + valstr = (String) cpool[i]; + break; + case CONSTANT_FIELD: + valstr = (String) cpool[i]; + break; + case CONSTANT_METHOD: + valstr = (String) cpool[i]; + break; + case CONSTANT_NAMEANDTYPE: + valstr = (String) cpool[i]; + break; + case CONSTANT_METHODHANDLE: + valstr = (String) cpool[i]; + break; + case CONSTANT_METHODTYPE: + valstr = (String) cpool[i]; + break; +// case CONSTANT_INVOKEDYNAMIC_TRANS: +// tagstr = "InvokeDynamicTrans"; +// valstr = (String) cpool[i]; +// break; + case CONSTANT_INVOKEDYNAMIC: + valstr = (String) cpool[i]; + break; + default: + throw new Error("invalid constant type: " + (int) btag); + } + out_print(tagstr + " " + valstr + "; // #" + i); + if (printDetails) { + out_println(" at " + toHex(pos)); + } else { + out_println(""); + } + } + out_end("} // Constant Pool"); + out.println(); + } + + String getStringPos() { + return " at " + toHex(countedin.getPos()); + } + + String getStringPosCond() { + if (printDetails) { + return getStringPos(); + } else { + return ""; + } + } + + String getCommentPosCond() { + if (printDetails) { + return " // " + getStringPos(); + } else { + return ""; + } + } + + void decodeCPXAttr(DataInputStream in, int len, String attrname, PrintWriter out) throws IOException { + decodeCPXAttrM(in, len, attrname, out, 1); + } + + void decodeCPXAttrM(DataInputStream in, int len, String attrname, PrintWriter out, int expectedIndices) throws IOException { + if (len != expectedIndices * 2) { + out_println("// invalid length of " + attrname + " attr: " + len + " (should be " + (expectedIndices * 2) + ") > "); + printBytes(out, in, len); + } else { + String outputString = ""; + String space = ""; + for (int k = 0; k < expectedIndices; k++) { + outputString += (space + "#" + in.readUnsignedShort()); + space = " "; + } + out_println(outputString + ";"); + } + } + + void printStackMap(DataInputStream in, int elementsNum) throws IOException { + int num; + if (elementsNum > 0) { + num = elementsNum; + } else { + num = in.readUnsignedShort(); + } + out.print(startArray(num) + (elementsNum > 0 ? "z" : "") + "{"); + for (int k = 0; k < num; k++) { + int maptype = in.readUnsignedByte(); + StackMapType mptyp = stackMapType(maptype); + String maptypeImg; + if (printDetails) { + maptypeImg = Integer.toString(maptype) + "b"; + } else { + try { + maptypeImg = mptyp.parsekey(); + // maptypeImg = ITEM_Names[maptype]; + } catch (ArrayIndexOutOfBoundsException e) { + maptypeImg = "/* BAD TYPE: */ " + maptype + "b"; + } + } + switch (mptyp) { + case ITEM_Object: + case ITEM_NewObject: + maptypeImg = maptypeImg + "," + in.readUnsignedShort(); + break; + default: + } + out.print(maptypeImg); + if (k < num - 1) { + out.print("; "); + } + } + out.print("}"); + } + + /** + * Processes JSR-308 extended_annotation structure. + */ + void decodeTargetTypeAndRefInfo(DataInputStream in, int len, PrintWriter out, boolean isWildcard) throws IOException { + int tt = in.readUnsignedShort(); + TargetType target_type = targetTypeEnum(tt); + InfoType info_type = target_type.infoType(); + out_println(toHex(tt, 2) + "; // target_type: " + target_type.parseKey()); + + switch (info_type) { + case TYPEPARAM: //[3.3.1] meth_type_param, class_type_param: + out_println(toHex(in.readUnsignedByte(), 1) + "; // param_index"); + break; + case SUPERTYPE: //[3.3.2] class_exts_impls + out_println(toHex(in.readUnsignedShort(), 2) + "; // type_index"); + break; + case TYPEPARAM_BOUND: //[3.3.3] class_type_param_bnds, meth_type_param_bnds + out_println(toHex(in.readUnsignedByte(), 1) + "; // param_index"); + out_println(toHex(in.readUnsignedByte(), 1) + "; // bound_index"); + break; + case EMPTY: //[3.3.4] meth_reciever, meth_ret_type, field + // NOTE: reference_info is empty for this annotation's target + break; + case METHODPARAM: //[3.3.5] meth_formal_param: + out_println(toHex(in.readUnsignedByte(), 1) + "; // parameter_index"); + break; + case EXCEPTION: //[3.3.61] throws_type + //KTL: Updated index to UShort for JSR308 change + out_println(in.readUnsignedShort() + "; // type_index"); + break; + case LOCALVAR: //[3.3.7] local_var, resource_var + { + int lv_num = in.readUnsignedShort(); + startArrayCmt(lv_num, "local_variables"); + for (int i = 0; i < lv_num; i++) { + out_println(in.readUnsignedShort() + " " + in.readUnsignedShort() + + " " + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + out_end("}"); + } + break; + case CATCH: //[3.3.8] exception_param + out_println(in.readUnsignedShort() + "; // exception_table_index"); + break; + case OFFSET: //[3.3.9] type_test (instanceof), obj_creat (new) + // constr_ref_receiver, meth_ref_receiver + out_println(in.readUnsignedShort() + "; // offset"); + break; + case TYPEARG: //[3.3.10] cast, constr_ref_typearg, meth_invoc_typearg + // constr_invoc_typearg, meth_ref_typearg + out_println(in.readUnsignedShort() + "; // offset"); + out_println(toHex(in.readUnsignedByte(), 1) + "; // type_index"); + break; + default: // should never happen + out_println(toHex(tt, 1) + "; // invalid target_info: " + tt); + throw new ClassFormatError(); + } + int path_len = in.readUnsignedShort(); + startArrayCmt(path_len, "type_paths"); + for (int i = 0; i < path_len; i++) { + // print the type Path elements + out_println("{ " + toHex(in.readUnsignedByte(), 1) + + "; " + toHex(in.readUnsignedByte(), 1) + + "; } // type_path[" + i + "]"); // type path kind + } + out_end("}"); + + } + + void decodeElementValue(DataInputStream in, int len, PrintWriter out) throws IOException { + out_begin("{ // element_value"); + char tg = (char) in.readByte(); + AnnotElemType tag = annotElemType(tg); + out_println("'" + tg + "';"); + switch (tag) { + case AE_BYTE: + case AE_CHAR: + case AE_DOUBLE: + case AE_FLOAT: + case AE_INT: + case AE_LONG: + case AE_SHORT: + case AE_BOOLEAN: + case AE_STRING: + decodeCPXAttr(in, 2, "const_value_index", out); + break; + case AE_ENUM: + out_begin("{ // enum_const_value"); + decodeCPXAttr(in, 2, "type_name_index", out); + decodeCPXAttr(in, 2, "const_name_index", out); + out_end("} // enum_const_value"); + break; + case AE_CLASS: + decodeCPXAttr(in, 2, "class_info_index", out); + break; + case AE_ANNOTATION: + decodeAnnotation(in, out); + break; + case AE_ARRAY: + int ev_num = in.readUnsignedShort(); + startArrayCmt(ev_num, "array_value"); + for (int i = 0; i < ev_num; i++) { + decodeElementValue(in, 0, out); + if (i < ev_num - 1) { + out_println(";"); + } + } + out_end("} // array_value"); + break; + default: + out_println(toHex(tg, 1) + "; // invalid element_value tag type: " + tg); + throw new ClassFormatError(); + } + out_end("} // element_value"); + } + + void decodeAnnotation(DataInputStream in, PrintWriter out) throws IOException { + out_begin("{ // annotation"); + decodeCPXAttr(in, 2, "field descriptor", out); + int evp_num = in.readUnsignedShort(); + startArrayCmt(evp_num, "element_value_pairs"); + for (int i = 0; i < evp_num; i++) { + out_begin("{ // element value pair"); + decodeCPXAttr(in, 2, "name of the annotation type element", out); + decodeElementValue(in, 0, out); + out_end("} // element value pair"); + if (i < evp_num - 1) { + out_println(";"); + } + } + out_end("} // element_value_pairs"); + out_end("} // annotation"); + } + + void decodeExtendedAnnotation(DataInputStream in, PrintWriter out) throws IOException { + out_begin("{ // annotation"); + decodeCPXAttr(in, 2, "field descriptor", out); + int evp_num = in.readUnsignedShort(); + startArrayCmt(evp_num, "element_value_pairs"); + for (int i = 0; i < evp_num; i++) { + out_begin("{ // element value pair"); + decodeCPXAttr(in, 2, "name of the annotation type element", out); + decodeElementValue(in, 0, out); + out_end("} // element value pair"); + if (i < evp_num - 1) { + out_println(";"); + } + } + out_end("} // element_value_pairs"); + decodeTargetTypeAndRefInfo(in, 0, out, false); + out_end("} // annotation"); + } + + void decodeBootstrapMethod(DataInputStream in, PrintWriter out) throws IOException { + out_begin("{ // bootstrap_method"); + out_println("#" + in.readUnsignedShort() + "; // bootstrap_method_ref"); + int bm_args_cnt = in.readUnsignedShort(); + startArrayCmt(bm_args_cnt, "bootstrap_arguments"); + for (int i = 0; i < bm_args_cnt; i++) { + out_println("#" + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + out_end("} // bootstrap_arguments"); + out_end("} // bootstrap_method"); + } + + void decodeAttr(DataInputStream in0, PrintWriter out) throws IOException { + // Read one attribute + String posComment = getStringPos(); + int name_cpx = in0.readUnsignedShort(), btag, len; + + String AttrName = ""; + try { + btag = types[name_cpx]; + ConstType tag = tag(btag); + + if (tag == ConstType.CONSTANT_UTF8) { + AttrName = (String) cpool[name_cpx]; + } + } catch (ArrayIndexOutOfBoundsException e) { + } + AttrTag tg = attrtag(AttrName); + String endingComment = AttrName; + len = in0.readInt(); + countedin.enter(len); + try { + if (printDetails) { + out_begin("Attr(#" + name_cpx + ", " + len + ") { // " + AttrName + posComment); + } else { + out_begin("Attr(#" + name_cpx + ") { // " + AttrName); + } + + switch (tg) { + case ATT_Code: + out_println(in.readUnsignedShort() + "; // max_stack"); + out_println(in.readUnsignedShort() + "; // max_locals"); + int code_len = in.readInt(); + out_begin("Bytes" + startArray(code_len) + "{"); + printBytes(out, in, code_len); + out_end("};"); + int trap_num = in.readUnsignedShort(); + startArrayCmt(trap_num, "Traps"); + for (int i = 0; i < trap_num; i++) { + out_println(in.readUnsignedShort() + " " + in.readUnsignedShort() + " " + in.readUnsignedShort() + " " + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + out_end("} // end Traps"); + + // Read the attributes + decodeAttrs(in, out); + break; + case ATT_ConstantValue: + decodeCPXAttr(in, len, AttrName, out); + break; + case ATT_Exceptions: + int exc_num = in.readUnsignedShort(); + startArrayCmt(exc_num, AttrName); + for (int i = 0; i < exc_num; i++) { + out_println("#" + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + out_end("}"); + break; + case ATT_LineNumberTable: + int ll_num = in.readUnsignedShort(); + startArrayCmt(ll_num, AttrName); + for (int i = 0; i < ll_num; i++) { + out_println(in.readUnsignedShort() + " " + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + out_end("}"); + break; + case ATT_LocalVariableTable: + int lv_num = in.readUnsignedShort(); + startArrayCmt(lv_num, AttrName); + for (int i = 0; i < lv_num; i++) { + out_println(in.readUnsignedShort() + " " + in.readUnsignedShort() + " " + in.readUnsignedShort() + " " + in.readUnsignedShort() + " " + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + out_end("}"); + break; + case ATT_LocalVariableTypeTable: + int lvt_num = in.readUnsignedShort(); + startArrayCmt(lvt_num, AttrName); + for (int i = 0; i < lvt_num; i++) { + out_println(in.readUnsignedShort() + " " + in.readUnsignedShort() + " " + in.readUnsignedShort() + " " + in.readUnsignedShort() + " " + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + out_end("}"); + break; + case ATT_InnerClasses: + int ic_num = in.readUnsignedShort(); + startArrayCmt(ic_num, AttrName); + for (int i = 0; i < ic_num; i++) { + out_println("#" + in.readUnsignedShort() + " #" + in.readUnsignedShort() + " #" + in.readUnsignedShort() + " " + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + out_end("}"); + break; + case ATT_Signature: + decodeCPXAttr(in, len, AttrName, out); + break; + case ATT_StackMap: + int e_num = in.readUnsignedShort(); + startArrayCmt(e_num, ""); + for (int k = 0; k < e_num; k++) { + int start_pc = in.readUnsignedShort(); + out_print("" + start_pc + ", "); + printStackMap(in, 0); + out.print(", "); + printStackMap(in, 0); + out.println(";"); + } + out_end("}"); + break; + case ATT_StackMapTable: + int et_num = in.readUnsignedShort(); + startArrayCmt(et_num, ""); + for (int k = 0; k < et_num; k++) { + int frame_type = in.readUnsignedByte(); + StackMapFrameType ftype = stackMapFrameType(frame_type); + switch (ftype) { + case SAME_FRAME: + // type is same_frame; + out_print("" + frame_type + "b"); + out.println("; // same_frame"); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + // type is same_locals_1_stack_item_frame + int offset = frame_type - 64; + out_print("" + frame_type + "b, "); + // read additional single stack element + printStackMap(in, 1); + out.println("; // same_locals_1_stack_item_frame"); + break; + case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME: + // type is same_locals_1_stack_item_frame_extended + int noffset = in.readUnsignedShort(); + out_print("" + frame_type + "b, " + noffset + ", "); + // read additional single stack element + printStackMap(in, 1); + out.println("; // same_locals_1_stack_item_frame_extended"); + break; + case CHOP_1_FRAME: + case CHOP_2_FRAME: + case CHOP_3_FRAME: + // type is chop_frame + int coffset = in.readUnsignedShort(); + out_print("" + frame_type + "b, " + coffset); + out.println("; // chop_frame " + (251 - frame_type)); + break; + case SAME_FRAME_EX: + // type is same_frame_extended; + int xoffset = in.readUnsignedShort(); + out_print("" + frame_type + "b, " + xoffset); + out.println("; // same_frame_extended"); + break; + case APPEND_FRAME: + // type is append_frame + int aoffset = in.readUnsignedShort(); + out_print("" + frame_type + "b, " + aoffset + ", "); + // read additional locals + printStackMap(in, frame_type - 251); + out.println("; // append_frame " + (frame_type - 251)); + break; + case FULL_FRAME: + // type is full_frame + int foffset = in.readUnsignedShort(); + out_print("" + frame_type + "b, " + foffset + ", "); + printStackMap(in, 0); + out.print(", "); + printStackMap(in, 0); + out.println("; // full_frame"); + break; + } + } + out_end("}"); + break; + case ATT_EnclosingMethod: + decodeCPXAttrM(in, len, AttrName, out, 2); + break; + case ATT_SourceFile: + decodeCPXAttr(in, len, AttrName, out); + break; + case ATT_AnnotationDefault: + decodeElementValue(in, len, out); + break; + case ATT_RuntimeInvisibleAnnotations: + case ATT_RuntimeVisibleAnnotations: + int an_num = in.readUnsignedShort(); + startArrayCmt(an_num, "annotations"); + for (int i = 0; i < an_num; i++) { + decodeAnnotation(in, out); + if (i < an_num - 1) { + out_println(";"); + } + } + out_end("}"); + break; + case ATT_RuntimeInvisibleTypeAnnotations: + case ATT_RuntimeVisibleTypeAnnotations: + int ant_num = in.readUnsignedShort(); + startArrayCmt(ant_num, "annotations"); + for (int i = 0; i < ant_num; i++) { + decodeExtendedAnnotation(in, out); + if (i < ant_num - 1) { + out_println(";"); + } + } + out_end("}"); + break; + case ATT_RuntimeInvisibleParameterAnnotations: + case ATT_RuntimeVisibleParameterAnnotations: + int pm_num = in.readUnsignedByte(); + startArrayCmtB(pm_num, "parameters"); + for (int k = 0; k < pm_num; k++) { + int anp_num = in.readUnsignedShort(); + startArrayCmt(anp_num, "annotations"); + for (int i = 0; i < anp_num; i++) { + decodeAnnotation(in, out); + if (k < anp_num - 1) { + out_println(";"); + } + } + out_end("}"); + if (k < pm_num - 1) { + out_println(";"); + } + } + out_end("}"); + break; + case ATT_BootstrapMethods: + int bm_num = in.readUnsignedShort(); + startArrayCmt(bm_num, "bootstrap_methods"); + for (int i = 0; i < bm_num; i++) { + decodeBootstrapMethod(in, out); + if (i < bm_num - 1) { + out_println(";"); + } + } + out_end("}"); + break; + default: + if (AttrName == null) { + printBytes(out, in, len); + endingComment = "Attr(#" + name_cpx + ")"; + } else { + // some kind of error? + printBytes(out, in, len); + } + } + + } catch (EOFException e) { + out.println("// ======== unexpected end of attribute array"); + } + int rest = countedin.available(); + if (rest > 0) { + out.println("// ======== attribute array started " + posComment + " has " + rest + " bytes more:"); + printBytes(out, in, rest); + } + out_end("} // end " + endingComment); + countedin.leave(); + } + + void decodeAttrs(DataInputStream in, PrintWriter out) throws IOException { + // Read the attributes + int attr_num = in.readUnsignedShort(); + startArrayCmt(attr_num, "Attributes"); + for (int i = 0; i < attr_num; i++) { + decodeAttr(in, out); + if (i + 1 < attr_num) { + out_println(";"); + } + } + out_end("} // Attributes"); + } + + void decodeMembers(DataInputStream in, PrintWriter out, String comment) throws IOException { + int nfields = in.readUnsignedShort(); + traceln(comment + "=" + nfields); + startArrayCmt(nfields, "" + comment); + try { + for (int i = 0; i < nfields; i++) { + out_begin("{ // Member" + getStringPosCond()); + int access = in.readShort(); + out_println(toHex(access, 2) + "; // access"); + int name_cpx = in.readUnsignedShort(); + out_println("#" + name_cpx + "; // name_cpx"); + int sig_cpx = in.readUnsignedShort(); + out_println("#" + sig_cpx + "; // sig_cpx"); + // Read the attributes + decodeAttrs(in, out); + out_end("} // Member"); + if (i + 1 < nfields) { + out_println(";"); + } + } + } finally { + out_end("} // " + comment); + out.println(); + } + } + + public void decodeClass() throws IOException { + String classname = "N/A"; + // Read the header + try { + int magic = in.readInt(); + int min_version = in.readUnsignedShort(); + int version = in.readUnsignedShort(); + + // Read the constant pool + readCP(in); + short access = in.readShort(); // dont care about sign + int this_cpx = in.readUnsignedShort(); + + try { + classname = (String) cpool[((Integer) cpool[this_cpx]).intValue()]; + out_begin("class " + classname + " {"); + } catch (Exception e) { + classname = inpname; + out.println("// " + e.getMessage() + " while accessing classname"); + out_begin("class " + classname + " { // source file name"); + } + + out_print(toHex(magic, 4) + ";"); + if (magic != JAVA_MAGIC) { + out.print(" // wrong magic: 0x" + Integer.toString(JAVA_MAGIC, 16) + " expected"); + } + out.println(); + out_println(min_version + "; // minor version"); + out_println(version + "; // version"); + + // Print the constant pool + printCP(out); + + out_println(toHex(access, 2) + "; // access"); + out_println("#" + this_cpx + ";// this_cpx"); + int super_cpx = in.readUnsignedShort(); + out_println("#" + super_cpx + ";// super_cpx"); + traceln(i18n.getString("jdec.trace.access_thisCpx_superCpx", access, this_cpx, super_cpx)); + out.println(); + + // Read the interface names + int numinterfaces = in.readUnsignedShort(); + traceln(i18n.getString("jdec.trace.numinterfaces", numinterfaces)); + startArrayCmt(numinterfaces, "Interfaces"); + for (int i = 0; i < numinterfaces; i++) { + int intrf_cpx = in.readUnsignedShort(); + traceln(i18n.getString("jdec.trace.intrf", i, intrf_cpx)); + out_println("#" + intrf_cpx + ";"); + } + out_end("} // Interfaces\n"); + + // Read the fields + decodeMembers(in, out, "fields"); + + // Read the methods + decodeMembers(in, out, "methods"); + + // Read the attributes + decodeAttrs(in, out); + } catch (EOFException e) { + } catch (ClassFormatError err) { + out.println("//------- ClassFormatError:" + err.getMessage()); + printRestOfBytes(); + } finally { + out_end("} // end class " + classname); + } + } // end decodeClass() + /* ====================================================== */ + public boolean DebugFlag = false; + + public void trace(String s) { + if (!DebugFlag) { + return; + } + System.out.print(s); + } + + public void traceln(String s) { + if (!DebugFlag) { + return; + } + System.out.println(s); + } +}// end class ClassData + diff --git a/src/org/openjdk/asmtools/jdec/Main.java b/src/org/openjdk/asmtools/jdec/Main.java new file mode 100644 index 0000000..4784f8c --- /dev/null +++ b/src/org/openjdk/asmtools/jdec/Main.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdec; + +import org.openjdk.asmtools.jdis.uEscWriter; +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Main program of the Java DeCoder + */ +public class Main { + + /*-------------------------------------------------------- */ + /* Main Fields */ + /** + * Name of the program. + */ + String program; + + /** + * The stream where error message are printed. + */ + PrintWriter out; + boolean DebugFlag = false; + int printFlags = 0; + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + /*-------------------------------------------------------- */ + /** + * Constructor. + */ + public Main(PrintWriter out, String program) { + this.out = out; + this.program = program; + } + /*-------------------------------------------------------- */ + + public void error(String msg) { + out.println(program + ": " + msg); + } + + /** + * Usage + */ + public void usage() { + out.println(i18n.getString("jdec.usage")); + out.println(i18n.getString("jdec.opt.g")); + out.println(i18n.getString("jdec.opt.version")); + } + + /** + * Run the decoder + */ + public synchronized boolean decode(String argv[]) { +// int flags = F_WARNINGS; + long tm = System.currentTimeMillis(); + ArrayList vargs = new ArrayList<>(); + ArrayList vj = new ArrayList<>(); + boolean nowrite = false; + int addOptions = 0; + + // Parse arguments + int i = 0; + for (String arg : argv) { + if (arg.equals("-g")) { + printFlags = printFlags | 1; + vargs.add(arg); +//out.println("arg["+i+"]="+argv[i]+"/printFlags"); + } else if (arg.equals("-v")) { + DebugFlag = true; + vargs.add(arg); + out.println("arg[" + i + "]=" + argv[i] + "/verbose"); + } else if (arg.equals("-version")) { + out.println(ProductInfo.FULL_VERSION); + } else if (arg.startsWith("-")) { +//out.println("arg["+i+"]="+argv[i]+"/invalid flag"); + error(i18n.getString("jdec.error.invalid_flag", arg)); + usage(); + return false; + } else { + vargs.add(arg); + vj.add(arg); + } + i += 1; + } + + if (vj.isEmpty()) { + usage(); + return false; + } + + String[] names = new String[0]; + names = vj.toArray(names); +decode: + for (String inpname : names) { + try { + ClassData cc = new ClassData(inpname, printFlags, out); + cc.DebugFlag = DebugFlag; + cc.decodeClass(); + continue decode; + } catch (FileNotFoundException ee) { + error(i18n.getString("jdec.error.cannot_read", inpname)); + } catch (Error ee) { + ee.printStackTrace(); + error(i18n.getString("jdec.error.fatal_error")); + } catch (Exception ee) { + ee.printStackTrace(); + error(i18n.getString("jdec.error.fatal_exception")); + } + return false; + } + return true; + } + + /** + * Main program + */ + public static void main(String argv[]) { + Main decoder = new Main(new PrintWriter(new uEscWriter(System.out)), "jdec"); + System.exit(decoder.decode(argv) ? 0 : 1); + } +} diff --git a/src/org/openjdk/asmtools/jdec/NestedByteArrayInputStream.java b/src/org/openjdk/asmtools/jdec/NestedByteArrayInputStream.java new file mode 100644 index 0000000..c222eae --- /dev/null +++ b/src/org/openjdk/asmtools/jdec/NestedByteArrayInputStream.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdec; + +import java.io.ByteArrayInputStream; +import java.util.Stack; + +/** + * this class provides functionality needed to read class files: + *

    + *
  • methods to read unsigned integers of various length + *
  • counts bytes read so far + *
+ */ +public class NestedByteArrayInputStream extends ByteArrayInputStream { + + NestedByteArrayInputStream(byte buf[]) { + super(buf); + } + + NestedByteArrayInputStream(byte buf[], int offset, int length) { + super(buf, offset, length); + } + + /*---- */ + public int getPos() { + return pos; + } + Stack savedStates = new Stack(); + + public void enter(int range) { + savedStates.push(new Integer(count)); + if (pos + range < count) { + count = pos + range; + } + } + + public void leave() { + pos = count; + count = ((Integer) savedStates.pop()).intValue(); + } +} // end class NestedByteArrayInputStream + diff --git a/src/org/openjdk/asmtools/jdec/i18n.properties b/src/org/openjdk/asmtools/jdec/i18n.properties new file mode 100644 index 0000000..b1c1abd --- /dev/null +++ b/src/org/openjdk/asmtools/jdec/i18n.properties @@ -0,0 +1,41 @@ +# Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +jdec.error.invalid_flag=invalid flag: " {0} +jdec.error.fatal_error=fatal error +jdec.error.fatal_exception=fatal exception +jdec.error.cannot_read=cannot read {0} +jdec.usage=\ +Usage: java -jar asmtools.jar jdec [options] FILE.class... > FILE.jcod\n\ +where possible options include: + +jdec.opt.g=\ +\ -g: detailed output format +jdec.opt.version=\ +\ -version: print version number and date + + +jdec.trace.CP_len=CP len= {0} +jdec.trace.CP_entry=CP entry # {0} tag= {1} +jdec.trace.access_thisCpx_superCpx=access={0} this_cpx={1} super_cpx={2} +jdec.trace.numinterfaces=numinterfaces={0} +jdec.trace.intrf=\ +\ intrf_cpx[{0}]={1} diff --git a/src/org/openjdk/asmtools/jdis/AnnotElem.java b/src/org/openjdk/asmtools/jdis/AnnotElem.java new file mode 100644 index 0000000..68b63dc --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/AnnotElem.java @@ -0,0 +1,397 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * + * AnnotElem + * + * Base class of all AnnotationElement entries + * + */ +public class AnnotElem { + + /** + * + * CPX_AnnotElem + * + * base class for an annotation value. + * + */ + public static class AnnotValue { + + /*-------------------------------------------------------- */ + /* AnnotElem Fields */ + /** + * tag the descriptor for the constant + */ + public AnnotElemType tag; + + // internal references + protected ClassData cls; + /*-------------------------------------------------------- */ + + public AnnotValue(AnnotElemType tagval, ClassData cls) { + tag = tagval; + this.cls = cls; + } + + public String stringVal() { + return ""; + } + + public void print(PrintWriter out, String tab) { + out.print(tag.val() + "\t"); + } + + @Override + public String toString() { + return ""; + } + } + + /** + * + * CPX_AnnotElem + * + * Annotation value which is described by a single CPX entry (ie. String, byte, char, + * int, short, boolean, float, long, double, class reference). + * + */ + public static class CPX_AnnotValue extends AnnotValue { + + /** + * tag the descriptor for the constant + */ + public int cpx; + + public CPX_AnnotValue(AnnotElemType tag, ClassData cls, int cpx) { + super(tag, cls); + this.cpx = cpx; + } + + @Override + public String stringVal() { + StringBuilder sb = new StringBuilder(); + switch (tag) { + case AE_STRING: // String + sb.append('"' + cls.pool.getString(cpx) + '"'); + break; + case AE_BYTE: // Byte + sb.append("byte " + cls.pool.getConst(cpx).stringVal()); + break; + case AE_CHAR: // Char + sb.append("char " + cls.pool.getConst(cpx).stringVal()); + break; + case AE_INT: // Int (no need to add keyword) + sb.append(cls.pool.getConst(cpx).stringVal()); + break; + case AE_SHORT: // Short + sb.append("short " + cls.pool.getConst(cpx).stringVal()); + break; + case AE_BOOLEAN: // Boolean + ConstantPool.CP_Int cns = (ConstantPool.CP_Int) cls.pool.getConst(cpx); + sb.append("boolean " + (cns.value == 0 ? "false" : "true")); + break; + case AE_FLOAT: // Float + sb.append(cls.pool.getConst(cpx).stringVal()); // + "f"); + break; + case AE_DOUBLE: // Double + sb.append(cls.pool.getConst(cpx).stringVal()); // + "d"); + break; + case AE_LONG: // Long + sb.append(cls.pool.getConst(cpx).stringVal()); // + "l"); + break; + case AE_CLASS: // Class + sb.append("class " + cls.pool.decodeClassDescriptor(cpx)); + break; + default: + break; + } + return sb.toString(); + } + + @Override + public void print(PrintWriter out, String tab) { + out.print(tab + stringVal()); + } + + @Override + public String toString() { + return ""; + } + } + + /** + * + * CPX_AnnotElem + * + * AnnotElements that contain 2 cpx indices (ie. enums). + * + */ + public static class CPX2_AnnotValue extends AnnotValue { + + /** + * tag the descriptor for the constant + */ + public int cpx1; + public int cpx2; + + public CPX2_AnnotValue(AnnotElemType tag, ClassData cls, int cpx1, int cpx2) { + super(tag, cls); + this.cpx1 = cpx1; + this.cpx2 = cpx2; + } + + @Override + public String stringVal() { + StringBuilder sb = new StringBuilder(); + switch (tag) { + + case AE_ENUM: // Enum + // print the enum type and constant name + sb.append("enum " + cls.pool.decodeClassDescriptor(cpx1) + + " " + cls.pool.getName(cpx2)); + break; + default: + break; + } + return sb.toString(); + } + + @Override + public void print(PrintWriter out, String tab) { + out.print(tab + stringVal()); + } + + @Override + public String toString() { + return ""; + } + } + + /** + * + * Array_AnnotElem + * + * Annotation value that is an array of annotation elements. + * + */ + public static class Array_AnnotValue extends AnnotValue { + + /** + * tag the descriptor for the constant + */ + public ArrayList array = new ArrayList<>(); + + public Array_AnnotValue(AnnotElemType tagval, ClassData cls) { + super(tagval, cls); + } + + @Override + public String stringVal() { + StringBuilder sb = new StringBuilder(); + sb.append(super.stringVal() + " = "); + sb.append("{"); + int i = 0; + int cnt = array.size(); + for (AnnotValue arrayelem : array) { + sb.append(arrayelem.toString()); + if (i < cnt - 1) { + sb.append(","); + } + } + sb.append("}"); + return sb.toString(); + } + + public void add(AnnotValue elem) { + array.add(elem); + } + + @Override + public void print(PrintWriter out, String tab) { + out.println("{"); + int i = 0; + int cnt = array.size(); + for (AnnotValue arrayelem : array) { + arrayelem.print(out, tab + " "); + if (i < cnt - 1) { + out.println(","); + } + i += 1; + } + out.println("}"); + } + + @Override + public String toString() { + return ""; + } + } + + /** + * + * Annot_AnnotValue + * + * Annotation value that is a reference to an annotation. + * + */ + public static class Annot_AnnotValue extends AnnotValue { + + /** + * tag the descriptor for the constant + */ + AnnotationData annot; + + public Annot_AnnotValue(AnnotElemType tagval, ClassData cls, AnnotationData annot) { + super(tagval, cls); + this.annot = annot; + } + + @Override + public String stringVal() { + return annot.toString(); + } + + @Override + public void print(PrintWriter out, String tab) { +// out.print(tag + "\t"); + annot.print(out, tab); + } + + @Override + public String toString() { + return ""; + } + } + + /*========================================================*/ + /* Factory Method */ + /** + * + * read + * + * Static factory - creates Annotation Elements. + * + */ + public static AnnotValue readValue(DataInputStream in, ClassData cls, boolean invisible) throws IOException { + AnnotValue val = null; + char tg = (char) in.readByte(); + AnnotElemType tag = annotElemType(tg); + + switch (tag) { + case AE_STRING: // String + case AE_BYTE: // Byte + case AE_CHAR: // Char + case AE_INT: // Int (no need to add keyword) + case AE_SHORT: // Short + case AE_BOOLEAN: // Boolean + case AE_FLOAT: // Float + case AE_DOUBLE: // Double + case AE_LONG: // Long + case AE_CLASS: // Class + // CPX based Annotation + int CPX = in.readShort(); + val = new CPX_AnnotValue(tag, cls, CPX); + break; + case AE_ENUM: // Enum + // CPX2 based Annotation + int CPX1 = in.readShort(); + int CPX2 = in.readShort(); + val = new CPX2_AnnotValue(tag, cls, CPX1, CPX2); + break; + case AE_ANNOTATION: // Annotation + AnnotationData ad = new AnnotationData(invisible, cls); + ad.read(in); + val = new Annot_AnnotValue(tag, cls, ad); + break; + case AE_ARRAY: // Array + Array_AnnotValue aelem = new Array_AnnotValue(tag, cls); + val = aelem; + int cnt = in.readShort(); + for (int i = 0; i < cnt; i++) { + aelem.add(readValue(in, cls, invisible)); + } + break; + default: + throw new IOException("Unknown tag in annotation '" + tg + "' [" + Integer.toHexString(tg) + "]"); + } + + return val; + } + + /*========================================================*/ + + /*-------------------------------------------------------- */ + /* AnnotElem Fields */ + /** + * constant pool index for the name of the Annotation Element + */ + public int name_cpx; + + public AnnotValue value = null; + + // internal references + protected ClassData cls; + /*-------------------------------------------------------- */ + + public AnnotElem(ClassData cls) { + this.cls = cls; + } + + /** + * + * read + * + * read and resolve the method data called from ClassData. precondition: NumFields has + * already been read from the stream. + * + */ + public void read(DataInputStream in, boolean invisible) throws IOException { + name_cpx = in.readShort(); + TraceUtils.traceln(" AnnotElem: name[" + name_cpx + "]=" + cls.pool.getString(name_cpx)); + value = readValue(in, cls, invisible); + TraceUtils.traceln(" " + value.toString()); + } + + public String stringVal() { + return cls.pool.getName(name_cpx); + } + + public void print(PrintWriter out, String tab) { + out.print(stringVal() + " = "); + value.print(out, tab); + } + + @Override + public String toString() { + return ""; + } +} diff --git a/src/org/openjdk/asmtools/jdis/AnnotationData.java b/src/org/openjdk/asmtools/jdis/AnnotationData.java new file mode 100644 index 0000000..66d71be --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/AnnotationData.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * + */ +public class AnnotationData { + /*-------------------------------------------------------- */ + /* AnnotData Fields */ + + private boolean invisible = false; + private int type_cpx = 0; //an index into the constant pool indicating the annotation type for this annotation. + private ArrayList array = new ArrayList<>(); + + private ClassData cls; + protected String visAnnotToken = "@+"; + protected String invAnnotToken = "@-"; + protected String dataName = "AnnotationData"; + /*-------------------------------------------------------- */ + + public AnnotationData(boolean invisible, ClassData cls) { + this.cls = cls; + this.invisible = invisible; + } + + public void read(DataInputStream in) throws IOException { + type_cpx = in.readShort(); + TraceUtils.traceln(" " + dataName + ": name[" + type_cpx + "]=" + cls.pool.getString(type_cpx)); + int elemValueLength = in.readShort(); + TraceUtils.traceln(" " + dataName + ": " + cls.pool.getString(type_cpx) + "num_elems: " + elemValueLength); + for (int evc = 0; evc < elemValueLength; evc++) { + AnnotElem elem = new AnnotElem(cls); + TraceUtils.traceln(" " + dataName + ": " + cls.pool.getString(type_cpx) + " reading [" + evc + "]"); + elem.read(in, invisible); + array.add(elem); + } + } + + public void print(PrintWriter out, String tab) { + printHeader(out, tab); + printBody(out, tab); + } + + protected void printHeader(PrintWriter out, String tab) { + //Print annotation Header, which consists of the + // Annotation Token ('@'), visibility ('+', '-'), + // and the annotation name (type index, CPX). + + // Mark whether it is invisible or not. + if (invisible) { + out.print(tab + invAnnotToken); + } else { + out.print(tab + visAnnotToken); + } + String annoName = cls.pool.getString(type_cpx); + + // converts class type to java class name + if (annoName.startsWith("L") && annoName.endsWith(";")) { + annoName = annoName.substring(1, annoName.length() - 1); + } + + out.print(annoName); + } + + protected void printBody(PrintWriter out, String tab) { + // For a standard annotation, print out brackets, + // and list the name/value pairs. + out.print(" { "); + + int i = 0; + for (AnnotElem elem : array) { + elem.print(out, tab); + + if (i++ < array.size() - 1) { + out.print(", "); + } + } + out.print("}"); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + String annoName = cls.pool.getString(type_cpx); + + // converts class type to java class name + if (annoName.startsWith("L") && annoName.endsWith(";")) { + annoName = annoName.substring(1, annoName.length() - 1); + } + + //Print annotation + // Mark whether it is invisible or not. + if (invisible) { + sb.append(invAnnotToken); + } else { + sb.append(visAnnotToken); + } + + sb.append(annoName); + sb.append(" { "); + + int i = 0; + for (AnnotElem elem : array) { + sb.append(elem.toString()); + + if (i++ < array.size() - 1) { + sb.append(", "); + } + } + + _toString(sb); + + sb.append("}"); + return sb.toString(); + } + + protected void _toString(StringBuilder sb) { + // sub-classes override this + } +} // end AnnotData + diff --git a/src/org/openjdk/asmtools/jdis/AttrData.java b/src/org/openjdk/asmtools/jdis/AttrData.java new file mode 100644 index 0000000..b24dc01 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/AttrData.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.Tables; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * + */ +public class AttrData { + + /** + * + * attributeTag + * + * returns either -1 (not found), or the hashed integer tag key. + * + */ + public static int attributeTag(String tagname) { + int intgr = Tables.attrtagValue(tagname); + + if (intgr == 0) { + return -1; + } + + return intgr; + } + + + /*-------------------------------------------------------- */ + /* AttrData Fields */ + int name_cpx; + byte data[]; + ClassData cls; + /*-------------------------------------------------------- */ + + public AttrData(ClassData cls) { + this.cls = cls; + } + + public void read(int name_cpx, int attrlen, DataInputStream in) throws IOException { + this.name_cpx = name_cpx; + data = new byte[attrlen]; + TraceUtils.traceln(" AttrData:#" + name_cpx + " len=" + attrlen); + in.readFully(data); + } +} diff --git a/src/org/openjdk/asmtools/jdis/BootstrapMethodData.java b/src/org/openjdk/asmtools/jdis/BootstrapMethodData.java new file mode 100644 index 0000000..5fc609c --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/BootstrapMethodData.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * + */ +public class BootstrapMethodData { + /*-------------------------------------------------------- */ + /* BootstrapMethodData Fields */ + + int bsm_index; + ArrayList bsm_args_indexes; + + // internal references + private Options options = Options.OptionObject(); + private ClassData cls; + private PrintWriter out; + /*-------------------------------------------------------- */ + + public BootstrapMethodData(ClassData cls) { + this.cls = cls; + out = cls.out; + } + + + /*========================================================*/ + /* Read Methods */ + /** + * + * read + * + * read and resolve the bootstrap method data called from ClassData. precondition: + * NumFields has already been read from the stream. + * + */ + public void read(DataInputStream in) throws IOException { + // read the Methods CP indexes + bsm_index = in.readUnsignedShort(); + int arg_num = in.readUnsignedShort(); + bsm_args_indexes = new ArrayList<>(arg_num); + for (int i = 0; i < arg_num; i++) { + bsm_args_indexes.add(in.readUnsignedShort()); + } + } + + + /*========================================================*/ + /* Print Methods */ + public void print() throws IOException { + out.print("BootstrapMethod "); + out.print("#" + bsm_index); + for (int i = 0; i < bsm_args_indexes.size(); i++) { +// cls.PrintConstant(bsm_args_indexes.elementAt(i)); + out.print(" #" + bsm_args_indexes.get(i)); + } + out.println(";"); + } +} diff --git a/src/org/openjdk/asmtools/jdis/ClassData.java b/src/org/openjdk/asmtools/jdis/ClassData.java new file mode 100644 index 0000000..7dbd0dd --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/ClassData.java @@ -0,0 +1,509 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.asmutils.HexUtils; +import org.openjdk.asmtools.jasm.Modifiers; +import static org.openjdk.asmtools.jasm.RuntimeConstants.*; +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.*; +import java.util.ArrayList; + +/** + * Central class data for of the Java Disassembler + */ +public class ClassData extends MemberData { + + /*-------------------------------------------------------- */ + /* ClassData Fields */ + // ----------------------------- + // Header Info + // ----------------------------- + /** + * Version info + */ + protected int minor_version, + major_version; + + /** + * Constant Pool index to this class + */ + protected int this_cpx; + /** + * Constant Pool index to this classes parent (super) + */ + protected int super_cpx; + /** + * Constant Pool index to a file reference to the Java source + */ + protected int source_cpx = 0; + + /** + * The Constant Pool + */ + protected ConstantPool pool; + + /** + * The interfaces this class implements + */ + protected int[] interfaces; + + /** + * The fields of this class + */ + protected ArrayList fields; + + /** + * The methods of this class + */ + protected ArrayList methods; + + /** + * The inner-classes of this class + */ + protected ArrayList innerClasses; + + /** + * The bootstrapmethods this class implements + */ + protected ArrayList bootstrapMethods; + + // other parsing fields + protected PrintWriter out; + protected String pkgPrefix = ""; + private int pkgPrefixLen = 0; + private int length; //The number of elements in the buffer + private TextLines source = null; + private static final String initialTab = ""; //The number of elements in the buffer + + + /* -------------------------------------------------------- */ + /* ClassData Methods */ + public ClassData(PrintWriter out) { + this.out = out; + init(this); + memberType = "ClassData"; + TraceUtils.traceln("printOptions=" + options.toString()); + pool = new ConstantPool(this); + + } + + /*========================================================*/ + /* Read Methods */ + public void read(File in) throws IOException { + read(new DataInputStream(new FileInputStream(in))); + } + + public void read(String in) throws IOException { + read(new DataInputStream(new FileInputStream(in))); + } + + /** + * + * readFields + * + * read and resolve the field data + * + */ + protected void readFields(DataInputStream in) throws IOException { + int nfields = in.readUnsignedShort(); + TraceUtils.traceln("nfields=" + nfields); + fields = new ArrayList<>(nfields); + for (int k = 0; k < nfields; k++) { + FieldData field = new FieldData(this); + TraceUtils.traceln(" FieldData: #" + k); + field.read(in); + fields.add(field); + } + } + + /** + * + * readMethods + * + * read and resolve the method data + * + */ + protected void readMethods(DataInputStream in) throws IOException { + int nmethods = in.readUnsignedShort(); + TraceUtils.traceln("nmethods=" + nmethods); + methods = new ArrayList<>(nmethods); + for (int k = 0; k < nmethods; k++) { + MethodData method = new MethodData(this); + TraceUtils.traceln(" MethodData: #" + k); + method.read(in); + methods.add(method); + } + } + + /** + * + * readInterfaces + * + * read and resolve the interface data + * + */ + protected void readInterfaces(DataInputStream in) throws IOException { + // Read the interface names + int numinterfaces = in.readUnsignedShort(); + TraceUtils.traceln("numinterfaces=" + numinterfaces); + interfaces = new int[numinterfaces]; + for (int i = 0; i < numinterfaces; i++) { + int intrf_cpx = in.readShort(); + TraceUtils.traceln(" intrf_cpx[" + i + "]=" + intrf_cpx); + interfaces[i] = intrf_cpx; + } + } + + /** + * + * readAttributes + * + * read and resolve the attribute data + * + */ + @Override + protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException { + // Read the Attributes + boolean handled = true; + switch (attrtag) { + case ATT_SourceFile: + // Read SourceFile Attr + if (attrlen != 2) { + throw new ClassFormatError("invalid attr length"); + } + source_cpx = in.readUnsignedShort(); + break; + case ATT_InnerClasses: + // Read InnerClasses Attr + int num1 = in.readUnsignedShort(); + if (2 + num1 * 8 != attrlen) { + throw new ClassFormatError("invalid attr length"); + } + innerClasses = new ArrayList<>(num1); + for (int j = 0; j < num1; j++) { + InnerClassData innerClass = new InnerClassData(this); + innerClass.read(in); + innerClasses.add(innerClass); + } + break; + case ATT_BootstrapMethods: + // Read BootstrapMethods Attr + int num2 = in.readUnsignedShort(); + bootstrapMethods = new ArrayList<>(num2); + for (int j = 0; j < num2; j++) { + BootstrapMethodData bsmData = new BootstrapMethodData(this); + bsmData.read(in); + bootstrapMethods.add(bsmData); + } + break; + default: + handled = false; + break; + } + return handled; + } + + /** + * + * read + * + * read and resolve the class data + * + */ + public void read(DataInputStream in) throws IOException { + // Read the header + // ----------------------------------------------- + int magic = in.readInt(); + if (magic != JAVA_MAGIC) { + throw new ClassFormatError("wrong magic: " + HexUtils.toHex(magic) + ", expected " + HexUtils.toHex(JAVA_MAGIC)); + } + minor_version = in.readShort(); + major_version = in.readShort(); +// if (major_version != JAVA_VERSION) { +// throw new ClassFormatError("wrong version: " + major_version + ", expected " + JAVA_VERSION); +// } + + // Read the constant pool + // ----------------------------------------------- + pool.read(in); + access = in.readUnsignedShort(); // & MM_CLASS; // Q + this_cpx = in.readUnsignedShort(); + super_cpx = in.readUnsignedShort(); + TraceUtils.traceln("access=" + access + " " + Modifiers.accessString(access, CF_Context.CTX_INNERCLASS) + " this_cpx=" + this_cpx + " super_cpx=" + super_cpx); + + // Read the interfaces + // ----------------------------------------------- + readInterfaces(in); + + // Read the fields + // ----------------------------------------------- + readFields(in); + + // Read the methods + // ----------------------------------------------- + readMethods(in); + + // Read the attributes + readAttributes(in); + + TraceUtils.traceln(""); + TraceUtils.traceln("--------------------------------------------------------------------"); + TraceUtils.traceln(""); + } + + /*========================================================*/ + /** + * + * getSrcLine + * + * read and resolve the attribute data + * + */ + public String getSrcLine(int lnum) { + if (source == null) { + return null; // impossible call + } + String line; + try { + line = source.getLine(lnum); + } catch (ArrayIndexOutOfBoundsException e) { + line = "Line number " + lnum + " is out of bounds"; + } + return line; + } + + public void print() throws IOException { + int k, l; +// Write the header + String classname = pool.getClassName(this_cpx); + pkgPrefixLen = classname.lastIndexOf("/") + 1; + // package-info compilation unit + if (classname.endsWith("package-info")) { + // Print the Annotations + if (visibleAnnotations != null) { + for (AnnotationData visad : visibleAnnotations) { + visad.print(out, initialTab); + out.println(); + } + } + if (invisibleAnnotations != null) { + for (AnnotationData invisad : invisibleAnnotations) { + invisad.print(out, initialTab); + out.println(); + } + } + if (visibleTypeAnnotations != null) { + out.println(); + for (TypeAnnotationData visad : visibleTypeAnnotations) { + visad.print(out, initialTab); + out.println(); + } + } + if (invisibleTypeAnnotations != null) { + out.println(); + for (TypeAnnotationData invisad : invisibleTypeAnnotations) { + invisad.print(out, initialTab); + out.println(); + } + } + + if (pkgPrefixLen != 0) { + pkgPrefix = classname.substring(0, pkgPrefixLen); + out.print("package " + pkgPrefix.substring(0, pkgPrefixLen - 1) + " "); + out.print("version " + major_version + ":" + minor_version + ";"); + } + out.println(); + return; + } + + if (pkgPrefixLen != 0) { + pkgPrefix = classname.substring(0, pkgPrefixLen); + out.println("package " + pkgPrefix.substring(0, pkgPrefixLen - 1) + ";"); + classname = pool.getShortClassName(this_cpx, pkgPrefix); + } + out.println(); + + // Print the Annotations + if (visibleAnnotations != null) { + for (AnnotationData visad : visibleAnnotations) { + visad.print(out, initialTab); + out.println(); + } + } + if (invisibleAnnotations != null) { + for (AnnotationData invisad : invisibleAnnotations) { + invisad.print(out, initialTab); + out.println(); + } + } + if (visibleTypeAnnotations != null) { + out.println(); + for (TypeAnnotationData visad : visibleTypeAnnotations) { + visad.print(out, initialTab); + out.println(); + } + } + if (invisibleTypeAnnotations != null) { + out.println(); + for (TypeAnnotationData invisad : invisibleTypeAnnotations) { + invisad.print(out, initialTab); + out.println(); + } + } + + if ((access & ACC_SUPER) != 0) { + out.print("super "); + access = access & ~ACC_SUPER; + } + + // see if we are going to print: abstract interface class +// then replace it with just: interface +printHeader: + { +printSugar: + { + if ((access & ACC_ABSTRACT) == 0) { + break printSugar; + } + if ((access & ACC_INTERFACE) == 0) { + break printSugar; + } + if (options.contains(Options.PR.CPX)) { + break printSugar; + } + if (this_cpx == 0) { + break printSugar; + } + + // make sure the this_class is a valid class ref + ConstantPool.Constant this_const = pool.getConst(this_cpx); + if (this_const == null || this_const.tag != ConstantPool.TAG.CONSTANT_CLASS) { + break printSugar; + } + + // all conditions met, print syntactic sugar: + out.print(Modifiers.accessString(access & ~ACC_ABSTRACT, CF_Context.CTX_CLASS)); + if (isSynthetic) { + out.print("synthetic "); + } + if (isDeprecated) { + out.print("deprecated "); + } + out.print(" " + pool.getShortClassName(this_cpx, pkgPrefix)); + break printHeader; + } + + // not all conditions met, print header in ordinary way: + out.print(Modifiers.accessString(access, CF_Context.CTX_CLASS)); + if (isSynthetic) { + out.print("synthetic "); + } + if (isDeprecated) { + out.print("deprecated "); + } + if (options.contains(Options.PR.CPX)) { + out.print("\t#" + this_cpx + "; //"); + } + pool.PrintConstant(out, this_cpx); + } + out.println(); + + if (!pool.getClassName(super_cpx).equals("java/lang/Object")) { + out.print("\textends "); + pool.printlnClassId(out, super_cpx); + out.println(); + } + l = interfaces.length; + + if (l > 0) { + for (k = 0; k < l; k++) { + if (k == 0) { + out.print("\timplements "); + } else { + out.print("\t\t "); + } + boolean printComma = (l > 1 && k < (l - 1)); + pool.printlnClassId(out, interfaces[k], printComma); + out.println(); + } + } + out.println("\tversion " + major_version + ":" + minor_version); + out.println("{"); + + if ((options.contains(Options.PR.SRC)) && (source_cpx != 0)) { + String source_name = pool.getName(source_cpx); + out.println("\t// Compiled from " + source_name); + try { + source = new TextLines(source_name); + } catch (IOException e) { + } + } + out.println(); + + // Print the constant pool + if (options.contains(Options.PR.CP)) { + pool.print(out); + out.println(); + } + + // Print the fields + if (fields != null) { + for (FieldData curf : fields) { + curf.print(); + } + } + + // Print the methods + if (methods != null) { + for (MethodData curm : methods) { + boolean skipBlankLine = false; + curm.print(skipBlankLine); + } + out.println(); + } + + // Print the inner classes + if (innerClasses != null) { + for (InnerClassData icd : innerClasses) { + icd.print(); + } + out.println(); + } + + // Print the BootstrapMethods + // + // Only print these if printing extended constants + if ((options.contains(Options.PR.CPX)) && bootstrapMethods != null) { + for (BootstrapMethodData bsmdd : bootstrapMethods) { + bsmdd.print(); + } + out.println(); + } + + out.println("} // end Class " + classname); + + } // end ClassData.print() + +}// end class ClassData + diff --git a/src/org/openjdk/asmtools/jdis/CodeData.java b/src/org/openjdk/asmtools/jdis/CodeData.java new file mode 100644 index 0000000..9caafed --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/CodeData.java @@ -0,0 +1,693 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.OpcodeTables.*; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * + * Code data for a code attribute in method members in a class of the Java Disassembler + */ +public class CodeData { + + /*-------------------------------------------------------- */ + /* Code Data inner classes */ + class LineNumData { + + short start_pc, line_number; + + public LineNumData() { + } + + public LineNumData(DataInputStream in) throws IOException { + start_pc = in.readShort(); + line_number = in.readShort(); + //traceln(" line:"+start_pc+":="+line_number); + } + } + + public static class LocVarData { + + short start_pc, length, name_cpx, sig_cpx, slot; + + public LocVarData() { + } + + public LocVarData(DataInputStream in) throws IOException { + start_pc = in.readShort(); + length = in.readShort(); + name_cpx = in.readShort(); + sig_cpx = in.readShort(); + slot = in.readShort(); + //cls.traceln(" var #"+name_cpx+" start:"+start_pc + //+" length:"+length+"sig_cpx:"+sig_cpx+" sig_cpx:"+sig_cpx); + } + } + + + /*-------------------------------------------------------- */ + /* CodeData Fields */ + /** + * Raw byte array for the byte codes + */ + protected byte[] code; + + /** + * Limit for the stack size + */ + protected int max_stack; + /** + * Limit for the number of local vars + */ + protected int max_locals; + + /** + * (parsed) Trap table, describes exceptions caught + */ + private ArrayList trap_table = new ArrayList<>(0); // TrapData + + /** + * (parsed) Line Number table, describes source lines associated with ByteCode indexes + */ + private ArrayList lin_num_tb = new ArrayList<>(0); // LineNumData + + /** + * (parsed) Local Variable table, describes variable scopes associated with ByteCode + * indexes + */ + private ArrayList loc_var_tb = new ArrayList<>(0); // LocVarData + + /** + * (parsed) stack map table, describes compiler hints for stack rep, associated with + * ByteCode indexes + */ + private ArrayList stack_map = null; + + /** + * (parsed) reversed bytecode index hash, associates labels with ByteCode indexes + */ + private HashMap iattrs = new HashMap<>(); + + /** + * The remaining attributes of this class + */ + protected ArrayList attrs = new ArrayList<>(0); // AttrData + + // internal references + protected ClassData cls; + protected MethodData meth; + private PrintWriter out; + /*-------------------------------------------------------- */ + + public CodeData(MethodData meth) { + this.meth = meth; + this.cls = meth.cls; + this.out = cls.out; + } + + private int getbyte(int pc) { + return code[pc]; + } + + private int getUbyte(int pc) { + return code[pc] & 0xFF; + } + + private int getShort(int pc) { + return (code[pc] << 8) | (code[pc + 1] & 0xFF); + } + + private int getUShort(int pc) { + return ((code[pc] << 8) | (code[pc + 1] & 0xFF)) & 0xFFFF; + } + + private int getInt(int pc) { + return (getShort(pc) << 16) | (getShort(pc + 2) & 0xFFFF); + } + + private static int align(int n) { + return (n + 3) & ~3; + } + + protected iAtt get_iAtt(int pc) { + Integer PC = new Integer(pc); + iAtt res = iattrs.get(PC); + if (res == null) { + res = new iAtt(this); + iattrs.put(PC, res); + } + return res; + } + + + /*========================================================*/ + /* Read Methods */ + private void readLineNumTable(DataInputStream in) throws IOException { + int len = in.readInt(); // attr_length + int numlines = in.readUnsignedShort(); + lin_num_tb = new ArrayList<>(numlines); + TraceUtils.traceln(" CodeAttr: LineNumTable[" + numlines + "] len=" + len); + for (int l = 0; l < numlines; l++) { + lin_num_tb.add(new LineNumData(in)); + } + } + + private void readLocVarTable(DataInputStream in) throws IOException { + int len = in.readInt(); // attr_length + int numlines = in.readUnsignedShort(); + loc_var_tb = new ArrayList<>(numlines); + TraceUtils.traceln(" CodeAttr: LocalVariableTable[" + numlines + "] len=" + len); + for (int l = 0; l < numlines; l++) { + loc_var_tb.add(new LocVarData(in)); + } + } + + private void readTrapTable(DataInputStream in) throws IOException { + int trap_table_len = in.readUnsignedShort(); + TraceUtils.traceln(" CodeAttr: TrapTable[" + trap_table_len + "]"); + trap_table = new ArrayList<>(trap_table_len); + for (int l = 0; l < trap_table_len; l++) { + trap_table.add(new TrapData(in, l)); + } + } + + private void readStackMap(DataInputStream in) throws IOException { + int len = in.readInt(); // attr_length + int stack_map_len = in.readUnsignedShort(); + TraceUtils.traceln(" CodeAttr: Stack_Map: attrlen=" + len + " num=" + stack_map_len); + stack_map = new ArrayList<>(stack_map_len); + StackMapData.prevFramePC = 0; + for (int k = 0; k < stack_map_len; k++) { + stack_map.add(new StackMapData(this, in)); + } + } + + private void readStackMapTable(DataInputStream in) throws IOException { + int len = in.readInt(); // attr_length + int stack_map_len = in.readUnsignedShort(); + TraceUtils.traceln(" CodeAttr: Stack_Map_Table: attrlen=" + len + " num=" + stack_map_len); + stack_map = new ArrayList<>(stack_map_len); + StackMapData.prevFramePC = 0; + for (int k = 0; k < stack_map_len; k++) { + stack_map.add(new StackMapData(this, in, true)); + } + } + + /** + * + * read + * + * read and resolve the code attribute data called from MethodData. precondition: + * NumFields has already been read from the stream. + * + */ + public void read(DataInputStream in, int codeattrlen) throws IOException { + + // Read the code in the Code Attribute + max_stack = in.readUnsignedShort(); + max_locals = in.readUnsignedShort(); + int codelen = in.readInt(); + TraceUtils.traceln(" CodeAttr: Codelen=" + codelen + " fulllen=" + codeattrlen + " max_stack=" + max_stack + " max_locals=" + max_locals); + + // read the raw code bytes + code = new byte[codelen]; + in.read(code, 0, codelen); + + //read the trap table + readTrapTable(in); + + // Read any attributes of the Code Attribute + int nattr = in.readUnsignedShort(); + TraceUtils.traceln(" CodeAttr: add.attr:" + nattr); + for (int k = 0; k < nattr; k++) { + int name_cpx = in.readUnsignedShort(); + // verify the Attrs name + ConstantPool.Constant name_const = cls.pool.getConst(name_cpx); + if (name_const != null && name_const.tag == ConstantPool.TAG.CONSTANT_UTF8) { + String attrname = cls.pool.getString(name_cpx); + TraceUtils.traceln(" CodeAttr: attr: " + attrname); + // process the attr + AttrTag attrtag = attrtag(attrname); + switch (attrtag) { + case ATT_LineNumberTable: + readLineNumTable(in); + break; + case ATT_LocalVariableTable: + readLocVarTable(in); + break; + case ATT_StackMap: + readStackMap(in); + break; + case ATT_StackMapTable: + readStackMapTable(in); + break; + default: + AttrData attr = new AttrData(cls); + int attrlen = in.readInt(); // attr_length + attr.read(name_cpx, attrlen, in); + attrs.add(attr); + break; + } + } + } + } + + /*========================================================*/ + /* Code Resolution Methods */ + private int checkForLabelRef(int pc) { + // throws IOException { + int opc = getUbyte(pc); + Opcode opcode = opcode(opc); + switch (opcode) { + case opc_tableswitch: { + int tb = align(pc + 1); + int default_skip = getInt(tb); /* default skip pamount */ + + int low = getInt(tb + 4); + int high = getInt(tb + 8); + int count = high - low; + for (int i = 0; i <= count; i++) { + get_iAtt(pc + getInt(tb + 12 + 4 * i)).referred = true; + } + get_iAtt(default_skip + pc).referred = true; + return tb - pc + 16 + count * 4; + } + case opc_lookupswitch: { + int tb = align(pc + 1); + int default_skip = getInt(tb); /* default skip pamount */ + + int npairs = getInt(tb + 4); + for (int i = 1; i <= npairs; i++) { + get_iAtt(pc + getInt(tb + 4 + i * 8)).referred = true; + } + get_iAtt(default_skip + pc).referred = true; + return tb - pc + (npairs + 1) * 8; + } + case opc_jsr: + case opc_goto: + case opc_ifeq: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_iflt: + case opc_ifne: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_icmplt: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_ifnull: + case opc_ifnonnull: + get_iAtt(pc + getShort(pc + 1)).referred = true; + return 3; + case opc_jsr_w: + case opc_goto_w: + get_iAtt(pc + getInt(pc + 1)).referred = true; + return 5; + case opc_wide: + case opc_nonpriv: + case opc_priv: + int opc2 = (opcode.value() << 8) + getUbyte(pc + 1); + opcode = opcode(opc2); + } + try { + int opclen = opcode.length(); + return opclen == 0 ? 1 : opclen; // bugfix for 4614404 + } catch (ArrayIndexOutOfBoundsException e) { + return 1; + } + } // end checkForLabelRef + + private void loadLabelTable() { + for (int pc = 0; pc < code.length;) { + pc = pc + checkForLabelRef(pc); + } + } + + private void loadLineNumTable() { + for (LineNumData entry : lin_num_tb) { + get_iAtt(entry.start_pc).lnum = entry.line_number; + } + } + + private void loadStackMap() { + for (StackMapData entry : stack_map) { + get_iAtt(entry.start_pc).stackMapEntry = entry; + } + } + + private void loadLocVarTable() throws IOException { + for (LocVarData entry : loc_var_tb) { + get_iAtt(entry.start_pc).add_var(entry); + get_iAtt(entry.start_pc + entry.length).add_endvar(entry); + } + } + + private void loadTrapTable() { + for (TrapData entry : trap_table) { + get_iAtt(entry.start_pc).add_trap(entry); + get_iAtt(entry.end_pc).add_endtrap(entry); + get_iAtt(entry.handler_pc).add_handler(entry); + } + } + + + /*========================================================*/ + /* Print Methods */ + private void PrintConstant(int cpx) { + out.print("\t"); + cls.pool.PrintConstant(out, cpx); + } + + private int printInstr(int pc) throws IOException { + boolean pr_cpx = meth.options.contains(Options.PR.CPX); + int opc = getUbyte(pc); + int opc2; + Opcode opcode = opcode(opc); + Opcode opcode2; + String mnem; + switch (opcode) { + case opc_nonpriv: + case opc_priv: + opc2 = getUbyte(pc + 1); + int finalopc = (opc << 8) + opc2; + opcode2 = opcode(finalopc); + if (opcode2 == null) { +// assume all (even nonexistent) priv and nonpriv instructions +// are 2 bytes long + mnem = opcode.parsekey() + " " + opc2; + } else { + mnem = opcode2.parsekey(); + } + out.print(mnem); + return 2; + case opc_wide: { + opc2 = getUbyte(pc + 1); + int finalopcwide = (opc << 8) + opc2; + opcode2 = opcode(finalopcwide); + if (opcode2 == null) { +// nonexistent opcode - but we have to print something + out.print("bytecode " + opcode); + return 1; + } else { + mnem = opcode2.parsekey(); + } + out.print(mnem + " " + getUShort(pc + 2)); + if (opcode2 == Opcode.opc_iinc) { + out.print(", " + getShort(pc + 4)); + return 6; + } + return 4; + } + } + mnem = opcode.parsekey(); + if (mnem == null) { +// nonexistent opcode - but we have to print something + out.print("bytecode " + opcode); + return 1; + } + if (opcode.value() > Opcode.opc_jsr_w.value()) { +// pseudo opcodes should be printed as bytecodes + out.print("bytecode " + opcode); + return 1; + } + out.print(opcode.parsekey()); +// TraceUtils.traceln("****** [CodeData.printInstr]: got an '" + opcode.parsekey() + "' [" + opc + "] instruction ****** "); + switch (opcode) { + case opc_aload: + case opc_astore: + case opc_fload: + case opc_fstore: + case opc_iload: + case opc_istore: + case opc_lload: + case opc_lstore: + case opc_dload: + case opc_dstore: + case opc_ret: + out.print("\t" + getUbyte(pc + 1)); + return 2; + case opc_iinc: + out.print("\t" + getUbyte(pc + 1) + ", " + getbyte(pc + 2)); + return 3; + case opc_tableswitch: { + int tb = align(pc + 1); + int default_skip = getInt(tb); /* default skip pamount */ + + int low = getInt(tb + 4); + int high = getInt(tb + 8); + int count = high - low; + out.print("{ //" + low + " to " + high); + for (int i = 0; i <= count; i++) { + out.print("\n\t\t" + (i + low) + ": " + meth.lP + (pc + getInt(tb + 12 + 4 * i)) + ";"); + } + out.print("\n\t\tdefault: " + meth.lP + (default_skip + pc) + " }"); + return tb - pc + 16 + count * 4; + } + case opc_lookupswitch: { + int tb = align(pc + 1); + int default_skip = getInt(tb); + int npairs = getInt(tb + 4); + out.print("{ //" + npairs); + for (int i = 1; i <= npairs; i++) { + out.print("\n\t\t" + getInt(tb + i * 8) + ": " + meth.lP + (pc + getInt(tb + 4 + i * 8)) + ";"); + } + out.print("\n\t\tdefault: " + meth.lP + (default_skip + pc) + " }"); + return tb - pc + (npairs + 1) * 8; + } + case opc_newarray: + int tp = getUbyte(pc + 1); + BasicType type = basictype(tp); + switch (type) { + case T_BOOLEAN: + out.print(" boolean"); + break; + case T_BYTE: + out.print(" byte"); + break; + case T_CHAR: + out.print(" char"); + break; + case T_SHORT: + out.print(" short"); + break; + case T_INT: + out.print(" int"); + break; + case T_LONG: + out.print(" long"); + break; + case T_FLOAT: + out.print(" float"); + break; + case T_DOUBLE: + out.print(" double"); + break; + case T_CLASS: + out.print(" class"); + break; + default: + out.print(" BOGUS TYPE:" + type); + } + return 2; + case opc_anewarray: { + int index = getUShort(pc + 1); + if (pr_cpx) { + out.print("\t#" + index + "; //"); + } + PrintConstant(index); + return 3; + } + case opc_sipush: + out.print("\t" + getShort(pc + 1)); + return 3; + case opc_bipush: + out.print("\t" + getbyte(pc + 1)); + return 2; + case opc_ldc: { + int index = getUbyte(pc + 1); + if (pr_cpx) { + out.print("\t#" + index + "; //"); + } + PrintConstant(index); + return 2; + } + case opc_ldc_w: + case opc_ldc2_w: + case opc_instanceof: + case opc_checkcast: + case opc_new: + case opc_putstatic: + case opc_getstatic: + case opc_putfield: + case opc_getfield: + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: { + int index = getUShort(pc + 1); + if (pr_cpx) { + out.print("\t#" + index + "; //"); + } + PrintConstant(index); + return 3; + } + case opc_invokeinterface: { + int index = getUShort(pc + 1), nargs = getUbyte(pc + 3); +// getUbyte(pc + 4); // reserved byte + if (pr_cpx) { + out.print("\t#" + index + ", " + nargs + "; //"); + PrintConstant(index); + } else { + PrintConstant(index); + out.print(", " + nargs); // args count + } + return 5; + } + case opc_invokedynamic: { // JSR-292 + int index = getUShort(pc + 1); + // getUbyte(pc + 3); // reserved byte + // getUbyte(pc + 4); // reserved byte + if (pr_cpx) { + out.print("\t#" + index + "; //"); + PrintConstant(index); + } else { + PrintConstant(index); + } + return 5; + } + case opc_multianewarray: { + int index = getUShort(pc + 1), dimensions = getUbyte(pc + 3); + if (pr_cpx) { + out.print("\t#" + index + ", " + dimensions + "; //"); + PrintConstant(index); + } else { + PrintConstant(index); + out.print(", " + dimensions); // dimensions count + } + return 4; + } + case opc_jsr: + case opc_goto: + case opc_ifeq: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_iflt: + case opc_ifne: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_icmplt: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_ifnull: + case opc_ifnonnull: + out.print("\t" + meth.lP + (pc + getShort(pc + 1))); + return 3; + case opc_jsr_w: + case opc_goto_w: + out.print("\t" + meth.lP + (pc + getInt(pc + 1))); + return 5; + default: + return 1; + } + } // end printInstr + + /** + * + * print + * + * prints the code data to the current output stream. called from MethodData. + * + */ + public void print() throws IOException { + if (!lin_num_tb.isEmpty()) { + loadLineNumTable(); + } + if (stack_map != null) { + loadStackMap(); + } + if (!meth.options.contains(Options.PR.PC)) { + loadLabelTable(); + } + loadTrapTable(); + if (!loc_var_tb.isEmpty()) { + loadLocVarTable(); + } + + out.println(); + out.println("\tstack " + max_stack + " locals " + max_locals); + + // Need to print ParamAnnotations here. + meth.printPAnnotations(); + + out.println("{"); + + iAtt iatt = iattrs.get(new Integer(0)); + for (int pc = 0; pc < code.length;) { + if (iatt != null) { + iatt.printBegins(); // equ. print("\t"); + } else { + out.print("\t"); + } + if (meth.options.contains(Options.PR.PC)) { + out.print(pc + ":\t"); + } else if ((iatt != null) && iatt.referred) { + out.print(meth.lP + pc + ":\t"); + } else { + out.print("\t"); + } + if (iatt != null) { + iatt.printStackMap(); + } + pc = pc + printInstr(pc); + out.println(";"); + iatt = iattrs.get(new Integer(pc)); + if (iatt != null) { + iatt.printEnds(); + } + } + // the right brace can be labelled: + if (iatt != null) { + iatt.printBegins(); // equ. print("\t"); + if (iatt.referred) { + out.print(meth.lP + code.length + ":\t"); + } + iatt.printStackMap(); + out.println(); + } + out.println("}"); + } + +} diff --git a/src/org/openjdk/asmtools/jdis/ConstantPool.java b/src/org/openjdk/asmtools/jdis/ConstantPool.java new file mode 100644 index 0000000..79bc0c7 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/ConstantPool.java @@ -0,0 +1,1007 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.asmutils.HexUtils; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Hashtable; + +/** + * + * ConstantPool + * + * Class representing the ConstantPool and inner-classes representing the Constants and + * their tags. + */ +public class ConstantPool { + + private static final Hashtable taghash = new Hashtable<>(); + private static final Hashtable subtaghash = new Hashtable<>(); + + + /*-------------------------------------------------------- */ + /* ConstantPool Inner Classes */ + /** + * TAG + * + * A Tag descriptor of constants in the constant pool + * + */ + static public enum TAG { + CONSTANT_UTF8 ((byte) 1, "Asciz", "CONSTANT_UTF8"), + CONSTANT_UNICODE ((byte) 2, "unicorn", "CONSTANT_UNICODE"), + CONSTANT_INTEGER ((byte) 3, "int", "CONSTANT_INTEGER"), + CONSTANT_FLOAT ((byte) 4, "float", "CONSTANT_FLOAT"), + CONSTANT_LONG ((byte) 5, "long", "CONSTANT_LONG"), + CONSTANT_DOUBLE ((byte) 6, "double", "CONSTANT_DOUBLE"), + CONSTANT_CLASS ((byte) 7, "class", "CONSTANT_CLASS"), + CONSTANT_STRING ((byte) 8, "String", "CONSTANT_STRING"), + CONSTANT_FIELD ((byte) 9, "Field", "CONSTANT_FIELD"), + CONSTANT_METHOD ((byte) 10, "Method", "CONSTANT_METHOD"), + CONSTANT_INTERFACEMETHOD ((byte) 11, "InterfaceMethod", "CONSTANT_INTERFACEMETHOD"), + CONSTANT_NAMEANDTYPE ((byte) 12, "NameAndType", "CONSTANT_NAMEANDTYPE"), + CONSTANT_METHODHANDLE ((byte) 15, "MethodHandle", "CONSTANT_METHODHANDLE"), + CONSTANT_METHODTYPE ((byte) 16, "MethodType", "CONSTANT_METHODTYPE"), +// CONSTANT_INVOKEDYNAMIC_TRANS ((byte) 17, "InvokeDynamicTrans", "method reciever"), + CONSTANT_INVOKEDYNAMIC ((byte) 18, "InvokeDynamic", "CONSTANT_INVOKEDYNAMIC"); + + private final Byte value; + private final String tagname; + private final String printval; + + TAG(byte val, String tgname, String print) { + value = val; + tagname = tgname; + printval = print; + // taghash.put(new Byte(val), this); + } + + public byte value() { + return value; + } + + public String tagname() { + return tagname; + } + + public String description() { + return printval; + } + + @Override + public String toString() { + return "<" + tagname + "> "; + } + }; + + + /** + * SUBTAG + * + * A Tag descriptor of form method-handle constants + * + */ + static public enum SUBTAG { + REF_GETFIELD ((byte) 1, "REF_getField", "REF_GETFIELD"), + REF_GETSTATIC ((byte) 2, "REF_getStatic", "REF_GETSTATIC"), + REF_PUTFIELD ((byte) 3, "REF_putField", "REF_PUTFIELD"), + REF_PUTSTATIC ((byte) 4, "REF_putStatic", "REF_PUTSTATIC"), + REF_INVOKEVIRTUAL ((byte) 5, "REF_invokeVirtual", "REF_INVOKEVIRTUAL"), + REF_INVOKESTATIC ((byte) 6, "REF_invokeStatic", "REF_INVOKESTATIC"), + REF_INVOKESPECIAL ((byte) 7, "REF_invokeSpecial", "REF_INVOKESPECIAL"), + REF_NEWINVOKESPECIAL ((byte) 8, "REF_newInvokeSpecial", "REF_NEWINVOKESPECIAL"), + REF_INVOKEINTERFACE ((byte) 9, "REF_invokeInterface", "REF_INVOKEINTERFACE"); + + private final Byte value; + private final String tagname; + private final String printval; + + SUBTAG(byte val, String tgname, String print) { + value = val; + tagname = tgname; + printval = print; +// subtaghash.put(new Byte(val), this); + } + + public byte value() { + return value; + } + + public String tagname() { + return tagname; + } + + public String description() { + return printval; + } + + @Override + public String toString() { + return "<" + tagname + "> "; + } + }; + + static { + + // Class initializer Code + // + // Make sure all of the tags get initialized before being used. + taghash.put(new Byte(TAG.CONSTANT_UTF8.value()), TAG.CONSTANT_UTF8); + taghash.put(new Byte(TAG.CONSTANT_UNICODE.value()), TAG.CONSTANT_UNICODE); + taghash.put(new Byte(TAG.CONSTANT_INTEGER.value()), TAG.CONSTANT_INTEGER); + taghash.put(new Byte(TAG.CONSTANT_FLOAT.value()), TAG.CONSTANT_FLOAT); + taghash.put(new Byte(TAG.CONSTANT_LONG.value()), TAG.CONSTANT_LONG); + taghash.put(new Byte(TAG.CONSTANT_DOUBLE.value()), TAG.CONSTANT_DOUBLE); + taghash.put(new Byte(TAG.CONSTANT_CLASS.value()), TAG.CONSTANT_CLASS); + taghash.put(new Byte(TAG.CONSTANT_STRING.value()), TAG.CONSTANT_STRING); + taghash.put(new Byte(TAG.CONSTANT_FIELD.value()), TAG.CONSTANT_FIELD); + taghash.put(new Byte(TAG.CONSTANT_METHOD.value()), TAG.CONSTANT_METHOD); + taghash.put(new Byte(TAG.CONSTANT_INTERFACEMETHOD.value()), TAG.CONSTANT_INTERFACEMETHOD); + taghash.put(new Byte(TAG.CONSTANT_NAMEANDTYPE.value()), TAG.CONSTANT_NAMEANDTYPE); + taghash.put(new Byte(TAG.CONSTANT_METHODHANDLE.value()), TAG.CONSTANT_METHODHANDLE); + taghash.put(new Byte(TAG.CONSTANT_METHODTYPE.value()), TAG.CONSTANT_METHODTYPE); +// taghash.put(new Byte(TAG.CONSTANT_INVOKEDYNAMIC_TRANS.value()), TAG.CONSTANT_INVOKEDYNAMIC_TRANS); + taghash.put(new Byte(TAG.CONSTANT_INVOKEDYNAMIC.value()), TAG.CONSTANT_INVOKEDYNAMIC); + + subtaghash.put(new Byte(SUBTAG.REF_GETFIELD.value()), SUBTAG.REF_GETFIELD); + subtaghash.put(new Byte(SUBTAG.REF_GETSTATIC.value()), SUBTAG.REF_GETSTATIC); + subtaghash.put(new Byte(SUBTAG.REF_PUTFIELD.value()), SUBTAG.REF_PUTFIELD); + subtaghash.put(new Byte(SUBTAG.REF_PUTSTATIC.value()), SUBTAG.REF_PUTSTATIC); + subtaghash.put(new Byte(SUBTAG.REF_INVOKEVIRTUAL.value()), SUBTAG.REF_INVOKEVIRTUAL); + subtaghash.put(new Byte(SUBTAG.REF_INVOKESTATIC.value()), SUBTAG.REF_INVOKESTATIC); + subtaghash.put(new Byte(SUBTAG.REF_INVOKESPECIAL.value()), SUBTAG.REF_INVOKESPECIAL); + subtaghash.put(new Byte(SUBTAG.REF_NEWINVOKESPECIAL.value()), SUBTAG.REF_NEWINVOKESPECIAL); + subtaghash.put(new Byte(SUBTAG.REF_INVOKEINTERFACE.value()), SUBTAG.REF_INVOKEINTERFACE); + + } + + /** + * + * Constant + * + * Base class of all constant entries + * + */ + public class Constant { + + /** + * tag the descriptor for the constant + */ + public TAG tag; + + public Constant(TAG tagval) { + tag = tagval; + } + + public String stringVal() { + return ""; + } + + public void print(PrintWriter out) { + out.print(tag.tagname + "\t"); + } + + public int size() { + return 1; + } + + @Override + public String toString() { + return ""; + } + } + + /* -------------------------------------------------------- */ + /* Constant Sub-classes */ + /** + * + * CP_Str + * + * Constant entries that contain String data. usually is a CONSTANT_UTF8 + * + */ + class CP_Str extends Constant { + + String value; + + CP_Str(TAG tagval, String str) { + super(tagval); + this.value = str; + } + + @Override + public String stringVal() { + StringBuilder sb = new StringBuilder(); + String s = value; + sb.append('\"'); + for (int k = 0; k < s.length(); k++) { + char c = s.charAt(k); + switch (c) { + case '\t': + sb.append('\\').append('t'); + break; + case '\n': + sb.append('\\').append('n'); + break; + case '\r': + sb.append('\\').append('r'); + break; + case '\b': + sb.append('\\').append('b'); + break; + case '\f': + sb.append('\\').append('f'); + break; + case '\"': + sb.append('\\').append('\"'); + break; + case '\'': + sb.append('\\').append('\''); + break; + case '\\': + sb.append('\\').append('\\'); + break; + default: + sb.append(c); + } + } + return sb.append('\"').toString(); + } + + @Override + public void print(PrintWriter out) { + super.print(out); + out.println(stringVal() + ";"); + } + } + + /** + * + * CP_Int + * + * Constant entries that contain Integer data. usually is a CONSTANT_INTEGER + * + */ + class CP_Int extends Constant { + + Integer value; + + CP_Int(TAG tagval, int intval) { + super(tagval); + this.value = new Integer(intval); + } + + @Override + public String stringVal() { + if (cd.options.contains(Options.PR.HEX)) { + return HexUtils.toHex(value.intValue()); + } + return value.toString(); + } + + @Override + public void print(PrintWriter out) { + super.print(out); + out.println(stringVal() + ";"); + } + } + + /** + * + * CP_Long + * + * Constant entries that contain LongInteger data. usually is a CONSTANT_LONG + * + * These take up 2 slots in the constant pool. + * + */ + class CP_Long extends Constant { + + Long value; + + CP_Long(TAG tagval, long intval) { + super(tagval); + this.value = new Long(intval); + } + + @Override + public String stringVal() { + if (cd.options.contains(Options.PR.HEX)) { + return HexUtils.toHex(value.longValue()) + 'l'; + } + return value.toString() + 'l'; + } + + @Override + public void print(PrintWriter out) { + super.print(out); + out.println(stringVal() + ";"); + } + + @Override + public int size() { + return 2; + } + } + + /** + * + * CP_Float + * + * Constant entries that contain Float data. usually is a CONSTANT_FLOAT + * + */ + class CP_Float extends Constant { + + Float value; + + CP_Float(TAG tagval, float fltvl) { + super(tagval); + this.value = new Float(fltvl); + } + + @Override + public String stringVal() { + if (cd.options.contains(Options.PR.HEX)) { + return "bits " + HexUtils.toHex(Float.floatToIntBits(value.floatValue())); + } + String sf = (value).toString(); + if (value.isNaN() || value.isInfinite()) { + return sf; + } + return sf + "f"; + } + + @Override + public void print(PrintWriter out) { + super.print(out); + out.println(stringVal() + ";"); + } + } + + /** + * + * CP_Double + * + * Constant entries that contain double-precision float data. usually is a + * CONSTANT_DOUBLE + * + * These take up 2 slots in the constant pool. + * + */ + class CP_Double extends Constant { + + Double value; + + CP_Double(TAG tagval, double fltvl) { + super(tagval); + this.value = new Double(fltvl); + } + + @Override + public String stringVal() { + if (cd.options.contains(Options.PR.HEX)) { + return "bits " + HexUtils.toHex(Double.doubleToLongBits(value.doubleValue())) + 'l'; + } + String sd = value.toString(); + if (value.isNaN() || value.isInfinite()) { + return sd; + } + return sd + "d"; + } + + @Override + public void print(PrintWriter out) { + super.print(out); + out.println(stringVal() + ";"); + } + + @Override + public int size() { + return 2; + } + } + + /** + * + * CPX + * + * Constant entries that contain a single constant-pool index. Usually, this includes: + * CONSTANT_CLASS CONSTANT_METHODTYPE CONSTANT_STRING + * + */ + class CPX extends Constant { + + int value; + + CPX(TAG tagval, int cpx) { + super(tagval); + this.value = cpx; + } + + @Override + public String stringVal() { + + String str = "UnknownTag"; + switch (tag) { + case CONSTANT_CLASS: + str = getShortClassName(getClassName(this), cd.pkgPrefix); + break; + case CONSTANT_METHODTYPE: + case CONSTANT_STRING: + str = StringValue(value); + break; + default: + break; + } + return str; + } + + @Override + public void print(PrintWriter out) { + super.print(out); + switch (tag) { + case CONSTANT_CLASS: + case CONSTANT_STRING: + case CONSTANT_METHODTYPE: + out.println("#" + (value) + ";\t// " + stringVal()); + break; + } + } + } + + /** + * + * CPX2 + * + * Constant entries that contain two constant-pool indices. Usually, this includes: + * CONSTANT_FIELD CONSTANT_METHOD CONSTANT_INTERFACEMETHOD CONSTANT_NAMEANDTYPE + * CONSTANT_METHODHANDLE CONSTANT_INVOKEDYNAMIC + * + */ + class CPX2 extends Constant { + + int value1, value2; + + CPX2(TAG tagval, int cpx1, int cpx2) { + super(tagval); + this.value1 = cpx1; + this.value2 = cpx2; + } + + @Override + public String stringVal() { + + String str = "UnknownTag"; + switch (tag) { + case CONSTANT_FIELD: + case CONSTANT_METHOD: + case CONSTANT_INTERFACEMETHOD: + str = getShortClassName(getClassName(value1), cd.pkgPrefix) + "." + StringValue(value2); + break; + case CONSTANT_NAMEANDTYPE: + str = getName(value1) + ":" + StringValue(value2); + break; + case CONSTANT_METHODHANDLE: + str = subtagToString(value1) + ":" + StringValue(value2); + break; + // Removed INVOKEDYNAMIC_TRANS +// case: CONSTANT_INVOKEDYNAMIC_TRANS: +// str = StringValue(value1) + ":" + StringValue(value2); +// break; + case CONSTANT_INVOKEDYNAMIC: + int bsm_attr_idx = value1; + int nape_idx = value2; + BootstrapMethodData bsmData; + try { + bsmData = cd.bootstrapMethods.get(bsm_attr_idx); + } catch (NullPointerException npe) { + return ""; + } catch (IndexOutOfBoundsException ioob) { + return ""; + } + + int bsm_ref = bsmData.bsm_index; + + StringBuilder bsm_args_str = new StringBuilder(); + int bsm_args_len = bsmData.bsm_args_indexes.size(); + for (int i = 0; i < bsm_args_len; i++) { + int bsm_arg_idx = bsmData.bsm_args_indexes.get(i); + bsm_args_str.append(" ") + .append(ConstantStrValue(bsm_arg_idx)); + if (i + 1 < bsm_args_len) { + bsm_args_str.append(","); + } + } + + str = StringValue(bsm_ref) + ":" + StringValue(nape_idx) + + bsm_args_str.toString(); + break; + default: + break; + } + + return str; + } + + @Override + public void print(PrintWriter out) { + super.print(out); + switch (tag) { + case CONSTANT_FIELD: + case CONSTANT_METHOD: + case CONSTANT_INTERFACEMETHOD: + out.println("#" + value1 + ".#" + value2 + ";\t// " + stringVal()); + break; + case CONSTANT_METHODHANDLE: + out.println(value1 + ":#" + value2 + ";\t// " + stringVal()); + break; + case CONSTANT_NAMEANDTYPE: + // case CONSTANT_INVOKEDYNAMIC_TRANS: + out.println("#" + value1 + ":#" + value2 + ";\t// " + stringVal()); + break; + case CONSTANT_INVOKEDYNAMIC: + out.println(value1 + ":#" + value2 + ";\t// " + stringVal()); + break; + default: + break; + } + } + } + + /* -------------------------------------------------------- */ + /* ConstantPool Fields */ + /** + * The actual pool of Constants + */ + public ArrayList pool; + /** + * Reference to the class data + */ + private ClassData cd; + + + /* -------------------------------------------------------- */ + /* ConstantPool Methods */ + + /* ConstantPool Constructors */ + public ConstantPool(ClassData cd) { + pool = null; + this.cd = cd; + } + + public ConstantPool(ClassData cd, int size) { + pool = new ArrayList<>(size); + this.cd = cd; + } + + /** + * + * read + * + * decodes a ConstantPool and it's constants from a data stream. + * + */ + void read(DataInputStream in) throws IOException { + int length = in.readUnsignedShort(); + pool = new ArrayList<>(length); + pool.add(0, null); + TraceUtils.traceln("CP len=" + length); + for (int i = 1; i < length; i++) { + byte tag = in.readByte(); + TAG tagobj = taghash.get(tag); + TraceUtils.traceln("CP entry #" + i + " + tagindex=" + tag + " tag=" + tagobj); + switch (tagobj) { + case CONSTANT_UTF8: + pool.add(i, new CP_Str(tagobj, in.readUTF())); + break; + case CONSTANT_INTEGER: + pool.add(i, new CP_Int(tagobj, in.readInt())); + break; + case CONSTANT_LONG: + pool.add(i, new CP_Long(tagobj, in.readLong())); + // handle null entry to account for Longs taking up 2 CP slots + i += 1; + pool.add(null); + break; + case CONSTANT_FLOAT: + pool.add(i, new CP_Float(tagobj, in.readFloat())); + break; + case CONSTANT_DOUBLE: + pool.add(i, new CP_Double(tagobj, in.readDouble())); + // handle null entry to account for Doubles taking up 2 CP slots + i += 1; + pool.add(null); + break; + case CONSTANT_CLASS: + case CONSTANT_STRING: + case CONSTANT_METHODTYPE: + pool.add(i, new CPX(tagobj, in.readUnsignedShort())); + break; + case CONSTANT_FIELD: + case CONSTANT_METHOD: + case CONSTANT_INTERFACEMETHOD: + case CONSTANT_NAMEANDTYPE: + case CONSTANT_INVOKEDYNAMIC: +// case CONSTANT_INVOKEDYNAMIC_TRANS: + pool.add(i, new CPX2(tagobj, in.readUnsignedShort(), in.readUnsignedShort())); + break; + case CONSTANT_METHODHANDLE: + pool.add(i, new CPX2(tagobj, in.readUnsignedByte(), in.readUnsignedShort())); + break; + + default: + throw new ClassFormatError("invalid constant type: " + (int) tag); + } + } + } + + /** + * + * inbounds + * + * bounds-check a CP index. + * + */ + private boolean inbounds(int cpx) { + return !(cpx == 0 || cpx >= pool.size()); + } + + /** + * + * getConst + * + * Public getter - Safely gets a Constant from the CP at a given index. + * + */ + public Constant getConst(int cpx) { + if (inbounds(cpx)) { + return pool.get(cpx); + } else { + return null; + } + } + + /** + * + * StringTag + * + * Public string val - Safely gets the string-rep of a Constant from the CP at a given + * index. + * + */ + public String StringTag(int cpx) { + String str = "Incorrect CP index:" + cpx; + if (inbounds(cpx)) { + Constant cns = pool.get(cpx); + if (cns != null) { + str = cns.tag.tagname; + } + } + return str; + } + + /** + * + * getString + * + * Public string val - Safely gets the string-rep of a ConstantUTF8 from the CP at a + * given index. + * + * Returns either null (if invalid), or the string value of the UTF8 + * + */ + public String getString(int cpx) { + String str = null; + if (inbounds(cpx)) { + Constant cns = pool.get(cpx); + if (cns != null && cns.tag == TAG.CONSTANT_UTF8) { + CP_Str cns1 = (CP_Str) cns; + str = cns1.value; + } + } + return str; + } + + /** + * + * getName + * + * Safely gets a Java name from a ConstantUTF8 from the CP at a given index. + * + * Returns either null (if invalid), or the Java name value of the UTF8 + * + */ + public String getName(int cpx) { + String str = getString(cpx); + if (str == null) { + return ""; + } + + return Utils.javaName(str); + } + + /** + * + * getClassName + * + * Safely gets a Java class name from a ConstantClass from the CP at a given index. + * + * Returns either the Java class name, or a CP index reference string. + * + */ + public String getClassName(int cpx) { + String res = "#" + cpx; + if (cpx == 0) { + return res; + } + if (!inbounds(cpx)) { + return res; + } + Constant cns = pool.get(cpx); + if (cns == null || cns.tag != TAG.CONSTANT_CLASS) { + return res; + } + + return getClassName((CPX) cns); + } + + /** + * + * getClassName + * + * Safely gets a Java class name from a ConstantClass from a CPX2 constant pool + * object. (eg. Method/Field/Interface Ref) + * + * Returns either the Java class name, or a CP index reference string. + * + */ + public String getClassName(CPX2 classConst) { + return _getClassName(classConst.value1); + } + + /** + * + * getClassName + * + * Safely gets a Java class name from a ConstantClass from a CPX constant pool object. + * (eg. Class Ref) + * + * Returns either the Java class name, or a CP index reference string. + * + */ + public String getClassName(CPX classConst) { + return _getClassName(classConst.value); + } + + /** + * + * _getClassName + * + * Helper for getting class name. Checks bounds, does name conversion. + * + */ + private String _getClassName(int nameIndex) { + String res = "#" + nameIndex; + if (!inbounds(nameIndex)) { + return res; + } + Constant nameconst = pool.get(nameIndex); + if (nameconst == null || nameconst.tag != TAG.CONSTANT_UTF8) { + return res; + } + CP_Str name = (CP_Str) nameconst; + + String classname = name.value; + + if (Utils.isClassArrayDescriptor(classname)) { + classname = "\"" + classname + "\""; + } + return classname; + } + + /** + * + * getShortClassName + * + * shortens a class name (if the class is in the given package). works with a + * string-encoded classname. + * + */ + public String getShortClassName(String className, String pkgPrefix) { + if (className.startsWith(pkgPrefix)) { + return className.substring(pkgPrefix.length()); + } + return className; + } + + /** + * + * getShortClassName + * + * shortens a class name (if the class is in the given package). works with a CP index + * to a ConstantClasss. + * + */ + public String getShortClassName(int cpx, String pkgPrefix) { + String name = Utils.javaName(getClassName(cpx)); + return getShortClassName(name, pkgPrefix); + } + + /** + * + * decodeClassDescriptor + * + * Pulls the class name out of a string (at the CP index). (drops any array + * descriptors, and the class descriptors ("L" and ";") + * + */ + public String decodeClassDescriptor(int cpx) { + // enum type is encoded as a descriptor + // need to remove '"'s and L (class descriptor) + + // TODO: might have to count '['s at the beginning for Arrays + String rawEnumName = getName(cpx); + int len = rawEnumName.length(); + int begin = (rawEnumName.startsWith("\"L")) ? 2 : 0; + int end = (begin > 0) ? len - 2 : len; + return rawEnumName.substring(begin, end); + } + + /** + * + * subtagToString + * + * Getter that safely gets the string descriptor of a subtag + * + */ + private String subtagToString(int subtag) { + SUBTAG st = subtaghash.get((byte) subtag); + if (st == null) { + return "BOGUS_SUBTAG:" + subtag; + } + return st.tagname; + } + + /** + * + * StringValue + * + * Safely gets the string value of any Constant at any CP index. + * + */ + public String StringValue(int cpx) { + if (cpx == 0) { + return "#0"; + } + if (!inbounds(cpx)) { + return ""; + } + Constant cnst = pool.get(cpx); + if (cnst == null) { + return ""; + } + return cnst.stringVal(); + } + + /** + * + * ConstantStrValue + * + * Safely gets the string value of any Constant at any CP index. This string is either + * a Constant's String value, or a CP index reference string. The Constant string has + * a tag descriptor in the beginning. + * + */ + String ConstantStrValue(int cpx) { + if (cpx == 0) { + return "#0"; + } + if (!inbounds(cpx)) { + return "#" + cpx; + } + Constant cns = pool.get(cpx); + if (cns == null) { + return "#" + cpx; + } + switch (cns.tag) { + case CONSTANT_METHODHANDLE: + case CONSTANT_INVOKEDYNAMIC: +// case CONSTANT_INVOKEDYNAMIC_TRANS: + case CONSTANT_METHOD: + case CONSTANT_INTERFACEMETHOD: + case CONSTANT_FIELD: { + CPX2 cns2 = (CPX2) cns; + if (cns2.value1 == cd.this_cpx) { + cpx = cns2.value2; + } + } + } + return cns.tag.tagname + " " + StringValue(cpx); + } + + /** + * + * print + * + * prints the entire constant pool. + * + */ + public void print(PrintWriter out) throws IOException { + int cpx = 0; + for (Constant cns : pool) { + if (cpx == 0) { + cpx += 1; + continue; + } + + out.print("const #" + cpx + " = "); + + if (cns == null) { + // do something + out.println("null"); + cpx += 1; + } else { + cns.print(out); + cpx += cns.size(); + } + } + out.println(); + } + + /** + * + * print + * + * prints the Constant value at a given CP index. + * + */ + void PrintConstant(PrintWriter out, int cpx) { + out.print(ConstantStrValue(cpx)); + } + + /** + * + * printlnClassId + * + * prints a constant value, with the print format based on the print options. + * + */ + public void printlnClassId(PrintWriter out, int cpx) throws IOException { + printlnClassId(out, cpx, false); + } + + public void printlnClassId(PrintWriter out, int cpx, boolean addComma) throws IOException { + if (!cd.options.contains(Options.PR.CPX)) { + out.print(getShortClassName(cpx, cd.pkgPrefix) + (addComma ? "," : "")); + } else { + out.print("\t#" + cpx + (addComma ? "," : "") + " //"); + PrintConstant(out, cpx); + } + } + +} diff --git a/src/org/openjdk/asmtools/jdis/FieldData.java b/src/org/openjdk/asmtools/jdis/FieldData.java new file mode 100644 index 0000000..b9527e6 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/FieldData.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.Modifiers; +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * Field data for field members in a class of the Java Disassembler + */ +public class FieldData extends MemberData { + + /*-------------------------------------------------------- */ + /* FieldData Fields */ + /** + * CP index to the field name + */ + protected int name_cpx; + + /** + * CP index to the field type + */ + protected int sig_cpx; + + /** + * CP index to the field value + */ + protected int value_cpx = 0; + + public static final String initialTab = ""; + + /*-------------------------------------------------------- */ + public FieldData(ClassData cls) { + init(cls); + memberType = "FieldData"; + } + + + /*========================================================*/ + /* Read Methods */ + @Override + protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException { + // Read the Attributes + boolean handled = true; + switch (attrtag) { + case ATT_ConstantValue: + if (attrlen != 2) { + throw new ClassFormatError("invalid ConstantValue attr length"); + } + value_cpx = in.readUnsignedShort(); + break; + default: + handled = false; + break; + } + return handled; + } + + /** + * + * read + * + * read and resolve the field data called from ClassData. precondition: NumFields has + * already been read from the stream. + * + */ + public void read(DataInputStream in) throws IOException { + // read the Fields CP indexes + access = in.readUnsignedShort(); + name_cpx = in.readUnsignedShort(); + sig_cpx = in.readUnsignedShort(); + TraceUtils.traceln(" FieldData: name[" + name_cpx + "]=" + cls.pool.getString(name_cpx) + + " sig[" + sig_cpx + "]=" + cls.pool.getString(sig_cpx)); + + // Read the attributes + readAttributes(in); + } // end read + + /*========================================================*/ + /* Print Methods */ + /** + * + * print + * + * prints the field data to the current output stream. called from ClassData. + * precondition: NumFields has already been printed to the stream. + * + */ + public void print() throws IOException { + // Print annotations first + // Print the Annotations + if (visibleAnnotations != null) { + out.println(); + for (AnnotationData visad : visibleAnnotations) { + visad.print(out, initialTab); + } + } + if (invisibleAnnotations != null) { + out.println(); + for (AnnotationData invisad : invisibleAnnotations) { + invisad.print(out, initialTab); + } + } + + if (visibleTypeAnnotations != null) { + out.println(); + for (TypeAnnotationData visad : visibleTypeAnnotations) { + visad.print(out, initialTab); + out.println(); + } + } + if (invisibleTypeAnnotations != null) { + out.println(); + for (TypeAnnotationData invisad : invisibleTypeAnnotations) { + invisad.print(out, initialTab); + out.println(); + } + } + + boolean pr_cpx = options.contains(Options.PR.CPX); + out.print(Modifiers.accessString(access, CF_Context.CTX_FIELD)); + if (isSynthetic) { + out.print("synthetic "); + } + if (isDeprecated) { + out.print("deprecated "); + } + out.print("Field "); + if (pr_cpx) { + out.print("#" + name_cpx + ":#" + sig_cpx); + } else { + out.print(cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx)); + } + if (value_cpx != 0) { + out.print("\t= "); + cls.pool.PrintConstant(cls.out, value_cpx); + } + if (pr_cpx) { + out.println(";\t // " + cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx)); + } else { + out.println(";"); + } + } +} // end FieldData + diff --git a/src/org/openjdk/asmtools/jdis/InnerClassData.java b/src/org/openjdk/asmtools/jdis/InnerClassData.java new file mode 100644 index 0000000..3917615 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/InnerClassData.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import static org.openjdk.asmtools.jasm.Tables.*; +import org.openjdk.asmtools.jasm.Modifiers; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * + */ +class InnerClassData { + /*-------------------------------------------------------- */ + /* InnerClassData Fields */ + + private Options options = Options.OptionObject(); + + ClassData cls; + int inner_class_info_index; + int outer_class_info_index; + int inner_name_index; + int access; + /*-------------------------------------------------------- */ + + public InnerClassData(ClassData cls) { + this.cls = cls; + } + + public void read(DataInputStream in) throws IOException { + inner_class_info_index = in.readUnsignedShort(); + outer_class_info_index = in.readUnsignedShort(); + inner_name_index = in.readUnsignedShort(); + access = in.readUnsignedShort(); + } // end read + + public void print() throws IOException { + boolean pr_cpx = options.contains(Options.PR.CPX); + cls.out.print(Modifiers.accessString(access, CF_Context.CTX_INNERCLASS)); + cls.out.print("InnerClass "); + if (pr_cpx) { + if (inner_name_index != 0) { + cls.out.print("#" + inner_name_index + "= "); + } + cls.out.print("#" + inner_class_info_index); + if (outer_class_info_index != 0) { + cls.out.print(" of #" + outer_class_info_index); + } + cls.out.print("; //"); + } + if (inner_name_index != 0) { + cls.out.print(cls.pool.getName(inner_name_index) + "="); + } + if (inner_class_info_index != 0) { + cls.pool.PrintConstant(cls.out, inner_class_info_index); + } + if (outer_class_info_index != 0) { + cls.out.print(" of "); + cls.pool.PrintConstant(cls.out, outer_class_info_index); + } + if (pr_cpx) { + cls.out.println(); + } else { + cls.out.println(";"); + } + } +} // end InnerClassData + diff --git a/src/org/openjdk/asmtools/jdis/Main.java b/src/org/openjdk/asmtools/jdis/Main.java new file mode 100644 index 0000000..66f192b --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/Main.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Main program of the Java Disassembler + */ +public class Main { + + private Options options = Options.OptionObject(); + /** + * Name of the program. + */ + String program; + /** + * The stream where error message are printed. + */ + PrintWriter out; + boolean DebugFlag = false; + /* debugging value, output stream will only allow this many + * bytes to be written before throwing an error. + */ + private int bytelimit = 0; + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + /** + * Constructor. + */ + public Main(PrintWriter out, String program) { + this.out = out; + this.program = program; + } + + /** + * Top level error message + */ + public void error(String msg) { + System.err.println(program + ": " + msg); + } + + /** + * Usage + */ + public void usage() { + error(i18n.getString("jdis.usage")); + error(i18n.getString("jdis.opt.g")); + error(i18n.getString("jdis.opt.sl")); + error(i18n.getString("jdis.opt.hx")); + error(i18n.getString("jdis.opt.v")); + error(i18n.getString("jdis.opt.version")); + } + + /** + * Run the disassembler + */ + public synchronized boolean disasm(String argv[]) { + ArrayList vj = new ArrayList<>(); + + // Parse arguments + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + switch (arg) { + case "-g": + options.set(Options.PR.DEBUG); + break; + case "-v": + options.setCodeOptions(); + break; + case "-sl": + options.set(Options.PR.SRC); + break; + case "-hx": + options.set(Options.PR.HEX); + break; + case "-version": + out.println(ProductInfo.FULL_VERSION); + break; + default: + if (arg.startsWith("-")) { + error(i18n.getString("jdis.error.invalid_option", arg)); + usage(); + return false; + } else { + vj.add(arg); + } + break; + } + } + + if (vj.isEmpty()) { + usage(); + return false; + } + +disasm: + for (String inpname : vj) { + if (inpname == null) { + continue; + } // cross out by CompilerChoice.compile + try { + ClassData cc = new ClassData(out); + cc.read(new DataInputStream(new FileInputStream(inpname))); + cc.print(); + continue disasm; + } catch (FileNotFoundException ee) { + error(i18n.getString("jdis.error.cannot_read", inpname)); + } catch (Error ee) { + ee.printStackTrace(); + error(i18n.getString("jdis.error.fatal_error", inpname)); + } catch (Exception ee) { + ee.printStackTrace(); + error(i18n.getString("jdis.error.fatal_exception", inpname)); + } + return false; + } + return true; + } + + /** + * Main program + */ + public static void main(String argv[]) { + Main disassembler = new Main(new PrintWriter(new uEscWriter(System.out)), "jdis"); + boolean result = disassembler.disasm(argv); + System.exit(result ? 0 : 1); + } +} diff --git a/src/org/openjdk/asmtools/jdis/MemberData.java b/src/org/openjdk/asmtools/jdis/MemberData.java new file mode 100644 index 0000000..2b4ccab --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/MemberData.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.Tables; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * + */ +public class MemberData { + + /** + * access flags (modifiers) + */ + protected int access; + + // flags + protected boolean isSynthetic = false; + protected boolean isDeprecated = false; + + /** + * The visible annotations for this class + */ + protected ArrayList visibleAnnotations; + + /** + * The invisible annotations for this class + */ + protected ArrayList invisibleAnnotations; + + /** + * The visible annotations for this class + */ + protected ArrayList visibleTypeAnnotations; + + /** + * The invisible annotations for this class + */ + protected ArrayList invisibleTypeAnnotations; + + /** + * The remaining attributes of this class + */ + protected ArrayList attrs; + + // internal references + protected Options options = Options.OptionObject(); + protected ClassData cls; + protected PrintWriter out; + protected String memberType = ""; + + /* -------------------------------------------------------- */ + /* ClassData Methods */ + public MemberData() { + } + + public void init(ClassData cls) { + this.out = cls.out; + this.cls = cls; + this.options = cls.options; + } + + protected boolean handleAttributes(DataInputStream in, Tables.AttrTag attrtag, int attrlen) throws IOException { + // sub-classes override + return false; + } + + protected void readAttributes(DataInputStream in) throws IOException { + // Read the Attributes + int natt = in.readUnsignedShort(); + TraceUtils.traceln("natt=" + natt); + attrs = new ArrayList<>(natt); + TraceUtils.traceln(memberType + " - Attributes: " + natt); + AttrData attr; + for (int k = 0; k < natt; k++) { + int name_cpx = in.readUnsignedShort(); + attr = new AttrData(cls); + attrs.add(attr); + String attr_name = cls.pool.getString(name_cpx); + TraceUtils.traceln(" " + memberType + ": #" + k + " name[" + name_cpx + "]=" + attr_name); + Tables.AttrTag tag = Tables.attrtag(attr_name); + int attrlen = in.readInt(); + switch (tag) { + case ATT_Synthetic: + // Read Synthetic Attr + if (attrlen != 0) { + throw new ClassFormatError("invalid Synthetic attr length"); + } + isSynthetic = true; + break; + case ATT_Deprecated: + // Read Deprecated Attr + if (attrlen != 0) { + throw new ClassFormatError("invalid Deprecated attr length"); + } + isDeprecated = true; + break; + case ATT_RuntimeVisibleAnnotations: + case ATT_RuntimeInvisibleAnnotations: + // Read Annotations Attr + int cnt = in.readShort(); + ArrayList annots = new ArrayList<>(cnt); + boolean invisible = (tag == Tables.AttrTag.ATT_RuntimeInvisibleAnnotations); + for (int i = 0; i < cnt; i++) { + TraceUtils.traceln(" AnnotationData: #" + i); + AnnotationData annot = new AnnotationData(invisible, cls); + annot.read(in); + annots.add(annot); + } + + if (invisible) { + invisibleAnnotations = annots; + } else { + visibleAnnotations = annots; + } + break; + case ATT_RuntimeVisibleTypeAnnotations: + case ATT_RuntimeInvisibleTypeAnnotations: + // Read Type Annotations Attr + int tcnt = in.readShort(); + ArrayList tannots = new ArrayList<>(tcnt); + boolean tinvisible = (tag == Tables.AttrTag.ATT_RuntimeInvisibleTypeAnnotations); + for (int tindex = 0; tindex < tcnt; tindex++) { + TraceUtils.traceln(" TypeAnnotationData: #" + tindex); + TypeAnnotationData tannot = new TypeAnnotationData(tinvisible, cls); + tannot.read(in); + tannots.add(tannot); + } + + if (tinvisible) { + invisibleTypeAnnotations = tannots; + } else { + visibleTypeAnnotations = tannots; + } + break; + default: + boolean handled = handleAttributes(in, tag, attrlen); + if (!handled) { + attr.read(name_cpx, attrlen, in); + } + break; + + } + } + } + +} diff --git a/src/org/openjdk/asmtools/jdis/MethodData.java b/src/org/openjdk/asmtools/jdis/MethodData.java new file mode 100644 index 0000000..335a900 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/MethodData.java @@ -0,0 +1,393 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.Modifiers; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.JasmTokens.Token; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; + +/** + * + * Method data for method members in a class of the Java Disassembler + */ +public class MethodData extends MemberData { + + /*-------------------------------------------------------- */ + /* MethodData Fields */ + /** + * MethodParamData + */ + class ParamNameData { + + public int access; + public int name_cpx; + + public ParamNameData(int name, int access) { + this.access = access; + this.name_cpx = name; + } + + } + /*-------------------------------------------------------- */ + + /** + * CP index to the method name + */ + protected int name_cpx; + + /** + * CP index to the method type + */ + protected int sig_cpx; + + /** + * The code data for this method. May be null + */ + private CodeData code; + + /** + * The exception table (thrown exceptions) for this method. May be null + */ + private int[] exc_table = null; + + protected String lP; // labelPrefix + public static final String initialTab = ""; + + /** + * The parameter names for this method + */ + protected ArrayList paramNames; + + /** + * The visible parameter annotations for this method + */ + protected ParameterAnnotationData visibleParameterAnnotations; + + /** + * The invisible parameter annotations for this method + */ + protected ParameterAnnotationData invisibleParameterAnnotations; + + /** + * The invisible parameter annotations for this method + */ + protected AnnotElem.AnnotValue defaultAnnotation; + + /*-------------------------------------------------------- */ + public MethodData(ClassData cls) { + init(cls); + memberType = "MethodData"; + lP = (options.contains(Options.PR.LABS)) ? "L" : ""; + paramNames = null; + } + + /*========================================================*/ + /* Read Methods */ + @Override + protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException { + // Read the Attributes + boolean handled = true; + switch (attrtag) { + case ATT_Code: + code = new CodeData(this); + code.read(in, attrlen); + break; + case ATT_Exceptions: + readExceptions(in); + break; + case ATT_MethodParameters: + readMethodParameters(in); + break; + case ATT_RuntimeVisibleParameterAnnotations: + case ATT_RuntimeInvisibleParameterAnnotations: + boolean invisible1 = (attrtag == AttrTag.ATT_RuntimeInvisibleParameterAnnotations); + ParameterAnnotationData pannots = new ParameterAnnotationData(cls, invisible1); + pannots.read(in); + if (invisible1) { + invisibleParameterAnnotations = pannots; + } else { + visibleParameterAnnotations = pannots; + } + break; + case ATT_AnnotationDefault: + defaultAnnotation = AnnotElem.readValue(in, cls, false); + break; + default: + handled = false; + break; + } + return handled; + } + + /** + * + * read + * + * read and resolve the method data called from ClassData. precondition: NumFields has + * already been read from the stream. + * + */ + public void read(DataInputStream in) throws IOException { + // read the Methods CP indexes + access = in.readUnsignedShort(); // & MM_METHOD; // Q + name_cpx = in.readUnsignedShort(); + sig_cpx = in.readUnsignedShort(); + TraceUtils.traceln(" MethodData: {modifiers}: " + Modifiers.toString(access, CF_Context.CTX_METHOD)); + TraceUtils.traceln(" MethodData: name[" + name_cpx + "]=" + cls.pool.getString(name_cpx) + + " sig[" + sig_cpx + "]=" + cls.pool.getString(sig_cpx)); + // Read the attributes + readAttributes(in); + } + + private void readExceptions(DataInputStream in) throws IOException { + // this is not really a CodeAttr attribute, it's part of the CodeAttr + int exc_table_len = in.readUnsignedShort(); + TraceUtils.traceln(" ExceptionsAttr[" + exc_table_len + "]"); + exc_table = new int[exc_table_len]; + for (int l = 0; l < exc_table_len; l++) { + int exc = in.readShort(); + TraceUtils.traceln(" throws:#" + exc); + exc_table[l] = exc; + } + } + + private void readMethodParameters(DataInputStream in) throws IOException { + // this is not really a CodeAttr attribute, it's part of the CodeAttr + int num_params = in.readUnsignedByte(); + TraceUtils.traceln(" MethodParametersAttr[" + num_params + "]"); + paramNames = new ArrayList<>(num_params); + for (int l = 0; l < num_params; l++) { + short pname_cpx = (short) in.readUnsignedShort(); + int paccess = in.readUnsignedShort(); + TraceUtils.traceln(" P[" + l + "] ={ name[" + pname_cpx + "]: " + cls.pool.getString(pname_cpx) + + " modifiers [" + paccess + "]: " + Modifiers.toString(paccess, CF_Context.CTX_METHOD) + "}"); + paramNames.add(l, new ParamNameData(pname_cpx, paccess)); + } + } + + /*========================================================*/ + /* Print Methods */ + /** + * + * printPAnnotations + * + * prints the parameter annotations for this method. called from CodeAttr (since JASM + * code integrates the PAnnotation Syntax inside the method body). + * + */ + // This is called from the CodeAttr + public void printPAnnotations() throws IOException { + int visSize = 0; + int invisSize = 0; + int pNumSize = 0; + + if (visibleParameterAnnotations != null) { + visSize = visibleParameterAnnotations.numParams(); + } + if (invisibleParameterAnnotations != null) { + invisSize = invisibleParameterAnnotations.numParams(); + } + if (paramNames != null) { + pNumSize = paramNames.size(); + } + + int maxParams; + maxParams = (pNumSize > invisSize) ? pNumSize : invisSize; + maxParams = (visSize > maxParams) ? visSize : maxParams; + + for (int paramNum = 0; paramNum < maxParams; paramNum++) { + ArrayList visAnnots = null; + if (visibleParameterAnnotations != null && paramNum < visSize) { + visAnnots = visibleParameterAnnotations.get(paramNum); + } + ArrayList invisAnnots = null; + if (invisibleParameterAnnotations != null && paramNum < invisSize) { + invisAnnots = invisibleParameterAnnotations.get(paramNum); + } + ParamNameData pname = (paramNames == null) ? null : paramNames.get(paramNum); + + boolean nullAnnots = ((visAnnots == null) && (invisAnnots == null)); + if (pname != null && pname.name_cpx == 0) { + pname = null; + } + + // Print the Param number (header) + if ((pname != null) || !nullAnnots) { + out.print("\t" + paramNum + ": "); + } else { + continue; + } + + boolean firstTime = true; + + // Print the Parameter name + if (pname != null) { + out.print(Token.PARAM_NAME.parsekey()); + out.print(Token.LBRACE.parsekey()); + out.print(cls.pool.getString(pname.name_cpx)); + out.print(" "); + out.print(Modifiers.toString(pname.access, CF_Context.CTX_METHOD)); + out.print(Token.RBRACE.parsekey()); + out.print(" "); + } + + // Print any visible param annotations + if (visAnnots != null) { + for (AnnotationData annot : visAnnots) { + if (!firstTime) { + out.print("\t "); + } + annot.print(out, initialTab); +// out.println(); + firstTime = false; + } + } + + // Print any invisible param annotations + if (invisAnnots != null) { + for (AnnotationData annot : invisAnnots) { + if (!firstTime) { + out.print("\t "); + } + annot.print(out, initialTab); +// out.println(); + firstTime = false; + } + } + + // Reset the line, if there were parameters + if ((pname != null) || !nullAnnots) { + out.println(""); + } + + } + + } + + /** + * + * print + * + * prints the method data to the current output stream. called from ClassData. + * precondition: NumMethods has already been printed to the stream. + * + */ + public void print(boolean skipBlankLine) throws IOException { + // Print the Annotations + if (visibleAnnotations != null) { + out.println(); + for (AnnotationData visad : visibleAnnotations) { + visad.print(out, initialTab); + out.println(); + skipBlankLine = true; + } + } + if (invisibleAnnotations != null) { + out.println(); + for (AnnotationData invisad : invisibleAnnotations) { + invisad.print(out, initialTab); + out.println(); + skipBlankLine = true; + } + } + + if (visibleTypeAnnotations != null) { + out.println(); + for (TypeAnnotationData visad : visibleTypeAnnotations) { + visad.print(out, initialTab); + out.println(); + skipBlankLine = true; + } + } + if (invisibleTypeAnnotations != null) { + out.println(); + for (TypeAnnotationData invisad : invisibleTypeAnnotations) { + invisad.print(out, initialTab); + out.println(); + skipBlankLine = true; + } + } + + boolean pr_cpx = options.contains(Options.PR.CPX); + + if (!skipBlankLine) { + out.println(); + } + out.print(Modifiers.accessString(access, CF_Context.CTX_METHOD)); + if (isSynthetic) { + out.print(Token.SYNTHETIC.parsekey() + " "); + } + if (isDeprecated) { + out.print(Token.DEPRECATED.parsekey() + " "); + } + out.print(Token.METHODREF.parsekey() + " "); + + if (pr_cpx) { + // print the CPX method descriptor + out.print("#" + name_cpx + ":#" + sig_cpx); + out.print("\t // "); + out.println(cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx)); + } else { + out.print(cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx)); + } + // followed by default annotation + if (defaultAnnotation != null) { + out.print(" default { "); + defaultAnnotation.print(out, initialTab); + out.print(" } "); + } + // followed by exception table + printExceptionTable(); + + // if ((code == null) || ((access & ACC_ABSTRACT) != 0)) { + if (code == null) { + out.print(";"); + + if (pr_cpx) { + out.print("\t // "); + + out.print(cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx)); + } + out.println(""); + } else { + code.print(); + } + } + + private void printExceptionTable() { + if (exc_table != null) { + out.print("\n\tthrows "); + int len = exc_table.length; + for (int exceptNum = 0; exceptNum < len; exceptNum++) { + out.print(cls.pool.getClassName(exc_table[exceptNum])); + if (exceptNum < len - 1) { + out.print(", "); + } + } + } + } +} // end MethodData + diff --git a/src/org/openjdk/asmtools/jdis/Options.java b/src/org/openjdk/asmtools/jdis/Options.java new file mode 100644 index 0000000..7671691 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/Options.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.util.EnumSet; + +/** + * + */ +public class Options { + + /*-------------------------------------------------------- */ + /* Options Fields */ + private static Options ref; + + static public enum PR { + + CP, // print Constant Pool + LNT, // print Line Number table + PC, // print Program Counter - for all instr + LABS, // print Labels (as identifiers) + CPX, // print CP indeX along with arguments + SRC, // print Source Line as comment + HEX, // print numbers as hexadecimals + VAR, // print local variables declarations + DEBUG; // Debug flag + }; + + static private final EnumSet JASM = EnumSet.of(PR.LABS); // default options + static private final EnumSet CODE = EnumSet.of( + PR.CP, + PR.LNT, + PR.PC, + PR.CPX, + PR.VAR + ); + + static private EnumSet printOptions = JASM; + /*-------------------------------------------------------- */ + + private Options() { + } + + public static Options OptionObject() { + if (ref == null) { + ref = new Options(); + } + return ref; + } + + public void set(PR val) { + printOptions.add(val); + } + + public void setCodeOptions() { + printOptions = CODE; + } + + public boolean contains(PR val) { + return printOptions.contains(val); + } + + public boolean debug() { + return printOptions.contains(PR.DEBUG); + } + + @Override + public String toString() { + return printOptions.toString(); + } + +} diff --git a/src/org/openjdk/asmtools/jdis/ParameterAnnotationData.java b/src/org/openjdk/asmtools/jdis/ParameterAnnotationData.java new file mode 100644 index 0000000..717b852 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/ParameterAnnotationData.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * + */ +public class ParameterAnnotationData { + /*-------------------------------------------------------- */ + /* AnnotData Fields */ + + private boolean invisible = false; + private static final String initialTab = ""; + private ArrayList> array = null; + + private ClassData cls; + /*-------------------------------------------------------- */ + + public ParameterAnnotationData(ClassData cls, boolean invisible) { + this.cls = cls; + this.invisible = invisible; + } + + public int numParams() { + if (array == null) { + return 0; + } + + return array.size(); + } + + public ArrayList get(int i) { + return array.get(i); + } + + public void read(DataInputStream in) throws IOException { + int numParams = in.readByte(); + TraceUtils.traceln(" ParameterAnnotationData[" + numParams + "]"); + array = new ArrayList<>(numParams); + for (int paramNum = 0; paramNum < numParams; paramNum++) { + + int numAnnots = in.readShort(); + TraceUtils.traceln(" Param#[" + paramNum + "]: numAnnots=" + numAnnots); + + if (numAnnots > 0) { + // read annotation + ArrayList p_annots = new ArrayList<>(numAnnots); + for (int annotIndex = 0; annotIndex < numAnnots; annotIndex++) { + AnnotationData annot = new AnnotationData(invisible, cls); + annot.read(in); + p_annots.add(annot); + } + array.add(paramNum, p_annots); + } else { + array.add(paramNum, null); + } + } + } + + // Don't need to do this -- + // we need to print annotations (both vis and invisible) per each param number + public void print(PrintWriter out, String tab) { + if (array != null && array.size() > 0) { + out.println(); + int paramNum = 0; + for (ArrayList p_annot : array) { + if (p_annot != null && p_annot.size() > 0) { + out.print("\t" + paramNum + ": "); + boolean firstTime = true; + for (AnnotationData annot : p_annot) { + if (!firstTime) { + out.print("\t "); + } + annot.print(out, initialTab); + firstTime = false; + } + } + + paramNum += 1; + out.println(); + } + } + } + +} diff --git a/src/org/openjdk/asmtools/jdis/StackMapData.java b/src/org/openjdk/asmtools/jdis/StackMapData.java new file mode 100644 index 0000000..0e425ac --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/StackMapData.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.asmutils.HexUtils; +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * represents one entry of StackMap attribute + */ +class StackMapData { + /*-------------------------------------------------------- */ + /* StackMapData Fields */ + + static int prevFramePC = 0; + boolean isStackMapTable = false; + StackMapFrameType stackFrameType = null; + int start_pc; + int[] lockMap; + int[] stackMap; + /*-------------------------------------------------------- */ + + public StackMapData() { + } + + public StackMapData(CodeData code, DataInputStream in) throws IOException { + start_pc = in.readUnsignedShort(); + TraceUtils.trace(" stack_map_entry:pc=" + start_pc); + TraceUtils.trace(" numloc="); + lockMap = readMap(code, in); + TraceUtils.trace(" numstack="); + stackMap = readMap(code, in); + TraceUtils.traceln(""); + } + + public StackMapData(CodeData code, DataInputStream in, + boolean isStackMapTable) throws IOException { + this.isStackMapTable = isStackMapTable; + int ft_val = in.readUnsignedByte(); + StackMapFrameType frame_type = stackMapFrameType(ft_val); + int offset = 0; + switch (frame_type) { + case SAME_FRAME: + // type is same_frame; + TraceUtils.trace(" same_frame=" + ft_val); + TraceUtils.traceln(""); + offset = ft_val; + break; + case SAME_FRAME_EX: + // type is same_frame_extended; + TraceUtils.trace(" same_frame_extended=" + ft_val); + TraceUtils.traceln(""); + offset = in.readUnsignedShort(); + TraceUtils.trace(" offset=" + offset); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + // type is same_locals_1_stack_item_frame + TraceUtils.trace(" same_locals_1_stack_item_frame=" + ft_val); + offset = ft_val - 64; + TraceUtils.trace(" offset=" + offset); + // read additional single stack element + TraceUtils.trace(" numstack="); + stackMap = readMapElements(code, in, 1); + TraceUtils.traceln(""); + break; + case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME: + // type is same_locals_1_stack_item_frame_extended + TraceUtils.trace(" same_locals_1_stack_item_frame_extended=" + ft_val); + offset = in.readUnsignedShort(); + TraceUtils.trace(" offset=" + offset); + // read additional single stack element + TraceUtils.trace(" numstack="); + stackMap = readMapElements(code, in, 1); + TraceUtils.traceln(""); + break; + case CHOP_1_FRAME: + case CHOP_2_FRAME: + case CHOP_3_FRAME: + // type is chop_frame + TraceUtils.trace(" chop_frame=" + ft_val); + TraceUtils.traceln(""); + offset = in.readUnsignedShort(); + TraceUtils.trace(" offset=" + offset); + break; + case APPEND_FRAME: + // type is append_frame + TraceUtils.trace(" append_frame=" + ft_val); + offset = in.readUnsignedShort(); + TraceUtils.trace(" offset=" + offset); + // read additional locals + TraceUtils.trace(" numloc="); + lockMap = readMapElements(code, in, ft_val - 251); + TraceUtils.traceln(""); + break; + case FULL_FRAME: + // type is full_frame + TraceUtils.trace(" full_frame=" + ft_val); + offset = in.readUnsignedShort(); + TraceUtils.trace(" offset=" + offset); + TraceUtils.trace(" numloc="); + lockMap = readMap(code, in); + TraceUtils.trace(" numstack="); + stackMap = readMap(code, in); + TraceUtils.traceln(""); + break; + default: + TraceUtils.trace("incorrect frame_type argument"); + + } + stackFrameType = frame_type; + start_pc = prevFramePC == 0 ? offset : prevFramePC + offset + 1; + prevFramePC = start_pc; + } + + private int[] readMap(CodeData code, DataInputStream in) throws IOException { + int num = in.readUnsignedShort(); + TraceUtils.trace("" + num); + return readMapElements(code, in, num); + } + + private int[] readMapElements(CodeData code, DataInputStream in, int num) throws IOException { + int[] map = new int[num]; + for (int k = 0; k < num; k++) { + int mt_val = in.readUnsignedByte(); + StackMapType maptype = stackMapType(mt_val); + switch (maptype) { + case ITEM_Object: + mt_val = mt_val | (in.readUnsignedShort() << 8); + break; + case ITEM_NewObject: { + int pc = in.readUnsignedShort(); + code.get_iAtt(pc).referred = true; + mt_val = mt_val | (pc << 8); + } + } + map[k] = mt_val; + TraceUtils.trace(" " + HexUtils.toHex(mt_val)); + } + return map; + } + +} diff --git a/src/org/openjdk/asmtools/jdis/TextLines.java b/src/org/openjdk/asmtools/jdis/TextLines.java new file mode 100644 index 0000000..f48b019 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/TextLines.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; + +/** + * + */ +public class TextLines { + /*-------------------------------------------------------- */ + /* TextLines Fields */ + + byte data[]; + // ends[k] points to the end of k-th line + ArrayList ends = new ArrayList<>(60); + public int length; + /*-------------------------------------------------------- */ + + public TextLines() { + } + + public TextLines(String textfilename) throws IOException { + read(textfilename); + } + + private void read(String textfilename) throws IOException { + FileInputStream in = new FileInputStream(textfilename); + data = new byte[in.available()]; + in.read(data); + in.close(); + ends.add(-1); + for (int k = 0; k < data.length; k++) { + if (data[k] == '\n') { + ends.add(k); + } + } + length = ends.size(); // but if text is empty?? + } + + public String getLine(int linenumber) { + int entry = linenumber - 1; + int start; + int end; + ends.add(entry); + start = ends.size() + 1; +searchEnd: + for (end = start; end < data.length; end++) { + switch (data[end]) { + case '\n': + case '\r': + break searchEnd; + } + } +//System.out.println("start="+start+" end="+end); + return new String(data, start, end - start); + } +} diff --git a/src/org/openjdk/asmtools/jdis/TraceUtils.java b/src/org/openjdk/asmtools/jdis/TraceUtils.java new file mode 100644 index 0000000..196357a --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/TraceUtils.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +/** + * + */ +public class TraceUtils { + + + /* ====================================================== */ + public static void trace(String s) { + if (!(Options.OptionObject()).debug()) { + return; + } + System.out.print(s); + } + + public static void traceln(String s) { + if (!(Options.OptionObject()).debug()) { + return; + } + System.out.println(s); + } +} diff --git a/src/org/openjdk/asmtools/jdis/TrapData.java b/src/org/openjdk/asmtools/jdis/TrapData.java new file mode 100644 index 0000000..945ce97 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/TrapData.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * + */ +class TrapData { + + short start_pc, end_pc, handler_pc, catch_cpx; + int num; + + public TrapData(DataInputStream in, int num) throws IOException { + this.num = num; + start_pc = in.readShort(); + end_pc = in.readShort(); + handler_pc = in.readShort(); + catch_cpx = in.readShort(); + } + + /* returns recommended identifier + */ + public String ident() { + return "t" + num; + } +} diff --git a/src/org/openjdk/asmtools/jdis/TypeAnnotationData.java b/src/org/openjdk/asmtools/jdis/TypeAnnotationData.java new file mode 100644 index 0000000..4096841 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/TypeAnnotationData.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import static org.openjdk.asmtools.jasm.TypeAnnotationUtils.*; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Type Annotation data is a specific kind of AnnotationData. As well as the normal data + * items needed to present an annotation, Type annotations require a TargetInfo + * descriptor. This descriptor is based on a TargetType, and it optionally may contain a + * location descriptor (when the Type is embedded in a collection). + * + * The TypeAnnotationData class is based on JDis's AnnotationData class, and contains the + * (jasm) class for representing TargetInfo. + */ +public class TypeAnnotationData extends AnnotationData { + + private TargetInfo target_info; + private ArrayList target_path; + private static TTVis TT_Visitor = new TTVis(); + + public TypeAnnotationData(boolean invisible, ClassData cls) { + super(invisible, cls); + target_info = null; + visAnnotToken = "@T+"; + invAnnotToken = "@T-"; + dataName = "TypeAnnotationData"; + } + + @Override + public void read(DataInputStream in) throws IOException { + super.read(in); + + // read everything related to the Type Annotation + // target type tag +// KTL 1/10/13 (changed short-> byte for latest spec rev) +// int tt = (char) in.readUnsignedShort(); // cast to introduce signedness + int tt = (byte) in.readUnsignedByte(); // cast to introduce signedness + Integer ttInt = new Integer(tt); + TargetType ttype; + ttype = targetTypeEnum(ttInt); + + if (ttype == null) { + // Throw some kind of error for bad target type index + throw new IOException("Bad target type: " + tt + " in TypeAnnotationData"); + } + + // read the target info + TT_Visitor.init(in); + TT_Visitor.visitExcept(ttype); + target_info = TT_Visitor.getTargetInfo(); + + // read the target path info + int len = in.readUnsignedShort(); + target_path = new ArrayList<>(len); + TraceUtils.traceln(" --------- [TypeAnnotationData.read]: Reading Location (length = " + len + ")."); + TraceUtils.trace(" --------- [TypeAnnotationData.read]: [ "); + for (int i = 0; i < len; i++) { + int pathType = in.readUnsignedByte(); + String pk = (getPathKind(pathType)).parsekey(); + char pathArgIndex = (char) in.readUnsignedByte(); + target_path.add(new TypePathEntry(pathType, pathArgIndex)); + TraceUtils.trace(" " + pk + "(" + pathType + "," + pathArgIndex + "), "); + } + TraceUtils.traceln("] "); + +// target_info.setLocation(location); + } + + @Override + protected void printBody(PrintWriter out, String tab) { + // For a type annotation, print out brackets, + // print out the (regular) annotation name/value pairs, + // then print out the target types. + out.print(" {"); + super.printBody(out, tab); + target_info.print(out, tab); + printPath(out, tab); + out.print("}"); + } + + protected void printPath(PrintWriter out, String tab) { + // For a type annotation, print out brackets, + // print out the (regular) annotation name/value pairs, + // then print out the target types. + out.print(" {"); + boolean first = true; + for (TypePathEntry tpe : target_path) { + if (!first) { + out.print(", "); + } + first = false; + out.print(tpe.toString()); + } + target_info.print(out, tab); + printPath(out, tab); + out.print("}"); + } + + @Override + protected void _toString(StringBuilder sb) { + // sub-classes override this + sb.append(target_info.toString()); + } + + /** + * TTVis + * + * Target Type visitor, used for constructing the target-info within a type + * annotation. visitExcept() is the entry point. ti is the constructed target info. + */ + private static class TTVis extends TypeAnnotationTargetVisitor { + + private TargetInfo ti = null; + private IOException IOProb = null; + private DataInputStream in; + + public TTVis() { + } + + public void init(DataInputStream in) { + this.in = in; + } + + public int scanByteVal() { + int val = 0; + try { + val = in.readUnsignedByte(); + } catch (IOException e) { + IOProb = e; + } + return val; + } + + public int scanShortVal() { + int val = 0; + try { + val = in.readUnsignedShort(); + } catch (IOException e) { + IOProb = e; + } + return val; + } + + public int scanIntVal() { + int val = 0; + try { + val = in.readInt(); + } catch (IOException e) { + IOProb = e; + } + return val; + } + + //This is the entry point for a visitor that tunnels exceptions + public void visitExcept(TargetType tt) throws IOException { + IOProb = null; + ti = null; + + TraceUtils.traceln(" Target Type: " + tt.parseKey()); + visit(tt); + + if (IOProb != null) { + throw IOProb; + } + } + + public TargetInfo getTargetInfo() { + return ti; + } + + private boolean error() { + return IOProb != null; + } + + @Override + public void visit_type_param_target(TargetType tt) { + TraceUtils.trace(" Type Param Target: "); + int byteval = scanByteVal(); // param index + TraceUtils.traceln("{ param_index: " + byteval + "}"); + if (!error()) { + ti = new typeparam_target(tt, byteval); + } + } + + @Override + public void visit_supertype_target(TargetType tt) { + TraceUtils.trace(" SuperType Target: "); + int shortval = scanShortVal(); // type index + TraceUtils.traceln("{ type_index: " + shortval + "}"); + if (!error()) { + ti = new supertype_target(tt, shortval); + } + } + + @Override + public void visit_typeparam_bound_target(TargetType tt) { + TraceUtils.trace(" TypeParam Bound Target: "); + int byteval1 = scanByteVal(); // param index + if (error()) { + return; + } + int byteval2 = scanByteVal(); // bound index + if (error()) { + return; + } + TraceUtils.traceln("{ param_index: " + byteval1 + " bound_index: " + byteval2 + "}"); + ti = new typeparam_bound_target(tt, byteval1, byteval2); + } + + @Override + public void visit_empty_target(TargetType tt) { + TraceUtils.traceln(" Empty Target: "); + if (!error()) { + ti = new empty_target(tt); + } + } + + @Override + public void visit_methodformalparam_target(TargetType tt) { + TraceUtils.trace(" MethodFormalParam Target: "); + int byteval = scanByteVal(); // param index + TraceUtils.traceln("{ param_index: " + byteval + "}"); + if (!error()) { + ti = new methodformalparam_target(tt, byteval); + } + } + + @Override + public void visit_throws_target(TargetType tt) { + TraceUtils.trace(" Throws Target: "); + int shortval = scanShortVal(); // exception index + TraceUtils.traceln("{ exception_index: " + shortval + "}"); + if (!error()) { + ti = new throws_target(tt, shortval); + } + } + + @Override + public void visit_localvar_target(TargetType tt) { + TraceUtils.traceln(" LocalVar Target: "); + int tblsize = scanShortVal(); // table length (short) + if (error()) { + return; + } + localvar_target locvartab = new localvar_target(tt, tblsize); + ti = locvartab; + + for (int i = 0; i < tblsize; i++) { + int shortval1 = scanShortVal(); // startPC + if (error()) { + return; + } + int shortval2 = scanShortVal(); // length + if (error()) { + return; + } + int shortval3 = scanShortVal(); // CPX + TraceUtils.trace(" LocalVar[" + i + "]: "); + TraceUtils.traceln("{ startPC: " + shortval1 + ", length: " + shortval2 + ", CPX: " + shortval3 + "}"); + locvartab.addEntry(shortval1, shortval2, shortval3); + } + } + + @Override + public void visit_catch_target(TargetType tt) { + TraceUtils.trace(" Catch Target: "); + int shortval = scanShortVal(); // catch index + TraceUtils.traceln("{ catch_index: " + shortval + "}"); + if (!error()) { + ti = new catch_target(tt, shortval); + } + } + + @Override + public void visit_offset_target(TargetType tt) { + TraceUtils.trace(" Offset Target: "); + int shortval = scanShortVal(); // offset index + TraceUtils.traceln("{ offset_index: " + shortval + "}"); + if (!error()) { + ti = new offset_target(tt, shortval); + } + } + + @Override + public void visit_typearg_target(TargetType tt) { + TraceUtils.trace(" TypeArg Target: "); + int shortval = scanShortVal(); // offset + if (error()) { + return; + } + int byteval = scanByteVal(); // type index + if (error()) { + return; + } + TraceUtils.traceln("{ offset: " + shortval + " type_index: " + byteval + "}"); + ti = new typearg_target(tt, shortval, byteval); + } + + } + +} diff --git a/src/org/openjdk/asmtools/jdis/Utils.java b/src/org/openjdk/asmtools/jdis/Utils.java new file mode 100644 index 0000000..c1012f4 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/Utils.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +/** + * + */ +public class Utils { + + static public String javaName(String name) { + if (name == null) { + return "null"; + } + int len = name.length(); + if (len == 0) { + return "\"\""; + } + char cc = '/'; +fullname: + { // xxx/yyy/zzz + for (int k = 0; k < len; k++) { + char c = name.charAt(k); + if (cc == '/') { + if (!Character.isJavaIdentifierStart(c) && c != '-') { + break fullname; + } + } else if (c != '/') { + if (!Character.isJavaIdentifierPart(c) && c != '-') { + break fullname; + } + } + cc = c; + } + return name; + } + return "\"" + name + "\""; + } + + static public boolean isClassArrayDescriptor(String name) { + boolean retval = false; + if (name != null) { + if (name.startsWith("[")) { + retval = true; + } + } + + return retval; + } + +} diff --git a/src/org/openjdk/asmtools/jdis/i18n.properties b/src/org/openjdk/asmtools/jdis/i18n.properties new file mode 100644 index 0000000..a2dfc27 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/i18n.properties @@ -0,0 +1,40 @@ +# Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +jdis.usage=\ +Usage: java -jar asmtools.jar jdis [options] FILE.class...\n\ +where possible options include: + +jdis.opt.g=\ +\ -g detailed output format +jdis.opt.sl=\ +\ -sl source lines in comments +jdis.opt.v=\ +\ -v debug mode +jdis.opt.hx=\ +\ -hx hexadecimal numbers +jdis.opt.version=\ +\ -version prints the version info + +jdis.error.invalid_option=invalid option: {0} +jdis.error.cannot_read=cannot read {0} +jdis.error.fatal_error=fatal error in file: {0} +jdis.error.fatal_exception=fatal exception in file: {0} diff --git a/src/org/openjdk/asmtools/jdis/iAtt.java b/src/org/openjdk/asmtools/jdis/iAtt.java new file mode 100644 index 0000000..9f71929 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/iAtt.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.RuntimeConstants; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.OpcodeTables.*; +import org.openjdk.asmtools.jasm.Tables; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * instruction attributes + */ +class iAtt { + + /*-------------------------------------------------------- */ + /* iAtt Fields */ + private Options options; + + short lnum = 0; + boolean referred = false; // from some other instruction + ArrayList vars; + ArrayList endvars; + ArrayList handlers; + ArrayList traps; + ArrayList endtraps; + StackMapData stackMapEntry; + CodeData code; + ClassData cls; + PrintWriter out; // =cls.out; + /*-------------------------------------------------------- */ + + public iAtt(CodeData code) { + this.code = code; + this.cls = code.meth.cls; + out = cls.out; + options = cls.options; + } + + void add_var(CodeData.LocVarData var) { + if (vars == null) { + vars = new ArrayList<>(4); + } + vars.add(var); + } + + void add_endvar(CodeData.LocVarData endvar) { + if (endvars == null) { + endvars = new ArrayList<>(4); + } + endvars.add(endvar); + } + + void add_trap(TrapData trap) { + if (traps == null) { + traps = new ArrayList<>(4); + } + traps.add(trap); + } + + void add_endtrap(TrapData endtrap) { + if (endtraps == null) { + endtraps = new ArrayList<>(4); + } + endtraps.add(endtrap); + } + + void add_handler(TrapData endtrap) { + if (handlers == null) { + handlers = new ArrayList<>(4); + } + handlers.add(endtrap); + } + + public void printEnds() throws IOException { +// prints additional information for instruction: +// end of local variable and trap scopes; + int len; + if ((endvars != null) && (options.contains(Options.PR.VAR))) { + len = endvars.size() - 1; + out.print("\t\tendvar"); + for (CodeData.LocVarData line : endvars) { + out.print(" " + line.slot); + if (len-- > 0) { + out.print(","); + } + } + out.println(";"); + } + + if (endtraps != null) { + len = endtraps.size() - 1; + out.print("\t\tendtry"); + for (TrapData line : endtraps) { + out.print(" " + line.ident()); + if (len-- > 0) { + out.print(","); + } + } + out.println(";"); + } + } + + public void printBegins() + throws IOException { +// prints additional information for instruction: +// source line number; +// start of exception handler; +// begin of locvar and trap scopes; + boolean eitherOpt = options.contains(Options.PR.LNT) || options.contains(Options.PR.SRC); + boolean bothOpt = options.contains(Options.PR.LNT) && options.contains(Options.PR.SRC); + int k; + + if ((lnum != 0) && eitherOpt) { + if (bothOpt) { + out.println("// " + lnum + ": " + cls.getSrcLine(lnum)); + } else if (options.contains(Options.PR.LNT)) { + out.print(lnum); + } else if (options.contains(Options.PR.SRC)) { + out.println("// " + cls.getSrcLine(lnum)); + } + } + out.print("\t"); + if (handlers != null) { + for (TrapData line : handlers) { + out.print("\tcatch " + line.ident()); + out.print(" " + cls.pool.getClassName(line.catch_cpx) + ";\n\t"); + } + } + if (traps != null) { + int len = traps.size() - 1; + out.print("\ttry"); + for (TrapData line : traps) { + out.print(" " + line.ident()); + if (len-- > 0) { + out.print(","); + } + } + out.print(";\n\t"); + } + if ((vars != null) && options.contains(Options.PR.VAR)) { + for (CodeData.LocVarData line : vars) { + out.println("\tvar " + line.slot + "; // " + cls.pool.getName(line.name_cpx) + ":" + cls.pool.getName(line.sig_cpx)); + out.print("\t"); + } + } + } + + public void printMapList(int[] map) { + boolean pr_cpx = options.contains(Options.PR.CPX); + + for (int k = 0; k < map.length; k++) { + int fullmaptype = map[k]; + int mt_val = fullmaptype & 0xFF; + StackMapType maptype = stackMapType(mt_val); + int argument = fullmaptype >> 8; + switch (maptype) { + case ITEM_Object: + if (pr_cpx) { + out.print(" #" + argument); + } else { + out.print(" "); + cls.pool.PrintConstant(out, argument); + } + break; + case ITEM_NewObject: + if (pr_cpx) { + out.print(" " + mt_val); + } else { + out.print(" " + maptype.printval()); + } + out.print(" " + code.meth.lP + argument); + break; + default: + if (pr_cpx) { + out.print(" " + mt_val); + } else { + out.print(" " + maptype.printval()); + } + } + out.print((k == (map.length - 1) ? ';' : ',')); + } + } + + public void printStackMap() throws IOException { + if (stackMapEntry == null) { + return; + } + boolean printed = false; + if (stackMapEntry.stackFrameType != null) { + out.print(Opcode.opc_stack_frame_type.parsekey()); // opcNamesTab[opc_stackframetype]); + out.print(" " + stackMapEntry.stackFrameType.parsekey() + ';'); + out.print("\n\t\t"); + printed = true; + } + int[] map = stackMapEntry.lockMap; + if ((map != null) && (map.length > 0)) { + out.print(Opcode.opc_locals_map.parsekey()); + printMapList(map); + out.print("\n\t\t"); + printed = true; + } + map = stackMapEntry.stackMap; + if ((map != null) && (map.length > 0)) { + out.print(Opcode.opc_stack_map.parsekey()); + printMapList(map); + out.print("\n\t\t"); + printed = true; + } + if (!printed) { +// empty attribute should be printed anyway - it should not +// be eliminated after jdis/jasm cycle + out.print(Opcode.opc_locals_map.parsekey() + " ;\n\t\t"); + } + } +} diff --git a/src/org/openjdk/asmtools/jdis/uEscWriter.java b/src/org/openjdk/asmtools/jdis/uEscWriter.java new file mode 100644 index 0000000..92d5935 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/uEscWriter.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; + +/** + * + */ +public class uEscWriter extends Writer { + /*-------------------------------------------------------- */ + /* uEscWriter Fields */ + + static final char[] hexTable = "0123456789ABCDEF".toCharArray(); + OutputStream out; + byte[] tmpl; + /*-------------------------------------------------------- */ + + public uEscWriter(OutputStream out) { + this.out = out; + tmpl = new byte[6]; + tmpl[0] = (byte) '\\'; + tmpl[1] = (byte) 'u'; + } + + @Override + public synchronized void write(int c) throws IOException { + if (c < 128) { + out.write(c); + return; + } + // write \udddd + byte[] tmpll = tmpl; + for (int k = 3; k >= 0; k--) { + tmpll[5 - k] = (byte) hexTable[(c >> 4 * k) & 0xF]; + } + out.write(tmpll, 0, 6); + } + + @Override + public synchronized void write(char[] cc, int ofs, int len) throws IOException { + for (int k = ofs; k < len; k++) { + write(cc[k]); + } + } + + @Override + public void flush() { + } + + @Override + public void close() { + } +} // end uEscWriter + diff --git a/src/org/openjdk/asmtools/util/I18NResourceBundle.java b/src/org/openjdk/asmtools/util/I18NResourceBundle.java new file mode 100644 index 0000000..f581628 --- /dev/null +++ b/src/org/openjdk/asmtools/util/I18NResourceBundle.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.util; + +import java.text.MessageFormat; +import java.util.Enumeration; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * A class that lazily opens a package-specific resource bundle containing localization + * data for a class. + */ +public class I18NResourceBundle extends ResourceBundle { + + /** + * Get a package-specific resource bundle for a class containing localization data. + * The bundle is named i18n.properties in the same package as the given class. + * + * @param c the class for which to obtain the resource bundle + * @return the appropriate resource bundle for the class + */ + public static I18NResourceBundle getBundleForClass(Class c) { + String cn = c.getName(); + int dot = cn.lastIndexOf('.'); + String rn = (dot == -1 ? "i18n" : cn.substring(0, dot) + ".i18n"); + return new I18NResourceBundle(rn, c.getClassLoader()); + } + + /** + * Create a resource bundle for the given name. The actual resource bundle will not be + * loaded until it is needed. + * + * @param name The name of the actual resource bundle to use. + */ + private I18NResourceBundle(String name, ClassLoader cl) { + this.name = name; + this.classLoader = cl; + } + + /** + * Get an entry from the resource bundle. If the resource cannot be found, a message + * is printed to the console and the result will be a string containing the method + * parameters. + * + * @param key the name of the entry to be returned + * @param arg an argument to be formatted into the result using + * {@link java.text.MessageFormat#format} + * @return the formatted string + */ + public String getString(String key, Object arg) { + return getString(key, new Object[]{arg}); + } + + /** + * Get an entry from the resource bundle. If the resource cannot be found, a message + * is printed to the console and the result will be a string containing the method + * parameters. + * + * @param key the name of the entry to be returned + * @param args an array of arguments to be formatted into the result using + * {@link java.text.MessageFormat#format} + * @return the formatted string + */ + public String getString(String key, Object... args) { + try { + return MessageFormat.format(getString(key), args); + } catch (MissingResourceException e) { + System.err.println("WARNING: missing resource: " + key + " for " + name); + StringBuffer sb = new StringBuffer(key); + for (int i = 0; i < args.length; i++) { + sb.append('\n'); + sb.append(args.toString()); + } + return sb.toString(); + } + } + + /** + * Get an entry from the bundle, returning null if it is not found. + * + * @param key the name of the entry to be returned + * @return the value of the entry, or null if it is not found. + */ + public String getOptionalString(String key) { + if (delegate == null) { + delegate = ResourceBundle.getBundle(name, Locale.getDefault(), classLoader); + } + try { + String s = (String) (delegate.getObject(key)); + if (s != null) { + System.out.println("i18n: " + key); + } + return s; + } catch (MissingResourceException e) { + return null; + } + } + + /** + * A required internal method for ResourceBundle. Load the actual resource bundle, if + * it has not yet been loaded, then hand the request off to that bundle. If the + * resource cannot be found, a message is printed to the console and the result will + * be the original key. + */ + protected Object handleGetObject(String key) throws MissingResourceException { + try { + if (delegate == null) { + delegate = ResourceBundle.getBundle(name, Locale.getDefault(), classLoader); + } + return delegate.getObject(key); + } catch (MissingResourceException e) { + System.err.println("WARNING: missing resource: " + key + " for " + name); + return key; + } + } + + /** + * A required internal method for ResourceBundle. Load the actual resource bundle, if + * it has not yet been loaded, then hand the request off to that bundle. + */ + public Enumeration getKeys() { + if (delegate == null) { + delegate = ResourceBundle.getBundle(name); + } + return delegate.getKeys(); + } + + /** + * Returns the name of this bundle (useful for methods using bundle name instead of + * instance, such as Logger creation, + * + * @return the name of this resource bundle + */ + public String getName() { + return name; + } + + private String name; + private ResourceBundle delegate; + private ClassLoader classLoader; +} diff --git a/src/org/openjdk/asmtools/util/ProductInfo.java b/src/org/openjdk/asmtools/util/ProductInfo.java new file mode 100644 index 0000000..77425c2 --- /dev/null +++ b/src/org/openjdk/asmtools/util/ProductInfo.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.util; + +import java.util.ResourceBundle; + +/** + * Class providing an access to the product info. + * productinfo.properties will be generated during the build + */ +public class ProductInfo { + + static { + init(); + } + + ; + + /** + * Returns the value of the specified property + */ + public static String getProperty(String propName) { + try { + return bundle.getString(propName); + } catch (java.util.MissingResourceException e) { + return null; + } + } + + /** + * Version of the product in the short format, like 5.0 + */ + public static final String VERSION = version(); + + /** + * Full version of the product, including build number and date of creation + */ + public static final String FULL_VERSION = fullVersion(); + + private static final String BUNDLE_NAME = "org.openjdk.asmtools.util.productinfo"; + + private static ResourceBundle bundle; + + /** + * Initializes the bundle + */ + private static void init() { + bundle = ResourceBundle.getBundle(BUNDLE_NAME); + } + + private static String version() { + return getProperty("PRODUCT_VERSION"); + } + + private static String fullVersion() { + return getProperty("PRODUCT_NAME_LONG") + ", version " + version() + + " " + getProperty("PRODUCT_MILESTONE") + + " " + getProperty("PRODUCT_BUILDNUMBER") + + " (" + getProperty("PRODUCT_DATE") + ")"; + + } + +} diff --git a/src/org/openjdk/asmtools/util/productinfo.properties b/src/org/openjdk/asmtools/util/productinfo.properties new file mode 100644 index 0000000..16a46d9 --- /dev/null +++ b/src/org/openjdk/asmtools/util/productinfo.properties @@ -0,0 +1,27 @@ +# Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +PRODUCT_NAME = %%PRODUCT_NAME%% +PRODUCT_VERSION = %%PRODUCT_VERSION%% +PRODUCT_MILESTONE = %%PRODUCT_MILESTONE%% +PRODUCT_BUILDNUMBER = %%PRODUCT_BUILDNUMBER%% +PRODUCT_NAME_LONG = %%PRODUCT_NAME_LONG%% +PRODUCT_DATE = %%BUILD_DATE%%