From b69f8b201ba3767020d33a7ebe066466f00d4223 Mon Sep 17 00:00:00 2001 From: Kenneth Russel Date: Tue, 3 Jan 2006 02:36:17 +0000 Subject: Added checks on number of remaining elements and bytes for Buffers and arrays passed to certain APIs. Default is to not emit such range checks. The checks are currently most fully implemented for image- and texture-related APIs. Verified with debugging code and with demos that all textures used in demos are properly checked. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@509 232f8b59-042b-4e1e-8c03-345bb8c30851 --- src/classes/com/sun/gluegen/JavaConfiguration.java | 53 +++++++++++++++ src/classes/com/sun/gluegen/JavaEmitter.java | 6 ++ .../com/sun/gluegen/JavaMethodBindingEmitter.java | 68 +++++++++++++++++-- .../com/sun/gluegen/runtime/BufferFactory.java | 77 ++++++++++++++++++++++ 4 files changed, 198 insertions(+), 6 deletions(-) (limited to 'src/classes') 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/**/ javaMethodRenames = new HashMap(); private Map/*>*/ javaPrologues = new HashMap(); private Map/*>*/ javaEpilogues = new HashMap(); + private Map/*>*/ rangeChecks = new HashMap(); + private Map/*>*/ 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/**/ rangeCheckExpressions(String functionName) { + return (Map/**/) 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/**/ byteRangeCheckExpressions(String functionName) { + return (Map/**/) 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/**/ checksForFunction = null; + if (inBytes) { + checksForFunction = (Map/**/) byteRangeChecks.get(functionName); + } else { + checksForFunction = (Map/**/) rangeChecks.get(functionName); + } + if (checksForFunction == null) { + checksForFunction = new HashMap/**/(); + 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/**/ rangeCheckExpressions; + private Map/**/ 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/**/ rangeChecks) { + this.rangeCheckExpressions = rangeChecks; + } + + public void setByteRangeCheckExpressions(Map/**/ 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/**/ 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); + } + } } -- cgit v1.2.3