| /* ClassWrapper.java - wrap ASM class objects |
| Copyright (C) 2006, 2007 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath 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 for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package gnu.classpath.tools.javah; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| |
| import org.objectweb.asm.tree.ClassNode; |
| import org.objectweb.asm.tree.FieldNode; |
| import org.objectweb.asm.tree.MethodNode; |
| |
| public class ClassWrapper |
| extends ClassNode |
| { |
| Main classpath; |
| |
| ClassWrapper superClass; |
| |
| ArrayList interfaceClasses; |
| |
| // The virtual table for this class. |
| ArrayList vtable; |
| |
| // A set of all the bridge method targets we've found. |
| HashSet bridgeTargets; |
| |
| // A set of all the method names in this class. |
| HashSet methodNames = new HashSet(); |
| |
| // This maps a method name + descriptor, e.g. "method()V", to the |
| // name chosen for the method. This is used when computing the |
| // names of bridge method targets. |
| HashMap methodNameMap = new HashMap(); |
| |
| public ClassWrapper(Main classpath) |
| { |
| this.classpath = classpath; |
| } |
| |
| public boolean hasNativeMethod() |
| { |
| Iterator i = methods.iterator(); |
| while (i.hasNext()) |
| { |
| MethodNode method = (MethodNode) i.next(); |
| if (Modifier.isNative(method.access)) |
| return true; |
| } |
| return false; |
| } |
| |
| public boolean isThrowable() throws IOException |
| { |
| linkSupers(); |
| ClassWrapper self = this; |
| while (self != null) |
| { |
| if (self.name.equals("java/lang/Throwable")) |
| return true; |
| self = self.superClass; |
| } |
| return false; |
| } |
| |
| private void linkSupers() throws IOException |
| { |
| if (superName == null) |
| { |
| // Object, do nothing. |
| return; |
| } |
| if (superClass == null) |
| { |
| superClass = classpath.getClass(superName); |
| assert interfaceClasses == null; |
| interfaceClasses = new ArrayList(); |
| for (int i = 0; i < interfaces.size(); ++i) |
| { |
| String ifname = (String) interfaces.get(i); |
| ClassWrapper iface = classpath.getClass(ifname); |
| iface.linkSupers(); |
| interfaceClasses.add(iface); |
| } |
| } |
| superClass.linkSupers(); |
| } |
| |
| private int findSlot(MethodNode method) |
| { |
| for (int i = vtable.size() - 1; i >= 0; --i) |
| { |
| MethodNode base = (MethodNode) vtable.get(i); |
| if (MethodHelper.overrides(method, base)) |
| return i; |
| } |
| return - 1; |
| } |
| |
| private void addInterfaceMethods(ClassWrapper iface) |
| { |
| Iterator i = iface.methods.iterator(); |
| while (i.hasNext()) |
| { |
| MethodNode im = (MethodNode) i.next(); |
| int slot = findSlot(im); |
| if (slot == - 1) |
| { |
| vtable.add(im); |
| // Also add it to our local methods. |
| methods.add(im); |
| } |
| } |
| addInterfaces(iface); |
| } |
| |
| private void addInterfaces(ClassWrapper base) |
| { |
| if (base.interfaceClasses == null) |
| return; |
| Iterator i = base.interfaceClasses.iterator(); |
| while (i.hasNext()) |
| { |
| ClassWrapper iface = (ClassWrapper) i.next(); |
| addInterfaceMethods(iface); |
| } |
| } |
| |
| private void addLocalMethods() |
| { |
| Iterator i = methods.iterator(); |
| while (i.hasNext()) |
| { |
| MethodNode meth = (MethodNode) i.next(); |
| methodNames.add(meth.name); |
| if (Modifier.isStatic(meth.access)) |
| continue; |
| int slot = findSlot(meth); |
| if (slot == - 1) |
| vtable.add(meth); |
| else |
| vtable.set(slot, meth); |
| } |
| } |
| |
| private void makeVtable() throws IOException |
| { |
| if (vtable != null) |
| return; |
| if (superClass != null) |
| { |
| superClass.makeVtable(); |
| vtable = new ArrayList(superClass.vtable); |
| bridgeTargets = new HashSet(superClass.bridgeTargets); |
| methodNameMap = new HashMap(superClass.methodNameMap); |
| } |
| else |
| { |
| // Object. |
| vtable = new ArrayList(); |
| bridgeTargets = new HashSet(); |
| methodNameMap = new HashMap(); |
| } |
| addLocalMethods(); |
| addInterfaces(this); |
| |
| // Make a set of all the targets of bridge methods. We rename |
| // bridge target methods to avoid problems with C++. You might |
| // think we could rename the bridge methods themselves, but bridge |
| // methods by definition override a method from the superclass -- |
| // and we have to consider the superclass' header as an |
| // unchangeable entity. |
| Iterator i = methods.iterator(); |
| while (i.hasNext()) |
| { |
| MethodNode m = (MethodNode) i.next(); |
| String desc = MethodHelper.getBridgeTarget(m); |
| if (desc != null) |
| bridgeTargets.add(m.name + desc); |
| } |
| } |
| |
| private void printFields(CniPrintStream out) |
| { |
| Iterator i = fields.iterator(); |
| ClassWrapper self = superClass; |
| while (i.hasNext()) |
| { |
| FieldNode f = (FieldNode) i.next(); |
| boolean hasMethodName = methodNames.contains(f.name); |
| if (FieldHelper.print(out, f, self, hasMethodName)) |
| self = null; |
| } |
| } |
| |
| private void printMethods(CniPrintStream out) throws IOException |
| { |
| makeVtable(); |
| |
| // A given method is either static, overrides a super method, or |
| // is already in vtable order. |
| Iterator i = methods.iterator(); |
| while (i.hasNext()) |
| { |
| MethodNode m = (MethodNode) i.next(); |
| String nameToUse; |
| String sum = m.name + m.desc; |
| if (bridgeTargets.contains(sum)) |
| { |
| if (methodNameMap.containsKey(sum)) |
| nameToUse = (String) methodNameMap.get(sum); |
| else |
| { |
| // Bridge target that is new in this class. |
| String cname = this.name; |
| int index = cname.lastIndexOf('/'); |
| cname = cname.substring(index + 1); |
| nameToUse = cname + "$" + m.name; |
| } |
| } |
| else |
| nameToUse = Keywords.getCxxName(m.name); |
| methodNameMap.put(sum, nameToUse); |
| MethodHelper.print(out, m, this, nameToUse); |
| } |
| } |
| |
| private void printTextList(PrintStream out, int what, ArrayList textList) |
| { |
| if (textList == null) |
| return; |
| Iterator i = textList.iterator(); |
| boolean first = true; |
| while (i.hasNext()) |
| { |
| Text item = (Text) i.next(); |
| if (item.type == what) |
| { |
| if (first) |
| { |
| out.println(); |
| first = false; |
| } |
| if (what == Text.FRIEND) |
| out.print(" friend "); |
| out.println(item.text); |
| } |
| } |
| } |
| |
| public void print(CniPrintStream out) |
| { |
| out.print("::" + name.replaceAll("/", "::")); |
| } |
| |
| // This prints the body of a class to a CxxPrintStream. |
| private void printContents(CniPrintStream out, ArrayList textList) |
| throws IOException |
| { |
| printTextList(out, Text.PREPEND, textList); |
| out.println(); |
| |
| out.print("class "); |
| // Don't use our print() -- we don't want the leading "::". |
| out.print(name.replaceAll("/", "::")); |
| if (superClass != null) |
| { |
| out.print(" : public "); |
| superClass.print(out); |
| } |
| out.println(); |
| out.println("{"); |
| |
| printTextList(out, Text.ADD, textList); |
| out.println(); |
| |
| // Note: methods must come first, as we build the list |
| // of method names while printing them. |
| printMethods(out); |
| printFields(out); |
| |
| out.setModifiers(Modifier.PUBLIC); |
| out.println(" static ::java::lang::Class class$;"); |
| |
| printTextList(out, Text.FRIEND, textList); |
| |
| out.print("}"); |
| if (Modifier.isInterface(access)) |
| out.print(" __attribute__ ((java_interface))"); |
| out.println(";"); |
| |
| printTextList(out, Text.APPEND, textList); |
| } |
| |
| public void printFully(PrintStream out) throws IOException |
| { |
| linkSupers(); |
| |
| ArrayList textList = classpath.getClassTextList(name); |
| |
| out.println("// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*-"); |
| out.println(); |
| String xname = "__" + name.replaceAll("/", "_") + "__"; |
| out.println("#ifndef " + xname); |
| out.println("#define " + xname); |
| out.println(); |
| out.println("#pragma interface"); |
| out.println(); |
| |
| if (superClass != null) |
| { |
| out.print("#include <"); |
| out.print(superName); |
| out.println(".h>"); |
| } |
| |
| // Write the body of the stream here. This lets |
| // us emit the namespaces without a second pass. |
| ByteArrayOutputStream bytes = new ByteArrayOutputStream(); |
| CniPrintStream cxxOut = new CniPrintStream(bytes); |
| cxxOut.addClass(this); |
| printContents(cxxOut, textList); |
| cxxOut.printNamespaces(out); |
| bytes.writeTo(out); |
| |
| out.println(); |
| out.println("#endif // " + xname); |
| } |
| |
| public String toString() |
| { |
| return name; |
| } |
| } |