summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--make/gl-common.cfg45
-rw-r--r--make/gl-impl-CustomJavaCode.java96
-rw-r--r--src/classes/com/sun/gluegen/JavaConfiguration.java53
-rw-r--r--src/classes/com/sun/gluegen/JavaEmitter.java6
-rw-r--r--src/classes/com/sun/gluegen/JavaMethodBindingEmitter.java68
-rw-r--r--src/classes/com/sun/gluegen/runtime/BufferFactory.java77
6 files changed, 339 insertions, 6 deletions
diff --git a/make/gl-common.cfg b/make/gl-common.cfg
index ab8af9e09..dfe01cc05 100644
--- a/make/gl-common.cfg
+++ b/make/gl-common.cfg
@@ -331,6 +331,51 @@ BufferObjectKind Element glDrawElements
BufferObjectKind Element glDrawRangeElements
BufferObjectKind Element glDrawRangeElementsEXT
+# Range check directives for various routines
+# FIXME: some of these are really the bare minimum and won't catch
+# many classes of errors. Should extend the DebugGL to perform much
+# more error checking with e.g. glDrawElements.
+RangeCheck glColorPointer 3 1
+RangeCheck glDrawElements 3 {1}
+RangeCheck glDrawRangeElements 5 {3}
+RangeCheck glEdgeFlagPointer 1 1
+RangeCheck glElementPointerATI 1 1
+RangeCheck glFogCoordPointer 2 1
+RangeCheck glFogCoordPointerEXT 2 1
+RangeCheck glInterleavedArrays 2 1
+RangeCheck glMatrixIndexPointerARB 3 1
+RangeCheck glNormalPointer 2 1
+RangeCheck glSecondaryColorPointer 3 1
+RangeCheck glSecondaryColorPointerEXT 3 1
+RangeCheck glTexCoordPointer 3 1
+RangeCheck glVariantPointerEXT 3 1
+RangeCheck glVertexPointer 3 1
+RangeCheck glVertexAttribPointer 5 1
+RangeCheck glVertexAttribPointerARB 5 1
+RangeCheck glWeightPointerARB 3 1
+
+# Range check directives for various image-related routines
+RangeCheckBytes glColorTable 5 imageSizeInBytes({3}, {4}, {2} , 1 , 1)
+RangeCheckBytes glColorTableEXT 5 imageSizeInBytes({3}, {4}, {2} , 1 , 1)
+RangeCheckBytes glConvolutionFilter1D 5 imageSizeInBytes({3}, {4}, {2} , 1 , 1)
+RangeCheckBytes glConvolutionFilter2D 6 imageSizeInBytes({4}, {5}, {2} , {3} , 1)
+RangeCheckBytes glDrawPixels 4 imageSizeInBytes({2}, {3}, {0} , {1} , 1)
+RangeCheckBytes glReadPixels 6 imageSizeInBytes({4}, {5}, {2} , {3} , 1)
+RangeCheckBytes glTexImage1D 7 imageSizeInBytes({5}, {6}, {3} + (2 * {4}), 1 , 1)
+RangeCheckBytes glTexImage2D 8 imageSizeInBytes({6}, {7}, {3} + (2 * {5}), {4} + (2 * {5}), 1)
+RangeCheckBytes glTexImage3D 9 imageSizeInBytes({7}, {8}, {3} + (2 * {6}), {4} + (2 * {6}), {5} + (2 * {6}))
+RangeCheckBytes glTexSubImage1D 6 imageSizeInBytes({4}, {5}, {3} , 1 , 1)
+RangeCheckBytes glTexSubImage2D 8 imageSizeInBytes({6}, {7}, {4} , {5} , 1)
+RangeCheckBytes glTexSubImage3D 10 imageSizeInBytes({8}, {9}, {5} , {6} , {7})
+# Note we don't support glTexImage4DSGIS / glTexSubImage4DSGIS
+
+# Not simple to produce good range checks for these as we would need
+# to query the pipeline to see the size of the returned data before
+# fetching it
+# RangeCheckBytes glGetTexImage
+
+
+
# Javadoc for the GL class
ClassJavadoc GL /**
ClassJavadoc GL * <P> The basic interface to OpenGL, providing access to core
diff --git a/make/gl-impl-CustomJavaCode.java b/make/gl-impl-CustomJavaCode.java
index 3d927d271..7f4902c83 100644
--- a/make/gl-impl-CustomJavaCode.java
+++ b/make/gl-impl-CustomJavaCode.java
@@ -37,6 +37,102 @@ public Object getPlatformGLExtensions() {
return _context.getPlatformGLExtensions();
}
+//
+// Helpers for ensuring the correct amount of texture data
+//
+
+/** Returns the number of bytes required to fill in the appropriate
+ texture. This is regrettably a lower bound as in certain
+ circumstances OpenGL state such as unpack alignment can cause more
+ data to be required. However this should be close enough that it
+ should catch most crashes. The logic in this routine is based on
+ code in the SGI OpenGL sample implementation. */
+
+private int imageSizeInBytes(int format, int type, int w, int h, int d) {
+ int elements = 0;
+ int esize = 0;
+
+ if (w < 0) return 0;
+ if (h < 0) return 0;
+ if (d < 0) return 0;
+ switch (format) {
+ case GL_COLOR_INDEX:
+ case GL_STENCIL_INDEX:
+ elements = 1;
+ break;
+ case GL_RED:
+ case GL_GREEN:
+ case GL_BLUE:
+ case GL_ALPHA:
+ case GL_LUMINANCE:
+ case GL_DEPTH_COMPONENT:
+ elements = 1;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ elements = 2;
+ break;
+ case GL_RGB:
+ case GL_BGR:
+ elements = 3;
+ break;
+ case GL_RGBA:
+ case GL_BGRA:
+ case GL_ABGR_EXT:
+ elements = 4;
+ break;
+ case GL_HILO_NV:
+ elements = 2;
+ break;
+ default:
+ return 0;
+ }
+ switch (type) {
+ case GL_BITMAP:
+ if (format == GL_COLOR_INDEX) {
+ return (d * (h * ((w+7)/8)));
+ } else {
+ return 0;
+ }
+ case GL_BYTE:
+ case GL_UNSIGNED_BYTE:
+ esize = 1;
+ break;
+ case GL_UNSIGNED_BYTE_3_3_2:
+ case GL_UNSIGNED_BYTE_2_3_3_REV:
+ esize = 1;
+ elements = 1;
+ break;
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ esize = 2;
+ break;
+ case GL_UNSIGNED_SHORT_5_6_5:
+ case GL_UNSIGNED_SHORT_5_6_5_REV:
+ case GL_UNSIGNED_SHORT_4_4_4_4:
+ case GL_UNSIGNED_SHORT_4_4_4_4_REV:
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ case GL_UNSIGNED_SHORT_1_5_5_5_REV:
+ esize = 2;
+ elements = 1;
+ break;
+ case GL_INT:
+ case GL_UNSIGNED_INT:
+ case GL_FLOAT:
+ esize = 4;
+ break;
+ case GL_UNSIGNED_INT_8_8_8_8:
+ case GL_UNSIGNED_INT_8_8_8_8_REV:
+ case GL_UNSIGNED_INT_10_10_10_2:
+ case GL_UNSIGNED_INT_2_10_10_10_REV:
+ esize = 4;
+ elements = 1;
+ break;
+ default:
+ return 0;
+ }
+ return (elements * esize * w * h * d);
+}
+
private int[] bufTmp = new int[1];
private void checkBufferObject(String extension1,
String extension2,
diff --git a/src/classes/com/sun/gluegen/JavaConfiguration.java b/src/classes/com/sun/gluegen/JavaConfiguration.java
index 152d9cf80..ae8c46eaa 100644
--- a/src/classes/com/sun/gluegen/JavaConfiguration.java
+++ b/src/classes/com/sun/gluegen/JavaConfiguration.java
@@ -116,6 +116,8 @@ public class JavaConfiguration {
private Map/*<String,String>*/ javaMethodRenames = new HashMap();
private Map/*<String,List<String>>*/ javaPrologues = new HashMap();
private Map/*<String,List<String>>*/ javaEpilogues = new HashMap();
+ private Map/*<String,Map<Integer,String>>*/ rangeChecks = new HashMap();
+ private Map/*<String,Map<Integer,String>>*/ byteRangeChecks = new HashMap();
/** Reads the configuration file.
@param filename path to file that should be read
@@ -569,6 +571,22 @@ public class JavaConfiguration {
return res;
}
+ /** Returns a map of Integer argument numbers to expressions to be
+ used to range check the given arguments to the given function.
+ Returns null if there were no range check expressions supplied
+ for this function. */
+ public Map/*<Integer, String>*/ rangeCheckExpressions(String functionName) {
+ return (Map/*<Integer, String>*/) rangeChecks.get(functionName);
+ }
+
+ /** Returns a map of Integer argument numbers to expressions to be
+ used to range check (in size of bytes) the given arguments to
+ the given function. Returns null if there were no range check
+ expressions supplied for this function. */
+ public Map/*<Integer, String>*/ byteRangeCheckExpressions(String functionName) {
+ return (Map/*<Integer, String>*/) byteRangeChecks.get(functionName);
+ }
+
//----------------------------------------------------------------------
// Internals only below this point
//
@@ -686,6 +704,14 @@ public class JavaConfiguration {
readJavaPrologueOrEpilogue(tok, filename, lineNo, false);
// Warning: make sure delimiters are reset at the top of this loop
// because readJavaPrologueOrEpilogue changes them.
+ } else if (cmd.equalsIgnoreCase("RangeCheck")) {
+ readRangeCheck(tok, filename, lineNo, false);
+ // Warning: make sure delimiters are reset at the top of this loop
+ // because RangeCheck changes them.
+ } else if (cmd.equalsIgnoreCase("RangeCheckBytes")) {
+ readRangeCheck(tok, filename, lineNo, true);
+ // Warning: make sure delimiters are reset at the top of this loop
+ // because RangeCheckBytes changes them.
} else {
throw new RuntimeException("Unknown command \"" + cmd +
"\" in command file " + filename +
@@ -1131,6 +1157,33 @@ public class JavaConfiguration {
}
}
+ protected void readRangeCheck(StringTokenizer tok, String filename, int lineNo, boolean inBytes) {
+ try {
+ String functionName = tok.nextToken();
+ int argNum = Integer.parseInt(tok.nextToken());
+ String restOfLine = tok.nextToken("\n\r\f");
+ restOfLine = restOfLine.trim();
+ Map/*<Integer, String>*/ checksForFunction = null;
+ if (inBytes) {
+ checksForFunction = (Map/*<Integer, String>*/) byteRangeChecks.get(functionName);
+ } else {
+ checksForFunction = (Map/*<Integer, String>*/) rangeChecks.get(functionName);
+ }
+ if (checksForFunction == null) {
+ checksForFunction = new HashMap/*<Integer, String>*/();
+ if (inBytes) {
+ byteRangeChecks.put(functionName, checksForFunction);
+ } else {
+ rangeChecks.put(functionName, checksForFunction);
+ }
+ }
+ checksForFunction.put(new Integer(argNum), restOfLine);
+ } catch (Exception e) {
+ throw new RuntimeException("Error parsing \"RangeCheck" + (inBytes ? "Bytes" : "") + "\" command at line " + lineNo +
+ " in file \"" + filename + "\"", e);
+ }
+ }
+
protected static TypeInfo parseTypeInfo(String cType, JavaType javaType) {
String typeName = null;
int pointerDepth = 0;
diff --git a/src/classes/com/sun/gluegen/JavaEmitter.java b/src/classes/com/sun/gluegen/JavaEmitter.java
index d4bafb7a8..f0799067e 100644
--- a/src/classes/com/sun/gluegen/JavaEmitter.java
+++ b/src/classes/com/sun/gluegen/JavaEmitter.java
@@ -377,6 +377,8 @@ public class JavaEmitter implements GlueEmitter {
emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName()));
emitter.setPrologue(prologue);
emitter.setEpilogue(epilogue);
+ emitter.setRangeCheckExpressions(cfg.rangeCheckExpressions(binding.getName()));
+ emitter.setByteRangeCheckExpressions(cfg.byteRangeCheckExpressions(binding.getName()));
allEmitters.add(emitter);
}
@@ -430,6 +432,8 @@ public class JavaEmitter implements GlueEmitter {
}
emitter.addModifier(JavaMethodBindingEmitter.NATIVE);
emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName()));
+ emitter.setRangeCheckExpressions(cfg.rangeCheckExpressions(binding.getName()));
+ emitter.setByteRangeCheckExpressions(cfg.byteRangeCheckExpressions(binding.getName()));
allEmitters.add(emitter);
// Optionally emit the entry point taking arrays which handles
@@ -455,6 +459,8 @@ public class JavaEmitter implements GlueEmitter {
}
emitter.addModifier(JavaMethodBindingEmitter.NATIVE);
emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName()));
+ emitter.setRangeCheckExpressions(cfg.rangeCheckExpressions(binding.getName()));
+ emitter.setByteRangeCheckExpressions(cfg.byteRangeCheckExpressions(binding.getName()));
allEmitters.add(emitter);
}
}
diff --git a/src/classes/com/sun/gluegen/JavaMethodBindingEmitter.java b/src/classes/com/sun/gluegen/JavaMethodBindingEmitter.java
index 5a7e065f7..7e08977a6 100644
--- a/src/classes/com/sun/gluegen/JavaMethodBindingEmitter.java
+++ b/src/classes/com/sun/gluegen/JavaMethodBindingEmitter.java
@@ -87,6 +87,10 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
// number of elements of the returned array.
private String returnedArrayLengthExpression;
+ // Range-check expressions for various Buffer arguments
+ private Map/*<Integer, String>*/ rangeCheckExpressions;
+ private Map/*<Integer, String>*/ byteRangeCheckExpressions;
+
public JavaMethodBindingEmitter(MethodBinding binding,
PrintWriter output,
String runtimeExceptionType,
@@ -126,9 +130,11 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
forDirectBufferImplementation = arg.forDirectBufferImplementation;
forIndirectBufferAndArrayImplementation = arg.forIndirectBufferAndArrayImplementation;
isUnimplemented = arg.isUnimplemented;
- returnedArrayLengthExpression = arg.returnedArrayLengthExpression;
prologue = arg.prologue;
epilogue = arg.epilogue;
+ returnedArrayLengthExpression = arg.returnedArrayLengthExpression;
+ rangeCheckExpressions = arg.rangeCheckExpressions;
+ byteRangeCheckExpressions = arg.byteRangeCheckExpressions;
}
public final MethodBinding getBinding() { return binding; }
@@ -193,6 +199,14 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
this.forImplementingMethodCall = impl;
}
+ public void setRangeCheckExpressions(Map/*<Integer, String>*/ rangeChecks) {
+ this.rangeCheckExpressions = rangeChecks;
+ }
+
+ public void setByteRangeCheckExpressions(Map/*<Integer, String>*/ rangeChecks) {
+ this.byteRangeCheckExpressions = rangeChecks;
+ }
+
protected void emitReturnType(PrintWriter writer)
{
writer.print(getReturnTypeString(false));
@@ -424,6 +438,44 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
}
}
}
+
+ emitManuallySpecifiedRangeChecks(rangeCheckExpressions, false, writer);
+ emitManuallySpecifiedRangeChecks(byteRangeCheckExpressions, true, writer);
+ }
+
+ protected void emitManuallySpecifiedRangeChecks(Map/*<Integer, String>*/ rangeChecks,
+ boolean inBytes,
+ PrintWriter writer) {
+ if (rangeChecks == null) {
+ return;
+ }
+
+ // Check lengths of arrays and buffers with user-specified checks
+ for (Iterator iter = rangeChecks.keySet().iterator(); iter.hasNext(); ) {
+ Integer argNumBox = (Integer) iter.next();
+ int argNum = argNumBox.intValue();
+ JavaType type = binding.getJavaArgumentType(argNum);
+ String argName = getArgumentName(argNum);
+ if (type.isPrimitiveArray()) {
+ String offsetArg = offsetArgName(argNum);
+ if (inBytes) {
+ throw new RuntimeException("Can not specify RangeCheckBytes for primitive array arguments (failed on function " +
+ binding.getName() + ", argument " + argName + ")");
+ }
+ writer.println(" BufferFactory.rangeCheck(" + argName + ", " + offsetArg + ", " +
+ new MessageFormat((String) rangeChecks.get(argNumBox)).format(argumentNameArray()) +
+ ");");
+ } else if (!type.isPrimitive()) {
+ // Assume it's a Buffer
+ writer.print(" BufferFactory.rangeCheck");
+ if (inBytes) {
+ writer.print("Bytes");
+ }
+ writer.println("(" + argName + ", " +
+ new MessageFormat((String) rangeChecks.get(argNumBox)).format(argumentNameArray()) +
+ ");");
+ }
+ }
}
protected void emitCall(MethodBinding binding, PrintWriter writer, boolean direct) {
@@ -601,11 +653,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
writer.print(" return " + returnType.getName() + ".create(_res.order(ByteOrder.nativeOrder()))");
} else {
writer.println(" _res.order(ByteOrder.nativeOrder());");
- String[] argumentNames = new String[binding.getNumArguments()];
- for (int i = 0; i < binding.getNumArguments(); i++) {
- argumentNames[i] = getArgumentName(i);
- }
- String expr = new MessageFormat(fmt).format(argumentNames);
+ String expr = new MessageFormat(fmt).format(argumentNameArray());
PointerType cReturnTypePointer = binding.getCReturnType().asPointer();
CompoundType cReturnType = null;
if (cReturnTypePointer != null) {
@@ -648,6 +696,14 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
}
}
+ protected String[] argumentNameArray() {
+ String[] argumentNames = new String[binding.getNumArguments()];
+ for (int i = 0; i < binding.getNumArguments(); i++) {
+ argumentNames[i] = getArgumentName(i);
+ }
+ return argumentNames;
+ }
+
public static String javaThisArgumentName() {
return "jthis0";
}
diff --git a/src/classes/com/sun/gluegen/runtime/BufferFactory.java b/src/classes/com/sun/gluegen/runtime/BufferFactory.java
index ca66e6915..dd8856a43 100644
--- a/src/classes/com/sun/gluegen/runtime/BufferFactory.java
+++ b/src/classes/com/sun/gluegen/runtime/BufferFactory.java
@@ -161,4 +161,81 @@ public class BufferFactory {
throw new RuntimeException("Unknown buffer type " + buf.getClass().getName());
}
+
+ public static void rangeCheck(byte[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(char[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(short[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(int[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(long[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(float[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(double[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(Buffer buffer, int minElementsRemaining) {
+ if (buffer == null) {
+ return;
+ }
+
+ if (buffer.remaining() < minElementsRemaining) {
+ throw new IndexOutOfBoundsException("Required " + minElementsRemaining + " remaining elements in buffer, only had " + buffer.remaining());
+ }
+ }
+
+ public static void rangeCheckBytes(Buffer buffer, int minBytesRemaining) {
+ if (buffer == null) {
+ return;
+ }
+
+ int elementsRemaining = buffer.remaining();
+ int bytesRemaining = 0;
+ if (buffer instanceof ByteBuffer) {
+ bytesRemaining = elementsRemaining;
+ } else if (buffer instanceof FloatBuffer) {
+ bytesRemaining = elementsRemaining * SIZEOF_FLOAT;
+ } else if (buffer instanceof IntBuffer) {
+ bytesRemaining = elementsRemaining * SIZEOF_INT;
+ } else if (buffer instanceof ShortBuffer) {
+ bytesRemaining = elementsRemaining * SIZEOF_SHORT;
+ } else if (buffer instanceof DoubleBuffer) {
+ bytesRemaining = elementsRemaining * SIZEOF_DOUBLE;
+ } else if (buffer instanceof LongBuffer) {
+ bytesRemaining = elementsRemaining * SIZEOF_LONG;
+ }
+ if (bytesRemaining < minBytesRemaining) {
+ throw new IndexOutOfBoundsException("Required " + minBytesRemaining + " remaining bytes in buffer, only had " + bytesRemaining);
+ }
+ }
}