/*
* @(#)JavaAPILister.java 1.11 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program 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 program 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 at /legal/license.txt).
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
import consts.*;
import components.*;
import util.*;
import jcc.*;
import vm.*;
import java.lang.Integer;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.PrintStream;
import java.io.StreamTokenizer;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;
public class JavaAPILister extends LinkerUtil {
int verbosity = 0;
String commandOptions;
ClassFileFinder searchPath;
ClassReader rdr;
ClassnameFilter includeClasses;
PrintStream memberOut = null;
PrintStream classOut = null;
String outName = JavaCodeCompact.getOutName();
String romOutName = outName + "MemberFilterData.c";
PrintStream romOut = null;
/* 'minput' should be the 'mout' generated by JavaAPILister
* when jar files (contains classes) are taken as the input.
* 'minput' and 'mout' contains a list of visiable classes
* and their members. When 'minput' is set, other arguments
* such as 'input', 'mout' and 'cout' are ignored.
*/
String minput = null;
/*
* The classes from the input jar file can have the same name as the
* ROMized CDC classes. Use a separate loader to keep track of the
* classes from the input jar file. We don't want to mix them with the
* ROMized classes. This is to make sure we obtian the member
* information from input classes, not the ROMized classes.
*/
components.ClassLoader apiloader =
new components.ClassLoader("apilister", null);
private static String
memberFileHeader[] = {
"#",
"# %"+"W% %"+"E%", // use constant expressions to fool SCCS
"# List of visible class members",
"#"
};
private static String
classFileHeader[] = {
"#",
"# %"+"W% %"+"E%", // use constant expressions to fool SCCS
"# List of classes",
"#"
};
private boolean
readFile( String fileName, Vector classesProcessed ){
if (rdr == null){
rdr = new ClassReader(0);
}
try {
if (fileName.endsWith(".zip") || fileName.endsWith(".jar")){
rdr.readZip(fileName, classesProcessed);
} else {
rdr.readFile(fileName, classesProcessed);
}
} catch ( IOException e ){
System.out.println(Localizer.getString(
"javacodecompact.could_not_read_file", fileName));
e.printStackTrace();
return false;
}
return true;
}
/* All classes read from the input jar file. Include excluded
(non-visible), such as com.sun.* classes. */
Vector classesProcessed = new Vector();
/* All visible classes. Sorted by class typeid. */
LinkedList sortedClasses = new LinkedList();
/* ROMized CDC classes corresponding to the classes in 'sortedClasses' */
LinkedList sortedCVMClasses = new LinkedList();
private void
addToIncludes( String pattern ){
if (includeClasses == null){
includeClasses = new ClassnameFilter();
}
includeClasses.includeName(pattern);
if (verbosity > 0){
System.err.print("Adding to include list: ");
System.err.println(pattern);
}
}
private boolean
isClassIncluded(String classname){
if (includeClasses == null){
return true;
}
return includeClasses.accept(null, classname);
}
private static BufferedPrintStream
openOutputFile(String name){
File outFile = new File(name);
try {
return new BufferedPrintStream( new FileOutputStream( outFile ) );
}catch(java.io.IOException e){
System.err.println("Could not open file "+name);
return null;
}
}
/* Process the command line options. The options should look like:
*
* -listapi:include=java/*,include=javax/*,
* input=<jar1>:<jar2>,minput=<minput>,mout=<mout>,cout=<cout>
*
* 'include' - Visible packages
* 'input' - Jar file containing classes
* 'minput' - The class/member list ('mout' output) generated by
* JavaAPILister when input jar files are given
* 'mout' - A txt file containing class/member list extracted from
* the input classes
* 'cout' - A txt file containing only class list extracted from
* the input classes
*
* If 'minput' exists, all other options are ignored.
*
* Following is examples of what the 'minput' and 'mout' txt file look
* like:
*
* CLASS java/util/Stack
* FIELDS
* elementData:[Ljava/lang/Object;
* elementCount:I
* capacityIncrement:I
* METHODS
* <init>:()V
* push:(Ljava/lang/Object;)Ljava/lang/Object;
* pop:()Ljava/lang/Object;
* peek:()Ljava/lang/Object;
* empty:()Z
* ...
*/
private boolean processOptions(String args) {
boolean success = true;
Vector classesThisRead = new Vector();
StringTokenizer options = new StringTokenizer(args.substring(9), ",");
int num = options.countTokens();
int si;
if ((si = args.indexOf("minput=")) != -1) {
/* minput exists, ignore the rest of the arguments */
int ei = args.indexOf(',' , si);
if (ei == -1) {
minput = args.substring(si+7);
} else {
minput = args.substring(si+7, ei);
}
return success;
}
/* minput doesn't exist, process all arguments */
for (int i = 0; i < num; i++) {
String s = options.nextToken();
if (s.startsWith("verbose=")) {
if (s.substring(8).equals("true")) {
verbosity++;
}
} else if (s.startsWith("include=")) {
addToIncludes(s.substring(8));
} else if (s.startsWith("input=")) {
String separator = System.getProperty("path.separator", ":");
StringTokenizer st = new StringTokenizer(s.substring(6), separator);
int numOfInput = st.countTokens();
for (int j = 0; j < numOfInput; j++) {
String f = st.nextToken();
classesThisRead.clear();
if (!readFile(f, classesThisRead)) {
success = false;
}
classesProcessed.addAll(classesThisRead);
ClassTable.enterClasses(
classesThisRead.elements(), apiloader);
}
} else if (s.startsWith("mout=")) {
memberOut = openOutputFile(s.substring(5));
if (memberOut == null) {
// message already written
success = false;
}
} else if (s.startsWith("cout=")) {
classOut = openOutputFile(s.substring(5));
if (classOut == null){
// message already written
success = false;
}
}
}
return success;
}
/*private void
printHeader0(PrintStream out, String commentString) {
out.println(commentString + " DO NOT EDIT. This file is auto-generated by JCC from");
out.println(commentString + " a fully ROMized build using following build options: ");
out.println(commentString + "");
out.println(commentString + " CVM_INCLUDE_MIDP=true PCSL_DIR=<pcsl_dir>");
out.println(commentString + " MIDP_DIR=<midp_dir> J2ME_CLASSLIB=foundation");
out.println(commentString + " CVM_DUAL_STACK=true CVM_PRELOAD_LIB=true");
out.println(commentString + " CVM_MIDPFILTERINPUT=<cldc_classes_zip>");
out.println(commentString + " gen_member_filter");
out.println(commentString + "");
out.println(commentString + " The output files are generated/javavm/runtime/romjavaMemberFilterData.c,");
out.println(commentString + " lib/MIDPFilterConfig.txt and lib/MIDPPermittedClasses.txt.");
out.println(commentString + " If CLDC API is changed, this file needs to be re-generated.");
}*/
private void
printHeader0(PrintStream out, String commentString) {
out.println(commentString + " DO NOT EDIT. This file is auto-generated by JCC.");
}
private void
printFileHeader(PrintStream out, String header[]){
int hLength = header.length;
for (int i=0; i<hLength; i++){
out.println(header[i]);
}
out.println("#");
printHeader0(out, "#");
out.println("#");
}
/* Write filter data into a .c file. Class data are sorted
* by typeid.
*/
private void
writeRomFileHeader(PrintStream out)
{
out.println("/*");
printHeader0(out, " *");
out.println(" * This file is a copy of the generated romjavaMemberFilterData.c.");
out.println(" */");
out.println();
out.println("#include \"generated/javavm/runtime/romjava.h\"");
out.println("#include \"javavm/include/dualstack_impl.h\"");
out.println("");
}
/* Find the ROMizer version of class member, so we can access the
* member typeid.
*/
private ClassMemberInfo findROMMember(ClassInfo cdcClass,
ClassMemberInfo[] cdccma,
String membername,
String membertype,
boolean isMethod)
{
ClassMemberInfo cdccm = null;
/* Search the ROMized class members to find a match */
for (int j = 0; j < cdccma.length; j++) {
if (membername.equals(cdccma[j].name.string)) {
if (isMethod) {
if (membertype.equals(cdccma[j].type.string)) {
// match
return cdccma[j];
}
} else {
// match
return cdccma[j];
}
}
}
/* Search the super class of the ROMized class for the member */
ClassConstant supercc = cdcClass.superClass;
if (supercc != null) {
ClassInfo superClass =
ClassTable.lookupClass(supercc.name.string);
ClassMemberInfo supercma[] = isMethod ?
((ClassMemberInfo[])superClass.methods) :
((ClassMemberInfo[])superClass.fields);
cdccm = findROMMember(superClass, supercma,
membername, membertype, isMethod);
if (cdccm != null) {
return cdccm;
}
}
/* search the interfaces */
int nInterfaces = cdcClass.interfaces.length;
for (int i = 0; i < nInterfaces; i++) {
ClassInfo interfaceClass = ClassTable.lookupClass(
cdcClass.interfaces[i].name.string);
ClassMemberInfo interfacecma[] = isMethod ?
((ClassMemberInfo[])interfaceClass.methods) :
((ClassMemberInfo[])interfaceClass.fields);
cdccm = findROMMember(interfaceClass, interfacecma,
membername, membertype, isMethod);
if (cdccm != null) {
return cdccm;
}
}
return null;
}
/*
* Replace members in the vector with the members from the
* corresponding ROMized class.
*/
private void replaceMembers(ClassInfo thisClass, Vector members,
boolean methods)
{
/* get the ROMized class */
ClassInfo cdcClass = ClassTable.lookupClass(thisClass.className);
if (cdcClass != null) {
ClassMemberInfo cdccma[] = (methods) ?
((ClassMemberInfo[])cdcClass.methods) :
((ClassMemberInfo[])cdcClass.fields);
int size = members.size();
for (int i = 0; i < size; i++) {
ClassMemberInfo cm = (ClassMemberInfo)(members.get(i));
ClassMemberInfo cdccm = findROMMember(
cdcClass, cdccma, cm.name.string, cm.type.string, methods);
/* replace the member */
if (cdccm != null) {
members.set(i, cdccm);
}
}
}
}
private void
collectClassComponents(
Vector components,
ClassInfo thisClass,
Vector classesAlreadySeen,
boolean methods,
boolean superIsFinal,
boolean leafClass)
{
boolean classIsFinal;
int permissionMask;
ClassMemberInfo cma[];
if (verbosity > 0){
String s = (methods) ? "methods" : "fields";
System.out.println("Collect " + s +
" for " + thisClass.className);
}
// if this class has already been seen,
// don't go looking at it again.
if (classesAlreadySeen.contains(thisClass)){
if (verbosity > 0){
System.err.println(thisClass.className + "has already seen.");
}
return;
}
classesAlreadySeen.addElement(thisClass);
// First look at all the elements in this class
// and see if we want to add any of them. See if
// they have already been added.
permissionMask = Const.ACC_PUBLIC;
classIsFinal = superIsFinal ||
((thisClass.access & Const.ACC_FINAL) != 0);
if (!classIsFinal){
permissionMask |= Const.ACC_PROTECTED;
}
cma = (methods) ? ((ClassMemberInfo[])thisClass.methods)
: ((ClassMemberInfo[])thisClass.fields);
if (cma != null){
int i;
int nMembers = cma.length;
ClassMemberInfo cm;
/* start collecting qualified members */
for (i = 0; i < nMembers; i++){
cm = cma[i];
if ((cm.access & permissionMask) == 0)
continue;
if (cm.name.string.equals("<clinit>"))
continue;
if (!leafClass){
// this is a superclass or interface.
/* Bug 6589171.
if (cm.isStaticMember()){
// subclasses don't inherit our statics
continue;
}*/
if (cm.name.string.equals("<init>")){
// subclasses don't inherit our constructors
continue;
}
}
// this looks like a candidate.
// see if its already on the list.
ClassMemberInfo otherCm;
Enumeration componentsE = components.elements();
boolean doPrint = true;
while (componentsE.hasMoreElements()){
otherCm = (ClassMemberInfo)componentsE.nextElement();
if (!cm.name.string.equals(otherCm.name.string)){
// names differ.
// keep looking.
continue;
}
// names the same
if (methods){
if (!cm.type.string.equals(otherCm.type.string)){
// is a method and types are not the same.
// methods override on name and type.
// otherCm does not override this one.
continue;
}
}
//
// otherCm is a field with the same name
// or a method with the same name and sig.
// cm has been overridden and should not be
// printed again.
doPrint = false;
break;
}
if (doPrint){
// we want to include this one on the list.
components.add(cm);
if (verbosity > 0){
System.out.println("\tFound member: " + cm);
}
}
}
}
String otherName;
ClassInfo otherClass;
ClassConstant superConstant = thisClass.superClass;
// look at superclass
if ((thisClass.access & Const.ACC_INTERFACE) == 0) {
if (superConstant != null){
// recurse into superclass
otherName = superConstant.name.string;
if ((otherClass = ClassTable.lookupClass(otherName, apiloader))
!= null){
collectClassComponents(components, otherClass,
classesAlreadySeen, methods, classIsFinal, false);
}
}
}
// look at interfaces. This should only matter
// when the current class is an interface or abstract.
if (thisClass.interfaces != null){
int nInterfaces = thisClass.interfaces.length;
for (int i=0; i<nInterfaces; i++){
otherName = thisClass.interfaces[i].name.string;
if ((otherClass = ClassTable.lookupClass(otherName, apiloader))
!= null){
// recurse into interfaces
collectClassComponents(components, otherClass,
classesAlreadySeen, methods, classIsFinal, false);
}
}
}
}
private void
printVector(String title, Object m[]){
if (m.length == 0)
return;
memberOut.println(title);
for (int i = 0; i < m.length; i++){
ClassMemberInfo cm = (ClassMemberInfo)(m[i]);
memberOut.print('\t');
memberOut.print(cm.name.string);
memberOut.print(':');
memberOut.println(cm.type.string);
}
}
private void
setClassMembers(ClassInfo thisClass, Vector members,
boolean methods)
{
if (methods) {
thisClass.methodtable = (MethodInfo[])members.toArray(new MethodInfo[0]);
} else {
thisClass.fieldtable = (FieldInfo[])members.toArray(new FieldInfo[0]);
}
}
private void
printClassComponents(ClassInfo thisClass)
{
Vector classesVisited;
Vector methods;
Vector fields;
// print fields
printVector(" FIELDS", thisClass.fieldtable);
// print methods
printVector(" METHODS", thisClass.methodtable);
}
private boolean
includedAndVisible(ClassInfo c){
String classname = c.className;
int access = c.access;
if (!isClassIncluded(classname)){
return false;
}
if ((access & Const.ACC_PUBLIC) == 0){
return false;
}
return true;
}
private void
printClassInfo( ClassInfo c ){
if (!includedAndVisible(c)){
if (verbosity > 0){
System.err.print("Excluded class ");
System.err.println(c.className);
}
return;
}
memberOut.print("\nCLASS ");
memberOut.println(c.className);
printClassComponents(c);
}
private void
printClassName( ClassInfo c )
{
String name = c.className.replace('/','.');
classOut.println(name);
}
public void writeRomFilter(PrintStream out)
{
int i, j;
int nClasses = sortedClasses.size();
ClassInfo thisClass;
ClassInfo cdcClass;
CVMClass cvmClass;
String cName;
writeRomFileHeader(romOut);
/* first write all class member arrays */
for (i = 0; i < nClasses; i++) {
thisClass = (ClassInfo)sortedClasses.get(i);
cvmClass = (CVMClass)sortedCVMClasses.get(i);
cName = cvmClass.getNativeName();
FieldInfo fa[] = thisClass.fieldtable;
MethodInfo ma[] = thisClass.methodtable;
int typeid, nameid;
/* write the class's fields */
if (fa.length > 0) {
out.println("/* " + cName + " fields */");
out.println("CVMFieldTypeID " + cName + "_f[] = {");
for (j = 0; j < fa.length; j++) {
FieldInfo f = fa[j];
typeid = CVMDataType.parseSignature((f.type).toUTF());
nameid = CVMMemberNameEntry.lookupEnter((f.name).toUTF());
out.println("\tRAW_TYPEID(0x" + Integer.toHexString(nameid) +
", 0x" + Integer.toHexString(typeid) + "), ");
}
out.println("};");
}
/* write the class' methods */
if (ma.length > 0) {
out.println("/* " + cName + " methods */");
out.println("CVMMethodTypeID " + cName + "_m[] = {");
for (j = 0; j < ma.length; j++) {
MethodInfo m = ma[j];
CVMMethodInfo cm = (CVMMethodInfo)m.vmMethodInfo;
typeid = cm.sigType.entryNo;
nameid = CVMMemberNameEntry.lookupEnter(
(cm.method.name).toUTF());
out.println("\tRAW_TYPEID(0x" + Integer.toHexString(nameid) +
", 0x" + Integer.toHexString(typeid) + "), ");
}
out.println("};");
}
}
/* write CVMClassRestrictions data struct */
out.println("\n/* The dual stack member filter. */");
if (nClasses != 0) {
out.println("struct CVMClassRestrictionElement cre[] = {");
for (i = 0; i < nClasses; i++) {
thisClass = (ClassInfo)sortedClasses.get(i);
cvmClass = (CVMClass)sortedCVMClasses.get(i);
cName = cvmClass.getNativeName();
/* class typeid */
out.print("{0x" + Integer.toHexString(cvmClass.getClassId()) +
", ");
/* number of methods */
out.print(thisClass.methodtable.length + ", ");
/* number of fields */
out.print(thisClass.fieldtable.length + ", ");
/* method table */
if (thisClass.methodtable.length == 0) {
out.print("NULL /* " + cName + "_m */, ");
} else {
out.print(cName + "_m, ");
}
/* field table */
if (thisClass.fieldtable.length == 0) {
out.println("NULL /* " + cName + "_f */},");
} else {
out.println(cName + "_f},");
}
}
out.println("};");
/* write CVMClassRestrictions data struct */
out.print("const struct CVMClassRestrictions CVMdualStackMemberFilter = {");
out.print(nClasses + ", ");
out.println("cre};");
} else {
out.println("const struct CVMClassRestrictions CVMdualStackMemberFilter = {0, NULL};");
}
out.flush();
}
private void writeMemberFile()
{
if (memberOut == null) {
/* mout is not set. Nothing to do here. */
return;
}
int nClasses = classesProcessed.size();
printFileHeader(memberOut, memberFileHeader);
for (int i = 0; i < nClasses; i++) {
ClassInfo c = (ClassInfo)classesProcessed.get(i);
if (!includedAndVisible(c)) {
continue; /* excluded. Skip this class. */
}
memberOut.print("\nCLASS ");
memberOut.println(c.className);
printClassComponents(c);
}
memberOut.flush();
}
private void writeClassFile()
{
if (classOut != null){
printFileHeader(classOut, classFileHeader);
int nClasses = classesProcessed.size();
for (int i = 0; i < nClasses; i++){
ClassInfo c = (ClassInfo)classesProcessed.get(i);
if (!includedAndVisible(c)) {
continue; /* excluded. Skip this class. */
}
printClassName(c);
}
classOut.flush();
}
}
/*
* Collect class members and store them in 'fieldtable' and
* 'methodtable'.
*/
private void processClasses()
{
if (minput != null) {
/*
* Parse the minput and create ClassInfo for each
* class in the list.
*/
parseMInput(minput);
return;
}
/*
* The classes are from a input jar file. Process
* classes and collect visiable members.
*/
int num = classesProcessed.size();
for (int i = 0; i < num; i++) {
ClassInfo thisClass = (ClassInfo)classesProcessed.get(i);
if (!includedAndVisible(thisClass)){
if (verbosity > 0){
continue; /* skip excluded class */
}
}
/* collect fields */
Vector fields = new Vector();
collectClassComponents(fields, thisClass, new Vector(),
false, false, true);
/* replace the fields with the ROMized version in order
* get the typeid info. */
replaceMembers(thisClass, fields, false);
/* set 'fieldtable' */
setClassMembers(thisClass, fields, false);
/* collect methods */
Vector methods = new Vector();
collectClassComponents(methods, thisClass, new Vector(),
true, false, true);
/* replace the methods with ROMized verison in order
* to get the typeid info. */
replaceMembers(thisClass, methods, true);
/* set 'methodtable' */
setClassMembers(thisClass, methods, true);
}
}
/*
* Sort classes in 'classesProcessed' Vector by typeid, and add them
* to 'sortedClasses' LinkedList. Only visible classes are added to
* 'sortedClasses'. Also add the corresponding internal VM class to
* 'sortedCVMClasses' in the same order.
*/
private boolean sortClasses()
{
int nClasses = classesProcessed.size();
for (int i = 0; i < nClasses; i++) {
ClassInfo thisClass = (ClassInfo)classesProcessed.get(i);
if (!includedAndVisible(thisClass)) {
continue; /* excluded. Skip this class. */
}
ClassInfo cdcClass;
CVMClass cvmClass;
int id;
int nSortedClasses;
int j;
cdcClass = ClassTable.lookupClass(thisClass.className);
if (cdcClass == null) {
sortedClasses.clear();
sortedCVMClasses.clear();
return false;
}
cvmClass = (CVMClass)cdcClass.vmClass;
id = cvmClass.getClassId();
nSortedClasses = sortedCVMClasses.size();
for (j = 0; j < nSortedClasses; j++) {
if (id < ((CVMClass)sortedCVMClasses.get(j)).getClassId()) {
break; /* found location */
}
}
/* add to the sorted linked lists */
sortedClasses.add(j, thisClass);
sortedCVMClasses.add(j, cvmClass);
}
return true;
}
// keywords in easy-to-manipulate integer form
private static final int NOKEY = 0;
private static final int CLASS = 1;
private static final int FIELDS = 2;
private static final int METHODS = 3;
private static Hashtable keywords = new Hashtable();
static {
keywords.put("", new Integer(NOKEY));
keywords.put("CLASS", new Integer(CLASS));
keywords.put("FIELDS", new Integer(FIELDS));
keywords.put("METHODS", new Integer(METHODS));
}
/* Get the integer form of the keyword */
private static int keyword(String word) {
Integer n = (Integer)keywords.get(word);
if (n != null) {
return n.intValue();
} else {
return NOKEY;
}
}
/* Get the next word from the 'in' stream */
private String
nextWord(StreamTokenizer in) throws Exception{
int token = in.nextToken();
if (token == in.TT_EOF){
throw new EOFException("EOF");
}
if (token != in.TT_WORD){
throw new Exception("token type");
}
return in.sval;
}
private int
expecting(StreamTokenizer in, int token1, int token2, int token3)
throws Exception{
int key;
key = keyword(nextWord(in));
if (key != token1 && key != token2 && key != token3){
throw new Exception("token value ".concat(in.sval));
}
return key;
}
static private void
printStringArray(String name, String val[]){
if (val == null || val.length == 0)
return;
System.out.println(name);
int l = val.length;
for(int i=0; i<l; i++){
System.out.print('\t');
System.out.println(val[i]);
}
}
/*
* Parse the input class and member list. Create a ClassInfo
* for the corresponding class.
*/
private void
parseClass(StreamTokenizer in) throws Exception {
String classname = null;
String word = null;
int keyword;
int token;
LinkedList methods = new LinkedList();
LinkedList fields = new LinkedList();
try{
// parse CLASS and classname
expecting(in, CLASS, -1, -1);
classname = nextWord(in);
while(true){
// parse FIELDS or METHODS
keyword = expecting(in, FIELDS, METHODS, CLASS);
LinkedList thislist;
if (keyword == CLASS){
// beginning of next class signals the end of this one.
in.pushBack();
break;
}
thislist = (keyword == FIELDS)? fields : methods;
while(true){
String memberName = nextWord(in);
// see if this name is a keyword.
// since the memberNames, if properly formed,
// must contain :, there should be no danger of
// confusion.
keyword = keyword(memberName);
if (keyword != NOKEY){
in.pushBack();
break;
}
// make sure we have our :
if (memberName.indexOf(':') == -1){
throw new Exception("format error in ".concat(memberName));
}
thislist.add(memberName);
}
}
}finally{
if (classname != null){
// here, using empty as a prototype for String[]
String empty[] = new String[0];
String methodstrings[] = (String[])methods.toArray(empty);
String fieldstrings[] = (String[])fields.toArray(empty);
if (verbosity > 0){
System.out.print("CLASS ");
System.out.println(classname);
printStringArray(" FIELDS", fieldstrings);
printStringArray(" METHODS", methodstrings);
}
enterClassInfo(classname, methodstrings, fieldstrings);
}
}
}
/*
* 'ci' is the ClassInfo created for a class name in the minput.
* 'cdcClass' is the corresponding ROMized class. For each member
* in the 'members' array, find the ClassMemberInfo
* from the ROMized 'cdcClass'. Set the methodtable (or
* fieldtable) of 'ci' with the ClassMemberInfo vector.
*/
private void enterMemberInfo(ClassInfo ci, ClassInfo cdcClass,
String members[], boolean isMethod) {
int length = members.length;
Vector mv = new Vector();
ClassMemberInfo cdcma[] = (isMethod) ?
((ClassMemberInfo[])cdcClass.methods) :
((ClassMemberInfo[])cdcClass.fields);
for (int i = 0; i < length; i++) {
int idx = members[i].indexOf(':');
String membername, membertype;
membername = members[i].substring(0, idx);
membertype = members[i].substring(idx + 1);
ClassMemberInfo f = findROMMember(
cdcClass, cdcma, membername, membertype, isMethod);
mv.add(f);
}
setClassMembers(ci, mv, isMethod);
}
/* Create a new ClassInfo and add it to te 'classesProcessed' vector. */
private void enterClassInfo(String classname,
String methods[],
String fields[])
throws ClassNotFoundException
{
ClassInfo cdcClass = ClassTable.lookupClass(classname);
if (cdcClass == null) {
/*
* The ROMized class doesn't exist. This probably is not
* a fully ROMized build, and we only support ROMized build.
* Remove all classes from the 'classesProcessed' vector
* and bail out.
*/
classesProcessed.removeAllElements();
throw new ClassNotFoundException("Can't find ROMized " + classname);
}
/* Create the ClassInfo, which only contains className,
methods and fields information that we care about.
*/
ClassInfo ci = new ClassInfo(false);
ci.className = classname;
ci.access = Const.ACC_PUBLIC;
/* enter fields info */
enterMemberInfo(ci, cdcClass, fields, false);
/* process methods */
enterMemberInfo(ci, cdcClass, methods, true);
/* add the class to the 'classesProcessed' vector */
classesProcessed.add(ci);
}
/*
* Parse the 'minput'. Create ClassInfo for each class in
* list. For each class member, find the ClassMemberInfo
* from the corresponding ROMized class and set the ClassInfo's
* methodtable and fieldtable with the collected ClassMemberInfo.
*/
private void parseMInput(String minput) {
try {
FileReader inFile = new FileReader(minput);
StreamTokenizer in = new StreamTokenizer(new BufferedReader(inFile));
in.commentChar('#');
in.slashSlashComments(false);
in.eolIsSignificant(false);
in.wordChars('a','z');
in.wordChars('A','[');
in.wordChars('0','9');
in.wordChars('<','<'); // appears in name <init>
in.wordChars('>','>');
in.wordChars('_','_');
in.wordChars(':',':'); // make special separator wordChar
in.wordChars(';',';'); // make all parts of a signature wordChar
in.wordChars('(',')');
in.wordChars('/','/'); // make name component separator wordChar
in.whitespaceChars(' ',' '); // ignore space
in.whitespaceChars('\t','\t'); // ignore tabs
try{
while(true){
parseClass(in);
}
}catch(EOFException e){
// normal termination. do nothing
}catch(ClassNotFoundException e) {
return;
}catch(IOException e){
e.printStackTrace();
return;
}catch(Exception e){
e.printStackTrace();
return;
}
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean process( String clist ) throws Exception {
if (clist != null) {
commandOptions = clist;
if (! processOptions( clist )){
// malformed command-line argument or file read error
return false;
}
}
if (minput != null || memberOut != null) {
/*
* Process classes and collect visible members. We use
* the 'fieldtable' and 'methodtable' to store collected
* members.
*/
processClasses();
/*
* Sort classes in typeid order
*/
sortClasses();
/*
* Write filter data into .c file
*/
romOut = openOutputFile(romOutName);
writeRomFilter(romOut);
/* Add .c file to romjavaList */
FileWriter listOut = new FileWriter(outName + "List", true);
listOut.write(romOutName, 0, romOutName.length());
listOut.close();
/*
* Write the member .txt file.
*/
writeMemberFile();
}
/*
* Now if a classlist output is specified do it too.
*/
writeClassFile();
return true;
}
public static void main( String clist[] ){
boolean success;
try {
try {
success = new JavaAPILister().process( clist[0] );
}finally{
System.out.flush();
System.err.flush();
}
}catch (Throwable t){
success = false;
t.printStackTrace();
}
if (!success){
// process threw error or failed
System.exit(1);
}
return;
}
}