632 lines
18 KiB
Java
Raw Permalink Normal View History

2021-07-16 17:12:20 -05:00
/*
* @(#) $(JCGO)/reflgen/com/ivmaisoft/jcgorefl/GenRefl.java --
* "GenRefl" utility source (part of JCGO).
**
* Project: JCGO (http://www.ivmaisoft.com/jcgo/)
* Copyright (C) 2001-2009 Ivan Maidanski <ivmai@ivmaisoft.com>
* Use is subject to license terms. No warranties. All rights reserved.
*/
/*
* This 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.
**
* This software 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 (GPL) for more details.
*/
/**
* The utility reads data file (eg., produced by "TraceJni" utility) and
* generates helper VMReflector class source files for JCGO translator.
* The data file contains method/field reflection dependency info, i.e.
* which reflected constructors, methods, fields (and proxy classes) are
* used and by which methods.
*/
package com.ivmaisoft.jcgorefl;
import java.io.*;
import java.util.Enumeration;
import java.util.Vector;
public final class GenRefl
{
static /* final */ String PROGNAME = "JCGO GenRefl";
static /* final */ String VERSION = "1.2";
private GenRefl() {}
private static Vector readFileLines(File file)
{
Vector lines = new Vector();
try
{
BufferedReader infile = new BufferedReader(new FileReader(file));
// Portability - use the following for Java 1.0
// DataInputStream infile = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
String str;
while ((str = infile.readLine()) != null)
lines.addElement(str);
infile.close();
}
catch (IOException e)
{
System.err.println("Cannot read file: " + file.getPath());
System.exit(2);
}
return lines;
}
public static final void main(String args[])
{
if (args.length <= 2 || !args[0].equals("-d"))
{
System.out.println(PROGNAME + " utility v" + VERSION);
System.out.println(
"Copyright (C) 2001-2009 Ivan Maidanski <ivmai@ivmaisoft.com>");
System.out.println(
"This is free software. All rights reserved. See README file.");
System.out.println(
"The utility generates VMReflector class source files for JCGO.");
System.out.println();
System.out.println("Arguments: -d <outdir> file1.dat ...");
if (args.length > 0)
System.exit(1);
return;
}
int i = 2;
Vector packages = new Vector();
do
{
readAndDecodeFile(packages, args[i]);
} while (++i < args.length);
System.out.println("Writing helper java files...");
writeAllPackages(new File(args[1]), packages);
System.out.println("Done.");
}
private static void readAndDecodeFile(Vector packages, String filename)
{
System.out.println("Reading data file: " + filename);
Vector lines = readFileLines(new File(filename));
System.out.println("Decoding data...");
decodeData(packages, lines);
}
private static void writeFileSafe(File basedir, String filename,
Vector lines)
{
File file = new File(basedir, filename);
String parent = file.getParent();
if (parent != null && basedir.exists())
(new File(parent)).mkdirs();
writeFileLines(file, lines);
}
private static void writeFileLines(File file, Vector lines)
{
try
{
PrintStream outfile = new PrintStream(new BufferedOutputStream(
new FileOutputStream(file)));
Enumeration en = lines.elements();
while (en.hasMoreElements())
outfile.println((String) en.nextElement());
outfile.close();
}
catch (IOException e)
{
System.err.println("Cannot write file: " + file.getPath());
System.exit(3);
}
}
private static void decodeData(Vector packages, Vector lines)
{
Enumeration en = lines.elements();
for (int lineNum = 1; en.hasMoreElements(); lineNum++)
{
String str = ((String) en.nextElement()).trim();
if (str.length() > 0 && str.charAt(0) != '#' && (!str.endsWith(".*") ||
!isValidName(str.substring(0, str.length() - 2))) &&
!decodeDataLine(packages, str))
{
System.err.println("Data file error at line: " + lineNum);
System.exit(4);
}
}
}
private static boolean decodeDataLine(Vector packages, String str)
{
String parts[] = splitDataLine(str);
if (parts == null)
return false;
if (parts[1].equals("?") || parts[2].equals("?") ||
parts[4].equals("<clinit>()"))
return true;
Object apackage[] = getElementArrByName(packages, parts[0]);
Object aclass[] = getElementArrByName((Vector) apackage[1], parts[1]);
Object method[] = getElementArrByName((Vector) aclass[1],
parts[2].equals("*") ? "_" :
parts[2].equals("<native>") ? "_native" :
parts[2].equals("<clinit>") ? "_clinit" : parts[2]);
Object klass[] = getElementArrByName((Vector) method[1], parts[3]);
return listInsertStr((Vector) klass[1], parts[4]) ||
!parts[2].equals(method[0]);
}
private static String[] splitDataLine(String str)
{
String parts[] = new String[5];
int colonPos = str.indexOf(':');
if (colonPos <= 3 || str.charAt(colonPos - 3) != '(' ||
str.charAt(colonPos - 2) != '*' || str.charAt(colonPos - 1) != ')')
return null;
int aclassEndPos = str.lastIndexOf('.', colonPos - 4);
if (aclassEndPos < 0)
return null;
int pkgEndPos = str.lastIndexOf('.', aclassEndPos - 1);
if (pkgEndPos >= 0)
{
parts[0] = str.substring(0, pkgEndPos);
if (!isValidName(parts[0]))
return null;
}
else parts[0] = "";
parts[1] = str.substring(pkgEndPos + 1, aclassEndPos);
if (!parts[1].equals("?") && !isValidId(parts[1]))
return null;
String id = str.substring(aclassEndPos + 1, colonPos - 3);
if (!id.equals("?") && !id.equals("<init>") && !id.equals("<clinit>") &&
!id.equals("<native>") && !id.equals("*") && !isValidId(id))
return null;
parts[2] = id;
int sigPos = str.indexOf('(', colonPos);
if (sigPos < 0)
sigPos = str.length();
int klassEndPos = str.lastIndexOf('.', sigPos - 1);
if (klassEndPos > colonPos)
{
parts[3] = str.substring(colonPos + 1, klassEndPos);
if (!isValidName(parts[3]) ||
parts[3].equals(str.substring(0, aclassEndPos)))
return null;
}
else
{
parts[3] = str.substring(0, aclassEndPos);
klassEndPos = colonPos;
}
id = str.substring(klassEndPos + 1, sigPos);
if (id.equals("<init>") || id.equals("<clinit>"))
{
if (sigPos >= str.length())
return null;
}
else if (!id.equals("*") && !isValidId(id))
return null;
if (sigPos < str.length())
{
if (str.charAt(str.length() - 1) != ')')
return null;
if (str.length() - sigPos != 3 || str.charAt(sigPos + 1) != '*')
{
if (str.indexOf('.', sigPos + 1) >= 0)
return null;
char ch;
while ((ch = str.charAt(++sigPos)) != ')')
{
while (ch == '[')
ch = str.charAt(++sigPos);
if (ch == 'L')
{
int pos = str.indexOf(';', sigPos + 1);
if (pos < 0 ||
!isValidName(str.substring(sigPos + 1, pos).replace('/', '.')))
return null;
sigPos = pos;
}
else if ("ZBCSIJFD".indexOf(ch) < 0)
return null;
}
}
}
parts[4] = str.substring(klassEndPos + 1);
return parts;
}
private static Object[] getElementArrByName(Vector vect, String name)
{
int low = 0;
int high = vect.size() - 1;
Object element[];
while (low <= high)
{
int mid = (low + high) >> 1;
element = (Object[]) vect.elementAt(mid);
int cmp = strCompare(name, (String) element[0]);
if (cmp == 0)
return element;
if (cmp > 0)
low = mid + 1;
else high = mid - 1;
}
element = new Object[2];
element[0] = name;
element[1] = new Vector();
vect.insertElementAt(element, low);
return element;
}
private static boolean listInsertStr(Vector vect, String str)
{
int pos = listSearchStr(vect, str);
if (pos >= 0)
return false;
vect.insertElementAt(str, -pos - 1);
return true;
}
private static boolean listContainsStartsWith(Vector vect, String str)
{
int pos = listSearchStr(vect, str);
if (pos >= 0)
return true;
pos = -pos - 1;
return vect.size() > pos && ((String) vect.elementAt(pos)).startsWith(str);
}
private static int listSearchStr(Vector vect, String str)
{
int low = 0;
int high = vect.size() - 1;
while (low <= high)
{
int mid = (low + high) >> 1;
int cmp = strCompare(str, (String) vect.elementAt(mid));
if (cmp == 0)
return mid;
if (cmp > 0)
low = mid + 1;
else high = mid - 1;
}
return -low - 1;
}
private static boolean isValidName(String name)
{
int pos = 0;
do
{
int next = name.indexOf('.', pos);
if (!isValidId(name.substring(pos, next >= 0 ? next : name.length())))
return false;
pos = next + 1;
} while (pos > 0);
return true;
}
private static boolean isValidId(String id)
{
int pos = id.length();
if (pos == 0)
return false;
char ch;
do
{
ch = id.charAt(--pos);
if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z') &&
(ch < '0' || ch > '9') && ch != '_' && ch != '$')
return false;
} while (pos > 0);
return ch < '0' || ch > '9';
}
private static void writeAllPackages(File basedir, Vector packages)
{
Enumeration en = packages.elements();
while (en.hasMoreElements())
{
Object apackage[] = (Object[]) en.nextElement();
writeFileForPackage(basedir, (String) apackage[0], (Vector) apackage[1]);
}
}
private static void writeFileForPackage(File basedir, String id,
Vector classes)
{
System.out.println("Producing VMReflector for package: " +
(id.length() > 0 ? id : "<unspecified>"));
writeFileSafe(basedir, (id.length() > 0 ?
id.replace('.', File.separatorChar) + File.separator : "") +
"VMReflector.java", produceLinesForPackage(id, classes));
}
private static Vector produceLinesForPackage(String id, Vector classes)
{
Vector lines = new Vector();
lines.addElement("/* DO NOT EDIT THIS FILE - it is machine generated (" +
PROGNAME + " v" + VERSION + ") */");
lines.addElement("");
if (id.length() > 0)
{
lines.addElement("package " + id + ";");
lines.addElement("");
}
lines.addElement("final class VMReflector");
lines.addElement("{");
Enumeration en = classes.elements();
while (en.hasMoreElements())
{
Object aclass[] = (Object[]) en.nextElement();
produceLinesForClass(lines, (String) aclass[0], (Vector) aclass[1]);
}
lines.addElement("}");
return lines;
}
private static void produceLinesForClass(Vector lines, String id,
Vector methods)
{
lines.addElement("");
lines.addElement(" static final class _" + id);
lines.addElement(" {");
Enumeration en = methods.elements();
while (en.hasMoreElements())
{
Object method[] = (Object[]) en.nextElement();
produceLinesForMethod(lines, id, (String) method[0], (Vector) method[1]);
}
lines.addElement(" }");
}
private static void produceLinesForMethod(Vector lines, String classId,
String id, Vector klasses)
{
lines.addElement("");
lines.addElement(" " + " " + (id.equals("<init>") ? "_" + classId :
"void " + id) + "()");
lines.addElement(" " + " " + " throws java.lang.Exception");
lines.addElement(" " + " {");
Enumeration en = klasses.elements();
while (en.hasMoreElements())
{
Object klass[] = (Object[]) en.nextElement();
produceGetDeclaredFor(lines, (String) klass[0], (Vector) klass[1]);
}
lines.addElement(" " + " }");
}
private static void produceGetDeclaredFor(Vector lines, String klassName,
Vector nameSigs)
{
boolean allFields = listSearchStr(nameSigs, "*") >= 0;
boolean allCtors = listSearchStr(nameSigs, "<init>(*)") >= 0;
boolean allMethods = listContainsStartsWith(nameSigs, "*(");
if (allFields)
produceGetDeclaredField(lines, klassName, "*");
if (allCtors)
{
String ifacesSig = decodeProxyClassName(klassName);
if (ifacesSig != null)
produceGetProxyClass(lines, ifacesSig);
else produceGetDeclaredConstr(lines, klassName, "*");
}
if (allMethods)
produceGetDeclaredMethod(lines, klassName, "*", "*");
Enumeration en = nameSigs.elements();
while (en.hasMoreElements())
{
String nameSig = (String) en.nextElement();
if (nameSig.endsWith(")"))
{
int sigPos = nameSig.indexOf('(');
if (nameSig.startsWith("<init>"))
{
if (!allCtors)
{
String ifacesSig;
if (nameSig.equals("<init>(Ljava/lang/reflect/InvocationHandler;)") &&
(ifacesSig = decodeProxyClassName(klassName)) != null)
produceGetProxyClass(lines, ifacesSig);
else produceGetDeclaredConstr(lines, klassName,
nameSig.substring(sigPos + 1, nameSig.length() - 1));
}
}
else
{
if (!allMethods && (nameSig.endsWith("(*)") ||
listSearchStr(nameSigs, nameSig.substring(0, sigPos) + "(*)") < 0))
produceGetDeclaredMethod(lines, klassName, nameSig.substring(0,
sigPos), nameSig.substring(sigPos + 1, nameSig.length() - 1));
}
}
else
{
if (!allFields)
produceGetDeclaredField(lines, klassName, nameSig);
}
}
}
private static String decodeProxyClassName(String klassName)
{
klassName = cutStrPrefix(klassName.substring(
klassName.lastIndexOf('.') + 1), "$Proxy");
if (klassName == null)
return null;
String ifacesSig = "";
int next = klassName.length();
while (next > 0)
{
int pos = klassName.lastIndexOf("$00", next - 1);
if (pos < 0)
return null;
String name = klassName.substring(pos + 3, next);
if (name.length() == 0)
return null;
next = pos;
pos = -1;
while ((pos = name.indexOf("$0", pos + 1)) >= 0)
name = name.substring(0, pos) + "/" + name.substring(pos + 2);
pos = -1;
while ((pos = name.indexOf("$$", pos + 1)) >= 0)
name = name.substring(0, pos) + name.substring(pos + 1);
ifacesSig = "L" + name + ";" + ifacesSig;
}
return ifacesSig;
}
private static String cutStrPrefix(String str, String prefix)
{
return str.startsWith(prefix) ? str.substring(prefix.length()) : null;
}
private static void produceGetProxyClass(Vector lines, String ifacesSig)
{
lines.addElement(" " + " " +
" java.lang.reflect.Proxy.getProxyClass(null, new java.lang.Class[]" +
(ifacesSig.length() > 0 ? "" : " {});"));
if (ifacesSig.length() > 0)
produceGetDeclaredTypesTail(lines, ifacesSig);
}
private static void produceGetDeclaredField(Vector lines, String klassName,
String id)
{
produceGetDeclaredHead(lines, klassName, true);
lines.addElement(" " + " " + " " + " " + (id.equals("*") ?
"getDeclaredFields(" : "getDeclaredField(\"" + id + "\"") + ");");
}
private static void produceGetDeclaredHead(Vector lines, String klassName,
boolean initialize)
{
lines.addElement(" " + " " + " " + produceClassLiteral(klassName, 0,
initialize) + ".");
}
private static void produceGetDeclaredConstr(Vector lines, String klassName,
String sig)
{
produceGetDeclaredHead(lines, klassName, false);
lines.addElement(" " + " " + " " + " " + (sig.equals("*") ?
"getDeclaredConstructors();" : "getDeclaredConstructor(" +
"new java.lang.Class[]" + (sig.length() > 0 ? "" : " {});")));
if (sig.length() > 0 && !sig.equals("*"))
produceGetDeclaredTypesTail(lines, sig);
}
private static void produceGetDeclaredMethod(Vector lines, String klassName,
String id, String sig)
{
produceGetDeclaredHead(lines, klassName, true);
lines.addElement(" " + " " + " " + " " + (id.equals("*") ?
"getDeclaredMethods();" : "getDeclaredMethod(\"" + id + "\", " +
(sig.equals("*") ? "null);" : "new java.lang.Class[]" +
(sig.length() > 0 ? "" : " {});"))));
if (sig.length() > 0 && !id.equals("*") && !sig.equals("*"))
produceGetDeclaredTypesTail(lines, sig);
}
private static void produceGetDeclaredTypesTail(Vector lines,
String sig)
{
lines.addElement(" " + " " + " " + " {");
int sigPos = 0;
while (sig.length() > sigPos)
{
int dims = 0;
char ch;
while ((ch = sig.charAt(sigPos++)) == '[')
dims++;
String typeName;
switch (ch)
{
case 'Z':
typeName = "boolean";
break;
case 'B':
typeName = "byte";
break;
case 'C':
typeName = "char";
break;
case 'S':
typeName = "short";
break;
case 'I':
typeName = "int";
break;
case 'J':
typeName = "long";
break;
case 'F':
typeName = "float";
break;
case 'D':
typeName = "double";
break;
default:
int pos = sig.indexOf(';', sigPos);
typeName = sig.substring(sigPos, pos).replace('/', '.');
sigPos = pos + 1;
break;
}
lines.addElement(" " + " " + " " + " " + " " +
produceClassLiteral(typeName, dims, false) + ",");
}
lines.addElement(" " + " " + " " + " });");
}
private static String produceClassLiteral(String klassName, int dims,
boolean initialize)
{
return isCoreClassName(klassName) ? klassName + strFill("[]", dims) +
".class" : "java.lang.Class.forName(\"" + (dims > 0 ?
strFill("[", dims) + "L" + klassName + ";" : klassName) + "\"" +
(initialize || dims > 0 ? "" : ", false, null") + ")";
}
private static boolean isCoreClassName(String klassName)
{
return klassName.equals("boolean") || klassName.equals("byte") ||
klassName.equals("char") || klassName.equals("short") ||
klassName.equals("int") || klassName.equals("long") ||
klassName.equals("float") || klassName.equals("double") ||
klassName.equals("java.lang.Class") ||
klassName.equals("java.lang.Object") ||
klassName.equals("java.lang.String");
}
private static int strCompare(String str, String str2)
{
int cmp = 0;
int len1 = str.length();
int len2 = str2.length();
int len = len1 < len2 ? len1 : len2;
for (int i = 0; i < len; i++)
if ((cmp = str.charAt(i) - str2.charAt(i)) != 0)
break;
if (cmp == 0)
cmp = len1 - len2;
return cmp;
}
private static String strFill(String str, int cnt)
{
String resStr = "";
while (cnt-- > 0)
resStr += str;
return resStr;
}
}