Improve glgen

+ gen script is really a bash script rather than a sh script,
  so declare that to be true. (For example, it uses pushd,
  which is a part of bash, but not a part of sh. Not sure
  how this worked until now. Possibly gen was only run in
  environments where /bin/sh was really bash.

+ Check the results of the java compile of the code generator,
  and abort the script if the compile fails.

+ Turn on the bash shell option that guards against using
  uninitialized variables in the script.

+ Remove the generated class files.

Refactor JniCodeEmitter into two classes: a general-purpose
JniCodeEmitter and a specific Jsr239CodeEmitter. The hope is
to use JniCodeEmitter as a base for emitting static OpenGL ES
bindings.
This commit is contained in:
Jack Palevich
2009-04-14 19:00:09 -07:00
parent fb9edfd165
commit 4e70a9e462
4 changed files with 281 additions and 280 deletions

View File

@@ -1,4 +1,5 @@
#!/bin/sh
#!/bin/bash
set -u
rm -rf out generated
mkdir out
@@ -12,12 +13,18 @@ echo "public interface Canvas {}" >> out/android/graphics/Canvas.java
GLFILE=out/javax/microedition/khronos/opengles/GL.java
cp stubs/GLHeader.java-if $GLFILE
GLGEN_FILES="CFunc.java CType.java CodeEmitter.java GenerateGL.java JFunc.java JType.java JniCodeEmitter.java ParameterChecker.java"
GLGEN_FILES="CFunc.java CType.java CodeEmitter.java GenerateGL.java JFunc.java JniCodeEmitter.java JType.java Jsr239CodeEmitter.java ParameterChecker.java"
pushd src > /dev/null
javac ${GLGEN_FILES}
JAVAC_RESULT=$?
if [ $JAVAC_RESULT -ne 0 ]; then
echo "Could not compile glgen."
exit $JAVAC_RESULT
fi
popd > /dev/null
java -classpath src GenerateGL -c glspec-1.0 glspec-1.0ext glspec-1.1 glspec-1.1ext glspec-1.1extpack glspec-checks
rm src/*.class
pushd out > /dev/null
mkdir classes

View File

@@ -23,7 +23,6 @@ public class GenerateGL {
PrintStream glImplStream,
PrintStream cStream) throws Exception {
String s = null;
int counter = 0;
while ((s = specReader.readLine()) != null) {
if (s.trim().startsWith("//")) {
continue;
@@ -120,7 +119,7 @@ public class GenerateGL {
ParameterChecker checker = new ParameterChecker(checksReader);
CodeEmitter emitter =
new JniCodeEmitter(classPathName,
new Jsr239CodeEmitter(classPathName,
checker,
gl10Stream, gl10ExtStream,
gl11Stream, gl11ExtStream, gl11ExtPackStream,

View File

@@ -4,90 +4,47 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
/**
* Emits a Java interface and Java & C implementation for a C function.
*
* <p> The Java interface will have Buffer and array variants for functions that
* have a typed pointer argument. The array variant will convert a single "<type> *data"
* argument to a pair of arguments "<type>[] data, int offset".
*/
public class JniCodeEmitter implements CodeEmitter {
public class JniCodeEmitter {
// If true, use C++ style for calling through a JNIEnv *:
// env->Func(...)
// If false, use C style:
// (*env)->Func(env, ...)
static final boolean mUseCPlusPlus = true;
boolean mUseContextPointer = true;
String mClassPathName;
ParameterChecker mChecker;
PrintStream mJava10InterfaceStream;
PrintStream mJava10ExtInterfaceStream;
PrintStream mJava11InterfaceStream;
PrintStream mJava11ExtInterfaceStream;
PrintStream mJava11ExtPackInterfaceStream;
PrintStream mJavaImplStream;
PrintStream mCStream;
PrintStream mJavaInterfaceStream;
List<String> nativeRegistrations = new ArrayList<String>();
protected boolean mUseContextPointer = true;
protected String mClassPathName;
protected ParameterChecker mChecker;
protected List<String> nativeRegistrations = new ArrayList<String>();
boolean needsExit;
static String indent = " ";
protected static String indent = " ";
HashSet<String> mFunctionsEmitted = new HashSet<String>();
/**
* @param java10InterfaceStream the PrintStream to which to emit the Java interface for GL 1.0 functions
* @param java10ExtInterfaceStream the PrintStream to which to emit the Java interface for GL 1.0 extension functions
* @param java11InterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 functions
* @param java11ExtInterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 Extension functions
* @param java11ExtPackInterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 Extension Pack functions
* @param javaImplStream the PrintStream to which to emit the Java implementation
* @param cStream the PrintStream to which to emit the C implementation
*/
public JniCodeEmitter(String classPathName,
ParameterChecker checker,
PrintStream java10InterfaceStream,
PrintStream java10ExtInterfaceStream,
PrintStream java11InterfaceStream,
PrintStream java11ExtInterfaceStream,
PrintStream java11ExtPackInterfaceStream,
PrintStream javaImplStream,
PrintStream cStream,
boolean useContextPointer) {
mClassPathName = classPathName;
mChecker = checker;
mJava10InterfaceStream = java10InterfaceStream;
mJava10ExtInterfaceStream = java10ExtInterfaceStream;
mJava11InterfaceStream = java11InterfaceStream;
mJava11ExtInterfaceStream = java11ExtInterfaceStream;
mJava11ExtPackInterfaceStream = java11ExtPackInterfaceStream;
mJavaImplStream = javaImplStream;
mCStream = cStream;
mUseContextPointer = useContextPointer;
}
public void setVersion(int version, boolean ext, boolean pack) {
if (version == 0) {
mJavaInterfaceStream = ext ? mJava10ExtInterfaceStream :
mJava10InterfaceStream;
} else if (version == 1) {
mJavaInterfaceStream = ext ?
(pack ? mJava11ExtPackInterfaceStream :
mJava11ExtInterfaceStream) :
mJava11InterfaceStream;
} else {
throw new RuntimeException("Bad version: " + version);
public static String getJniName(JType jType) {
String jniName = "";
if (jType.isClass()) {
return "L" + jType.getBaseType() + ";";
} else if (jType.isArray()) {
jniName = "[";
}
String baseType = jType.getBaseType();
if (baseType.equals("int")) {
jniName += "I";
} else if (baseType.equals("float")) {
jniName += "F";
} else if (baseType.equals("boolean")) {
jniName += "Z";
} else if (baseType.equals("short")) {
jniName += "S";
} else if (baseType.equals("long")) {
jniName += "L";
} else if (baseType.equals("byte")) {
jniName += "B";
}
return jniName;
}
public void emitCode(CFunc cfunc, String original) {
public void emitCode(CFunc cfunc, String original,
PrintStream javaInterfaceStream,
PrintStream javaImplStream,
PrintStream cStream) {
JFunc jfunc;
String signature;
boolean duplicate;
@@ -107,12 +64,12 @@ public class JniCodeEmitter implements CodeEmitter {
}
if (!duplicate) {
emitNativeDeclaration(jfunc, mJavaImplStream);
emitJavaCode(jfunc, mJavaImplStream);
emitNativeDeclaration(jfunc, javaImplStream);
emitJavaCode(jfunc, javaImplStream);
}
emitJavaInterfaceCode(jfunc, mJavaInterfaceStream);
emitJavaInterfaceCode(jfunc, javaInterfaceStream);
if (!duplicate) {
emitJniCode(jfunc, mCStream);
emitJniCode(jfunc, cStream);
}
}
@@ -127,12 +84,12 @@ public class JniCodeEmitter implements CodeEmitter {
}
if (!duplicate) {
emitNativeDeclaration(jfunc, mJavaImplStream);
emitNativeDeclaration(jfunc, javaImplStream);
}
emitJavaInterfaceCode(jfunc, mJavaInterfaceStream);
emitJavaInterfaceCode(jfunc, javaInterfaceStream);
if (!duplicate) {
emitJavaCode(jfunc, mJavaImplStream);
emitJniCode(jfunc, mCStream);
emitJavaCode(jfunc, javaImplStream);
emitJniCode(jfunc, cStream);
}
}
@@ -151,7 +108,7 @@ public class JniCodeEmitter implements CodeEmitter {
emitFunction(jfunc, out, false, false);
}
void emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray ) {
void emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray) {
boolean isVoid = jfunc.getType().isVoid();
boolean isPointerFunc = jfunc.getName().endsWith("Pointer") &&
jfunc.getCFunc().hasPointerArg();
@@ -194,37 +151,36 @@ public class JniCodeEmitter implements CodeEmitter {
out.println(iii + ");");
}
void printIfcheckPostamble(PrintStream out, boolean isBuffer,
boolean emitExceptionCheck, String iii) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
"offset", "_remaining", iii);
}
void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
String iii) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
"offset", "_remaining", iii);
}
void printIfcheckPostamble(PrintStream out, boolean isBuffer,
boolean emitExceptionCheck,
String offset, String remaining, String iii) {
out.println(iii + " default:");
out.println(iii + " _needed = 0;");
out.println(iii + " break;");
out.println(iii + "}");
void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
String offset, String remaining, String iii) {
out.println(iii + " default:");
out.println(iii + " _needed = 0;");
out.println(iii + " break;");
out.println(iii + "}");
out.println(iii + "if (" + remaining + " < _needed) {");
if (emitExceptionCheck) {
out.println(iii + indent + "_exception = 1;");
}
out.println(iii + indent +
(mUseCPlusPlus ? "_env" : "(*_env)") +
"->ThrowNew(" +
(mUseCPlusPlus ? "" : "_env, ") +
"IAEClass, " +
"\"" +
(isBuffer ?
"remaining()" : "length - " + offset) +
" < needed\");");
out.println(iii + indent + "goto exit;");
needsExit = true;
out.println(iii + "}");
}
out.println(iii + "if (" + remaining + " < _needed) {");
if (emitExceptionCheck) {
out.println(iii + indent + "_exception = 1;");
}
out.println(iii + indent +
(mUseCPlusPlus ? "_env" : "(*_env)") +
"->ThrowNew(" +
(mUseCPlusPlus ? "" : "_env, ") +
"IAEClass, " +
"\"" +
(isBuffer ?
"remaining()" : "length - " + offset) +
" < needed\");");
out.println(iii + indent + "goto exit;");
needsExit = true;
out.println(iii + "}");
}
boolean isNullAllowed(CFunc cfunc) {
String[] checks = mChecker.getChecks(cfunc.getName());
@@ -310,115 +266,106 @@ public class JniCodeEmitter implements CodeEmitter {
}
void emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out,
boolean isBuffer, boolean emitExceptionCheck,
String offset, String remaining, String iii) {
CType returnType = cfunc.getType();
boolean isVoid = returnType.isVoid();
boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii) {
String[] checks = mChecker.getChecks(cfunc.getName());
String checkVar;
String retval = getErrorReturnValue(cfunc);
String[] checks = mChecker.getChecks(cfunc.getName());
boolean lastWasIfcheck = false;
boolean lastWasIfcheck = false;
int index = 1;
if (checks != null) {
boolean remainingDeclared = false;
boolean nullCheckDeclared = false;
boolean offsetChecked = false;
while (index < checks.length) {
if (checks[index].startsWith("check")) {
if (lastWasIfcheck) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
offset, remaining, iii);
int index = 1;
if (checks != null) {
while (index < checks.length) {
if (checks[index].startsWith("check")) {
if (lastWasIfcheck) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
offset, remaining, iii);
}
lastWasIfcheck = false;
if (cname != null && !cname.equals(checks[index + 1])) {
index += 3;
continue;
}
out.println(iii + "if (" + remaining + " < " +
checks[index + 2] +
") {");
if (emitExceptionCheck) {
out.println(iii + indent + "_exception = 1;");
}
String exceptionClassName = "IAEClass";
// If the "check" keyword was of the form
// "check_<class name>", use the class name in the
// exception to be thrown
int underscore = checks[index].indexOf('_');
if (underscore >= 0) {
exceptionClassName = checks[index].substring(underscore + 1) + "Class";
}
lastWasIfcheck = false;
if (cname != null && !cname.equals(checks[index + 1])) {
index += 3;
continue;
out.println(iii + indent +
(mUseCPlusPlus ? "_env" : "(*_env)") +
"->ThrowNew(" +
(mUseCPlusPlus ? "" : "_env, ") +
exceptionClassName + ", " +
"\"" +
(isBuffer ?
"remaining()" : "length - " + offset) +
" < " + checks[index + 2] +
"\");");
out.println(iii + indent + "goto exit;");
needsExit = true;
out.println(iii + "}");
index += 3;
} else if (checks[index].equals("ifcheck")) {
String[] matches = checks[index + 4].split(",");
if (!lastWasIfcheck) {
out.println(iii + "int _needed;");
out.println(iii +
"switch (" +
checks[index + 3] +
") {");
}
for (int i = 0; i < matches.length; i++) {
out.println("#if defined(" + matches[i] + ")");
out.println(iii +
" case " +
matches[i] +
":");
out.println("#endif // defined(" + matches[i] + ")");
}
out.println(iii +
" _needed = " +
checks[index + 2] +
";");
out.println(iii +
" break;");
lastWasIfcheck = true;
index += 5;
} else if (checks[index].equals("return")) {
// ignore
index += 2;
} else if (checks[index].equals("unsupported")) {
// ignore
index += 1;
} else if (checks[index].equals("nullAllowed")) {
// ignore
index += 1;
} else {
System.out.println("Error: unknown keyword \"" +
checks[index] + "\"");
System.exit(0);
}
}
out.println(iii + "if (" + remaining + " < " +
checks[index + 2] +
") {");
if (emitExceptionCheck) {
out.println(iii + indent + "_exception = 1;");
}
String exceptionClassName = "IAEClass";
// If the "check" keyword was of the form
// "check_<class name>", use the class name in the
// exception to be thrown
int underscore = checks[index].indexOf('_');
if (underscore >= 0) {
exceptionClassName = checks[index].substring(underscore + 1) + "Class";
}
out.println(iii + indent +
(mUseCPlusPlus ? "_env" : "(*_env)") +
"->ThrowNew(" +
(mUseCPlusPlus ? "" : "_env, ") +
exceptionClassName + ", " +
"\"" +
(isBuffer ?
"remaining()" : "length - " + offset) +
" < " + checks[index + 2] +
"\");");
}
out.println(iii + indent + "goto exit;");
needsExit = true;
out.println(iii + "}");
index += 3;
} else if (checks[index].equals("ifcheck")) {
String[] matches = checks[index + 4].split(",");
if (!lastWasIfcheck) {
out.println(iii + "int _needed;");
out.println(iii +
"switch (" +
checks[index + 3] +
") {");
}
for (int i = 0; i < matches.length; i++) {
out.println("#if defined(" + matches[i] + ")");
out.println(iii +
" case " +
matches[i] +
":");
out.println("#endif // defined(" + matches[i] + ")");
}
out.println(iii +
" _needed = " +
checks[index + 2] +
";");
out.println(iii +
" break;");
lastWasIfcheck = true;
index += 5;
} else if (checks[index].equals("return")) {
// ignore
index += 2;
} else if (checks[index].equals("unsupported")) {
// ignore
index += 1;
} else if (checks[index].equals("nullAllowed")) {
// ignore
index += 1;
} else {
System.out.println("Error: unknown keyword \"" +
checks[index] + "\"");
System.exit(0);
if (lastWasIfcheck) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii);
}
}
}
if (lastWasIfcheck) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii);
}
}
boolean hasNonConstArg(JFunc jfunc, CFunc cfunc,
List<Integer> nonPrimitiveArgs) {
boolean hasNonConstArg(JFunc jfunc, CFunc cfunc, List<Integer> nonPrimitiveArgs) {
if (nonPrimitiveArgs.size() > 0) {
for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
int idx = nonPrimitiveArgs.get(i).intValue();
@@ -447,9 +394,7 @@ public class JniCodeEmitter implements CodeEmitter {
* if interfaceDecl: public <returntype> func(args);
* if !interfaceDecl: public <returntype> func(args) { body }
*/
void emitFunction(JFunc jfunc,
PrintStream out,
boolean nativeDecl, boolean interfaceDecl) {
void emitFunction(JFunc jfunc, PrintStream out, boolean nativeDecl, boolean interfaceDecl) {
boolean isPointerFunc =
jfunc.getName().endsWith("Pointer") &&
jfunc.getCFunc().hasPointerArg();
@@ -559,29 +504,43 @@ public class JniCodeEmitter implements CodeEmitter {
out.println();
}
public static String getJniName(JType jType) {
String jniName = "";
if (jType.isClass()) {
return "L" + jType.getBaseType() + ";";
} else if (jType.isArray()) {
jniName = "[";
public void addNativeRegistration(String s) {
nativeRegistrations.add(s);
}
public void emitNativeRegistration(PrintStream cStream) {
cStream.println("static const char *classPathName = \"" +
mClassPathName +
"\";");
cStream.println();
cStream.println("static JNINativeMethod methods[] = {");
cStream.println("{\"_nativeClassInit\", \"()V\", (void*)nativeClassInit },");
Iterator<String> i = nativeRegistrations.iterator();
while (i.hasNext()) {
cStream.println(i.next());
}
String baseType = jType.getBaseType();
if (baseType.equals("int")) {
jniName += "I";
} else if (baseType.equals("float")) {
jniName += "F";
} else if (baseType.equals("boolean")) {
jniName += "Z";
} else if (baseType.equals("short")) {
jniName += "S";
} else if (baseType.equals("long")) {
jniName += "L";
} else if (baseType.equals("byte")) {
jniName += "B";
}
return jniName;
cStream.println("};");
cStream.println();
cStream.println("int register_com_google_android_gles_jni_GLImpl(JNIEnv *_env)");
cStream.println("{");
cStream.println(indent +
"int err;");
cStream.println(indent +
"err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));");
cStream.println(indent + "return err;");
cStream.println("}");
}
public JniCodeEmitter() {
super();
}
String getJniType(JType jType) {
@@ -721,8 +680,6 @@ public class JniCodeEmitter implements CodeEmitter {
int numBuffers = 0;
for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
int idx = nonPrimitiveArgs.get(i).intValue();
int cIndex = jfunc.getArgCIndex(idx);
String cname = cfunc.getArgName(cIndex);
if (jfunc.getArgType(idx).isArray()) {
++numArrays;
}
@@ -832,8 +789,6 @@ public class JniCodeEmitter implements CodeEmitter {
out.println();
}
String retval = isVoid ? "" : " _returnValue";
// Emit 'GetPrimitiveArrayCritical' for arrays
// Emit 'GetPointer' calls for Buffer pointers
int bufArgIdx = 0;
@@ -1047,38 +1002,4 @@ public class JniCodeEmitter implements CodeEmitter {
out.println();
}
public void addNativeRegistration(String s) {
nativeRegistrations.add(s);
}
public void emitNativeRegistration() {
mCStream.println("static const char *classPathName = \"" +
mClassPathName +
"\";");
mCStream.println();
mCStream.println("static JNINativeMethod methods[] = {");
mCStream.println("{\"_nativeClassInit\", \"()V\", (void*)nativeClassInit },");
Iterator<String> i = nativeRegistrations.iterator();
while (i.hasNext()) {
mCStream.println(i.next());
}
mCStream.println("};");
mCStream.println();
mCStream.println("int register_com_google_android_gles_jni_GLImpl(JNIEnv *_env)");
mCStream.println("{");
mCStream.println(indent +
"int err;");
mCStream.println(indent +
"err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));");
mCStream.println(indent + "return err;");
mCStream.println("}");
}
}

View File

@@ -0,0 +1,74 @@
import java.io.PrintStream;
/**
* Emits a Java interface and Java & C implementation for a C function.
*
* <p> The Java interface will have Buffer and array variants for functions that
* have a typed pointer argument. The array variant will convert a single "<type> *data"
* argument to a pair of arguments "<type>[] data, int offset".
*/
public class Jsr239CodeEmitter extends JniCodeEmitter implements CodeEmitter {
PrintStream mJava10InterfaceStream;
PrintStream mJava10ExtInterfaceStream;
PrintStream mJava11InterfaceStream;
PrintStream mJava11ExtInterfaceStream;
PrintStream mJava11ExtPackInterfaceStream;
PrintStream mJavaImplStream;
PrintStream mCStream;
PrintStream mJavaInterfaceStream;
/**
* @param java10InterfaceStream the PrintStream to which to emit the Java interface for GL 1.0 functions
* @param java10ExtInterfaceStream the PrintStream to which to emit the Java interface for GL 1.0 extension functions
* @param java11InterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 functions
* @param java11ExtInterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 Extension functions
* @param java11ExtPackInterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 Extension Pack functions
* @param javaImplStream the PrintStream to which to emit the Java implementation
* @param cStream the PrintStream to which to emit the C implementation
*/
public Jsr239CodeEmitter(String classPathName,
ParameterChecker checker,
PrintStream java10InterfaceStream,
PrintStream java10ExtInterfaceStream,
PrintStream java11InterfaceStream,
PrintStream java11ExtInterfaceStream,
PrintStream java11ExtPackInterfaceStream,
PrintStream javaImplStream,
PrintStream cStream,
boolean useContextPointer) {
mClassPathName = classPathName;
mChecker = checker;
mJava10InterfaceStream = java10InterfaceStream;
mJava10ExtInterfaceStream = java10ExtInterfaceStream;
mJava11InterfaceStream = java11InterfaceStream;
mJava11ExtInterfaceStream = java11ExtInterfaceStream;
mJava11ExtPackInterfaceStream = java11ExtPackInterfaceStream;
mJavaImplStream = javaImplStream;
mCStream = cStream;
mUseContextPointer = useContextPointer;
}
public void setVersion(int version, boolean ext, boolean pack) {
if (version == 0) {
mJavaInterfaceStream = ext ? mJava10ExtInterfaceStream :
mJava10InterfaceStream;
} else if (version == 1) {
mJavaInterfaceStream = ext ?
(pack ? mJava11ExtPackInterfaceStream :
mJava11ExtInterfaceStream) :
mJava11InterfaceStream;
} else {
throw new RuntimeException("Bad version: " + version);
}
}
public void emitCode(CFunc cfunc, String original) {
emitCode(cfunc, original, mJavaInterfaceStream, mJavaImplStream, mCStream);
}
public void emitNativeRegistration() {
emitNativeRegistration(mCStream);
}
}