From 9cfbe25a203a52f382c7aed47597f0882f371c43 Mon Sep 17 00:00:00 2001 From: Leon Linhart Date: Wed, 8 Nov 2017 12:03:06 +0100 Subject: [PATCH 1/4] feat: add nullability annotations --- .idea/libraries/jsr305.xml | 11 ++++ .idea/modules/Core.iml | 1 + build.xml | 7 ++- doc/notes/3.1.6.md | 3 + .../main/java/org/lwjgl/PointerBuffer.java | 5 +- .../main/java/org/lwjgl/openal/ALUtil.java | 2 + .../main/java/org/lwjgl/opengl/GLUtil.java | 3 + .../src/main/java/org/lwjgl/package-info.java | 1 + .../main/java/org/lwjgl/system/APIUtil.java | 15 +++-- .../main/java/org/lwjgl/system/Callback.java | 2 + .../main/java/org/lwjgl/system/Checks.java | 55 ++++++++++--------- .../java/org/lwjgl/system/Configuration.java | 5 +- .../java/org/lwjgl/system/CustomBuffer.java | 6 +- .../main/java/org/lwjgl/system/Library.java | 45 ++++++++------- .../java/org/lwjgl/system/NonnullDefault.java | 15 +++++ .../main/java/org/lwjgl/system/Pointer.java | 4 +- .../org/lwjgl/system/SharedLibraryLoader.java | 2 + .../java/org/lwjgl/system/StackWalkUtil.java | 2 + .../main/java/org/lwjgl/system/Struct.java | 4 +- .../java/org/lwjgl/system/StructBuffer.java | 14 +++-- .../java/org/lwjgl/system/package-info.java | 1 + .../org/lwjgl/generator/CallbackFunction.kt | 5 +- .../kotlin/org/lwjgl/generator/Functions.kt | 13 +++++ .../org/lwjgl/generator/GeneratorTarget.kt | 4 +- .../kotlin/org/lwjgl/generator/GlobalTypes.kt | 2 +- .../main/kotlin/org/lwjgl/generator/JNI.kt | 3 +- .../kotlin/org/lwjgl/generator/NativeClass.kt | 8 +++ .../kotlin/org/lwjgl/generator/Structs.kt | 12 +++- .../main/kotlin/org/lwjgl/generator/Types.kt | 15 ++++- .../org/lwjgl/assimp/templates/Assimp.kt | 4 +- .../org/lwjgl/nuklear/templates/nuklear.kt | 1 + .../main/kotlin/org/lwjgl/openal/ALCTypes.kt | 2 +- .../main/kotlin/org/lwjgl/openal/ALTypes.kt | 2 +- .../opengl/templates/WGL_NV_DX_interop.kt | 4 +- .../main/kotlin/org/lwjgl/openvr/OpenVR.kt | 4 +- .../org/lwjgl/system/dyncall/DynCallTypes.kt | 2 +- .../kotlin/org/lwjgl/system/jawt/JAWTTypes.kt | 4 +- .../kotlin/org/lwjgl/system/jni/JNITypes.kt | 22 ++++---- .../org/lwjgl/system/macosx/templates/ObjC.kt | 2 +- .../org/lwjgl/system/windows/WindowsTypes.kt | 2 +- update-dependencies.xml | 2 + 41 files changed, 219 insertions(+), 97 deletions(-) create mode 100644 .idea/libraries/jsr305.xml create mode 100644 modules/core/src/main/java/org/lwjgl/system/NonnullDefault.java diff --git a/.idea/libraries/jsr305.xml b/.idea/libraries/jsr305.xml new file mode 100644 index 0000000000..790ef0de39 --- /dev/null +++ b/.idea/libraries/jsr305.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/Core.iml b/.idea/modules/Core.iml index 4c9edd1f5c..71145dcc7a 100644 --- a/.idea/modules/Core.iml +++ b/.idea/modules/Core.iml @@ -15,5 +15,6 @@ + \ No newline at end of file diff --git a/build.xml b/build.xml index f2154038cb..37d35d0e41 100644 --- a/build.xml +++ b/build.xml @@ -383,13 +383,16 @@ + + + + - @@ -541,6 +544,7 @@ + @@ -687,6 +691,7 @@ + diff --git a/doc/notes/3.1.6.md b/doc/notes/3.1.6.md index cb5685ca60..9eb91dff46 100644 --- a/doc/notes/3.1.6.md +++ b/doc/notes/3.1.6.md @@ -23,6 +23,9 @@ This build includes the following changes: #### Improvements +- Added [JSR-305](https://jcp.org/en/jsr/detail?id=305) nullability annotations to the core and all bindings. + * Enables static analysis tools ([FindBugs](http://findbugs.sourceforge.net/), IDEs) to detect accesses that could cause `NullPointerException`. Eliminating those improves the quality of LWJGL applications. + * Enables better interopation with JVM-based languages that feature built-in null-safety. For example, see [Kotlin's JSR-305 support](https://kotlinlang.org/docs/reference/java-interop.html#jsr-305-support). - Added `Configuration` setting to disable function lookup checks. - lmdb: Databases are now binary compatible across 32 & 64-bit architectures. (#364) * `MDB_VL32` is enabled on 32-bit builds. diff --git a/modules/core/src/main/java/org/lwjgl/PointerBuffer.java b/modules/core/src/main/java/org/lwjgl/PointerBuffer.java index d917546ac5..423da82adc 100644 --- a/modules/core/src/main/java/org/lwjgl/PointerBuffer.java +++ b/modules/core/src/main/java/org/lwjgl/PointerBuffer.java @@ -6,6 +6,7 @@ import org.lwjgl.system.*; +import javax.annotation.*; import java.nio.*; import static org.lwjgl.system.MemoryUtil.*; @@ -13,7 +14,7 @@ /** This class is a container for architecture-independent pointer data. Its interface mirrors the {@link LongBuffer} API for convenience. */ public class PointerBuffer extends CustomBuffer implements Comparable { - protected PointerBuffer(long address, ByteBuffer container, int mark, int position, int limit, int capacity) { + protected PointerBuffer(long address, @Nullable ByteBuffer container, int mark, int position, int limit, int capacity) { super(address, container, mark, position, limit, capacity); } @@ -64,7 +65,7 @@ public int sizeof() { } @Override - protected PointerBuffer newBufferInstance(long address, ByteBuffer container, int mark, int position, int limit, int capacity) { + protected PointerBuffer newBufferInstance(long address, @Nullable ByteBuffer container, int mark, int position, int limit, int capacity) { return new PointerBuffer(address, container, mark, position, limit, capacity); } diff --git a/modules/core/src/main/java/org/lwjgl/openal/ALUtil.java b/modules/core/src/main/java/org/lwjgl/openal/ALUtil.java index fca13bde74..4306ec9823 100644 --- a/modules/core/src/main/java/org/lwjgl/openal/ALUtil.java +++ b/modules/core/src/main/java/org/lwjgl/openal/ALUtil.java @@ -4,6 +4,7 @@ */ package org.lwjgl.openal; +import javax.annotation.*; import java.nio.*; import java.util.*; @@ -22,6 +23,7 @@ private ALUtil() { * @param deviceHandle the device to query * @param token the information to query. One of:
{@link ALC11#ALC_ALL_DEVICES_SPECIFIER}, {@link ALC11#ALC_CAPTURE_DEVICE_SPECIFIER} */ + @Nullable public static List getStringList(long deviceHandle, int token) { long __result = nalcGetString(deviceHandle, token); if (__result == NULL) { diff --git a/modules/core/src/main/java/org/lwjgl/opengl/GLUtil.java b/modules/core/src/main/java/org/lwjgl/opengl/GLUtil.java index 51e99deff0..ba86fa8c74 100644 --- a/modules/core/src/main/java/org/lwjgl/opengl/GLUtil.java +++ b/modules/core/src/main/java/org/lwjgl/opengl/GLUtil.java @@ -6,6 +6,7 @@ import org.lwjgl.system.*; +import javax.annotation.*; import java.io.*; import static org.lwjgl.opengl.AMDDebugOutput.*; @@ -26,6 +27,7 @@ private GLUtil() { * Detects the best debug output functionality to use and creates a callback that prints information to {@link APIUtil#DEBUG_STREAM}. The callback * function is returned as a {@link Callback}, that should be {@link Callback#free freed} when no longer needed. */ + @Nullable public static Callback setupDebugMessageCallback() { return setupDebugMessageCallback(APIUtil.DEBUG_STREAM); } @@ -36,6 +38,7 @@ public static Callback setupDebugMessageCallback() { * * @param stream the output {@link PrintStream} */ + @Nullable public static Callback setupDebugMessageCallback(PrintStream stream) { GLCapabilities caps = GL.getCapabilities(); diff --git a/modules/core/src/main/java/org/lwjgl/package-info.java b/modules/core/src/main/java/org/lwjgl/package-info.java index 59c28db4a4..89e6056923 100644 --- a/modules/core/src/main/java/org/lwjgl/package-info.java +++ b/modules/core/src/main/java/org/lwjgl/package-info.java @@ -18,4 +18,5 @@ * * @see www.lwjgl.org */ +@org.lwjgl.system.NonnullDefault package org.lwjgl; diff --git a/modules/core/src/main/java/org/lwjgl/system/APIUtil.java b/modules/core/src/main/java/org/lwjgl/system/APIUtil.java index 361a76f7b5..3773329d7c 100644 --- a/modules/core/src/main/java/org/lwjgl/system/APIUtil.java +++ b/modules/core/src/main/java/org/lwjgl/system/APIUtil.java @@ -9,6 +9,7 @@ import org.lwjgl.system.macosx.*; import org.lwjgl.system.windows.*; +import javax.annotation.*; import java.io.*; import java.lang.reflect.*; import java.net.*; @@ -69,7 +70,7 @@ private APIUtil() { * * @param msg the message to print */ - public static void apiLog(CharSequence msg) { + public static void apiLog(@Nullable CharSequence msg) { if (DEBUG) { DEBUG_STREAM.print("[LWJGL] "); DEBUG_STREAM.println(msg); @@ -142,7 +143,8 @@ private static void requiredFunctionMissing(String functionName) { } } - public static ByteBuffer apiGetMappedBuffer(ByteBuffer buffer, long mappedAddress, int capacity) { + @Nullable + public static ByteBuffer apiGetMappedBuffer(@Nullable ByteBuffer buffer, long mappedAddress, int capacity) { return buffer == null || memAddress(buffer) != mappedAddress || buffer.capacity() != capacity ? memByteBuffer(mappedAddress, capacity) : buffer; @@ -173,15 +175,17 @@ public static class APIVersion { public final int minor; /** Returns the API revision. May be null. */ + @Nullable public final String revision; /** Returns the API implementation-specific versioning information. May be null. */ + @Nullable public final String implementation; public APIVersion(int major, int minor) { this(major, minor, null, null); } - public APIVersion(int major, int minor, String revision, String implementation) { + public APIVersion(int major, int minor, @Nullable String revision, @Nullable String implementation) { this.major = major; this.minor = minor; this.revision = revision; @@ -207,6 +211,7 @@ public String toString() { * * @param option the option to query */ + @Nullable public static APIVersion apiParseVersion(Configuration option) { APIVersion version; @@ -245,7 +250,7 @@ public static APIVersion apiParseVersion(String version) { * * @return the parsed {@link APIVersion} */ - public static APIVersion apiParseVersion(String version, String prefix) { + public static APIVersion apiParseVersion(String version, @Nullable String prefix) { String pattern = "([0-9]+)[.]([0-9]+)([.]\\S+)?\\s*(.+)?"; if (prefix != null) { pattern = "(?:" + prefix + "\\s+)?" + pattern; @@ -284,7 +289,7 @@ public static String apiUnknownToken(String description, int token) { * * @return the token map */ - public static Map apiClassTokens(BiPredicate filter, Map target, Class... tokenClasses) { + public static Map apiClassTokens(@Nullable BiPredicate filter, @Nullable Map target, Class... tokenClasses) { if (target == null) { target = new HashMap<>(64); } diff --git a/modules/core/src/main/java/org/lwjgl/system/Callback.java b/modules/core/src/main/java/org/lwjgl/system/Callback.java index 309eaf8bd9..a5c985d249 100644 --- a/modules/core/src/main/java/org/lwjgl/system/Callback.java +++ b/modules/core/src/main/java/org/lwjgl/system/Callback.java @@ -6,6 +6,7 @@ import org.lwjgl.*; +import javax.annotation.*; import java.lang.reflect.*; import static org.lwjgl.system.MemoryStack.*; @@ -164,6 +165,7 @@ private static long getNativeFunction(char type) { * * @return the {@code CallbackI} instance, or null if the function pointer is {@code NULL}. */ + @Nullable public static T get(long functionPointer) { if (functionPointer == NULL) { return null; diff --git a/modules/core/src/main/java/org/lwjgl/system/Checks.java b/modules/core/src/main/java/org/lwjgl/system/Checks.java index 1fb4288c23..6825573bb9 100644 --- a/modules/core/src/main/java/org/lwjgl/system/Checks.java +++ b/modules/core/src/main/java/org/lwjgl/system/Checks.java @@ -6,6 +6,7 @@ import org.lwjgl.*; +import javax.annotation.*; import java.nio.*; import static org.lwjgl.system.MemoryUtil.*; @@ -62,13 +63,13 @@ public final class Checks { private Checks() { } - public static int lengthSafe(short[] array) { return array == null ? 0 : array.length;} - public static int lengthSafe(int[] array) { return array == null ? 0 : array.length;} - public static int lengthSafe(long[] array) { return array == null ? 0 : array.length; } - public static int lengthSafe(float[] array) { return array == null ? 0 : array.length;} - public static int lengthSafe(double[] array) { return array == null ? 0 : array.length;} - public static int remainingSafe(Buffer buffer) { return buffer == null ? 0 : buffer.remaining(); } - public static int remainingSafe(CustomBuffer buffer) { return buffer == null ? 0 : buffer.remaining(); } + public static int lengthSafe(@Nullable short[] array) { return array == null ? 0 : array.length;} + public static int lengthSafe(@Nullable int[] array) { return array == null ? 0 : array.length;} + public static int lengthSafe(@Nullable long[] array) { return array == null ? 0 : array.length; } + public static int lengthSafe(@Nullable float[] array) { return array == null ? 0 : array.length;} + public static int lengthSafe(@Nullable double[] array) { return array == null ? 0 : array.length;} + public static int remainingSafe(@Nullable Buffer buffer) { return buffer == null ? 0 : buffer.remaining(); } + public static int remainingSafe(@Nullable CustomBuffer buffer) { return buffer == null ? 0 : buffer.remaining(); } /** * Checks if any of the specified functions pointers is {@code NULL}. @@ -188,62 +189,62 @@ public static void checkNT(PointerBuffer buf, long terminator) { } } - public static void checkNTSafe(int[] buf) { + public static void checkNTSafe(@Nullable int[] buf) { if (buf != null) { checkNT(buf); } } - public static void checkNTSafe(int[] buf, int terminator) { + public static void checkNTSafe(@Nullable int[] buf, int terminator) { if (buf != null) { checkNT(buf, terminator); } } - public static void checkNTSafe(long[] buf) { + public static void checkNTSafe(@Nullable long[] buf) { if (buf != null) { checkNT(buf); } } - public static void checkNTSafe(float[] buf) { + public static void checkNTSafe(@Nullable float[] buf) { if (buf != null) { checkNT(buf); } } - public static void checkNT1Safe(ByteBuffer buf) { + public static void checkNT1Safe(@Nullable ByteBuffer buf) { if (buf != null) { checkNT1(buf); } } - public static void checkNT2Safe(ByteBuffer buf) { + public static void checkNT2Safe(@Nullable ByteBuffer buf) { if (buf != null) { checkNT2(buf); } } - public static void checkNTSafe(IntBuffer buf) { + public static void checkNTSafe(@Nullable IntBuffer buf) { if (buf != null) { checkNT(buf); } } - public static void checkNTSafe(IntBuffer buf, int terminator) { + public static void checkNTSafe(@Nullable IntBuffer buf, int terminator) { if (buf != null) { checkNT(buf, terminator); } } - public static void checkNTSafe(LongBuffer buf) { + public static void checkNTSafe(@Nullable LongBuffer buf) { if (buf != null) { checkNT(buf); } } - public static void checkNTSafe(FloatBuffer buf) { + public static void checkNTSafe(@Nullable FloatBuffer buf) { if (buf != null) { checkNT(buf); } } - public static void checkNTSafe(PointerBuffer buf) { + public static void checkNTSafe(@Nullable PointerBuffer buf) { if (buf != null) { checkNT(buf); } } - public static void checkNTSafe(PointerBuffer buf, long terminator) { + public static void checkNTSafe(@Nullable PointerBuffer buf, long terminator) { if (buf != null) { checkNT(buf, terminator); } @@ -383,42 +384,42 @@ public static void check(CustomBuffer buf, long size) { check(buf, (int)size); } - public static void checkSafe(short[] buf, int size) { + public static void checkSafe(@Nullable short[] buf, int size) { if (buf != null) { check(buf, size); } } - public static void checkSafe(int[] buf, int size) { + public static void checkSafe(@Nullable int[] buf, int size) { if (buf != null) { check(buf, size); } } - public static void checkSafe(long[] buf, int size) { + public static void checkSafe(@Nullable long[] buf, int size) { if (buf != null) { check(buf, size); } } - public static void checkSafe(float[] buf, int size) { + public static void checkSafe(@Nullable float[] buf, int size) { if (buf != null) { check(buf, size); } } - public static void checkSafe(double[] buf, int size) { + public static void checkSafe(@Nullable double[] buf, int size) { if (buf != null) { check(buf, size); } } - public static void checkSafe(Buffer buf, int size) { + public static void checkSafe(@Nullable Buffer buf, int size) { if (buf != null) { check(buf, size); } } - public static void checkSafe(Buffer buf, long size) { + public static void checkSafe(@Nullable Buffer buf, long size) { if (buf != null) { check(buf, size); } } - public static void checkSafe(CustomBuffer buf, int size) { + public static void checkSafe(@Nullable CustomBuffer buf, int size) { if (buf != null) { check(buf, size); } diff --git a/modules/core/src/main/java/org/lwjgl/system/Configuration.java b/modules/core/src/main/java/org/lwjgl/system/Configuration.java index 48fe534f85..2818c4d3f5 100644 --- a/modules/core/src/main/java/org/lwjgl/system/Configuration.java +++ b/modules/core/src/main/java/org/lwjgl/system/Configuration.java @@ -6,6 +6,7 @@ import org.lwjgl.system.MemoryUtil.*; +import javax.annotation.*; import java.io.*; import java.util.function.*; @@ -345,6 +346,7 @@ private interface StateInit extends Function { private final String property; + @Nullable private T state; Configuration(String property, StateInit init) { @@ -361,7 +363,7 @@ public String getProperty() { * * @param value the value to set */ - public void set(T value) { + public void set(@Nullable T value) { this.state = value; } @@ -370,6 +372,7 @@ public void set(T value) { * *

If the option value has not been set, null will be returned.

*/ + @Nullable public T get() { return state; } diff --git a/modules/core/src/main/java/org/lwjgl/system/CustomBuffer.java b/modules/core/src/main/java/org/lwjgl/system/CustomBuffer.java index 5340b78349..5284c9b7d3 100644 --- a/modules/core/src/main/java/org/lwjgl/system/CustomBuffer.java +++ b/modules/core/src/main/java/org/lwjgl/system/CustomBuffer.java @@ -4,6 +4,7 @@ */ package org.lwjgl.system; +import javax.annotation.*; import java.nio.*; import static org.lwjgl.system.MemoryUtil.*; @@ -13,6 +14,7 @@ public abstract class CustomBuffer> implements P protected long address; + @Nullable protected ByteBuffer container; protected int @@ -21,7 +23,7 @@ public abstract class CustomBuffer> implements P limit, capacity; - protected CustomBuffer(long address, ByteBuffer container, int mark, int position, int limit, int capacity) { + protected CustomBuffer(long address, @Nullable ByteBuffer container, int mark, int position, int limit, int capacity) { this.address = address; this.container = container; @@ -369,7 +371,7 @@ public String toString() { protected abstract SELF self(); - protected abstract SELF newBufferInstance(long address, ByteBuffer container, int mark, int position, int limit, int capacity); + protected abstract SELF newBufferInstance(long address, @Nullable ByteBuffer container, int mark, int position, int limit, int capacity); protected long nextGetIndex() { if (position >= limit) { diff --git a/modules/core/src/main/java/org/lwjgl/system/Library.java b/modules/core/src/main/java/org/lwjgl/system/Library.java index e2d087053a..8fcef04ee2 100644 --- a/modules/core/src/main/java/org/lwjgl/system/Library.java +++ b/modules/core/src/main/java/org/lwjgl/system/Library.java @@ -6,6 +6,7 @@ import org.lwjgl.*; +import javax.annotation.*; import java.io.*; import java.lang.reflect.*; import java.net.*; @@ -231,7 +232,10 @@ public static SharedLibrary loadNative(Class context, String name, boolean bu } // Extract from classpath and try org.lwjgl.librarypath try (FileChannel ignored = SharedLibraryLoader.load(name, libName, libURL)) { - return loadNative(context, libName, Configuration.LIBRARY_PATH); + lib = loadNative(context, libName, Configuration.LIBRARY_PATH); + if (lib != null) { + return lib; + } } } catch (Exception e) { if (debugLoader) { @@ -289,6 +293,7 @@ public static SharedLibrary loadNative(Class context, String name, boolean bu throw new UnsatisfiedLinkError("Failed to locate library: " + libName); } + @Nullable private static SharedLibrary loadNativeFromSystem(String libName) { SharedLibrary lib; try { @@ -301,6 +306,7 @@ private static SharedLibrary loadNativeFromSystem(String libName) { return lib; } + @Nullable private static SharedLibrary loadNative(Class context, String libName, Configuration property) { String paths = property.get(); if (paths != null) { @@ -312,6 +318,7 @@ private static SharedLibrary loadNative(Class context, String libName, Config return null; } + @Nullable private static SharedLibrary loadNative(Class context, String libName, String property, String paths) { Path libFile = findLibrary(paths, libName); if (libFile == null) { @@ -337,34 +344,32 @@ private static SharedLibrary loadNative(Class context, String libName, String * @throws UnsatisfiedLinkError if the library could not be loaded */ public static SharedLibrary loadNative(Class context, Configuration name, String... defaultNames) { - if (name.get() != null) { - return loadNative(context, name.get()); - } else if (defaultNames.length <= 1) { + String libraryName = name.get(); + if (libraryName != null) { + return loadNative(context, libraryName); + } + + if (defaultNames.length <= 1) { if (defaultNames.length == 0) { throw new RuntimeException("No default names specified."); } - return loadNative(context, defaultNames[0]); - } else { - SharedLibrary library = null; - try { - library = Library.loadNative(context, defaultNames[0]); // try first - } catch (Throwable t) { - for (int i = 1; i < defaultNames.length; i++) { // try alternatives - try { - library = Library.loadNative(context, defaultNames[i]); - break; - } catch (Throwable ignored) { - } - } - if (library == null) { - throw t; // original error + } + + try { + return loadNative(context, defaultNames[0]); // try first + } catch (Throwable t) { + for (int i = 1; i < defaultNames.length; i++) { // try alternatives + try { + return loadNative(context, defaultNames[i]); + } catch (Throwable ignored) { } } - return library; + throw t; // original error } } + @Nullable private static Path findLibrary(String path, String libName) { for (String directory : PATH_SEPARATOR.split(path)) { Path p = Paths.get(directory, libName); diff --git a/modules/core/src/main/java/org/lwjgl/system/NonnullDefault.java b/modules/core/src/main/java/org/lwjgl/system/NonnullDefault.java new file mode 100644 index 0000000000..7ceef49e3f --- /dev/null +++ b/modules/core/src/main/java/org/lwjgl/system/NonnullDefault.java @@ -0,0 +1,15 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ +package org.lwjgl.system; + +import javax.annotation.*; +import javax.annotation.meta.*; +import java.lang.annotation.*; + +@Documented +@Nonnull +@TypeQualifierDefault({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface NonnullDefault {} \ No newline at end of file diff --git a/modules/core/src/main/java/org/lwjgl/system/Pointer.java b/modules/core/src/main/java/org/lwjgl/system/Pointer.java index 4967d11b13..da9b4d03c3 100644 --- a/modules/core/src/main/java/org/lwjgl/system/Pointer.java +++ b/modules/core/src/main/java/org/lwjgl/system/Pointer.java @@ -6,6 +6,8 @@ import org.lwjgl.*; +import javax.annotation.*; + /** * Pointer interface. * @@ -52,7 +54,7 @@ public long address() { return address; } - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/modules/core/src/main/java/org/lwjgl/system/SharedLibraryLoader.java b/modules/core/src/main/java/org/lwjgl/system/SharedLibraryLoader.java index 0be489a423..303e97fa3a 100644 --- a/modules/core/src/main/java/org/lwjgl/system/SharedLibraryLoader.java +++ b/modules/core/src/main/java/org/lwjgl/system/SharedLibraryLoader.java @@ -6,6 +6,7 @@ import org.lwjgl.*; +import javax.annotation.*; import java.io.*; import java.net.*; import java.nio.channels.*; @@ -25,6 +26,7 @@ */ final class SharedLibraryLoader { + @Nullable private static Path extractPath; private SharedLibraryLoader() { diff --git a/modules/core/src/main/java/org/lwjgl/system/StackWalkUtil.java b/modules/core/src/main/java/org/lwjgl/system/StackWalkUtil.java index 0729ac1945..101d6e1c3f 100644 --- a/modules/core/src/main/java/org/lwjgl/system/StackWalkUtil.java +++ b/modules/core/src/main/java/org/lwjgl/system/StackWalkUtil.java @@ -4,6 +4,7 @@ */ package org.lwjgl.system; +import javax.annotation.*; import java.util.*; // Multi-release version: Java 8 @@ -54,6 +55,7 @@ private static boolean isAutoCloseable(StackTraceElement element, StackTraceElem return false; } + @Nullable static Object stackWalkCheckPop(Class after, Object pushedObj) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); diff --git a/modules/core/src/main/java/org/lwjgl/system/Struct.java b/modules/core/src/main/java/org/lwjgl/system/Struct.java index 330ad21672..5434648d73 100644 --- a/modules/core/src/main/java/org/lwjgl/system/Struct.java +++ b/modules/core/src/main/java/org/lwjgl/system/Struct.java @@ -6,6 +6,7 @@ import org.lwjgl.*; +import javax.annotation.*; import java.nio.*; import java.util.*; @@ -21,9 +22,10 @@ public abstract class Struct extends Pointer.Default { } @SuppressWarnings({"unused", "FieldCanBeLocal"}) + @Nullable private ByteBuffer container; - protected Struct(long address, ByteBuffer container) { + protected Struct(long address, @Nullable ByteBuffer container) { super(address); this.container = container; } diff --git a/modules/core/src/main/java/org/lwjgl/system/StructBuffer.java b/modules/core/src/main/java/org/lwjgl/system/StructBuffer.java index 3a4ae42a3c..6e2fcc928d 100644 --- a/modules/core/src/main/java/org/lwjgl/system/StructBuffer.java +++ b/modules/core/src/main/java/org/lwjgl/system/StructBuffer.java @@ -4,6 +4,7 @@ */ package org.lwjgl.system; +import javax.annotation.*; import java.nio.*; import java.util.*; import java.util.function.*; @@ -18,7 +19,7 @@ protected StructBuffer(ByteBuffer container, int remaining) { this(memAddress(container), container, -1, 0, remaining, remaining); } - protected StructBuffer(long address, ByteBuffer container, int mark, int position, int limit, int capacity) { + protected StructBuffer(long address, @Nullable ByteBuffer container, int mark, int position, int limit, int capacity) { super(address, container, mark, position, limit, capacity); } @@ -115,7 +116,8 @@ public SELF put(int index, T value) { // -------------------------------------- - @Override public Iterator iterator() { + @Override + public Iterator iterator() { return new Iterator() { int cursor = position; @@ -138,14 +140,16 @@ public SELF put(int index, T value) { }; } - @Override public void forEach(Consumer action) { + @Override + public void forEach(Consumer action) { Objects.requireNonNull(action); for (int i = position; i < limit; i++) { action.accept(get(i)); } } - @Override public Spliterator spliterator() { + @Override + public Spliterator spliterator() { return new StructSpliterator(); } @@ -174,7 +178,9 @@ public boolean tryAdvance(Consumer action) { return false; } + @Override + @Nullable public Spliterator trySplit() { int lo = index, mid = (lo + fence) >>> 1; diff --git a/modules/core/src/main/java/org/lwjgl/system/package-info.java b/modules/core/src/main/java/org/lwjgl/system/package-info.java index 57a7a12e44..1b5bacf7c5 100644 --- a/modules/core/src/main/java/org/lwjgl/system/package-info.java +++ b/modules/core/src/main/java/org/lwjgl/system/package-info.java @@ -29,4 +29,5 @@ *
  • {@link org.lwjgl.system.Struct Struct} and {@link org.lwjgl.system.StructBuffer StructBuffer}, the base classes for struct types and struct buffers.
  • * */ +@NonnullDefault package org.lwjgl.system; diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/CallbackFunction.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/CallbackFunction.kt index cdd490e843..6fd906be27 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/CallbackFunction.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/CallbackFunction.kt @@ -54,7 +54,9 @@ class CallbackFunction( print(HEADER) println("package $packageName;\n") - print("""import org.lwjgl.system.*; + print("""import javax.annotation.*; + +import org.lwjgl.system.*; import static org.lwjgl.system.MemoryUtil.*; @@ -69,6 +71,7 @@ import static org.lwjgl.system.MemoryUtil.*; print("""${access.modifier}abstract class $className extends Callback implements ${className}I { /** Creates a {@code $className} instance from the specified function pointer. */ + @Nullable public static $className create(long functionPointer) { if (functionPointer == NULL) { return null; diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt index e6b058d9bc..83b64aa843 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt @@ -215,6 +215,12 @@ class Func( } else { "$it $name" } + }.let { + if (nativeType.isReference && has(nullable)) { + "@Nullable $it" + } else { + it + } } private fun Parameter.asNativeMethodParam(nativeOnly: Boolean) = @@ -637,6 +643,10 @@ class Func( printUnsafeJavadoc(constantMacro, nativeOnly) + if (returns.nativeType is JObjectType && !has(Nonnull)) { + println("$t@Nullable") + } + val retType = returnsNativeMethodType if (nativeOnly) { @@ -670,6 +680,9 @@ class Func( println() printUnsafeJavadoc(constantMacro) + if (returns.nativeType is JObjectType && !has(Nonnull)) { + println("$t@Nullable") + } print("$t${if (constantMacro) "private " else accessModifier}static $returnsNativeMethodType n$name(") printList(getNativeParams()) { if (it.isFunctionProvider) diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/GeneratorTarget.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/GeneratorTarget.kt index fe908315cd..acb50edd5b 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/GeneratorTarget.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/GeneratorTarget.kt @@ -358,7 +358,9 @@ fun packageInfo( print(HEADER) println() println(processDocumentation(documentation, forcePackage = true).toJavaDoc(indentation = "", see = see, since = since)) - println("package $packageName;\n") + println("""@org.lwjgl.system.NonnullDefault +package $packageName; +""") } } diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/GlobalTypes.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/GlobalTypes.kt index d88639a56b..9e90d22160 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/GlobalTypes.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/GlobalTypes.kt @@ -4,7 +4,7 @@ */ package org.lwjgl.generator -val void = NativeType("void", TypeMapping.VOID) +val void = "void".void val opaque_p = "void".p val void_p = PointerType("void", PointerMapping.DATA) val void_pp = void_p.p diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/JNI.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/JNI.kt index 73043ff1d0..475ecb5126 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/JNI.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/JNI.kt @@ -57,6 +57,7 @@ object JNI : GeneratorTargetNative("org.lwjgl.system", "JNI") { """ )} """ + javaImport("javax.annotation.*") } override fun PrintWriter.generateJava() { @@ -84,7 +85,7 @@ object JNI : GeneratorTargetNative("org.lwjgl.system", "JNI") { sortedSignaturesArray.forEach { print("${t}public static native ${it.returnType.nativeMethodType} ${it.signature}(long $FUNCTION_ADDRESS") if (it.arguments.isNotEmpty()) - print(it.arguments.asSequence().mapIndexed { i, param -> if (param is ArrayType) "${(param.mapping as PointerMapping).primitive}[] param$i" else "${param.nativeMethodType} param$i" }.joinToString(", ", prefix = ", ")) + print(it.arguments.asSequence().mapIndexed { i, param -> if (param is ArrayType) "@Nullable ${(param.mapping as PointerMapping).primitive}[] param$i" else "${param.nativeMethodType} param$i" }.joinToString(", ", prefix = ", ")) println(");") } println("\n}") diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt index aecf0993fb..1a6a31762e 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt @@ -408,6 +408,14 @@ class NativeClass( val hasFunctions = !_functions.isEmpty() if (hasFunctions || binding is SimpleBinding) { // TODO: This is horrible. Refactor so that we build imports after code generation. + if (functions.any { + it.parameters.any { + it.nativeType.isReference && it.has(nullable) + } + }) { + println("import javax.annotation.*;\n") + } + val hasBuffers = functions.any { it.returns.nativeType.isPointerData || it.hasParam { it.nativeType.isPointerData } } if (hasBuffers) { diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/Structs.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/Structs.kt index e8e0d4ccad..d7a532ab19 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/Structs.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/Structs.kt @@ -485,6 +485,8 @@ $indentation}""" print(HEADER) println("package $packageName;\n") + println("import javax.annotation.*;\n") + println("import java.nio.*;\n") if (mallocable || members.any { m -> m.nativeType.let { @@ -603,7 +605,7 @@ $indentation}""" print(""" - $className(long address, ByteBuffer container) { + $className(long address, @Nullable ByteBuffer container) { super(address, container); } @@ -680,6 +682,7 @@ $indentation}""" print(""" /** Returns a new {@link $className} instance for the specified memory address or {@code null} if the address is {@code NULL}. */ + @Nullable public static $className create(long address) { return address == NULL ? null : new $className(address, null); } @@ -691,6 +694,7 @@ $indentation}""" * * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ + @Nullable public static Buffer malloc(int $BUFFER_CAPACITY_PARAM) { return create(__malloc($BUFFER_CAPACITY_PARAM, SIZEOF), $BUFFER_CAPACITY_PARAM); } @@ -700,6 +704,7 @@ $indentation}""" * * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ + @Nullable public static Buffer calloc(int $BUFFER_CAPACITY_PARAM) { return create(nmemCalloc($BUFFER_CAPACITY_PARAM, SIZEOF), $BUFFER_CAPACITY_PARAM); } @@ -722,6 +727,7 @@ $indentation}""" * @param address the memory address * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ + @Nullable public static Buffer create(long address, int $BUFFER_CAPACITY_PARAM) { return address == NULL ? null : new Buffer(address, null, -1, 0, $BUFFER_CAPACITY_PARAM, $BUFFER_CAPACITY_PARAM); } @@ -881,7 +887,7 @@ ${validations.joinToString("\n")} print(""" - Buffer(long address, ByteBuffer container, int mark, int pos, int lim, int cap) { + Buffer(long address, @Nullable ByteBuffer container, int mark, int pos, int lim, int cap) { super(address, container, mark, pos, lim, cap); } @@ -891,7 +897,7 @@ ${validations.joinToString("\n")} } @Override - protected Buffer newBufferInstance(long address, ByteBuffer container, int mark, int pos, int lim, int cap) { + protected Buffer newBufferInstance(long address, @Nullable ByteBuffer container, int mark, int pos, int lim, int cap) { return new Buffer(address, container, mark, pos, lim, cap); } diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/Types.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/Types.kt index 94ec0db113..8de0116670 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/Types.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/Types.kt @@ -10,7 +10,9 @@ import kotlin.reflect.* /* - NativeType + - JObjectType - ValueType + - VoidType - PrimitiveType - IntegerType - CharType @@ -23,7 +25,7 @@ import kotlin.reflect.* - C++ classes (if we add support in the future) */ -open class NativeType( +open class NativeType internal constructor( /** The type used in the native API. */ val name: String, /** The type we map the native type to. */ @@ -75,10 +77,14 @@ open class NativeType( } // Java instance passed as jobject to native code -val KClass<*>.jobject: NativeType get() = NativeType("jobject", TypeMapping("jobject", this, this)) +class JObjectType(name: String, type: KClass<*>) : NativeType(name, TypeMapping(name, type, type)) +val KClass<*>.jobject: NativeType get() = JObjectType("jobject", this) abstract class ValueType internal constructor(name: String, mapping: TypeMapping) : NativeType(name, mapping) +class VoidType constructor(name: String) : ValueType(name, TypeMapping.VOID) +val String.void: VoidType get() = VoidType(this) + // Specialization for primitives. open class PrimitiveType(name: String, mapping: PrimitiveMapping) : ValueType(name, mapping) @@ -230,7 +236,7 @@ open class TypeMapping( ) : this(jniFunctionType, nativeMethodClass.java, javaMethodClass.java) companion object { - val VOID = TypeMapping("void", Void.TYPE, Void.TYPE) + internal val VOID = TypeMapping("void", Void.TYPE, Void.TYPE) } private val Class<*>.javaName get() = this.typeParameters.let { @@ -365,6 +371,9 @@ val NativeType.isPointer val NativeType.isPointerData get() = this is PointerType && this.mapping !== PointerMapping.OPAQUE_POINTER +val NativeType.isReference + get() = this !is ValueType && (this.mapping !== PointerMapping.OPAQUE_POINTER || this is ObjectType) + val TypeMapping.isPointerSize get() = this === PointerMapping.DATA_INT || this === PointerMapping.DATA_POINTER diff --git a/modules/templates/src/main/kotlin/org/lwjgl/assimp/templates/Assimp.kt b/modules/templates/src/main/kotlin/org/lwjgl/assimp/templates/Assimp.kt index cb4b7b10b0..a8414f957d 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/assimp/templates/Assimp.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/assimp/templates/Assimp.kt @@ -1349,7 +1349,7 @@ val Assimp = "Assimp".nativeClass(packageName = ASSIMP_PACKAGE, prefix = "ai", p "Will be used to open the model file itself and any other files the loader needs to open. Pass #NULL to use the default implementation." ), - returnDoc = "Pointer to the imported data or NULL if the import failed." + returnDoc = "Pointer to the imported data or #NULL if the import failed." ) aiScene_p( @@ -2869,7 +2869,7 @@ x1""")} "ASSIMP_CFLAGS_SINGLETHREADED"..0x10 ).noPrefix() - const..charASCII_p( + Nonnull..const..charASCII_p( "GetLegalString", "Returns a string with legal copyright and licensing information about Assimp. The string may include multiple lines.", diff --git a/modules/templates/src/main/kotlin/org/lwjgl/nuklear/templates/nuklear.kt b/modules/templates/src/main/kotlin/org/lwjgl/nuklear/templates/nuklear.kt index 93637f1322..ec229034b1 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/nuklear/templates/nuklear.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/nuklear/templates/nuklear.kt @@ -814,6 +814,7 @@ nk_style_pop_vec2(ctx);""")} ctx ) + void( "free", """ diff --git a/modules/templates/src/main/kotlin/org/lwjgl/openal/ALCTypes.kt b/modules/templates/src/main/kotlin/org/lwjgl/openal/ALCTypes.kt index 9cac7818a5..723c29255a 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/openal/ALCTypes.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/openal/ALCTypes.kt @@ -8,7 +8,7 @@ import org.lwjgl.generator.* // void -val ALCvoid = NativeType("ALCvoid", TypeMapping.VOID) +val ALCvoid = "ALCvoid".void val ALCvoid_p = PointerType("ALCvoid", PointerMapping.DATA) // numeric diff --git a/modules/templates/src/main/kotlin/org/lwjgl/openal/ALTypes.kt b/modules/templates/src/main/kotlin/org/lwjgl/openal/ALTypes.kt index d214e9b653..6ccef6d3cb 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/openal/ALTypes.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/openal/ALTypes.kt @@ -28,7 +28,7 @@ fun config() { // void -val ALvoid = NativeType("ALvoid", TypeMapping.VOID) +val ALvoid = "ALvoid".void val ALvoid_p = PointerType("ALvoid", PointerMapping.DATA) // numeric diff --git a/modules/templates/src/main/kotlin/org/lwjgl/opengl/templates/WGL_NV_DX_interop.kt b/modules/templates/src/main/kotlin/org/lwjgl/opengl/templates/WGL_NV_DX_interop.kt index 831570ff64..144a3cce3e 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/opengl/templates/WGL_NV_DX_interop.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/opengl/templates/WGL_NV_DX_interop.kt @@ -49,7 +49,7 @@ val WGL_NV_DX_interop = "WGLNVDXInterop".nativeClassWGL("WGL_NV_DX_interop", NV) "", HANDLE.IN("device", "") - ); + ) HANDLE( "DXRegisterObjectNV", @@ -63,7 +63,7 @@ val WGL_NV_DX_interop = "WGLNVDXInterop".nativeClassWGL("WGL_NV_DX_interop", NV) ), GLenum.IN("type", "the GL object type that will map to the DirectX resource being shared"), GLenum.IN("access", "indicates the intended usage of the resource in GL", accessModes) - ); + ) BOOL( diff --git a/modules/templates/src/main/kotlin/org/lwjgl/openvr/OpenVR.kt b/modules/templates/src/main/kotlin/org/lwjgl/openvr/OpenVR.kt index 72adc1137e..4b58e83757 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/openvr/OpenVR.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/openvr/OpenVR.kt @@ -42,6 +42,7 @@ val OPENVR_FNTABLE_BINDING: APIBinding = Generator.register(object : APIBinding( javaImport( "java.nio.*", "java.util.function.*", + "javax.annotation.Nullable", "org.lwjgl.*", "static org.lwjgl.openvr.VR.*", "static org.lwjgl.system.MemoryStack.*", @@ -72,7 +73,7 @@ val OPENVR_FNTABLE_BINDING: APIBinding = Generator.register(object : APIBinding( VRTrackedCamera ) - println(interfaces.joinToString("\n$t", prefix = t) { "public static ${it.capabilitiesClass} ${it.capabilitiesField};" }) + println(interfaces.joinToString("\n$t", prefix = t) { "@Nullable public static ${it.capabilitiesClass} ${it.capabilitiesField};" }) // Common constructor print(""" @@ -99,6 +100,7 @@ val OPENVR_FNTABLE_BINDING: APIBinding = Generator.register(object : APIBinding( print(""" } + @Nullable private static T getGenericInterface(String interfaceNameVersion, LongFunction supplier) { try (MemoryStack stack = stackPush()) { IntBuffer peError = stack.mallocInt(1); diff --git a/modules/templates/src/main/kotlin/org/lwjgl/system/dyncall/DynCallTypes.kt b/modules/templates/src/main/kotlin/org/lwjgl/system/dyncall/DynCallTypes.kt index 03b03a97f4..b32bda79e1 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/system/dyncall/DynCallTypes.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/system/dyncall/DynCallTypes.kt @@ -34,7 +34,7 @@ fun config() { val DCCallVM_p = "DCCallVM".p val DCstruct_p = "DCstruct".p -val DCvoid = NativeType("DCvoid", TypeMapping.VOID) +val DCvoid = "DCvoid".void val DCbool = typedef(intb, "DCbool") val DCchar = typedef(char, "DCchar") diff --git a/modules/templates/src/main/kotlin/org/lwjgl/system/jawt/JAWTTypes.kt b/modules/templates/src/main/kotlin/org/lwjgl/system/jawt/JAWTTypes.kt index fefb011b03..bb8769d19f 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/system/jawt/JAWTTypes.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/system/jawt/JAWTTypes.kt @@ -74,8 +74,8 @@ val JAWT_p = struct(JAWT_PACKAGE, "JAWT") { nullable..opaque_p.member("SynthesizeWindowActivation", "") }.p -val Component = NativeType("jobject", TypeMapping("jobject", java.awt.Component::class.java, java.awt.Component::class.java)) -val Frame = NativeType("jobject", TypeMapping("jobject", java.awt.Frame::class.java, java.awt.Frame::class.java)) +val Component = java.awt.Component::class.jobject +val Frame = java.awt.Frame::class.jobject fun config() { packageInfo( diff --git a/modules/templates/src/main/kotlin/org/lwjgl/system/jni/JNITypes.kt b/modules/templates/src/main/kotlin/org/lwjgl/system/jni/JNITypes.kt index f04196a6de..079e7d1039 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/system/jni/JNITypes.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/system/jni/JNITypes.kt @@ -25,27 +25,27 @@ val jlong = IntegerType("jlong", PrimitiveMapping.LONG) val jfloat = IntegerType("jfloat", PrimitiveMapping.FLOAT) val jdouble = IntegerType("jdouble", PrimitiveMapping.DOUBLE) -val jbooleanArray = NativeType("jbooleanArray", TypeMapping("jbooleanArray", ByteArray::class, ByteArray::class)) -val jbyteArray = NativeType("jbyteArray", TypeMapping("jbyteArray", ByteArray::class, ByteArray::class)) -val jcharArray = NativeType("jcharArray", TypeMapping("jcharArray", CharArray::class, CharArray::class)) -val jshortArray = NativeType("jshortArray", TypeMapping("jshortArray", ShortArray::class, ShortArray::class)) -val jintArray = NativeType("jintArray", TypeMapping("jintArray", IntArray::class, IntArray::class)) -val jlongArray = NativeType("jlongArray", TypeMapping("jlongArray", LongArray::class, LongArray::class)) -val jfloatArray = NativeType("jfloatArray", TypeMapping("jfloatArray", FloatArray::class, FloatArray::class)) -val jdoubleArray = NativeType("jdoubleArray", TypeMapping("jdoubleArray", DoubleArray::class, DoubleArray::class)) +val jbooleanArray = JObjectType("jbooleanArray", ByteArray::class) +val jbyteArray = JObjectType("jbyteArray", ByteArray::class) +val jcharArray = JObjectType("jcharArray", CharArray::class) +val jshortArray = JObjectType("jshortArray", ShortArray::class) +val jintArray = JObjectType("jintArray", IntArray::class) +val jlongArray = JObjectType("jlongArray", LongArray::class) +val jfloatArray = JObjectType("jfloatArray", FloatArray::class) +val jdoubleArray = JObjectType("jdoubleArray", DoubleArray::class) val jsize = typedef(jint, "jsize") -val jclass = NativeType("jclass", TypeMapping("jclass", Class::class, Class::class)) +val jclass = JObjectType("jclass", Class::class) val jmethodID = "jmethodID".opaque_p val jfieldID = "jfieldID".opaque_p -val jobject = NativeType("jobject", TypeMapping("jobject", Any::class.java, Any::class.java)) +val jobject = Any::class.jobject val jobject_p = "jobject".opaque_p val jobjectRefType = "jobjectRefType".enumType -val jstring = NativeType("jstring", TypeMapping("jstring", String::class, String::class)) +val jstring = JObjectType("jstring", String::class) val java_lang_reflect_Method = java.lang.reflect.Method::class.jobject val java_lang_reflect_Field = java.lang.reflect.Field::class.jobject diff --git a/modules/templates/src/main/kotlin/org/lwjgl/system/macosx/templates/ObjC.kt b/modules/templates/src/main/kotlin/org/lwjgl/system/macosx/templates/ObjC.kt index f57917e3d6..5e07841ce5 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/system/macosx/templates/ObjC.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/system/macosx/templates/ObjC.kt @@ -1105,7 +1105,7 @@ void myMethodIMP(id self, SEL _cmd) """ ) - (objc_method_description_p)( + objc_method_description_p( "protocol_copyMethodDescriptionList", """ Returns an array of method descriptions of methods meeting a given specification for a given protocol. diff --git a/modules/templates/src/main/kotlin/org/lwjgl/system/windows/WindowsTypes.kt b/modules/templates/src/main/kotlin/org/lwjgl/system/windows/WindowsTypes.kt index 7944580704..728c8171ed 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/system/windows/WindowsTypes.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/system/windows/WindowsTypes.kt @@ -19,7 +19,7 @@ val SaveLastError = Code(nativeAfterCall = "${t}saveLastError();") // UNICODE is defined WindowsLWJGL.h, so all T* types below are UTF16. -val VOID = NativeType("VOID", TypeMapping.VOID) +val VOID = "VOID".void val HANDLE = "HANDLE".opaque_p val HANDLE_p = HANDLE.p diff --git a/update-dependencies.xml b/update-dependencies.xml index 308a1a0a44..1598ac6348 100644 --- a/update-dependencies.xml +++ b/update-dependencies.xml @@ -10,6 +10,7 @@ + @@ -67,6 +68,7 @@ + From 8e56d273456550a5147f842b34629a203d7c9d5a Mon Sep 17 00:00:00 2001 From: Leon Linhart Date: Mon, 4 Dec 2017 22:59:26 +0100 Subject: [PATCH 2/4] feat: add Nonnull modifier for return values --- .../org/lwjgl/generator/FunctionModifiers.kt | 11 +++++++++++ .../main/kotlin/org/lwjgl/generator/Functions.kt | 14 ++++++++++++-- .../main/kotlin/org/lwjgl/generator/NativeClass.kt | 2 +- .../main/kotlin/org/lwjgl/glfw/templates/GLFW.kt | 2 +- .../src/main/kotlin/org/lwjgl/ovr/templates/OVR.kt | 2 +- .../kotlin/org/lwjgl/util/lmdb/templates/lmdb.kt | 2 +- .../kotlin/org/lwjgl/util/lz4/templates/LZ4.kt | 2 +- .../kotlin/org/lwjgl/util/yoga/templates/yoga.kt | 2 +- .../kotlin/org/lwjgl/util/zstd/templates/Zstd.kt | 4 ++-- .../org/lwjgl/util/zstd/templates/ZstdErrors.kt | 2 +- 10 files changed, 32 insertions(+), 11 deletions(-) diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/FunctionModifiers.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/FunctionModifiers.kt index c2c1caa05d..488e176a6c 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/FunctionModifiers.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/FunctionModifiers.kt @@ -178,4 +178,15 @@ class Construct( /** Returns the address of the return value, instead of the return value itself. */ object Address : FunctionModifier { override val isSpecial = false +} + +/** Marks a pointer or Java instance return type as non-nullable. */ +object Nonnull : FunctionModifier { + override val isSpecial = false + + override fun validate(func: Func) { + if (func.returns.nativeType !is PointerType && func.returns.nativeType !is JObjectType) + throw IllegalArgumentException("The Nonnull modifier can only be applied on functions with pointer or Java instance return types.") + } + } \ No newline at end of file diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt index 83b64aa843..e4f9d23875 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt @@ -789,11 +789,16 @@ class Func( // Method signature + if (returns.nativeType.isReference && !has(Nonnull)) { + println("$t@Nullable") + } + val retType = returnsJavaMethodType val retTypeAnnotation = returns.nativeType.annotation(retType, has(const)) - if (retTypeAnnotation != null) + if (retTypeAnnotation != null) { println("$t$retTypeAnnotation") + } print("$t${if (constantMacro) "private " else accessModifier}static $retType $name(") printList(getNativeParams()) { @@ -1382,11 +1387,16 @@ class Func( // Method signature + if (returns.nativeType.isReference && !has(Nonnull)) { + println("$t@Nullable") + } + val retType = returns.transformDeclarationOrElse(transforms, returnsJavaMethodType, false, false)!! val retTypeAnnotation = returns.nativeType.annotation(retType, has(const)) - if (retTypeAnnotation != null) + if (retTypeAnnotation != null) { println("$t$retTypeAnnotation") + } print("$t${if (constantMacro) "private " else accessModifier}static $retType $name(") printList(getParams { it !== JNI_ENV }) { diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt index 1a6a31762e..743a2be14d 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt @@ -409,7 +409,7 @@ class NativeClass( if (hasFunctions || binding is SimpleBinding) { // TODO: This is horrible. Refactor so that we build imports after code generation. if (functions.any { - it.parameters.any { + (it.returns.nativeType.isReference && !it.has(Nonnull)) || it.parameters.any { it.nativeType.isReference && it.has(nullable) } }) { diff --git a/modules/templates/src/main/kotlin/org/lwjgl/glfw/templates/GLFW.kt b/modules/templates/src/main/kotlin/org/lwjgl/glfw/templates/GLFW.kt index 762cfdbced..734ed1f72b 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/glfw/templates/GLFW.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/glfw/templates/GLFW.kt @@ -827,7 +827,7 @@ val GLFW = "GLFW".nativeClass(packageName = GLFW_PACKAGE, prefix = "GLFW", bindi since = "version 1.0" ) - const..charASCII_p( + Nonnull..const..charASCII_p( "GetVersionString", """ Returns the compile-time generated version string of the GLFW library binary. It describes the version, platform, compiler and any platform-specific diff --git a/modules/templates/src/main/kotlin/org/lwjgl/ovr/templates/OVR.kt b/modules/templates/src/main/kotlin/org/lwjgl/ovr/templates/OVR.kt index 71406837ea..2518ad9f84 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/ovr/templates/OVR.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/ovr/templates/OVR.kt @@ -521,7 +521,7 @@ ENABLE_WARNINGS()""") ovrErrorInfo_p.OUT("errorInfo", "The last ##OVRErrorInfo for the current thread") ) - const..charUTF8_p( + Nonnull..const..charUTF8_p( "GetVersionString", """ Returns the version string representing the LibOVRRT version. diff --git a/modules/templates/src/main/kotlin/org/lwjgl/util/lmdb/templates/lmdb.kt b/modules/templates/src/main/kotlin/org/lwjgl/util/lmdb/templates/lmdb.kt index d235ede08c..b17eda6474 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/util/lmdb/templates/lmdb.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/util/lmdb/templates/lmdb.kt @@ -254,7 +254,7 @@ ENABLE_WARNINGS()""") returnDoc = "the library version as a string" ) - charASCII_p( + Nonnull..charASCII_p( "strerror", """ Returns a string describing a given error code. diff --git a/modules/templates/src/main/kotlin/org/lwjgl/util/lz4/templates/LZ4.kt b/modules/templates/src/main/kotlin/org/lwjgl/util/lz4/templates/LZ4.kt index 4eaf4dae6e..1d271b30e7 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/util/lz4/templates/LZ4.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/util/lz4/templates/LZ4.kt @@ -43,7 +43,7 @@ ENABLE_WARNINGS()""") StringConstant("Version string.", "VERSION_STRING".."""LZ4_VERSION_MAJOR + "." + LZ4_VERSION_MINOR + "." + LZ4_VERSION_RELEASE""") int("versionNumber", "Returns the version number.") - const..charASCII_p("versionString", "Returns the version string.") + Nonnull..const..charASCII_p("versionString", "Returns the version string.") IntConstant( "Maximum input size.", diff --git a/modules/templates/src/main/kotlin/org/lwjgl/util/yoga/templates/yoga.kt b/modules/templates/src/main/kotlin/org/lwjgl/util/yoga/templates/yoga.kt index 78462508ca..caa472936c 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/util/yoga/templates/yoga.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/util/yoga/templates/yoga.kt @@ -755,7 +755,7 @@ div { fun YG_TYPE_TO_STRING(type: IntegerType) { val name = type.name.substring(2) - const..charASCII_p( + Nonnull..const..charASCII_p( "${name}ToString", "", diff --git a/modules/templates/src/main/kotlin/org/lwjgl/util/zstd/templates/Zstd.kt b/modules/templates/src/main/kotlin/org/lwjgl/util/zstd/templates/Zstd.kt index a9e1e1a175..57ca61c777 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/util/zstd/templates/Zstd.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/util/zstd/templates/Zstd.kt @@ -87,7 +87,7 @@ ENABLE_WARNINGS()""") StringConstant("Version string.", "VERSION_STRING".."""ZSTD_VERSION_MAJOR + "." + ZSTD_VERSION_MINOR + "." + ZSTD_VERSION_RELEASE""") unsigned("versionNumber", "Returns the version number.") - const..charASCII_p("versionString", "Returns the version string.") + Nonnull..const..charASCII_p("versionString", "Returns the version string.") LongConstant( "Content size.", @@ -207,7 +207,7 @@ ENABLE_WARNINGS()""") size_t.IN("code", "") ) - const..charASCII_p( + Nonnull..const..charASCII_p( "getErrorName", "Provides readable string from an error code.", diff --git a/modules/templates/src/main/kotlin/org/lwjgl/util/zstd/templates/ZstdErrors.kt b/modules/templates/src/main/kotlin/org/lwjgl/util/zstd/templates/ZstdErrors.kt index b8e1cd379b..263f33792f 100644 --- a/modules/templates/src/main/kotlin/org/lwjgl/util/zstd/templates/ZstdErrors.kt +++ b/modules/templates/src/main/kotlin/org/lwjgl/util/zstd/templates/ZstdErrors.kt @@ -52,7 +52,7 @@ ENABLE_WARNINGS()""")*/ size_t.IN("functionResult", "") ) - const..charASCII_p( + Nonnull..const..charASCII_p( "getErrorString", "", From c53ffebab59a0b490c86603c029c52afb7ff3204 Mon Sep 17 00:00:00 2001 From: Ioannis Tsakpinis Date: Fri, 24 Nov 2017 00:09:52 +0200 Subject: [PATCH 3/4] feat: nullability support in structs, callbacks, memory management --- doc/notes/3.1.6.md | 9 + .../core/src/main/java/org/lwjgl/egl/EGL.java | 32 +- .../src/main/java/org/lwjgl/openal/AL.java | 43 +- .../src/main/java/org/lwjgl/openal/ALC.java | 21 +- .../src/main/java/org/lwjgl/opencl/CL.java | 34 +- .../src/main/java/org/lwjgl/opengl/GL.java | 53 ++- .../main/java/org/lwjgl/opengles/GLES.java | 38 +- .../main/java/org/lwjgl/system/APIUtil.java | 4 +- .../main/java/org/lwjgl/system/Callback.java | 17 +- .../main/java/org/lwjgl/system/Checks.java | 154 ++---- .../java/org/lwjgl/system/CustomBuffer.java | 4 + .../java/org/lwjgl/system/MemoryAccess.java | 7 +- .../java/org/lwjgl/system/MemoryManage.java | 2 + .../java/org/lwjgl/system/MemoryStack.java | 79 +++- .../java/org/lwjgl/system/MemoryUtil.java | 440 ++++++++++++------ .../main/java/org/lwjgl/system/Pointer.java | 5 + .../org/lwjgl/system/SharedLibraryLoader.java | 19 +- .../main/java/org/lwjgl/system/Struct.java | 29 +- .../src/main/java/org/lwjgl/vulkan/VK.java | 19 +- .../java/org/lwjgl/system/BufferTest.java | 18 +- .../java/org/lwjgl/system/MemoryUtilTest.java | 28 +- .../test/java/org/lwjgl/system/StackTest.java | 24 +- .../java/org/lwjgl/system/StructTest.java | 64 +++ .../org/lwjgl/generator/CallbackFunction.kt | 17 +- .../org/lwjgl/generator/FunctionTransforms.kt | 18 +- .../kotlin/org/lwjgl/generator/Functions.kt | 40 +- .../kotlin/org/lwjgl/generator/NativeClass.kt | 2 +- .../kotlin/org/lwjgl/generator/Structs.kt | 240 ++++++---- 28 files changed, 955 insertions(+), 505 deletions(-) create mode 100644 modules/core/src/test/java/org/lwjgl/system/StructTest.java diff --git a/doc/notes/3.1.6.md b/doc/notes/3.1.6.md index 9eb91dff46..482b09e345 100644 --- a/doc/notes/3.1.6.md +++ b/doc/notes/3.1.6.md @@ -43,6 +43,15 @@ This build includes the following changes: #### Breaking Changes +- Several methods that previously accepted `null`/`NULL` and returned `null`, now require non-null input. + * Applies to: struct & callback creation methods and `memByteBuffer`/`memUTF8`/`stack.UTF8`/etc. + * Added corresponding methods with the `Safe` suffix that accept `null`/`NULL`, matching the old behavior. + * With this change the common case (non-null input) requires no code changes and is warning/error-free. The uncommon case (null input) is recognizable (the suffix) and must be handled properly to eliminate warnings/errors. +- Allocation methods that returned `null`/`NULL` on allocation failure, now throw `OutOfMemoryError` instead. This matches the behavior of `ByteBuffer.allocateDirect`. + * Applies to: struct allocation methods and `memAlloc`/`memCalloc`/etc. + * Does not apply to allocations via direct binding calls (e.g. `LibCStdlib.malloc`). +- Getters of struct members that should never be `NULL`, throw `NullPointerException` instead of returning `null` when the struct instance is not initialized. + * Use `Struct::isNull` to test pointer members of untrusted struct instances. - glfw: `glfwInitHintString` has been renamed to `glfwWindowHintString`. - lmdb: Databases developed on 32-bit architectures must be recreated. - par_shapes: Changed `par_shapes_mesh::triangles` from `uint16_t/ShortBuffer` to `uint32_t/IntBuffer`. diff --git a/modules/core/src/main/java/org/lwjgl/egl/EGL.java b/modules/core/src/main/java/org/lwjgl/egl/EGL.java index d0e4ae3248..e11bb5e207 100644 --- a/modules/core/src/main/java/org/lwjgl/egl/EGL.java +++ b/modules/core/src/main/java/org/lwjgl/egl/EGL.java @@ -6,6 +6,7 @@ import org.lwjgl.system.*; +import javax.annotation.*; import java.nio.*; import java.util.*; @@ -35,8 +36,10 @@ */ public final class EGL { + @Nullable private static FunctionProvider functionProvider; + @Nullable private static EGLCapabilities caps; static { @@ -135,6 +138,7 @@ public static void destroy() { } /** Returns the {@link FunctionProvider} for the EGL native library. */ + @Nullable public static FunctionProvider getFunctionProvider() { return functionProvider; } @@ -145,11 +149,17 @@ public static FunctionProvider getFunctionProvider() { * The capability flags in this instance are only set for the core EGL versions and client extensions. This may only happen if EGL 1.5 or the * {@link EGLCapabilities#EGL_EXT_client_extensions} extension are supported. If not, all flags will be false and the version fields zero. */ + @Nullable public static EGLCapabilities getCapabilities() { return caps; } private static EGLCapabilities createClientCapabilities() { + FunctionProvider functionProvider = EGL.functionProvider; + if (functionProvider == null) { + throw new IllegalStateException("EGL library has not been loaded."); + } + Set ext = new HashSet<>(32); long QueryString = functionProvider.getFunctionAddress("eglQueryString"); @@ -160,11 +170,11 @@ private static EGLCapabilities createClientCapabilities() { callI(functionProvider.getFunctionAddress("eglGetError")); // clear error } else { // Available on EGL 1.5 only - long versionString = callPP(QueryString, EGL_NO_DISPLAY, EGL_VERSION); - if (versionString == NULL) { + String versionString = memASCIISafe(callPP(QueryString, EGL_NO_DISPLAY, EGL_VERSION)); + if (versionString == null) { callI(functionProvider.getFunctionAddress("eglGetError")); // clear error } else { - APIVersion version = apiParseVersion(memASCII(versionString), "EGL"); + APIVersion version = apiParseVersion(versionString, "EGL"); addEGLVersions(version.major, version.minor, ext); } } @@ -182,7 +192,11 @@ private static EGLCapabilities createClientCapabilities() { * @return the {@link EGLCapabilities instance} */ public static EGLCapabilities createDisplayCapabilities(long dpy) { - APIVersion version = apiParseVersion(eglQueryString(dpy, EGL_VERSION)); + String versionString = eglQueryString(dpy, EGL_VERSION); + if (versionString == null) { + throw new IllegalArgumentException("Invalid EGLDisplay handle specified."); + } + APIVersion version = apiParseVersion(versionString); return createDisplayCapabilities(dpy, version.major, version.minor); } @@ -198,12 +212,20 @@ public static EGLCapabilities createDisplayCapabilities(long dpy) { * @return the {@link EGLCapabilities instance} */ public static EGLCapabilities createDisplayCapabilities(long dpy, int majorVersion, int minorVersion) { + FunctionProvider functionProvider = EGL.functionProvider; + if (functionProvider == null) { + throw new IllegalStateException("EGL library has not been loaded."); + } + Set supportedExtensions = new HashSet<>(32); // Add EGL versions addEGLVersions(majorVersion, minorVersion, supportedExtensions); // Parse display EGL_EXTENSIONS string - addExtensions(eglQueryString(dpy, EGL_EXTENSIONS), supportedExtensions); + String extensionsString = eglQueryString(dpy, EGL_EXTENSIONS); + if (extensionsString != null) { + addExtensions(extensionsString, supportedExtensions); + } return new EGLCapabilities(functionProvider, supportedExtensions); } diff --git a/modules/core/src/main/java/org/lwjgl/openal/AL.java b/modules/core/src/main/java/org/lwjgl/openal/AL.java index a6082cf6dc..d4a33cd8ae 100644 --- a/modules/core/src/main/java/org/lwjgl/openal/AL.java +++ b/modules/core/src/main/java/org/lwjgl/openal/AL.java @@ -6,13 +6,13 @@ import org.lwjgl.system.*; +import javax.annotation.*; import java.nio.*; import java.util.*; import static org.lwjgl.openal.AL10.*; import static org.lwjgl.openal.EXTThreadLocalContext.*; import static org.lwjgl.system.APIUtil.*; -import static org.lwjgl.system.Checks.*; import static org.lwjgl.system.JNI.*; import static org.lwjgl.system.MemoryStack.*; import static org.lwjgl.system.MemoryUtil.*; @@ -43,8 +43,10 @@ */ public final class AL { + @Nullable private static FunctionProvider functionProvider; + @Nullable private static ALCapabilities processCaps; private static final ThreadLocal capabilitiesTLS = new ThreadLocal<>(); @@ -89,7 +91,7 @@ static void destroy() { * * @param caps the {@link ALCapabilities} to make current, or null */ - public static void setCurrentProcess(ALCapabilities caps) { + public static void setCurrentProcess(@Nullable ALCapabilities caps) { processCaps = caps; capabilitiesTLS.set(null); // See EXT_thread_local_context, second Q. icd.set(caps); @@ -102,7 +104,7 @@ public static void setCurrentProcess(ALCapabilities caps) { * * @param caps the {@link ALCapabilities} to make current, or null */ - public static void setCurrentThread(ALCapabilities caps) { + public static void setCurrentThread(@Nullable ALCapabilities caps) { capabilitiesTLS.set(caps); icd.set(caps); } @@ -121,8 +123,8 @@ public static ALCapabilities getCapabilities() { return checkCapabilities(caps); } - private static ALCapabilities checkCapabilities(ALCapabilities caps) { - if (CHECKS && caps == null) { + private static ALCapabilities checkCapabilities(@Nullable ALCapabilities caps) { + if (caps == null) { throw new IllegalStateException( "No ALCapabilities instance set for the current thread or process. Possible solutions:\n" + "\ta) Call AL.createCapabilities() after making a context current.\n" + @@ -140,6 +142,8 @@ private static ALCapabilities checkCapabilities(ALCapabilities caps) { * @return the ALCapabilities instance */ public static ALCapabilities createCapabilities(ALCCapabilities alcCaps) { + FunctionProvider functionProvider = ALC.check(AL.functionProvider); + ALCapabilities caps = null; try { @@ -150,12 +154,12 @@ public static ALCapabilities createCapabilities(ALCCapabilities alcCaps) { throw new IllegalStateException("Core OpenAL functions could not be found. Make sure that the OpenAL library has been loaded correctly."); } - long versionString = invokeP(GetString, AL_VERSION); - if (versionString == NULL || invokeI(GetError) != AL_NO_ERROR) { + String versionString = memASCIISafe(invokeP(GetString, AL_VERSION)); + if (versionString == null || invokeI(GetError) != AL_NO_ERROR) { throw new IllegalStateException("There is no OpenAL context current in the current thread or process."); } - APIVersion apiVersion = apiParseVersion(memASCII(versionString)); + APIVersion apiVersion = apiParseVersion(versionString); int majorVersion = apiVersion.major; int minorVersion = apiVersion.minor; @@ -176,19 +180,17 @@ public static ALCapabilities createCapabilities(ALCCapabilities alcCaps) { } // Parse EXTENSIONS string - String extensionsString = memASCII(invokeP(GetString, AL_EXTENSIONS)); + String extensionsString = memASCIISafe(invokeP(GetString, AL_EXTENSIONS)); if (extensionsString != null) { - MemoryStack stack = stackGet(); + MemoryStack stack = stackGet(); + StringTokenizer tokenizer = new StringTokenizer(extensionsString); while (tokenizer.hasMoreTokens()) { String extName = tokenizer.nextToken(); - stack.push(); - try { - if (invokePZ(IsExtensionPresent, memAddress(stack.ASCII(extName, true)))) { + try (MemoryStack frame = stack.push()) { + if (invokePZ(IsExtensionPresent, memAddress(frame.ASCII(extName, true)))) { supportedExtensions.add(extName); } - } finally { - stack.pop(); } } } @@ -208,13 +210,13 @@ public static ALCapabilities createCapabilities(ALCCapabilities alcCaps) { } static ALCapabilities getICD() { - return icd.get(); + return ALC.check(icd.get()); } /** Function pointer provider. */ private interface ICD { - default void set(ALCapabilities caps) {} - ALCapabilities get(); + default void set(@Nullable ALCapabilities caps) {} + @Nullable ALCapabilities get(); } /** @@ -226,10 +228,11 @@ default void set(ALCapabilities caps) {} */ private static class ICDStatic implements ICD { + @Nullable private static ALCapabilities tempCaps; @Override - public void set(ALCapabilities caps) { + public void set(@Nullable ALCapabilities caps) { if (tempCaps == null) { tempCaps = caps; } else if (caps != null && caps != tempCaps && !ThreadLocalUtil.compareCapabilities(tempCaps.addresses, caps.addresses)) { @@ -239,12 +242,14 @@ public void set(ALCapabilities caps) { } @Override + @Nullable public ALCapabilities get() { return WriteOnce.caps; } private static final class WriteOnce { // This will be initialized the first time get() above is called + @Nullable private static final ALCapabilities caps = ICDStatic.tempCaps; static { diff --git a/modules/core/src/main/java/org/lwjgl/openal/ALC.java b/modules/core/src/main/java/org/lwjgl/openal/ALC.java index f8f991d310..7ea4c34191 100644 --- a/modules/core/src/main/java/org/lwjgl/openal/ALC.java +++ b/modules/core/src/main/java/org/lwjgl/openal/ALC.java @@ -6,12 +6,12 @@ import org.lwjgl.system.*; +import javax.annotation.*; import java.nio.*; import java.util.*; import static org.lwjgl.openal.ALC10.*; import static org.lwjgl.system.APIUtil.*; -import static org.lwjgl.system.Checks.*; import static org.lwjgl.system.JNI.*; import static org.lwjgl.system.MemoryStack.*; import static org.lwjgl.system.MemoryUtil.*; @@ -37,8 +37,10 @@ */ public final class ALC { + @Nullable private static FunctionProviderLocal functionProvider; + @Nullable private static ALCCapabilities icd; static { @@ -146,14 +148,21 @@ public static void destroy() { functionProvider = null; } + static T check(@Nullable T t) { + if (t == null) { + throw new IllegalStateException("OpenAL library has not been loaded."); + } + return t; + } + /** Returns the {@link FunctionProviderLocal} for the OpenAL native library. */ public static FunctionProviderLocal getFunctionProvider() { - return functionProvider; + return check(ALC.functionProvider); } /** Returns the {@link ALCCapabilities} of the OpenAL implementation. */ static ALCCapabilities getICD() { - return icd; + return check(icd); } /** @@ -162,6 +171,8 @@ static ALCCapabilities getICD() { * @return the {@code ALCCapabilities} instance */ public static ALCCapabilities createCapabilities(long device) { + FunctionProviderLocal functionProvider = getFunctionProvider(); + // We don't have an ALCCapabilities instance when this method is called // so we have to use the native bindings directly. long GetIntegerv = functionProvider.getFunctionAddress("alcGetIntegerv"); @@ -201,7 +212,7 @@ public static ALCCapabilities createCapabilities(long device) { } // Parse EXTENSIONS string - String extensionsString = memASCII(check(invokePP(GetString, device, ALC_EXTENSIONS))); + String extensionsString = memASCIISafe(invokePP(GetString, device, ALC_EXTENSIONS)); if (extensionsString != null) { StringTokenizer tokenizer = new StringTokenizer(extensionsString); while (tokenizer.hasMoreTokens()) { @@ -214,7 +225,7 @@ public static ALCCapabilities createCapabilities(long device) { } } - return new ALCCapabilities(getFunctionProvider(), device, supportedExtensions); + return new ALCCapabilities(functionProvider, device, supportedExtensions); } } \ No newline at end of file diff --git a/modules/core/src/main/java/org/lwjgl/opencl/CL.java b/modules/core/src/main/java/org/lwjgl/opencl/CL.java index e323798d2f..50187f36c2 100644 --- a/modules/core/src/main/java/org/lwjgl/opencl/CL.java +++ b/modules/core/src/main/java/org/lwjgl/opencl/CL.java @@ -8,6 +8,7 @@ import org.lwjgl.system.*; import org.lwjgl.system.macosx.*; +import javax.annotation.*; import java.nio.*; import java.util.*; @@ -33,8 +34,10 @@ */ public final class CL { + @Nullable private static FunctionProviderLocal functionProvider; + @Nullable private static CLCapabilities icd; static { @@ -213,12 +216,13 @@ public static void destroy() { } /** Returns the {@link FunctionProviderLocal} for the OpenCL native library. */ + @Nullable public static FunctionProviderLocal getFunctionProvider() { return functionProvider; } /** Returns the {@link CLCapabilities} of the ICD. */ - public static CLCapabilities getICD() { return icd; } + @Nullable public static CLCapabilities getICD() { return icd; } /** * Creates a {@link CLCapabilities} instance for the specified OpenCL platform. @@ -236,34 +240,22 @@ public static CLCapabilities createPlatformCapabilities(long cl_platform_id) { CL.addExtensions(getPlatformInfoStringASCII(cl_platform_id, CL_PLATFORM_EXTENSIONS), supportedExtensions); // Enumerate devices - { - int num_devices; - long[] devices; - - try (MemoryStack stack = stackPush()) { - IntBuffer pi = stack.mallocInt(1); - - checkCLError(nclGetDeviceIDs(cl_platform_id, CL_DEVICE_TYPE_ALL, 0, NULL, memAddress(pi))); - num_devices = pi.get(0); - if (num_devices == 0) { - return null; - } + try (MemoryStack stack = stackPush()) { + IntBuffer pi = stack.mallocInt(1); + checkCLError(nclGetDeviceIDs(cl_platform_id, CL_DEVICE_TYPE_ALL, 0, NULL, memAddress(pi))); + int num_devices = pi.get(0); + if (num_devices != 0) { PointerBuffer pp = stack.mallocPointer(num_devices); checkCLError(nclGetDeviceIDs(cl_platform_id, CL_DEVICE_TYPE_ALL, num_devices, memAddress(pp), NULL)); - devices = new long[num_devices]; + // Add device extensions to the set for (int i = 0; i < num_devices; i++) { - devices[i] = pp.get(i); + String extensionsString = getDeviceInfoStringASCII(pp.get(i), CL_DEVICE_EXTENSIONS); + CL.addExtensions(extensionsString, supportedExtensions); } } - - // Add device extensions to the set - for (int i = 0; i < num_devices; i++) { - String extensionsString = getDeviceInfoStringASCII(devices[i], CL_DEVICE_EXTENSIONS); - CL.addExtensions(extensionsString, supportedExtensions); - } } // Parse PLATFORM_VERSION string diff --git a/modules/core/src/main/java/org/lwjgl/opengl/GL.java b/modules/core/src/main/java/org/lwjgl/opengl/GL.java index 568c36d379..e456403c39 100644 --- a/modules/core/src/main/java/org/lwjgl/opengl/GL.java +++ b/modules/core/src/main/java/org/lwjgl/opengl/GL.java @@ -8,6 +8,7 @@ import org.lwjgl.system.macosx.*; import org.lwjgl.system.windows.*; +import javax.annotation.*; import java.nio.*; import java.util.*; @@ -57,17 +58,22 @@ */ public final class GL { + @Nullable private static final APIVersion MAX_VERSION; + @Nullable private static FunctionProvider functionProvider; private static final ThreadLocal capabilitiesTLS = new ThreadLocal<>(); private static ICD icd = new ICDStatic(); + @Nullable private static WGLCapabilities capabilitiesWGL; + @Nullable private static GLXCapabilities capabilitiesGLXClient; + @Nullable private static GLXCapabilities capabilitiesGLX; static { @@ -197,7 +203,7 @@ long getExtensionAddress(long name) { */ public static void create(FunctionProvider functionProvider) { if (GL.functionProvider != null) { - throw new IllegalStateException("OpenGL has already been created."); + throw new IllegalStateException("OpenGL library has already been loaded."); } GL.functionProvider = functionProvider; @@ -219,6 +225,7 @@ public static void destroy() { } /** Returns the {@link FunctionProvider} for the OpenGL native library. */ + @Nullable public static FunctionProvider getFunctionProvider() { return functionProvider; } @@ -229,7 +236,7 @@ public static FunctionProvider getFunctionProvider() { *

    This {@code GLCapabilities} instance will be used by any OpenGL call in the current thread, until {@code setCapabilities} is called again with a * different value.

    */ - public static void setCapabilities(GLCapabilities caps) { + public static void setCapabilities(@Nullable GLCapabilities caps) { capabilitiesTLS.set(caps); ThreadLocalUtil.setEnv(caps == null ? NULL : memAddress(caps.addresses), 3); icd.set(caps); @@ -244,7 +251,7 @@ public static GLCapabilities getCapabilities() { return checkCapabilities(capabilitiesTLS.get()); } - private static GLCapabilities checkCapabilities(GLCapabilities caps) { + private static GLCapabilities checkCapabilities(@Nullable GLCapabilities caps) { if (CHECKS && caps == null) { throw new IllegalStateException( "No GLCapabilities instance set for the current thread. Possible solutions:\n" + @@ -252,6 +259,7 @@ private static GLCapabilities checkCapabilities(GLCapabilities caps) { "\tb) Call GL.setCapabilities() if a GLCapabilities instance already exists for the current context." ); } + //noinspection ConstantConditions return caps; } @@ -326,6 +334,11 @@ public static GLCapabilities createCapabilities() { */ @SuppressWarnings("AssignmentToMethodParameter") public static GLCapabilities createCapabilities(boolean forwardCompatible) { + FunctionProvider functionProvider = GL.functionProvider; + if (functionProvider == null) { + throw new IllegalStateException("OpenGL library has not been loaded."); + } + GLCapabilities caps = null; try { @@ -358,12 +371,12 @@ public static GLCapabilities createCapabilities(boolean forwardCompatible) { minorVersion = version.get(0); } else { // Fallback to the string query. - long versionString = callP(GetString, GL_VERSION); - if (versionString == NULL || callI(GetError) != GL_NO_ERROR) { + String versionString = memUTF8Safe(callP(GetString, GL_VERSION)); + if (versionString == null || callI(GetError) != GL_NO_ERROR) { throw new IllegalStateException("There is no OpenGL context current in the current thread."); } - APIVersion apiVersion = apiParseVersion(memUTF8(versionString)); + APIVersion apiVersion = apiParseVersion(versionString); majorVersion = apiVersion.major; minorVersion = apiVersion.minor; @@ -403,7 +416,7 @@ public static GLCapabilities createCapabilities(boolean forwardCompatible) { if (majorVersion < 3) { // Parse EXTENSIONS string - String extensionsString = memASCII(callP(GetString, GL_EXTENSIONS)); + String extensionsString = memASCIISafe(callP(GetString, GL_EXTENSIONS)); if (extensionsString != null) { StringTokenizer tokenizer = new StringTokenizer(extensionsString); while (tokenizer.hasMoreTokens()) { @@ -448,7 +461,7 @@ public static GLCapabilities createCapabilities(boolean forwardCompatible) { } } - return caps = new GLCapabilities(getFunctionProvider(), supportedExtensions, forwardCompatible); + return caps = new GLCapabilities(functionProvider, supportedExtensions, forwardCompatible); } finally { setCapabilities(caps); } @@ -548,6 +561,11 @@ public static WGLCapabilities createCapabilitiesWGL() { * @param hdc the device context handle ({@code HDC}) */ private static WGLCapabilities createCapabilitiesWGL(long hdc) { + FunctionProvider functionProvider = GL.functionProvider; + if (functionProvider == null) { + throw new IllegalStateException("OpenGL library has not been loaded."); + } + String extensionsString = null; long wglGetExtensionsString = functionProvider.getFunctionAddress("wglGetExtensionsStringARB"); @@ -592,6 +610,11 @@ public static GLXCapabilities createCapabilitiesGLX(long display) { * @param screen the screen index */ public static GLXCapabilities createCapabilitiesGLX(long display, int screen) { + FunctionProvider functionProvider = GL.functionProvider; + if (functionProvider == null) { + throw new IllegalStateException("OpenGL library has not been loaded."); + } + int majorVersion; int minorVersion; @@ -630,10 +653,10 @@ public static GLXCapabilities createCapabilitiesGLX(long display, int screen) { if (screen == -1) { long glXGetClientString = functionProvider.getFunctionAddress("glXGetClientString"); - extensionsString = memASCII(callPP(glXGetClientString, display, GLX_EXTENSIONS)); + extensionsString = memASCIISafe(callPP(glXGetClientString, display, GLX_EXTENSIONS)); } else { long glXQueryExtensionsString = functionProvider.getFunctionAddress("glXQueryExtensionsString"); - extensionsString = memASCII(callPP(glXQueryExtensionsString, display, screen)); + extensionsString = memASCIISafe(callPP(glXQueryExtensionsString, display, screen)); } if (extensionsString != null) { @@ -649,13 +672,13 @@ public static GLXCapabilities createCapabilitiesGLX(long display, int screen) { // Only used by array overloads static GLCapabilities getICD() { - return icd.get(); + return checkCapabilities(icd.get()); } /** Function pointer provider. */ private interface ICD { - default void set(GLCapabilities caps) {} - GLCapabilities get(); + default void set(@Nullable GLCapabilities caps) {} + @Nullable GLCapabilities get(); } /** @@ -666,10 +689,11 @@ default void set(GLCapabilities caps) {} */ private static class ICDStatic implements ICD { + @Nullable private static GLCapabilities tempCaps; @Override - public void set(GLCapabilities caps) { + public void set(@Nullable GLCapabilities caps) { if (tempCaps == null) { tempCaps = caps; } else if (caps != null && caps != tempCaps && !ThreadLocalUtil.compareCapabilities(tempCaps.addresses, caps.addresses)) { @@ -685,6 +709,7 @@ public GLCapabilities get() { private static final class WriteOnce { // This will be initialized the first time get() above is called + @Nullable private static final GLCapabilities caps = ICDStatic.tempCaps; static { diff --git a/modules/core/src/main/java/org/lwjgl/opengles/GLES.java b/modules/core/src/main/java/org/lwjgl/opengles/GLES.java index 194b1f0027..2f04541c37 100644 --- a/modules/core/src/main/java/org/lwjgl/opengles/GLES.java +++ b/modules/core/src/main/java/org/lwjgl/opengles/GLES.java @@ -7,6 +7,7 @@ import org.lwjgl.egl.*; import org.lwjgl.system.*; +import javax.annotation.*; import java.nio.*; import java.util.*; @@ -48,8 +49,10 @@ */ public final class GLES { + @Nullable private static final APIVersion MAX_VERSION; + @Nullable private static FunctionProvider functionProvider; private static final ThreadLocal capabilitiesTLS = new ThreadLocal<>(); @@ -148,6 +151,7 @@ public static void destroy() { } /** Returns the {@link FunctionProvider} for the OpenGL ES native library. */ + @Nullable public static FunctionProvider getFunctionProvider() { return functionProvider; } @@ -158,7 +162,7 @@ public static FunctionProvider getFunctionProvider() { *

    This {@code GLESCapabilities} instance will be used by any OpenGL ES call in the current thread, until {@code setCapabilities} is called again with * a different value.

    */ - public static void setCapabilities(GLESCapabilities caps) { + public static void setCapabilities(@Nullable GLESCapabilities caps) { capabilitiesTLS.set(caps); ThreadLocalUtil.setEnv(caps == null ? NULL : memAddress(caps.addresses), 3); icd.set(caps); @@ -173,8 +177,8 @@ public static GLESCapabilities getCapabilities() { return checkCapabilities(capabilitiesTLS.get()); } - private static GLESCapabilities checkCapabilities(GLESCapabilities caps) { - if (CHECKS && caps == null) { + private static GLESCapabilities checkCapabilities(@Nullable GLESCapabilities caps) { + if (caps == null) { throw new IllegalStateException( "No GLESCapabilities instance set for the current thread. Possible solutions:\n" + "\ta) Call GLES.createCapabilities() after making a context current in the current thread.\n" + @@ -194,6 +198,11 @@ private static GLESCapabilities checkCapabilities(GLESCapabilities caps) { * @throws IllegalStateException if no OpenGL ES context is current in the current thread */ public static GLESCapabilities createCapabilities() { + FunctionProvider functionProvider = GLES.functionProvider; + if (functionProvider == null) { + throw new IllegalStateException("OpenGL ES library not been loaded."); + } + GLESCapabilities caps = null; try { @@ -228,12 +237,12 @@ public static GLESCapabilities createCapabilities() { minorVersion = pi.get(0); } else { // Fallback to the string query. - long versionString = callP(GetString, GL_VERSION); - if (versionString == NULL || callI(GetError) != GL_NO_ERROR) { + String versionString = memUTF8Safe(callP(GetString, GL_VERSION)); + if (versionString == null || callI(GetError) != GL_NO_ERROR) { throw new IllegalStateException("There is no OpenGL ES context current in the current thread."); } - APIVersion version = apiParseVersion(memUTF8(versionString), "OpenGL ES"); + APIVersion version = apiParseVersion(versionString, "OpenGL ES"); majorVersion = version.major; minorVersion = version.minor; @@ -272,7 +281,7 @@ public static GLESCapabilities createCapabilities() { if (majorVersion < 3) { // Parse EXTENSIONS string - String extensionsString = memASCII(check(callP(GetString, GL_EXTENSIONS))); + String extensionsString = memASCIISafe(callP(GetString, GL_EXTENSIONS)); if (extensionsString != null) { StringTokenizer tokenizer = new StringTokenizer(extensionsString); while (tokenizer.hasMoreTokens()) { @@ -292,11 +301,11 @@ public static GLESCapabilities createCapabilities() { long GetStringi = apiGetFunctionAddress(functionProvider, "glGetStringi"); for (int i = 0; i < extensionCount; i++) { - supportedExtensions.add(memASCII(check(callP(GetStringi, GL_EXTENSIONS, i)))); + supportedExtensions.add(memASCII(callP(GetStringi, GL_EXTENSIONS, i))); } } - caps = new GLESCapabilities(getFunctionProvider(), supportedExtensions); + caps = new GLESCapabilities(functionProvider, supportedExtensions); } finally { setCapabilities(caps); } @@ -306,13 +315,13 @@ public static GLESCapabilities createCapabilities() { // Only used by array overloads static GLESCapabilities getICD() { - return icd.get(); + return checkCapabilities(icd.get()); } /** Function pointer provider. */ private interface ICD { - default void set(GLESCapabilities caps) {} - GLESCapabilities get(); + default void set(@Nullable GLESCapabilities caps) {} + @Nullable GLESCapabilities get(); } /** @@ -323,10 +332,11 @@ default void set(GLESCapabilities caps) {} */ private static class ICDStatic implements ICD { + @Nullable private static GLESCapabilities tempCaps; @Override - public void set(GLESCapabilities caps) { + public void set(@Nullable GLESCapabilities caps) { if (tempCaps == null) { tempCaps = caps; } else if (caps != null && caps != tempCaps && !ThreadLocalUtil.compareCapabilities(tempCaps.addresses, caps.addresses)) { @@ -336,12 +346,14 @@ public void set(GLESCapabilities caps) { } @Override + @Nullable public GLESCapabilities get() { return WriteOnce.caps; } private static final class WriteOnce { // This will be initialized the first time get() above is called + @Nullable private static final GLESCapabilities caps = ICDStatic.tempCaps; static { diff --git a/modules/core/src/main/java/org/lwjgl/system/APIUtil.java b/modules/core/src/main/java/org/lwjgl/system/APIUtil.java index 3773329d7c..63a98538c0 100644 --- a/modules/core/src/main/java/org/lwjgl/system/APIUtil.java +++ b/modules/core/src/main/java/org/lwjgl/system/APIUtil.java @@ -138,7 +138,7 @@ public static long apiGetFunctionAddress(FunctionProvider provider, String funct } private static void requiredFunctionMissing(String functionName) { - if (!Configuration.DISABLE_FUNCTION_CHECKS.get()) { + if (!Configuration.DISABLE_FUNCTION_CHECKS.get(false)) { throw new NullPointerException("A required function is missing: " + functionName); } } @@ -146,7 +146,7 @@ private static void requiredFunctionMissing(String functionName) { @Nullable public static ByteBuffer apiGetMappedBuffer(@Nullable ByteBuffer buffer, long mappedAddress, int capacity) { return buffer == null || memAddress(buffer) != mappedAddress || buffer.capacity() != capacity - ? memByteBuffer(mappedAddress, capacity) + ? memByteBufferSafe(mappedAddress, capacity) : buffer; } diff --git a/modules/core/src/main/java/org/lwjgl/system/Callback.java b/modules/core/src/main/java/org/lwjgl/system/Callback.java index a5c985d249..c37f8767aa 100644 --- a/modules/core/src/main/java/org/lwjgl/system/Callback.java +++ b/modules/core/src/main/java/org/lwjgl/system/Callback.java @@ -9,6 +9,7 @@ import javax.annotation.*; import java.lang.reflect.*; +import static org.lwjgl.system.Checks.*; import static org.lwjgl.system.MemoryStack.*; import static org.lwjgl.system.MemoryUtil.*; import static org.lwjgl.system.dyncall.DynCallback.*; @@ -88,6 +89,9 @@ protected Callback(String signature) { * @param address the function address */ protected Callback(long address) { + if (CHECKS) { + check(address); + } this.address = address; } @@ -163,17 +167,18 @@ private static long getNativeFunction(char type) { * @param functionPointer a function pointer * @param the {@code CallbackI} instance type * - * @return the {@code CallbackI} instance, or null if the function pointer is {@code NULL}. + * @return the {@code CallbackI} instance */ - @Nullable public static T get(long functionPointer) { - if (functionPointer == NULL) { - return null; - } - return memGlobalRefToObject(dcbGetUserData(functionPointer)); } + /** Like {@link #get}, but returns {@code null} if {@code functionPointer} is {@code NULL}. */ + @Nullable + public static T getSafe(long functionPointer) { + return functionPointer == NULL ? null : get(functionPointer); + } + /** * Frees any resources held by the specified function pointer. * diff --git a/modules/core/src/main/java/org/lwjgl/system/Checks.java b/modules/core/src/main/java/org/lwjgl/system/Checks.java index 6825573bb9..b383417313 100644 --- a/modules/core/src/main/java/org/lwjgl/system/Checks.java +++ b/modules/core/src/main/java/org/lwjgl/system/Checks.java @@ -102,6 +102,12 @@ public static long check(long pointer) { return pointer; } + private static void assertNT(boolean found) { + if (!found) { + throw new IllegalArgumentException("Missing termination"); + } + } + /** Ensures that the specified array is null-terminated. */ public static void checkNT(int[] buf) { checkNT(buf, 0); @@ -110,41 +116,31 @@ public static void checkNT(int[] buf) { /** Ensures that the specified array is terminated with the specified terminator. */ public static void checkNT(int[] buf, int terminator) { check(buf, 1); - if (buf[buf.length - 1] != terminator) { - throw new IllegalArgumentException("Missing termination"); - } + assertNT(buf[buf.length - 1] == terminator); } /** Ensures that the specified array is null-terminated. */ public static void checkNT(long[] buf) { check(buf, 1); - if (buf[buf.length - 1] != NULL) { - throw new IllegalArgumentException("Missing termination"); - } + assertNT(buf[buf.length - 1] == NULL); } /** Ensures that the specified array is null-terminated. */ public static void checkNT(float[] buf) { check(buf, 1); - if (buf[buf.length - 1] != 0.0f) { - throw new IllegalArgumentException("Missing termination"); - } + assertNT(buf[buf.length - 1] == 0.0f); } /** Ensures that the specified ByteBuffer is null-terminated (last byte equal to 0). */ public static void checkNT1(ByteBuffer buf) { check(buf, 1); - if (buf.get(buf.limit() - 1) != 0) { - throw new IllegalArgumentException("Missing null termination"); - } + assertNT(buf.get(buf.limit() - 1) == 0); } /** Ensures that the specified ByteBuffer is null-terminated (last 2 bytes equal to 0). */ public static void checkNT2(ByteBuffer buf) { check(buf, 2); - if (buf.getShort(buf.limit() - 2) != 0) { - throw new IllegalArgumentException("Missing null termination"); - } + assertNT(buf.get(buf.limit() - 2) == 0); } /** Ensures that the specified IntBuffer is null-terminated. */ @@ -155,25 +151,19 @@ public static void checkNT(IntBuffer buf) { /** Ensures that the specified IntBuffer is terminated with the specified terminator. */ public static void checkNT(IntBuffer buf, int terminator) { check(buf, 1); - if (buf.get(buf.limit() - 1) != terminator) { - throw new IllegalArgumentException("Missing termination"); - } + assertNT(buf.get(buf.limit() - 1) == terminator); } /** Ensures that the specified LongBuffer is null-terminated. */ public static void checkNT(LongBuffer buf) { check(buf, 1); - if (buf.get(buf.limit() - 1) != NULL) { - throw new IllegalArgumentException("Missing termination"); - } + assertNT(buf.get(buf.limit() - 1) == NULL); } /** Ensures that the specified FloatBuffer is null-terminated. */ public static void checkNT(FloatBuffer buf) { check(buf, 1); - if (buf.get(buf.limit() - 1) != 0.0f) { - throw new IllegalArgumentException("Missing termination"); - } + assertNT(buf.get(buf.limit() - 1) == 0.0f); } /** Ensures that the specified PointerBuffer is null-terminated. */ @@ -184,9 +174,7 @@ public static void checkNT(PointerBuffer buf) { /** Ensures that the specified PointerBuffer is terminated with the specified terminator. */ public static void checkNT(PointerBuffer buf, long terminator) { check(buf, 1); - if (buf.get(buf.limit() - 1) != terminator) { - throw new IllegalArgumentException("Missing termination"); - } + assertNT(buf.get(buf.limit() - 1) == terminator); } public static void checkNTSafe(@Nullable int[] buf) { @@ -250,6 +238,12 @@ public static void checkNTSafe(@Nullable PointerBuffer buf, long terminator) { } } + private static void checkBuffer(int bufferSize, int minimumSize) { + if (bufferSize < minimumSize) { + throwIAE(bufferSize, minimumSize); + } + } + /** * Helper method to ensure a array has enough capacity. * @@ -259,9 +253,7 @@ public static void checkNTSafe(@Nullable PointerBuffer buf, long terminator) { * @throws IllegalArgumentException */ public static void check(byte[] buf, int size) { - if (buf.length < size) { - throwIAE(buf, size); - } + checkBuffer(buf.length, size); } /** @@ -273,9 +265,7 @@ public static void check(byte[] buf, int size) { * @throws IllegalArgumentException */ public static void check(short[] buf, int size) { - if (buf.length < size) { - throwIAE(buf, size); - } + checkBuffer(buf.length, size); } /** @@ -287,9 +277,7 @@ public static void check(short[] buf, int size) { * @throws IllegalArgumentException */ public static void check(int[] buf, int size) { - if (buf.length < size) { - throwIAE(buf, size); - } + checkBuffer(buf.length, size); } /** @@ -301,9 +289,7 @@ public static void check(int[] buf, int size) { * @throws IllegalArgumentException */ public static void check(long[] buf, int size) { - if (buf.length < size) { - throwIAE(buf, size); - } + checkBuffer(buf.length, size); } /** @@ -315,9 +301,7 @@ public static void check(long[] buf, int size) { * @throws IllegalArgumentException */ public static void check(float[] buf, int size) { - if (buf.length < size) { - throwIAE(buf, size); - } + checkBuffer(buf.length, size); } /** @@ -329,9 +313,7 @@ public static void check(float[] buf, int size) { * @throws IllegalArgumentException */ public static void check(double[] buf, int size) { - if (buf.length < size) { - throwIAE(buf, size); - } + checkBuffer(buf.length, size); } /** @@ -341,9 +323,7 @@ public static void check(double[] buf, int size) { * @param size the minimum number of characters */ public static void check(CharSequence text, int size) { - if (text.length() < size) { - throwIAE(text, size); - } + checkBuffer(text.length(), size); } /** @@ -355,9 +335,7 @@ public static void check(CharSequence text, int size) { * @throws IllegalArgumentException */ public static void check(Buffer buf, int size) { - if (buf.remaining() < size) { - throwIAE(buf, size); - } + checkBuffer(buf.remaining(), size); } /** @see #check(Buffer, int) */ @@ -374,9 +352,7 @@ public static void check(Buffer buf, long size) { * @throws IllegalArgumentException */ public static void check(CustomBuffer buf, int size) { - if (buf.remaining() < size) { - throwIAE(buf, size); - } + checkBuffer(buf.remaining(), size); } /** @see #check(CustomBuffer, int) */ @@ -426,27 +402,21 @@ public static void checkSafe(@Nullable CustomBuffer buf, int size) { } public static void check(Object[] array, int size) { - if (array.length < size) { - throwIAE(array, size); - } + checkBuffer(array.length, size); } - public static void checkGT(Buffer buf, int size) { - if (size < buf.remaining()) { - throwIAEGT(buf, size); + private static void checkBufferGT(int bufferSize, int maximumSize) { + if (maximumSize < bufferSize) { + throwIAEGT(bufferSize, maximumSize); } } - public static void checkGT(CustomBuffer buf, int size) { - if (size < buf.remaining()) { - throwIAEGT(buf, size); - } + public static void checkGT(Buffer buf, int size) { + checkBufferGT(buf.remaining(), size); } - public static void checkGT(StructBuffer buf, int size) { - if (size < buf.remaining()) { - throwIAEGT(buf, size); - } + public static void checkGT(CustomBuffer buf, int size) { + checkBufferGT(buf.remaining(), size); } public static void check(int index, int size) { @@ -457,52 +427,12 @@ public static void check(int index, int size) { // Separate calls to help inline check. - private static void throwIAE(Buffer buf, int size) { - throw new IllegalArgumentException("Number of remaining buffer elements is " + buf.remaining() + ", must be at least " + size); - } - - private static void throwIAE(CustomBuffer buf, int size) { - throw new IllegalArgumentException("Number of remaining buffer elements is " + buf.remaining() + ", must be at least " + size); - } - - private static void throwIAE(Object[] array, int size) { - throw new IllegalArgumentException("Number of array elements is " + array.length + ", must be at least " + size); - } - - private static void throwIAE(byte[] array, int size) { - throw new IllegalArgumentException("Number of array elements is " + array.length + ", must be at least " + size); - } - - private static void throwIAE(short[] array, int size) { - throw new IllegalArgumentException("Number of array elements is " + array.length + ", must be at least " + size); - } - - private static void throwIAE(int[] array, int size) { - throw new IllegalArgumentException("Number of array elements is " + array.length + ", must be at least " + size); - } - - private static void throwIAE(long[] array, int size) { - throw new IllegalArgumentException("Number of array elements is " + array.length + ", must be at least " + size); - } - - private static void throwIAE(float[] array, int size) { - throw new IllegalArgumentException("Number of array elements is " + array.length + ", must be at least " + size); - } - - private static void throwIAE(double[] array, int size) { - throw new IllegalArgumentException("Number of array elements is " + array.length + ", must be at least " + size); - } - - private static void throwIAE(CharSequence text, int size) { - throw new IllegalArgumentException("Number of characters is " + text.length() + ", must be at least " + size); - } - - private static void throwIAEGT(Buffer buf, int size) { - throw new IllegalArgumentException("Number of remaining buffer elements is " + buf.remaining() + ", must be at most " + size + "."); + private static void throwIAE(int bufferSize, int minimumSize) { + throw new IllegalArgumentException("Number of remaining elements is " + bufferSize + ", must be at least " + minimumSize); } - private static void throwIAEGT(CustomBuffer buf, int size) { - throw new IllegalArgumentException("Number of remaining buffer elements is " + buf.remaining() + ", must be at most " + size + "."); + private static void throwIAEGT(int bufferSize, int maximumSize) { + throw new IllegalArgumentException("Number of remaining buffer elements is " + bufferSize + ", must be at most " + maximumSize); } private static void throwIOBE(int index, int size) { diff --git a/modules/core/src/main/java/org/lwjgl/system/CustomBuffer.java b/modules/core/src/main/java/org/lwjgl/system/CustomBuffer.java index 5284c9b7d3..892bbc7c85 100644 --- a/modules/core/src/main/java/org/lwjgl/system/CustomBuffer.java +++ b/modules/core/src/main/java/org/lwjgl/system/CustomBuffer.java @@ -7,6 +7,7 @@ import javax.annotation.*; import java.nio.*; +import static org.lwjgl.system.Checks.*; import static org.lwjgl.system.MemoryUtil.*; /** Base class of custom buffers with an API that mirrors {@code java.nio} for convenience. */ @@ -24,6 +25,9 @@ public abstract class CustomBuffer> implements P capacity; protected CustomBuffer(long address, @Nullable ByteBuffer container, int mark, int position, int limit, int capacity) { + if (CHECKS) { + check(address); + } this.address = address; this.container = container; diff --git a/modules/core/src/main/java/org/lwjgl/system/MemoryAccess.java b/modules/core/src/main/java/org/lwjgl/system/MemoryAccess.java index 26dcf55951..1688be609e 100644 --- a/modules/core/src/main/java/org/lwjgl/system/MemoryAccess.java +++ b/modules/core/src/main/java/org/lwjgl/system/MemoryAccess.java @@ -5,6 +5,7 @@ package org.lwjgl.system; import java.nio.*; +import java.util.*; import static java.lang.Character.*; import static org.lwjgl.system.APIUtil.*; @@ -56,7 +57,7 @@ default long memAddress0(Buffer buffer) { return GetDirectBufferAddress(buffer); } - default ByteBuffer memByteBuffer(long address, int capacity) { return NewDirectByteBuffer(address, capacity).order(ByteOrder.nativeOrder()); } + default ByteBuffer memByteBuffer(long address, int capacity) { return Objects.requireNonNull(NewDirectByteBuffer(address, capacity)).order(ByteOrder.nativeOrder()); } default ShortBuffer memShortBuffer(long address, int capacity) { return memByteBuffer(address, capacity << 1).asShortBuffer(); } default CharBuffer memCharBuffer(long address, int capacity) { return memByteBuffer(address, capacity << 1).asCharBuffer(); } @@ -141,7 +142,7 @@ private static long getAddressOffset() { MAGIC_ADDRESS &= 0xFFFFFFFFL; } - ByteBuffer bb = NewDirectByteBuffer(MAGIC_ADDRESS, 0); + ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(MAGIC_ADDRESS, 0)); long offset = 8L; // 8 byte aligned, cannot be at 0 while (true) { @@ -155,7 +156,7 @@ private static long getAddressOffset() { private static long getCapacityOffset() { int MAGIC_CAPACITY = 0x0D15EA5E; - ByteBuffer bb = NewDirectByteBuffer(-1L, MAGIC_CAPACITY); + ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(-1L, MAGIC_CAPACITY)); bb.limit(0); long offset = 4L; // 4 byte aligned, cannot be at 0 diff --git a/modules/core/src/main/java/org/lwjgl/system/MemoryManage.java b/modules/core/src/main/java/org/lwjgl/system/MemoryManage.java index 65879cf5e3..c0a742bf08 100644 --- a/modules/core/src/main/java/org/lwjgl/system/MemoryManage.java +++ b/modules/core/src/main/java/org/lwjgl/system/MemoryManage.java @@ -4,6 +4,7 @@ */ package org.lwjgl.system; +import javax.annotation.*; import java.util.*; import java.util.Map.*; import java.util.concurrent.*; @@ -198,6 +199,7 @@ private static class Allocation { private final Object[] stackTrace; + @Nullable private StackTraceElement[] elements; // lazy init final long size; diff --git a/modules/core/src/main/java/org/lwjgl/system/MemoryStack.java b/modules/core/src/main/java/org/lwjgl/system/MemoryStack.java index d958f61790..163f837abe 100644 --- a/modules/core/src/main/java/org/lwjgl/system/MemoryStack.java +++ b/modules/core/src/main/java/org/lwjgl/system/MemoryStack.java @@ -6,6 +6,7 @@ import org.lwjgl.*; +import javax.annotation.*; import java.nio.*; import java.util.*; @@ -522,7 +523,7 @@ public PointerBuffer pointers(Pointer... values) { /** * Encodes the specified text on the stack using ASCII encoding and returns a ByteBuffer that points to the encoded text, including a null-terminator. * - * @param text the text to encode. If {@code text} is null, null is returned. + * @param text the text to encode */ public ByteBuffer ASCII(CharSequence text) { return ASCII(text, true); @@ -531,23 +532,31 @@ public ByteBuffer ASCII(CharSequence text) { /** * Encodes the specified text on the stack using ASCII encoding and returns a ByteBuffer that points to the encoded text. * - * @param text the text to encode. If {@code text} is null, null is returned. + * @param text the text to encode * @param nullTerminated if true, a null-terminator is included at the end of the encoded text */ public ByteBuffer ASCII(CharSequence text, boolean nullTerminated) { - if (text == null) { - return null; - } - ByteBuffer encoded = malloc(memLengthASCII(text, nullTerminated)); memASCII(text, nullTerminated, encoded); return encoded; } + /** Like {@link #ASCII(CharSequence) ASCII}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public ByteBuffer ASCIISafe(@Nullable CharSequence text) { + return ASCIISafe(text, true); + } + + /** Like {@link #ASCII(CharSequence, boolean) ASCII}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public ByteBuffer ASCIISafe(@Nullable CharSequence text, boolean nullTerminated) { + return text == null ? null : ASCII(text, nullTerminated); + } + /** * Encodes the specified text on the stack using UTF8 encoding and returns a ByteBuffer that points to the encoded text, including a null-terminator. * - * @param text the text to encode. If {@code text} is null, null is returned. + * @param text the text to encode */ public ByteBuffer UTF8(CharSequence text) { return UTF8(text, true); @@ -556,23 +565,31 @@ public ByteBuffer UTF8(CharSequence text) { /** * Encodes the specified text on the stack using UTF8 encoding and returns a ByteBuffer that points to the encoded text. * - * @param text the text to encode. If {@code text} is null, null is returned. + * @param text the text to encode * @param nullTerminated if true, a null-terminator is included at the end of the encoded text */ public ByteBuffer UTF8(CharSequence text, boolean nullTerminated) { - if (text == null) { - return null; - } - ByteBuffer encoded = malloc(memLengthUTF8(text, nullTerminated)); memUTF8(text, nullTerminated, encoded); return encoded; } + /** Like {@link #UTF8(CharSequence) UTF8}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public ByteBuffer UTF8Safe(@Nullable CharSequence text) { + return UTF8Safe(text, true); + } + + /** Like {@link #UTF8(CharSequence, boolean) UTF8}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public ByteBuffer UTF8Safe(@Nullable CharSequence text, boolean nullTerminated) { + return text == null ? null : UTF8(text, nullTerminated); + } + /** * Encodes the specified text on the stack using UTF16 encoding and returns a ByteBuffer that points to the encoded text, including a null-terminator. * - * @param text the text to encode. If {@code text} is null, null is returned. + * @param text the text to encode */ public ByteBuffer UTF16(CharSequence text) { return UTF16(text, true); @@ -581,19 +598,27 @@ public ByteBuffer UTF16(CharSequence text) { /** * Encodes the specified text on the stack using UTF16 encoding and returns a ByteBuffer that points to the encoded text. * - * @param text the text to encode. If {@code text} is null, null is returned. + * @param text the text to encode * @param nullTerminated if true, a null-terminator is included at the end of the encoded text */ public ByteBuffer UTF16(CharSequence text, boolean nullTerminated) { - if (text == null) { - return null; - } - ByteBuffer encoded = malloc(2, memLengthUTF16(text, nullTerminated)); memUTF16(text, nullTerminated, encoded); return encoded; } + /** Like {@link #UTF16(CharSequence) UTF16}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public ByteBuffer UTF16Safe(@Nullable CharSequence text) { + return UTF16Safe(text, true); + } + + /** Like {@link #UTF16(CharSequence, boolean) UTF16}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public ByteBuffer UTF16Safe(@Nullable CharSequence text, boolean nullTerminated) { + return text == null ? null : UTF16(text, nullTerminated); + } + // ----------------------------------------------------- // ----------------------------------------------------- // ----------------------------------------------------- @@ -785,4 +810,22 @@ public static MemoryStack stackPop() { /** Thread-local version of {@link #UTF16(CharSequence, boolean)}. */ public static ByteBuffer stackUTF16(CharSequence text, boolean nullTerminated) { return stackGet().UTF16(text, nullTerminated); } + /** Thread-local version of {@link #ASCII(CharSequence)}. */ + @Nullable public static ByteBuffer stackASCIISafe(@Nullable CharSequence text) { return stackGet().ASCIISafe(text); } + + /** Thread-local version of {@link #ASCII(CharSequence, boolean)}. */ + @Nullable public static ByteBuffer stackASCIISafe(@Nullable CharSequence text, boolean nullTerminated) { return stackGet().ASCIISafe(text, nullTerminated); } + + /** Thread-local version of {@link #UTF8(CharSequence)}. */ + @Nullable public static ByteBuffer stackUTF8Safe(@Nullable CharSequence text) { return stackGet().UTF8Safe(text); } + + /** Thread-local version of {@link #UTF8(CharSequence, boolean)}. */ + @Nullable public static ByteBuffer stackUTF8Safe(@Nullable CharSequence text, boolean nullTerminated) { return stackGet().UTF8Safe(text, nullTerminated); } + + /** Thread-local version of {@link #UTF16(CharSequence)}. */ + @Nullable public static ByteBuffer stackUTF16Safe(@Nullable CharSequence text) { return stackGet().UTF16Safe(text); } + + /** Thread-local version of {@link #UTF16(CharSequence, boolean)}. */ + @Nullable public static ByteBuffer stackUTF16Safe(@Nullable CharSequence text, boolean nullTerminated) { return stackGet().UTF16Safe(text, nullTerminated); } + } \ No newline at end of file diff --git a/modules/core/src/main/java/org/lwjgl/system/MemoryUtil.java b/modules/core/src/main/java/org/lwjgl/system/MemoryUtil.java index 5498106216..d1dd678f9c 100644 --- a/modules/core/src/main/java/org/lwjgl/system/MemoryUtil.java +++ b/modules/core/src/main/java/org/lwjgl/system/MemoryUtil.java @@ -10,6 +10,7 @@ import org.lwjgl.system.MemoryUtil.MemoryAllocationReport.*; import org.lwjgl.system.jni.*; +import javax.annotation.*; import java.nio.*; import java.util.*; @@ -149,6 +150,26 @@ public static long nmemAlloc(long size) { return ALLOCATOR.malloc(size); } + /** + * Unsafe version of {@link #memAlloc} that checks the requested {@code size}. + * + * @return {@link #NULL} if {@code size} is zero, otherwise a pointer to the memory block allocated by the function on success. + * + * @throws OutOfMemoryError if the function failed to allocate the requested block of memory + */ + public static long nmemAllocChecked(long size) { + long address; + if (size == 0L) { + address = NULL; + } else { + address = nmemAlloc(size); + if (CHECKS && address == NULL) { + throw new OutOfMemoryError(); + } + } + return address; + } + /** * The standard C malloc function. * @@ -160,11 +181,12 @@ public static long nmemAlloc(long size) { * @param size the size of the memory block to allocate, in bytes. If {@code size} is zero, the return value depends on the particular library * implementation (it may or may not be a null pointer), but the returned pointer shall not be dereferenced. * - * @return on success, a pointer to the memory block allocated by the function. If the function failed to allocate the requested block of memory, a {@link - * #NULL} pointer is returned. + * @return on success, a pointer to the memory block allocated by the function + * + * @throws OutOfMemoryError if the function failed to allocate the requested block of memory */ public static ByteBuffer memAlloc(int size) { - return memByteBuffer(nmemAlloc(size), size); + return ACCESSOR.memByteBuffer(nmemAllocChecked(size), size); } private static long getAllocationSize(int elements, int elementShift) { @@ -179,7 +201,7 @@ private static long getAllocationSize(int elements, int elementShift) { * @param size the number of short values to allocate. */ public static ShortBuffer memAllocShort(int size) { - return memShortBuffer(nmemAlloc(getAllocationSize(size, 1)), size); + return ACCESSOR.memShortBuffer(nmemAllocChecked(getAllocationSize(size, 1)), size); } /** @@ -188,7 +210,7 @@ public static ShortBuffer memAllocShort(int size) { * @param size the number of int values to allocate. */ public static IntBuffer memAllocInt(int size) { - return memIntBuffer(nmemAlloc(getAllocationSize(size, 2)), size); + return ACCESSOR.memIntBuffer(nmemAllocChecked(getAllocationSize(size, 2)), size); } /** @@ -197,7 +219,7 @@ public static IntBuffer memAllocInt(int size) { * @param size the number of float values to allocate. */ public static FloatBuffer memAllocFloat(int size) { - return memFloatBuffer(nmemAlloc(getAllocationSize(size, 2)), size); + return ACCESSOR.memFloatBuffer(nmemAllocChecked(getAllocationSize(size, 2)), size); } /** @@ -206,7 +228,7 @@ public static FloatBuffer memAllocFloat(int size) { * @param size the number of long values to allocate. */ public static LongBuffer memAllocLong(int size) { - return memLongBuffer(nmemAlloc(getAllocationSize(size, 3)), size); + return ACCESSOR.memLongBuffer(nmemAllocChecked(getAllocationSize(size, 3)), size); } /** @@ -215,7 +237,7 @@ public static LongBuffer memAllocLong(int size) { * @param size the number of double values to allocate. */ public static DoubleBuffer memAllocDouble(int size) { - return memDoubleBuffer(nmemAlloc(getAllocationSize(size, 3)), size); + return ACCESSOR.memDoubleBuffer(nmemAllocChecked(getAllocationSize(size, 3)), size); } /** @@ -224,7 +246,7 @@ public static DoubleBuffer memAllocDouble(int size) { * @param size the number of pointer values to allocate. */ public static PointerBuffer memAllocPointer(int size) { - return memPointerBuffer(nmemAlloc(getAllocationSize(size, POINTER_SHIFT)), size); + return PointerBuffer.create(nmemAllocChecked(getAllocationSize(size, POINTER_SHIFT)), size); } /** Unsafe version of {@link #memFree}. */ @@ -242,12 +264,12 @@ public static void nmemFree(long ptr) { * point to a block of memory allocated with the above functions, it causes undefined behavior. If {@code ptr} is a {@link #NULL} pointer, the * function does nothing. */ - public static void memFree(Buffer ptr) { + public static void memFree(@Nullable Buffer ptr) { nmemFree(memAddress0Safe(ptr)); } /** PointerBuffer version of {@link #memFree}. */ - public static void memFree(PointerBuffer ptr) { + public static void memFree(@Nullable PointerBuffer ptr) { nmemFree(memAddress0Safe(ptr)); } @@ -258,6 +280,26 @@ public static long nmemCalloc(long num, long size) { return ALLOCATOR.calloc(num, size); } + /** + * Unsafe version of {@link #memCalloc} that checks the requested {@code size}. + * + * @return {@link #NULL} if {@code num} or {@code size} are zero, otherwise a pointer to the memory block allocated by the function on success. + * + * @throws OutOfMemoryError if the function failed to allocate the requested block of memory + */ + public static long nmemCallocChecked(long num, long size) { + long address; + if (num == 0L || size == 0L) { + address = NULL; + } else { + address = nmemCalloc(num, size); + if (CHECKS && address == NULL) { + throw new OutOfMemoryError(); + } + } + return address; + } + /** * The standard C calloc function. * @@ -270,11 +312,12 @@ public static long nmemCalloc(long num, long size) { * @param size the size of each element. If {@code size} is zero, the return value depends on the particular library implementation (it may or may not be * a null pointer), but the returned pointer shall not be dereferenced. * - * @return on success, a pointer to the memory block allocated by the function. If the function failed to allocate the requested block of memory, a {@link - * #NULL} pointer is returned. + * @return on success, a pointer to the memory block allocated by the function + * + * @throws OutOfMemoryError if the function failed to allocate the requested block of memory */ public static ByteBuffer memCalloc(int num, int size) { - return memByteBuffer(nmemCalloc(num, size), num * size); + return ACCESSOR.memByteBuffer(nmemCallocChecked(num, size), num * size); } /** @@ -283,7 +326,7 @@ public static ByteBuffer memCalloc(int num, int size) { * @param num the number of bytes to allocate. */ public static ByteBuffer memCalloc(int num) { - return memByteBuffer(nmemCalloc(num, 1), num); + return ACCESSOR.memByteBuffer(nmemCallocChecked(num, 1), num); } /** @@ -292,7 +335,7 @@ public static ByteBuffer memCalloc(int num) { * @param num the number of short values to allocate. */ public static ShortBuffer memCallocShort(int num) { - return memShortBuffer(nmemCalloc(num, 2), num); + return ACCESSOR.memShortBuffer(nmemCallocChecked(num, 2), num); } /** @@ -301,7 +344,7 @@ public static ShortBuffer memCallocShort(int num) { * @param num the number of int values to allocate. */ public static IntBuffer memCallocInt(int num) { - return memIntBuffer(nmemCalloc(num, 4), num); + return ACCESSOR.memIntBuffer(nmemCallocChecked(num, 4), num); } /** @@ -310,7 +353,7 @@ public static IntBuffer memCallocInt(int num) { * @param num the number of float values to allocate. */ public static FloatBuffer memCallocFloat(int num) { - return memFloatBuffer(nmemCalloc(num, 4), num); + return ACCESSOR.memFloatBuffer(nmemCallocChecked(num, 4), num); } /** @@ -319,7 +362,7 @@ public static FloatBuffer memCallocFloat(int num) { * @param num the number of long values to allocate. */ public static LongBuffer memCallocLong(int num) { - return memLongBuffer(nmemCalloc(num, 8), num); + return ACCESSOR.memLongBuffer(nmemCallocChecked(num, 8), num); } /** @@ -328,7 +371,7 @@ public static LongBuffer memCallocLong(int num) { * @param num the number of double values to allocate. */ public static DoubleBuffer memCallocDouble(int num) { - return memDoubleBuffer(nmemCalloc(num, 8), num); + return ACCESSOR.memDoubleBuffer(nmemCallocChecked(num, 8), num); } /** @@ -337,7 +380,7 @@ public static DoubleBuffer memCallocDouble(int num) { * @param num the number of pointer values to allocate. */ public static PointerBuffer memCallocPointer(int num) { - return memPointerBuffer(nmemCalloc(num, POINTER_SIZE), num); + return PointerBuffer.create(nmemCallocChecked(num, POINTER_SIZE), num); } // --- [ memRealloc] --- @@ -347,7 +390,8 @@ public static long nmemRealloc(long ptr, long size) { return ALLOCATOR.realloc(ptr, size); } - private static T realloc(T old_p, T new_p, int size) { + @Nullable + private static T realloc(@Nullable T old_p, @Nullable T new_p, int size) { if (old_p != null && new_p != null) { new_p.position(min(old_p.position(), size)); } @@ -373,7 +417,8 @@ private static T realloc(T old_p, T new_p, int size) { * requested block of memory, a {@link #NULL} pointer is returned, and the memory block pointed to by argument {@code ptr} is not deallocated (it is still * valid, and with its contents unchanged). */ - public static ByteBuffer memRealloc(ByteBuffer ptr, int size) { + @Nullable + public static ByteBuffer memRealloc(@Nullable ByteBuffer ptr, int size) { return realloc(ptr, memByteBuffer(nmemRealloc(memAddress0Safe(ptr), size), size), size); } @@ -382,7 +427,8 @@ public static ByteBuffer memRealloc(ByteBuffer ptr, int size) { * * @param size the number of short values to allocate. */ - public static ShortBuffer memRealloc(ShortBuffer ptr, int size) { + @Nullable + public static ShortBuffer memRealloc(@Nullable ShortBuffer ptr, int size) { return realloc(ptr, memShortBuffer(nmemRealloc(memAddress0Safe(ptr), getAllocationSize(size, 1)), size), size); } @@ -391,7 +437,8 @@ public static ShortBuffer memRealloc(ShortBuffer ptr, int size) { * * @param size the number of int values to allocate. */ - public static IntBuffer memRealloc(IntBuffer ptr, int size) { + @Nullable + public static IntBuffer memRealloc(@Nullable IntBuffer ptr, int size) { return realloc(ptr, memIntBuffer(nmemRealloc(memAddress0Safe(ptr), getAllocationSize(size, 2)), size), size); } @@ -400,7 +447,8 @@ public static IntBuffer memRealloc(IntBuffer ptr, int size) { * * @param size the number of long values to allocate. */ - public static LongBuffer memRealloc(LongBuffer ptr, int size) { + @Nullable + public static LongBuffer memRealloc(@Nullable LongBuffer ptr, int size) { return realloc(ptr, memLongBuffer(nmemRealloc(memAddress0Safe(ptr), getAllocationSize(size, 3)), size), size); } @@ -409,7 +457,8 @@ public static LongBuffer memRealloc(LongBuffer ptr, int size) { * * @param size the number of float values to allocate. */ - public static FloatBuffer memRealloc(FloatBuffer ptr, int size) { + @Nullable + public static FloatBuffer memRealloc(@Nullable FloatBuffer ptr, int size) { return realloc(ptr, memFloatBuffer(nmemRealloc(memAddress0Safe(ptr), getAllocationSize(size, 2)), size), size); } @@ -418,7 +467,8 @@ public static FloatBuffer memRealloc(FloatBuffer ptr, int size) { * * @param size the number of double values to allocate. */ - public static DoubleBuffer memRealloc(DoubleBuffer ptr, int size) { + @Nullable + public static DoubleBuffer memRealloc(@Nullable DoubleBuffer ptr, int size) { return realloc(ptr, memDoubleBuffer(nmemRealloc(memAddress0Safe(ptr), getAllocationSize(size, 3)), size), size); } @@ -427,8 +477,9 @@ public static DoubleBuffer memRealloc(DoubleBuffer ptr, int size) { * * @param size the number of pointer values to allocate. */ - public static PointerBuffer memRealloc(PointerBuffer ptr, int size) { - PointerBuffer buffer = memPointerBuffer(nmemRealloc(memAddress0Safe(ptr), getAllocationSize(size, POINTER_SHIFT)), size); + @Nullable + public static PointerBuffer memRealloc(@Nullable PointerBuffer ptr, int size) { + PointerBuffer buffer = memPointerBufferSafe(nmemRealloc(memAddress0Safe(ptr), getAllocationSize(size, POINTER_SHIFT)), size); if (ptr != null && buffer != null) { buffer.position(min(ptr.position(), size)); } @@ -442,6 +493,26 @@ public static long nmemAlignedAlloc(long alignment, long size) { return ALLOCATOR.aligned_alloc(alignment, size); } + /** + * Unsafe version of {@link #memAlignedAlloc} that checks the requested {@code size}. + * + * @return {@link #NULL} if {@code size} is zero, otherwise a pointer to the memory block allocated by the function on success. + * + * @throws OutOfMemoryError if the function failed to allocate the requested block of memory + */ + public static long nmemAlignedAllocChecked(long alignment, long size) { + long address; + if (size == 0L) { + address = NULL; + } else { + address = nmemAlignedAlloc(alignment, size); + if (DEBUG && address == NULL) { + throw new OutOfMemoryError(); + } + } + return address; + } + /** * The standard C aligned_alloc function. * @@ -452,7 +523,7 @@ public static long nmemAlignedAlloc(long alignment, long size) { * @param size the number of bytes to allocate. Must be a multiple of {@code alignment}. */ public static ByteBuffer memAlignedAlloc(int alignment, int size) { - return memByteBuffer(ALLOCATOR.aligned_alloc(alignment, size), size); + return ACCESSOR.memByteBuffer(nmemAlignedAllocChecked(alignment, size), size); } // --- [ memAlignedFree ] --- @@ -482,9 +553,9 @@ public interface MemoryAllocationReport { * @param memory the amount of memory allocated, in bytes * @param threadId id of the thread that allocated the memory. May be {@link #NULL}. * @param threadName name of the thread that allocated the memory. May be {@code null}. - * @param stacktrace the allocation stacktrace. May be null. + * @param stacktrace the allocation stacktrace. May be {@code null}. */ - void invoke(long memory, long threadId, String threadName, StackTraceElement... stacktrace); + void invoke(long memory, long threadId, @Nullable String threadName, @Nullable StackTraceElement... stacktrace); /** Specifies how to aggregate the reported allocations. */ enum Aggregate { @@ -542,13 +613,13 @@ public static void memReport(MemoryAllocationReport report, Aggregate groupBySta public static long memAddress0(Buffer buffer) { return ACCESSOR.memAddress0(buffer); } /** Null-safe version of {@link #memAddress0(Buffer)}. Returns {@link #NULL} if the specified buffer is null. */ - public static long memAddress0Safe(Buffer buffer) { return buffer == null ? NULL : ACCESSOR.memAddress0(buffer); } + public static long memAddress0Safe(@Nullable Buffer buffer) { return buffer == null ? NULL : ACCESSOR.memAddress0(buffer); } /** CustomBuffer version of {@link #memAddress0(Buffer)}. */ public static long memAddress0(CustomBuffer buffer) { return buffer.address0(); } /** CustomBuffer version of {@link #memAddress0Safe(Buffer)}. */ - public static long memAddress0Safe(CustomBuffer buffer) { return buffer == null ? NULL : buffer.address0(); } + public static long memAddress0Safe(@Nullable CustomBuffer buffer) { return buffer == null ? NULL : buffer.address0(); } // --- [ Buffer address ] --- @@ -616,48 +687,51 @@ private static long address(Buffer buffer, int position, int elementShift) { // --- [ Buffer address - Safe ] --- /** Null-safe version of {@link #memAddress(ByteBuffer)}. Returns {@link #NULL} if the specified buffer is null. */ - public static long memAddressSafe(ByteBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } + public static long memAddressSafe(@Nullable ByteBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } /** Null-safe version of {@link #memAddress(ByteBuffer, int)}. Returns {@link #NULL} if the specified buffer is null. */ - public static long memAddressSafe(ByteBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } + public static long memAddressSafe(@Nullable ByteBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } /** ShortBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ - public static long memAddressSafe(ShortBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } + public static long memAddressSafe(@Nullable ShortBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } /** ShortBuffer version of {@link #memAddressSafe(ByteBuffer, int)}. */ - public static long memAddressSafe(ShortBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } + public static long memAddressSafe(@Nullable ShortBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } /** CharBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ - public static long memAddressSafe(CharBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } + public static long memAddressSafe(@Nullable CharBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } /** CharBuffer version of {@link #memAddressSafe(ByteBuffer, int)}. */ - public static long memAddressSafe(CharBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } + public static long memAddressSafe(@Nullable CharBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } /** IntBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ - public static long memAddressSafe(IntBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } + public static long memAddressSafe(@Nullable IntBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } /** IntBuffer version of {@link #memAddressSafe(ByteBuffer, int)}. */ - public static long memAddressSafe(IntBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } + public static long memAddressSafe(@Nullable IntBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } /** FloatBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ - public static long memAddressSafe(FloatBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } + public static long memAddressSafe(@Nullable FloatBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } /** FloatBuffer version of {@link #memAddressSafe(ByteBuffer, int)}. */ - public static long memAddressSafe(FloatBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } + public static long memAddressSafe(@Nullable FloatBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } /** LongBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ - public static long memAddressSafe(LongBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } + public static long memAddressSafe(@Nullable LongBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } /** LongBuffer version of {@link #memAddressSafe(ByteBuffer, int)}. */ - public static long memAddressSafe(LongBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } + public static long memAddressSafe(@Nullable LongBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } /** DoubleBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ - public static long memAddressSafe(DoubleBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } + public static long memAddressSafe(@Nullable DoubleBuffer buffer) { return buffer == null ? NULL : memAddress(buffer); } /** DoubleBuffer version of {@link #memAddressSafe(ByteBuffer, int)}. */ - public static long memAddressSafe(DoubleBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } + public static long memAddressSafe(@Nullable DoubleBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } /** CustomBuffer version of {@link #memAddressSafe(ByteBuffer, int)}. */ - public static long memAddressSafe(CustomBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } + public static long memAddressSafe(@Nullable CustomBuffer buffer, int position) { return buffer == null ? NULL : memAddress(buffer, position); } /** Pointer version of {@link #memAddressSafe(ByteBuffer)}. */ - public static long memAddressSafe(Pointer pointer) { return pointer == null ? NULL : pointer.address(); } + public static long memAddressSafe(@Nullable Pointer pointer) { return pointer == null ? NULL : pointer.address(); } // --- [ Buffer allocation ] --- private static long checkAlignment(long address, int mask) { + if (CHECKS) { + check(address); + } if (DEBUG && (address & mask) != 0L) { throw new IllegalArgumentException("Unaligned memory address"); } @@ -666,7 +740,7 @@ private static long checkAlignment(long address, int mask) { /** * Creates a new direct ByteBuffer that starts at the specified memory address and has the specified capacity. The returned ByteBuffer instance will be set - * to the native ByteOrder. + * to the native {@link ByteOrder}. * * @param address the starting memory address * @param capacity the buffer capacity @@ -674,6 +748,15 @@ private static long checkAlignment(long address, int mask) { * @return the new ByteBuffer */ public static ByteBuffer memByteBuffer(long address, int capacity) { + if (CHECKS) { + check(address); + } + return ACCESSOR.memByteBuffer(address, capacity); + } + + /** Like {@link #memByteBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static ByteBuffer memByteBufferSafe(long address, int capacity) { if (address == NULL) { return null; } @@ -692,13 +775,15 @@ public static ByteBuffer memByteBuffer(long address, int capacity) { * @return the new ShortBuffer */ public static ShortBuffer memShortBuffer(long address, int capacity) { - if (address == NULL) { - return null; - } - return ACCESSOR.memShortBuffer(checkAlignment(address, 2 - 1), capacity); } + /** Like {@link #memShortBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static ShortBuffer memShortBufferSafe(long address, int capacity) { + return address == NULL ? null : memShortBuffer(address, capacity); + } + /** * Creates a new direct CharBuffer that starts at the specified memory address and has the specified capacity. * @@ -710,13 +795,15 @@ public static ShortBuffer memShortBuffer(long address, int capacity) { * @return the new CharBuffer */ public static CharBuffer memCharBuffer(long address, int capacity) { - if (address == NULL) { - return null; - } - return ACCESSOR.memCharBuffer(checkAlignment(address, 2 - 1), capacity); } + /** Like {@link #memCharBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static CharBuffer memCharBufferSafe(long address, int capacity) { + return address == NULL ? null : memCharBuffer(address, capacity); + } + /** * Creates a new direct IntBuffer that starts at the specified memory address and has the specified capacity. * @@ -728,13 +815,15 @@ public static CharBuffer memCharBuffer(long address, int capacity) { * @return the new IntBuffer */ public static IntBuffer memIntBuffer(long address, int capacity) { - if (address == NULL) { - return null; - } - return ACCESSOR.memIntBuffer(checkAlignment(address, 4 - 1), capacity); } + /** Like {@link #memIntBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static IntBuffer memIntBufferSafe(long address, int capacity) { + return address == NULL ? null : memIntBuffer(address, capacity); + } + /** * Creates a new direct LongBuffer that starts at the specified memory address and has the specified capacity. * @@ -746,13 +835,15 @@ public static IntBuffer memIntBuffer(long address, int capacity) { * @return the new LongBuffer */ public static LongBuffer memLongBuffer(long address, int capacity) { - if (address == NULL) { - return null; - } - return ACCESSOR.memLongBuffer(checkAlignment(address, 8 - 1), capacity); } + /** Like {@link #memLongBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static LongBuffer memLongBufferSafe(long address, int capacity) { + return address == NULL ? null : memLongBuffer(address, capacity); + } + /** * Creates a new direct FloatBuffer that starts at the specified memory address and has the specified capacity. * @@ -764,13 +855,15 @@ public static LongBuffer memLongBuffer(long address, int capacity) { * @return the new FloatBuffer */ public static FloatBuffer memFloatBuffer(long address, int capacity) { - if (address == NULL) { - return null; - } - return ACCESSOR.memFloatBuffer(checkAlignment(address, 4 - 1), capacity); } + /** Like {@link #memFloatBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static FloatBuffer memFloatBufferSafe(long address, int capacity) { + return address == NULL ? null : memFloatBuffer(address, capacity); + } + /** * Creates a new direct DoubleBuffer that starts at the specified memory address and has the specified capacity. * @@ -782,13 +875,15 @@ public static FloatBuffer memFloatBuffer(long address, int capacity) { * @return the new DoubleBuffer */ public static DoubleBuffer memDoubleBuffer(long address, int capacity) { - if (address == NULL) { - return null; - } - return ACCESSOR.memDoubleBuffer(checkAlignment(address, 8 - 1), capacity); } + /** Like {@link #memDoubleBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static DoubleBuffer memDoubleBufferSafe(long address, int capacity) { + return address == NULL ? null : memDoubleBuffer(address, capacity); + } + /** * Creates a new PointerBuffer that starts at the specified memory address and has the specified capacity. * @@ -801,13 +896,15 @@ public static DoubleBuffer memDoubleBuffer(long address, int capacity) { * @return the new PointerBuffer */ public static PointerBuffer memPointerBuffer(long address, int capacity) { - if (address == NULL) { - return null; - } - return PointerBuffer.create(checkAlignment(address, POINTER_SIZE - 1), capacity); } + /** Like {@link #memPointerBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static PointerBuffer memPointerBufferSafe(long address, int capacity) { + return address == NULL ? null : memPointerBuffer(address, capacity); + } + // --- [ Buffer slicing ] --- /** @@ -1262,34 +1359,42 @@ public static void memCopy(long src, long dst, long bytes) { ------------------------------------- */ /** - * Returns a ByteBuffer containing the specified text ASCII encoded and null-terminated. If text is null, null is returned. + * Returns a ByteBuffer containing the specified text ASCII encoded and null-terminated. * * @param text the text to encode * - * @return the encoded text or null + * @return the encoded text */ public static ByteBuffer memASCII(CharSequence text) { return memASCII(text, true); } + /** Like {@link #memASCII(CharSequence) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public static ByteBuffer memASCIISafe(@Nullable CharSequence text) { + return memASCIISafe(text, true); + } + /** - * Returns a ByteBuffer containing the specified text ASCII encoded and optionally null-terminated. If text is null, null is returned. + * Returns a ByteBuffer containing the specified text ASCII encoded and optionally null-terminated. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * - * @return the encoded text or null + * @return the encoded text */ public static ByteBuffer memASCII(CharSequence text, boolean nullTerminated) { - if (text == null) { - return null; - } - ByteBuffer target = memAlloc(memLengthASCII(text, nullTerminated)); memASCII(text, nullTerminated, target); return target; } + /** Like {@link #memASCII(CharSequence, boolean) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public static ByteBuffer memASCIISafe(@Nullable CharSequence text, boolean nullTerminated) { + return text == null ? null : memASCII(text, nullTerminated); + } + /** * Encodes and optionally null-terminates the specified text using ASCII encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the * current buffer position. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough remaining @@ -1332,34 +1437,42 @@ public static int memLengthASCII(CharSequence value, boolean nullTerminated) { } /** - * Returns a ByteBuffer containing the specified text UTF-8 encoded and null-terminated. If text is null, null is returned. + * Returns a ByteBuffer containing the specified text UTF-8 encoded and null-terminated. * * @param text the text to encode * - * @return the encoded text or null + * @return the encoded text */ public static ByteBuffer memUTF8(CharSequence text) { return memUTF8(text, true); } + /** Like {@link #memUTF8(CharSequence) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public static ByteBuffer memUTF8Safe(@Nullable CharSequence text) { + return memUTF8Safe(text, true); + } + /** - * Returns a ByteBuffer containing the specified text UTF-8 encoded and optionally null-terminated. If text is null, null is returned. + * Returns a ByteBuffer containing the specified text UTF-8 encoded and optionally null-terminated. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * - * @return the encoded text or null + * @return the encoded text */ public static ByteBuffer memUTF8(CharSequence text, boolean nullTerminated) { - if (text == null) { - return null; - } - ByteBuffer target = memAlloc(memLengthUTF8(text, nullTerminated)); memUTF8(text, nullTerminated, target); return target; } + /** Like {@link #memUTF8(CharSequence, boolean) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public static ByteBuffer memUTF8Safe(@Nullable CharSequence text, boolean nullTerminated) { + return text == null ? null : memUTF8(text, nullTerminated); + } + /** * Encodes and optionally null-terminates the specified text using UTF-8 encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the * current buffer position. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough remaining @@ -1404,7 +1517,7 @@ public static int memLengthUTF8(CharSequence value, boolean nullTerminated) { } /** - * Returns a ByteBuffer containing the specified text UTF-16 encoded and null-terminated. If text is null, null is returned. + * Returns a ByteBuffer containing the specified text UTF-16 encoded and null-terminated. * * @param text the text to encode * @@ -1414,8 +1527,14 @@ public static ByteBuffer memUTF16(CharSequence text) { return memUTF16(text, true); } + /** Like {@link #memUTF16(CharSequence) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public static ByteBuffer memUTF16Safe(@Nullable CharSequence text) { + return memUTF16Safe(text, true); + } + /** - * Returns a ByteBuffer containing the specified text UTF-16 encoded and optionally null-terminated. If text is null, null is returned. + * Returns a ByteBuffer containing the specified text UTF-16 encoded and optionally null-terminated. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. @@ -1423,15 +1542,17 @@ public static ByteBuffer memUTF16(CharSequence text) { * @return the encoded text */ public static ByteBuffer memUTF16(CharSequence text, boolean nullTerminated) { - if (text == null) { - return null; - } - ByteBuffer target = memAlloc(memLengthUTF16(text, nullTerminated)); memUTF16(text, nullTerminated, target); return target; } + /** Like {@link #memUTF16(CharSequence, boolean) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ + @Nullable + public static ByteBuffer memUTF16Safe(@Nullable CharSequence text, boolean nullTerminated) { + return text == null ? null : memUTF16(text, nullTerminated); + } + /** * Encodes and optionally null-terminates the specified text using UTF-16 encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the * current buffer position. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough remaining @@ -1482,6 +1603,9 @@ public static int memLengthUTF16(CharSequence value, boolean nullTerminated) { ------------------------------------- */ private static int memLengthNT1(long address, int maxLength) { + if (CHECKS) { + check(address); + } return BITS64 ? TEXT_UTIL.strlen64NT1(address, maxLength) : TEXT_UTIL.strlen32NT1(address, maxLength); @@ -1502,6 +1626,9 @@ public static int memLengthNT1(ByteBuffer buffer) { } private static int memLengthNT2(long address, int maxLength) { + if (CHECKS) { + check(address); + } return BITS64 ? TEXT_UTIL.strlen64NT2(address, maxLength) : TEXT_UTIL.strlen32NT2(address, maxLength); @@ -1547,13 +1674,21 @@ public static ByteBuffer memByteBufferNT1(long address) { * @return the new ByteBuffer */ public static ByteBuffer memByteBufferNT1(long address, int maxLength) { - if (address == NULL) { - return null; - } - return memByteBuffer(address, memLengthNT1(address, maxLength)); } + /** Like {@link #memByteBufferNT1(long) memByteBufferNT1}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static ByteBuffer memByteBufferNT1Safe(long address) { + return memByteBufferNT1Safe(address, Integer.MAX_VALUE); + } + + /** Like {@link #memByteBufferNT1(long, int) memByteBufferNT1}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static ByteBuffer memByteBufferNT1Safe(long address, int maxLength) { + return address == NULL ? null : memByteBufferNT1(address, maxLength); + } + /** * Creates a new direct ByteBuffer that starts at the specified memory address and has capacity equal to the null-terminated string starting at that * address. Two \0 characters will terminate the string. The returned buffer will NOT include the \0 bytes. @@ -1579,10 +1714,6 @@ public static ByteBuffer memByteBufferNT2(long address) { * @return the new ByteBuffer */ public static ByteBuffer memByteBufferNT2(long address, int maxLength) { - if (address == NULL) { - return null; - } - if (DEBUG) { if ((address & 1L) != 0L) { throw new IllegalArgumentException("The string address is not aligned."); @@ -1592,22 +1723,29 @@ public static ByteBuffer memByteBufferNT2(long address, int maxLength) { throw new IllegalArgumentException("The maximum length must be an even number."); } } - return memByteBuffer(address, memLengthNT2(address, maxLength)); } + /** Like {@link #memByteBufferNT2(long) memByteBufferNT2}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static ByteBuffer memByteBufferNT2Safe(long address) { + return memByteBufferNT2Safe(address, Integer.MAX_VALUE - 1); + } + + /** Like {@link #memByteBufferNT2(long, int) memByteBufferNT2}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static ByteBuffer memByteBufferNT2Safe(long address, int maxLength) { + return address == NULL ? null : memByteBufferNT2(address, maxLength); + } + /** * Converts the null-terminated ASCII encoded string at the specified memory address to a {@link String}. * * @param address the string memory address * - * @return the decoded {@link String} or null if the specified {@code address} is null + * @return the decoded {@link String} */ public static String memASCII(long address) { - if (address == NULL) { - return null; - } - ByteBuffer buffer = memByteBufferNT1(address); return memASCII(buffer, buffer.remaining(), 0); } @@ -1617,18 +1755,26 @@ public static String memASCII(long address) { * *

    The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

    * - * @param buffer the {@link ByteBuffer} to decode, or null + * @param buffer the {@link ByteBuffer} to decode * - * @return the decoded {@link String} or null if the specified {@code buffer} is null + * @return the decoded {@link String} */ public static String memASCII(ByteBuffer buffer) { - if (buffer == null) { - return null; - } - return memASCII(buffer, buffer.remaining()); } + /** Like {@link #memASCII(long) memASCII}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static String memASCIISafe(long address) { + return address == NULL ? null : memASCII(address); + } + + /** Like {@link #memASCII(ByteBuffer) memASCII}, but returns {@code null} if {@code buffer} is {@code null}. */ + @Nullable + public static String memASCIISafe(@Nullable ByteBuffer buffer) { + return buffer == null ? null : memASCII(buffer); + } + /** * Decodes the bytes with index {@code [position(), position()+length}) in {@code buffer}, as an ASCII string. * @@ -1664,13 +1810,9 @@ public static String memASCII(ByteBuffer buffer, int length, int offset) { * * @param address the string memory address * - * @return the decoded {@link String} or null if the specified {@code address} is null + * @return the decoded {@link String} */ public static String memUTF8(long address) { - if (address == NULL) { - return null; - } - ByteBuffer buffer = memByteBufferNT1(address); return memUTF8(buffer, buffer.remaining(), 0); } @@ -1680,18 +1822,26 @@ public static String memUTF8(long address) { * *

    The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

    * - * @param buffer the {@link ByteBuffer} to decode, or null + * @param buffer the {@link ByteBuffer} to decode * - * @return the decoded {@link String} or null if the specified {@code buffer} is null + * @return the decoded {@link String} */ public static String memUTF8(ByteBuffer buffer) { - if (buffer == null) { - return null; - } - return memUTF8(buffer, buffer.remaining()); } + /** Like {@link #memUTF8(long) memUTF8}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static String memUTF8Safe(long address) { + return address == NULL ? null : memUTF8(address); + } + + /** Like {@link #memUTF8(ByteBuffer) memUTF8}, but returns {@code null} if {@code buffer} is {@code null}. */ + @Nullable + public static String memUTF8Safe(@Nullable ByteBuffer buffer) { + return buffer == null ? null : memUTF8(buffer); + } + /** * Decodes the bytes with index {@code [position(), position()+length}) in {@code buffer}, as a UTF-8 string. * @@ -1727,10 +1877,10 @@ public static String memUTF8(ByteBuffer buffer, int length, int offset) { * * @param address the string memory address * - * @return the decoded {@link String} or null if the specified {@code address} is null + * @return the decoded {@link String} */ public static String memUTF16(long address) { - return address == NULL ? null : memUTF16(memByteBufferNT2(address)); + return memUTF16(memByteBufferNT2(address)); } /** @@ -1738,18 +1888,26 @@ public static String memUTF16(long address) { * *

    The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

    * - * @param buffer the {@link ByteBuffer} to decode, or null + * @param buffer the {@link ByteBuffer} to decode * - * @return the decoded {@link String} or null if the specified {@code buffer} is null + * @return the decoded {@link String} */ public static String memUTF16(ByteBuffer buffer) { - if (buffer == null) { - return null; - } - return memUTF16(buffer, buffer.remaining() >> 1); } + /** Like {@link #memUTF16(long) memUTF16}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static String memUTF16Safe(long address) { + return address == NULL ? null : memUTF16(address); + } + + /** Like {@link #memUTF16(ByteBuffer) memUTF16}, but returns {@code null} if {@code buffer} is {@code null}. */ + @Nullable + public static String memUTF16Safe(@Nullable ByteBuffer buffer) { + return buffer == null ? null : memUTF16(buffer); + } + /** * Decodes the bytes with index {@code [position(), position()+(length*2)}) in {@code buffer}, as a UTF-16 string. * diff --git a/modules/core/src/main/java/org/lwjgl/system/Pointer.java b/modules/core/src/main/java/org/lwjgl/system/Pointer.java index da9b4d03c3..c3132a0e27 100644 --- a/modules/core/src/main/java/org/lwjgl/system/Pointer.java +++ b/modules/core/src/main/java/org/lwjgl/system/Pointer.java @@ -8,6 +8,8 @@ import javax.annotation.*; +import static org.lwjgl.system.Checks.*; + /** * Pointer interface. * @@ -46,6 +48,9 @@ abstract class Default implements Pointer { private long address; protected Default(long address) { + if (CHECKS) { + check(address); + } this.address = address; } diff --git a/modules/core/src/main/java/org/lwjgl/system/SharedLibraryLoader.java b/modules/core/src/main/java/org/lwjgl/system/SharedLibraryLoader.java index 303e97fa3a..33e822b07a 100644 --- a/modules/core/src/main/java/org/lwjgl/system/SharedLibraryLoader.java +++ b/modules/core/src/main/java/org/lwjgl/system/SharedLibraryLoader.java @@ -75,16 +75,16 @@ private static Path extractFile(String libraryFile, URL libURL) throws IOExcepti if (extractPath == null) { extractPath = extractedFile.getParent(); + String newLibPath = extractPath.toAbsolutePath().toString(); + // Prepend the path in which the libraries were extracted to org.lwjgl.librarypath String libPath = Configuration.LIBRARY_PATH.get(); - if (libPath == null || libPath.isEmpty()) { - libPath = extractPath.toAbsolutePath().toString(); - } else { - libPath = extractPath.toAbsolutePath() + File.pathSeparator + libPath; + if (libPath != null && !libPath.isEmpty()) { + newLibPath += File.pathSeparator + libPath; } - System.setProperty(Configuration.LIBRARY_PATH.getProperty(), libPath); - Configuration.LIBRARY_PATH.set(libPath); + System.setProperty(Configuration.LIBRARY_PATH.getProperty(), newLibPath); + Configuration.LIBRARY_PATH.set(newLibPath); } extractFile(libURL, extractedFile); @@ -100,14 +100,15 @@ private static Path extractFile(String libraryFile, URL libURL) throws IOExcepti * * @return the extracted library */ - private static Path getExtractedFile(Path libraryPath, String fileName) { + private static Path getExtractedFile(@Nullable Path libraryPath, String fileName) { // Reuse the lwjgl shared library location if (libraryPath != null && Files.isDirectory(libraryPath)) { return libraryPath.resolve(fileName); } - if (Configuration.SHARED_LIBRARY_EXTRACT_PATH.get() != null) { - return Paths.get(Configuration.SHARED_LIBRARY_EXTRACT_PATH.get(), fileName); + String override = Configuration.SHARED_LIBRARY_EXTRACT_PATH.get(); + if (override != null) { + return Paths.get(override, fileName); } String version = Version.getVersion().replace(' ', '-'); diff --git a/modules/core/src/main/java/org/lwjgl/system/Struct.java b/modules/core/src/main/java/org/lwjgl/system/Struct.java index 5434648d73..48b8d831c8 100644 --- a/modules/core/src/main/java/org/lwjgl/system/Struct.java +++ b/modules/core/src/main/java/org/lwjgl/system/Struct.java @@ -47,13 +47,34 @@ public void free() { nmemFree(address()); } + /** + * Returns true if the pointer member that corresponds to the specified {@code memberOffset} is {@code NULL}. + * + *

    This is useful to verify that not nullable members of an untrusted struct instance are indeed not {@code NULL}.

    + * + * @param memberOffset the byte offset of the member to query + * + * @return true if the member is {@code NULL} + */ + public boolean isNull(int memberOffset) { + if (DEBUG) { + checkMemberOffset(memberOffset); + } + return memGetAddress(address() + memberOffset) == NULL; + } + // ---------------- Implementation utilities ---------------- - protected static ByteBuffer checkContainer(ByteBuffer container, int sizeof) { - if (Checks.CHECKS && container != null) { - check(container, sizeof); + private void checkMemberOffset(int memberOffset) { + if (memberOffset < 0 || memberOffset + POINTER_SIZE > sizeof()) { + throw new IllegalArgumentException("Invalid member offset."); } + } + protected static ByteBuffer __checkContainer(ByteBuffer container, int sizeof) { + if (CHECKS) { + check(container, sizeof); + } return container; } @@ -64,7 +85,7 @@ private static long getBytes(int elements, int elementSize) { protected static long __malloc(int elements, int elementSize) { long bytes = getBytes(elements, elementSize); apiCheckAllocation(elements, bytes, BITS64 ? Long.MAX_VALUE : 0xFFFFFFFFL); - return nmemAlloc(bytes); + return nmemAllocChecked(bytes); } protected static ByteBuffer __create(int elements, int elementSize) { diff --git a/modules/core/src/main/java/org/lwjgl/vulkan/VK.java b/modules/core/src/main/java/org/lwjgl/vulkan/VK.java index caad639611..c468101058 100644 --- a/modules/core/src/main/java/org/lwjgl/vulkan/VK.java +++ b/modules/core/src/main/java/org/lwjgl/vulkan/VK.java @@ -7,6 +7,7 @@ import org.lwjgl.*; import org.lwjgl.system.*; +import javax.annotation.*; import java.util.*; import static java.lang.Math.*; @@ -24,8 +25,10 @@ */ public final class VK { + @Nullable private static FunctionProvider functionProvider; + @Nullable private static GlobalCommands globalCommands; static { @@ -100,9 +103,16 @@ public static void destroy() { globalCommands = null; } + private static T check(@Nullable T t) { + if (t == null) { + throw new IllegalStateException("Vulkan library has not been loaded."); + } + return t; + } + /** Returns the {@link FunctionProvider} for the Vulkan shared library. */ public static FunctionProvider getFunctionProvider() { - return functionProvider; + return check(functionProvider); } static class GlobalCommands { @@ -136,9 +146,9 @@ private long getFunctionAddress(String name) { } /** Returns the Vulkan global commands. */ - static GlobalCommands getGlobalCommands() { return globalCommands; } + static GlobalCommands getGlobalCommands() { return check(globalCommands); } - static Set getEnabledExtensionSet(int apiVersion, PointerBuffer extensionNames) { + static Set getEnabledExtensionSet(int apiVersion, @Nullable PointerBuffer extensionNames) { Set enabledExtensions = new HashSet<>(16); int majorVersion = VK_VERSION_MAJOR(apiVersion); @@ -168,7 +178,8 @@ static Set getEnabledExtensionSet(int apiVersion, PointerBuffer extensio return enabledExtensions; } - static T checkExtension(String extension, T functions) { + @Nullable + static T checkExtension(String extension, @Nullable T functions) { if (functions != null) { return functions; } diff --git a/modules/core/src/test/java/org/lwjgl/system/BufferTest.java b/modules/core/src/test/java/org/lwjgl/system/BufferTest.java index a0d1af6d24..5174a57ce0 100644 --- a/modules/core/src/test/java/org/lwjgl/system/BufferTest.java +++ b/modules/core/src/test/java/org/lwjgl/system/BufferTest.java @@ -17,22 +17,22 @@ @Test public class BufferTest { - @Test(expectedExceptions = IllegalArgumentException.class) public void testLargeBufferNIO() { // ByteBuffer.allocateDirect supports up to Integer.MAX_VALUE bytes - BufferUtils.createShortBuffer(0x3FFFFFFF + 1); + expectThrows(IllegalArgumentException.class, () -> BufferUtils.createShortBuffer(0x3FFFFFFF + 1)); } public void testLargeBuffer() { - ShortBuffer buffer = memCallocShort(0x3FFFFFFF + 1); - if (buffer == null) { - throw new SkipException("Large buffer allocation failed."); // 32-bit JVM - } + try { + ShortBuffer buffer = memCallocShort(0x3FFFFFFF + 1); - buffer.put(buffer.limit() - 1, (short)0xBEEF); - assertEquals(buffer.get(buffer.limit() - 1), (short)0xBEEF); + buffer.put(buffer.limit() - 1, (short)0xBEEF); + assertEquals(buffer.get(buffer.limit() - 1), (short)0xBEEF); - memFree(buffer); + memFree(buffer); + } catch (OutOfMemoryError e) { + throw new SkipException("Large buffer allocation failed."); // 32-bit JVM + } } private static long fillBuffer(JNINativeMethod.Buffer buffer) { diff --git a/modules/core/src/test/java/org/lwjgl/system/MemoryUtilTest.java b/modules/core/src/test/java/org/lwjgl/system/MemoryUtilTest.java index b59dc8eb32..a7d89d3598 100644 --- a/modules/core/src/test/java/org/lwjgl/system/MemoryUtilTest.java +++ b/modules/core/src/test/java/org/lwjgl/system/MemoryUtilTest.java @@ -5,11 +5,14 @@ package org.lwjgl.system; import org.lwjgl.*; +import org.testng.*; import org.testng.annotations.*; import java.nio.*; import java.nio.charset.*; +import java.util.*; +import static org.lwjgl.system.Checks.*; import static org.lwjgl.system.MemoryUtil.*; import static org.lwjgl.system.jni.JNINativeInterface.*; import static org.testng.Assert.*; @@ -17,6 +20,29 @@ @Test public class MemoryUtilTest { + public void testZeroAllocation() { + long address = nmemAllocChecked(0); + assertEquals(address, NULL); + nmemFree(address); + + address = nmemCallocChecked(1, 0); + assertEquals(address, NULL); + nmemFree(address); + + address = nmemCallocChecked(0, 8); + assertEquals(address, NULL); + nmemFree(address); + } + + public void testOOME() { + if (!CHECKS) { + throw new SkipException("This test may not run with checks disabled."); + } + + expectThrows(OutOfMemoryError.class, () -> nmemAllocChecked(-1L)); + expectThrows(OutOfMemoryError.class, () -> nmemCallocChecked(1, -1L)); + } + public void testMemSet() { ByteBuffer buffer = BufferUtils.createByteBuffer(32); for (int i = 0; i < buffer.capacity(); i++) { @@ -80,7 +106,7 @@ public void testJNINewBuffer() { long address = GetDirectBufferAddress(buffer); assertTrue(address != NULL); - ByteBuffer view = NewDirectByteBuffer(address + 8, 16); + ByteBuffer view = Objects.requireNonNull(NewDirectByteBuffer(address + 8, 16)); assertEquals(view.order(), ByteOrder.BIG_ENDIAN); for (int i = 0; i < view.capacity(); i++) { assertEquals(view.get(i), buffer.get(i + 8)); diff --git a/modules/core/src/test/java/org/lwjgl/system/StackTest.java b/modules/core/src/test/java/org/lwjgl/system/StackTest.java index c01474dd91..464f098e05 100644 --- a/modules/core/src/test/java/org/lwjgl/system/StackTest.java +++ b/modules/core/src/test/java/org/lwjgl/system/StackTest.java @@ -77,27 +77,29 @@ public void testAlignment() { } } - @Test(expectedExceptions = OutOfMemoryError.class) public void testOOME() { if (!CHECKS) { throw new SkipException("This test may not run with checks disabled."); } - MemoryStack stack = new MemoryStack(8); + expectThrows(OutOfMemoryError.class, () -> { + MemoryStack stack = new MemoryStack(8); - stack.push(); - stack.malloc(8); - stack.malloc(1); - stack.pop(); + stack.push(); + stack.malloc(8); + stack.malloc(1); + stack.pop(); + }); } - @Test(expectedExceptions = StackOverflowError.class) public void testSOE() { - MemoryStack stack = MemoryStack.create(); + expectThrows(StackOverflowError.class, () -> { + MemoryStack stack = MemoryStack.create(); - // Test growing of the stack frame array, - // the Java stack should overflow before we have any issues - recursivePush(stack); + // Test growing of the stack frame array, + // the Java stack should overflow before we have any issues + recursivePush(stack); + }); } private static void recursivePush(MemoryStack stack) { diff --git a/modules/core/src/test/java/org/lwjgl/system/StructTest.java b/modules/core/src/test/java/org/lwjgl/system/StructTest.java new file mode 100644 index 0000000000..f49d40f083 --- /dev/null +++ b/modules/core/src/test/java/org/lwjgl/system/StructTest.java @@ -0,0 +1,64 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ +package org.lwjgl.system; + +import org.lwjgl.*; +import org.lwjgl.system.jni.*; +import org.testng.annotations.*; + +import static org.lwjgl.system.MemoryStack.*; +import static org.testng.Assert.*; + +@Test +public class StructTest { + + public void testAllocation() { + JNINativeMethod.create(); + JNINativeMethod.malloc().free(); + JNINativeMethod.calloc().free(); + + try (MemoryStack stack = stackPush()) { + JNINativeMethod.mallocStack(stack); + JNINativeMethod.callocStack(stack); + } + + expectThrows(IllegalArgumentException.class, () -> new JNINativeMethod(BufferUtils.createByteBuffer(4))); + } + + public void testAllocationBuffer() { + JNINativeMethod.create(10); + JNINativeMethod.malloc(10).free(); + JNINativeMethod.calloc(10).free(); + + try (MemoryStack stack = stackPush()) { + JNINativeMethod.mallocStack(10, stack); + JNINativeMethod.callocStack(10, stack); + } + + assertEquals(new JNINativeMethod.Buffer(BufferUtils.createByteBuffer(4)).capacity(), 0); + } + + public void testValidation() { + try (MemoryStack stack = stackPush()) { + JNINativeMethod s = JNINativeMethod.callocStack(stack); + + assertTrue(s.isNull(JNINativeMethod.NAME)); + expectThrows(NullPointerException.class, s::name); + + expectThrows(NullPointerException.class, () -> JNINativeMethod.validate(s.address())); + s.name(stack.UTF8("myMethodName")); + expectThrows(NullPointerException.class, () -> JNINativeMethod.validate(s.address())); + s.signature(stack.UTF8("(I)I")); + expectThrows(NullPointerException.class, () -> JNINativeMethod.validate(s.address())); + s.fnPtr(0x12341234L); + JNINativeMethod.validate(s.address()); + + assertFalse(s.isNull(JNINativeMethod.NAME)); + assertEquals(s.nameString(), "myMethodName"); + + } + } + +} diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/CallbackFunction.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/CallbackFunction.kt index 6fd906be27..0651118446 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/CallbackFunction.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/CallbackFunction.kt @@ -70,19 +70,24 @@ import static org.lwjgl.system.MemoryUtil.*; .toJavaDoc(indentation = "", see = see, since = since)) print("""${access.modifier}abstract class $className extends Callback implements ${className}I { - /** Creates a {@code $className} instance from the specified function pointer. */ - @Nullable + /** + * Creates a {@code $className} instance from the specified function pointer. + * + * @return the new {@code $className} + */ public static $className create(long functionPointer) { - if (functionPointer == NULL) { - return null; - } - ${className}I instance = Callback.get(functionPointer); return instance instanceof $className ? ($className)instance : new Container(functionPointer, instance); } + /** Like {@link #create(long) create}, but returns {@code null} if {@code functionPointer} is {@link #NULL}. */ + @Nullable + public static $className createSafe(long functionPointer) { + return functionPointer == NULL ? null : create(functionPointer); + } + /** Creates a {@code $className} instance that delegates to the specified {@code ${className}I} instance. */ public static $className create(${className}I instance) { return instance instanceof $className diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/FunctionTransforms.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/FunctionTransforms.kt index 271571e6b5..f5438bcb4c 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/FunctionTransforms.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/FunctionTransforms.kt @@ -159,22 +159,26 @@ internal class CharSequenceTransform( "memAddress(${param.name}Encoded)" override fun setupStack(func: Func, qtype: Parameter, writer: PrintWriter) { - writer.print("$t$t${t}ByteBuffer ${qtype.name}Encoded = ") - writer.print("stack.${(qtype.nativeType as CharSequenceType).charMapping.charset}(${qtype.name}") + writer.print("$t$t${t}ByteBuffer ${qtype.name}Encoded = stack.") + writer.print((qtype.nativeType as CharSequenceType).charMapping.charset) + if (qtype.has(nullable)) { + writer.print("Safe") + } + writer.print("(${qtype.name}") if (!nullTerminated) writer.print(", false") writer.println(");") } } -internal object StringReturnTransform : FunctionTransform { +internal class StringReturnTransform(private val nullable: Boolean) : FunctionTransform { override fun transformDeclaration(param: ReturnValue, original: String) = "String" override fun transformCall(param: ReturnValue, original: String): String { val expression = if (original.startsWith("memByteBufferNT")) original.substring(17, original.length - 1) else original - return "mem${(param.nativeType as CharSequenceType).charMapping.charset}($expression)" + return "mem${(param.nativeType as CharSequenceType).charMapping.charset}${if (nullable) "Safe" else ""}($expression)" } } @@ -187,7 +191,7 @@ internal class PrimitiveValueReturnTransform( else (bufferType.mapping as PointerMapping).primitive // Replace void with the buffer value type override fun transformCall(param: ReturnValue, original: String) = if (bufferType.elementType is PointerType && bufferType.elementType.elementType is StructType) - "$t${t}return ${bufferType.elementType.javaMethodType}.create($paramName.get(0));" + "$t${t}return ${bufferType.elementType.javaMethodType}.createSafe($paramName.get(0));" else if (bufferType.mapping === PointerMapping.DATA_BOOLEAN) "$t${t}return $paramName.get(0) != 0;" else @@ -306,9 +310,9 @@ internal class BufferAutoSizeReturnTransform( override fun transformCall(param: ReturnValue, original: String) = (outParam.nativeType as PointerType).elementType!!.let { "$t${t}return ${if (it.dereference is StructType) - "${it.javaMethodType}.create" + "${it.javaMethodType}.createSafe" else - "mem${it.javaMethodType}" + "mem${it.javaMethodType}Safe" }(${outParam.name}.get(0), $lengthExpression);" } } diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt index e4f9d23875..07337baed8 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/Functions.kt @@ -216,7 +216,7 @@ class Func( "$it $name" } }.let { - if (nativeType.isReference && has(nullable)) { + if (annotate && nativeType.isReference && has(nullable)) { "@Nullable $it" } else { it @@ -866,7 +866,7 @@ class Func( print(t) print("$t$t") if (returns.nativeType.dereference is StructType) { - println("return ${returns.nativeType.javaMethodType}.create($RESULT${parameters.asSequence().singleOrNull { it.has() }.let { if (it != null) ", ${it.name}.get(0)" else "" }});") + println("return ${returns.nativeType.javaMethodType}.createSafe($RESULT${parameters.asSequence().singleOrNull { it.has() }.let { if (it != null) ", ${it.name}.get(0)" else "" }});") } else { val isNullTerminated = returns.nativeType is CharSequenceType val bufferType = if (isNullTerminated || returns.nativeType.mapping === PointerMapping.DATA) @@ -875,8 +875,10 @@ class Func( returns.nativeType.mapping.javaMethodName print("return mem$bufferType") - if (isNullTerminated) - print("NT${(returns.nativeType as CharSequenceType).charMapping.bytes}") + print(if (isNullTerminated) + "NT${(returns.nativeType as CharSequenceType).charMapping.bytes}" + else + "Safe") print("($RESULT") if (has()) get().sizeExpression.let { expression -> @@ -971,11 +973,11 @@ class Func( if (returnLater || returns.nativeType.isPointerData) { print("$returnType $RESULT = ") if (returnsObject) - print("$returnType.create(") + print("$returnType.createSafe(") } else { print("return ") if (returnsObject) - print("$returnType.create(") + print("$returnType.createSafe(") } } @@ -1024,7 +1026,7 @@ class Func( nativeClass.binding?.generateAlternativeMethods(this, this@Func, transforms) if (returns.nativeType is CharSequenceType) - transforms[returns] = StringReturnTransform + transforms[returns] = StringReturnTransform(!has()) else if (has()) { val mapPointer = get() @@ -1399,24 +1401,30 @@ class Func( } print("$t${if (constantMacro) "private " else accessModifier}static $retType $name(") - printList(getParams { it !== JNI_ENV }) { - if (it.isAutoSizeResultOut && hideAutoSizeResultParam) + printList(getParams { it !== JNI_ENV }) { param -> + if (param.isAutoSizeResultOut && hideAutoSizeResultParam) null else - it.transformDeclarationOrElse(transforms, it.asJavaMethodParam(false), true, it.has(const)) + param.transformDeclarationOrElse(transforms, param.asJavaMethodParam(false), true, param.has(const)).let { + if (it != null && param.nativeType.isReference && param.has(nullable)) { + "@Nullable $it" + } else { + it + } + } } val returnTransform = transforms[returns] if (returnTransform is MapPointerTransform) { if (!parameters.isEmpty()) print(", ") - print("ByteBuffer $MAP_OLD") + print("@Nullable ByteBuffer $MAP_OLD") } else if (returnTransform != null && returnTransform::class.java === MapPointerExplicitTransform::class.java) { if (!parameters.isEmpty()) print(", ") val mapPointerExplicit = returnTransform as MapPointerExplicitTransform if (mapPointerExplicit.addParam) print("long ${mapPointerExplicit.lengthParam}, ") - print("ByteBuffer $MAP_OLD") + print("@Nullable ByteBuffer $MAP_OLD") } if (returns.isStructValue) { if (!parameters.isEmpty()) print(", ") @@ -1520,7 +1528,7 @@ class Func( print(t) print("$t$t") if (returns.nativeType.dereference is StructType) { - println("return ${returns.nativeType.javaMethodType}.create($RESULT${parameters.asSequence().singleOrNull { it.has() }.let { if (it != null) ", ${it.name}.get(${it.name}.position()" else "" }});") + println("return ${returns.nativeType.javaMethodType}.createSafe($RESULT${parameters.asSequence().singleOrNull { it.has() }.let { if (it != null) ", ${it.name}.get(${it.name}.position()" else "" }});") } else { val isNullTerminated = returns.nativeType is CharSequenceType val bufferType = if (isNullTerminated || returns.nativeType.mapping === PointerMapping.DATA) @@ -1530,8 +1538,10 @@ class Func( val builder = StringBuilder() builder.append("mem$bufferType") - if (isNullTerminated) - builder.append("NT${(returns.nativeType as CharSequenceType).charMapping.bytes}") + builder.append(if (isNullTerminated) + "NT${(returns.nativeType as CharSequenceType).charMapping.bytes}" + else + "Safe") builder.append("($RESULT") if (has()) builder.append(", ${get().sizeExpression}") diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt index 743a2be14d..754f185838 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/NativeClass.kt @@ -411,7 +411,7 @@ class NativeClass( if (functions.any { (it.returns.nativeType.isReference && !it.has(Nonnull)) || it.parameters.any { it.nativeType.isReference && it.has(nullable) - } + } || it.has() }) { println("import javax.annotation.*;\n") } diff --git a/modules/generator/src/main/kotlin/org/lwjgl/generator/Structs.kt b/modules/generator/src/main/kotlin/org/lwjgl/generator/Structs.kt index d7a532ab19..60ddd81c4c 100644 --- a/modules/generator/src/main/kotlin/org/lwjgl/generator/Structs.kt +++ b/modules/generator/src/main/kotlin/org/lwjgl/generator/Structs.kt @@ -9,6 +9,31 @@ import java.io.* private const val STRUCT = "struct" private const val ANONYMOUS = "*" // very easy to introduce bugs, unless this is an invalid character in Java identifiers +private val KEYWORDS = setOf( + // Java keywords + "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", + "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", + "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", + "try", "void", "volatile", "while", + // Object + "equals", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait", + // Pointer + "address", + // AutoCloseable + "close", + // NativeResource + "free", + // Struct + "create", "callocStack", "calloc", "isNull", "malloc", "mallocStack", "sizeof", + // Iterable + "iterator", "forEach", "spliterator", + // CustomBuffer + "address0", "capacity", "clear", "compact", "duplicate", "flip", "hasRemaining", "limit", "mark", "position", "remaining", "reset", "rewind", + "slice", + // StructBuffer + "get", "parallelStream", "put", "stream" +) + open class StructMember( val nativeType: NativeType, val name: String, @@ -28,10 +53,15 @@ open class StructMember( } internal fun field(parentMember: String) = if (parentMember.isEmpty()) - if (name == "class") "$name\$" else name // TODO: use a list of Java keywords here + if (KEYWORDS.contains(name)) "$name\$" else name else "${parentMember}_$name" + internal fun fieldName(parentMember: String) = if (parentMember.isEmpty()) + name + else + "$parentMember.$name" + /** hidden if false, contributes to layout only */ var public = true /** mutable if true, even if the parent struct is not mutable */ @@ -616,7 +646,7 @@ $indentation}""" *

    The created instance holds a strong reference to the container object.

    */ ${access.modifier}$className(ByteBuffer container) { - this(memAddress(container), checkContainer(container, SIZEOF)); + this(memAddress(container), __checkContainer(container, SIZEOF)); } @Override @@ -665,12 +695,12 @@ $indentation}""" print(""" /** Returns a new {@link $className} instance allocated with {@link MemoryUtil#memAlloc memAlloc}. The instance must be explicitly freed. */ public static $className malloc() { - return create(nmemAlloc(SIZEOF)); + return create(nmemAllocChecked(SIZEOF)); } /** Returns a new {@link $className} instance allocated with {@link MemoryUtil#memCalloc memCalloc}. The instance must be explicitly freed. */ public static $className calloc() { - return create(nmemCalloc(1, SIZEOF)); + return create(nmemCallocChecked(1, SIZEOF)); } /** Returns a new {@link $className} instance allocated with {@link BufferUtils}. */ @@ -681,10 +711,15 @@ $indentation}""" } print(""" - /** Returns a new {@link $className} instance for the specified memory address or {@code null} if the address is {@code NULL}. */ - @Nullable + /** Returns a new {@link $className} instance for the specified memory address. */ public static $className create(long address) { - return address == NULL ? null : new $className(address, null); + return new $className(address, null); + } + + /** Like {@link #create(long) create}, but returns {@code null} if {@code address} is {@code NULL}. */ + @Nullable + public static $className createSafe(long address) { + return address == NULL ? null : create(address); } """) if (mallocable) { @@ -694,8 +729,7 @@ $indentation}""" * * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ - @Nullable - public static Buffer malloc(int $BUFFER_CAPACITY_PARAM) { + public static $className.Buffer malloc(int $BUFFER_CAPACITY_PARAM) { return create(__malloc($BUFFER_CAPACITY_PARAM, SIZEOF), $BUFFER_CAPACITY_PARAM); } @@ -704,9 +738,8 @@ $indentation}""" * * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ - @Nullable - public static Buffer calloc(int $BUFFER_CAPACITY_PARAM) { - return create(nmemCalloc($BUFFER_CAPACITY_PARAM, SIZEOF), $BUFFER_CAPACITY_PARAM); + public static $className.Buffer calloc(int $BUFFER_CAPACITY_PARAM) { + return create(nmemCallocChecked($BUFFER_CAPACITY_PARAM, SIZEOF), $BUFFER_CAPACITY_PARAM); } /** @@ -714,7 +747,7 @@ $indentation}""" * * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ - public static Buffer create(int $BUFFER_CAPACITY_PARAM) { + public static $className.Buffer create(int $BUFFER_CAPACITY_PARAM) { return new Buffer(__create($BUFFER_CAPACITY_PARAM, SIZEOF)); } """) @@ -727,9 +760,14 @@ $indentation}""" * @param address the memory address * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ + public static $className.Buffer create(long address, int $BUFFER_CAPACITY_PARAM) { + return new Buffer(address, $BUFFER_CAPACITY_PARAM); + } + + /** Like {@link #create(long, int) create}, but returns {@code null} if {@code address} is {@code NULL}. */ @Nullable - public static Buffer create(long address, int $BUFFER_CAPACITY_PARAM) { - return address == NULL ? null : new Buffer(address, null, -1, 0, $BUFFER_CAPACITY_PARAM, $BUFFER_CAPACITY_PARAM); + public static $className.Buffer createSafe(long address, int $BUFFER_CAPACITY_PARAM) { + return address == NULL ? null : create(address, $BUFFER_CAPACITY_PARAM); } """) @@ -770,7 +808,7 @@ $indentation}""" * * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ - public static Buffer mallocStack(int $BUFFER_CAPACITY_PARAM) { + public static $className.Buffer mallocStack(int $BUFFER_CAPACITY_PARAM) { return mallocStack($BUFFER_CAPACITY_PARAM, stackGet()); } @@ -779,7 +817,7 @@ $indentation}""" * * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ - public static Buffer callocStack(int $BUFFER_CAPACITY_PARAM) { + public static $className.Buffer callocStack(int $BUFFER_CAPACITY_PARAM) { return callocStack($BUFFER_CAPACITY_PARAM, stackGet()); } @@ -789,7 +827,7 @@ $indentation}""" * @param stack the stack from which to allocate * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ - public static Buffer mallocStack(int $BUFFER_CAPACITY_PARAM, MemoryStack stack) { + public static $className.Buffer mallocStack(int $BUFFER_CAPACITY_PARAM, MemoryStack stack) { return create(stack.nmalloc(ALIGNOF, $BUFFER_CAPACITY_PARAM * SIZEOF), $BUFFER_CAPACITY_PARAM); } @@ -799,7 +837,7 @@ $indentation}""" * @param stack the stack from which to allocate * @param $BUFFER_CAPACITY_PARAM the buffer capacity */ - public static Buffer callocStack(int $BUFFER_CAPACITY_PARAM, MemoryStack stack) { + public static $className.Buffer callocStack(int $BUFFER_CAPACITY_PARAM, MemoryStack stack) { return create(stack.ncalloc(ALIGNOF, $BUFFER_CAPACITY_PARAM, SIZEOF), $BUFFER_CAPACITY_PARAM); } """) @@ -887,6 +925,10 @@ ${validations.joinToString("\n")} print(""" + public Buffer(long address, int cap) { + super(address, null, -1, 0, cap, cap); + } + Buffer(long address, @Nullable ByteBuffer container, int mark, int pos, int lim, int cap) { super(address, container, mark, pos, lim, cap); } @@ -1245,7 +1287,7 @@ ${validations.joinToString("\n")} if (it.nativeType is ObjectType) { if (it.public) println("$t/** Unsafe version of {@link #$setter(${it.nativeType.javaMethodType}) $setter}. */") - println("${t}public static void n$setter(long $STRUCT, ${it.nativeType.javaMethodType} value) { memPutAddress($STRUCT + $field, ${it.addressValue}); }") + println("${t}public static void n$setter(long $STRUCT, ${it.nullable(it.nativeType.javaMethodType)} value) { memPutAddress($STRUCT + $field, ${it.addressValue}); }") } else { val javaType = it.nativeType.nativeMethodType val bufferMethod = getBufferMethod(it, javaType) @@ -1286,7 +1328,7 @@ ${validations.joinToString("\n")} val structTypeIndexed = "$structType${if (getReferenceMember(it.name) == null) "" else ".Buffer"}" if (it.public) println("$t/** Unsafe version of {@link #$setter(int, $structTypeIndexed) $setter}. */") - println("${t}public static void n$setter(long $STRUCT, int index, $structTypeIndexed value) {") + println("${t}public static void n$setter(long $STRUCT, int index, ${it.nullable(structTypeIndexed, isArray = false)} value) {") if (Binding.CHECKS) println("$t${t}if (CHECKS) { check(index, ${it.size}); }") println("$t${t}memPutAddress($STRUCT + $field + index * POINTER_SIZE, ${it.addressValue});") @@ -1353,9 +1395,9 @@ ${validations.joinToString("\n")} if (it.public) println("$t/** Unsafe version of {@link #$setter(ByteBuffer) $setter}. */") - println("${t}public static void n$setter(long $STRUCT, ByteBuffer value) {") + println("${t}public static void n$setter(long $STRUCT, ${it.nullable("ByteBuffer")} value) {") if (Binding.CHECKS) - println("$t${t}if (CHECKS) { checkNT${mapping.bytes}Safe(value); }") + println("$t${t}if (CHECKS) { checkNT${mapping.bytes}${if (it.isNullable) "Safe" else ""}(value); }") println("$t${t}memPutAddress($STRUCT + $field, ${it.memAddressValue});") println("$t}") } else if (it.nativeType.isPointerData) { @@ -1364,18 +1406,18 @@ ${validations.joinToString("\n")} if (it is StructMemberBuffer) { if (it.public) println("$t/** Unsafe version of {@link #$setter($paramType.Buffer) $setter}. */") - print("${t}public static void n$setter(long $STRUCT, $paramType.Buffer value) { memPutAddress($STRUCT + $field, ${it.addressValue});") + print("${t}public static void n$setter(long $STRUCT, ${it.nullable("$paramType.Buffer")} value) { memPutAddress($STRUCT + $field, ${it.addressValue});") setRemaining(it) println(" }") } else { if (it.public) println("$t/** Unsafe version of {@link #$setter($paramType) $setter}. */") - println("${t}public static void n$setter(long $STRUCT, $paramType value) { memPutAddress($STRUCT + $field, ${it.addressValue}); }") + println("${t}public static void n$setter(long $STRUCT, ${it.nullable(paramType)} value) { memPutAddress($STRUCT + $field, ${it.addressValue}); }") } } else { if (it.public) println("$t/** Unsafe version of {@link #$setter($paramType) $setter}. */") - print("${t}public static void n$setter(long $STRUCT, $paramType value) { memPutAddress($STRUCT + $field, ${it.memAddressValue});") + print("${t}public static void n$setter(long $STRUCT, ${it.nullable(paramType)} value) { memPutAddress($STRUCT + $field, ${it.memAddressValue});") setRemaining(it) println(" }") } @@ -1384,17 +1426,42 @@ ${validations.joinToString("\n")} } } - private fun StructMember.annotate(type: String, arraySize: String? = null) = nativeType.annotate(type, has(const), arraySize) + private fun StructMember.annotate(type: String, arraySize: String? = null) = nativeType.annotate(type, has(const), arraySize).let { + if (arraySize == null) + nullable(it, false) + else + it + } + + private fun StructMember.nullable(type: String, isArray: Boolean = this is StructMemberArray) = + if (nativeType.isReference && isNullable && !isArray) { + "@Nullable $type" + } else { + type + } + + private fun StructMember.construct(type: String) = if (nativeType.isReference && isNullable) { + "$type.createSafe" + } else { + "$type.create" + } + + private fun StructMember.mem(type: String) = if (nativeType.isReference && isNullable && this !is StructMemberArray) { + "mem${type}Safe" + } else { + "mem$type" + } private fun PrintWriter.generateSetters( accessMode: AccessMode, members: Sequence, + parentSetter: String = "", parentMember: String = "" ) { val n = if (accessMode === AccessMode.INSTANCE) "n" else "$className.n" members.forEach { - val setter = it.field(parentMember) - val field = it.name + val setter = it.field(parentSetter) + val member = it.fieldName(parentMember) val indent = accessMode.indent val overrides = extends != null && extends.members.any { parentMember -> parentMember.name == it.name } @@ -1407,9 +1474,14 @@ ${validations.joinToString("\n")} if (it.isNestedStruct) { val structType = it.nativeType.javaMethodType if (structType === ANONYMOUS) - generateSetters(accessMode, it.nestedMembers, if (field === ANONYMOUS) parentMember else field) + generateSetters( + accessMode, + it.nestedMembers, + if (it.name === ANONYMOUS) parentSetter else setter, + if (it.name === ANONYMOUS) parentMember else member + ) else { - println("$indent/** Copies the specified {@link $structType} to the {@code $field} field. */") + println("$indent/** Copies the specified {@link $structType} to the {@code $member} field. */") if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(${it.annotate(structType)} value) { $n$setter($ADDRESS, value); return this; }") } @@ -1417,7 +1489,7 @@ ${validations.joinToString("\n")} // Setter if (it !is StructMemberArray && !it.nativeType.isPointerData) { - println("$indent/** Sets the specified value to the {@code $field} field. */") + println("$indent/** Sets the specified value to the {@code $member} field. */") if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(${it.annotate(it.nativeType.javaMethodType)} value) { $n$setter($ADDRESS, value${if (it.nativeType.mapping === PrimitiveMapping.BOOLEAN4) " ? 1 : 0" else ""}); return this; }") } @@ -1428,43 +1500,43 @@ ${validations.joinToString("\n")} if (it.nativeType.dereference is StructType) { val structType = it.nativeType.javaMethodType if (it.nativeType is PointerType) { - println("$indent/** Copies the specified {@link PointerBuffer} to the {@code $field} field. */") + println("$indent/** Copies the specified {@link PointerBuffer} to the {@code $member} field. */") if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(${it.annotate("PointerBuffer", it.size)} value) { $n$setter($ADDRESS, value); return this; }") - println("$indent/** Copies the address of the specified {@link $structType} at the specified index of the {@code $field} field. */") + println("$indent/** Copies the address of the specified {@link $structType} at the specified index of the {@code $member} field. */") } else { - println("$indent/** Copies the specified {@link $structType.Buffer} to the {@code $field} field. */") + println("$indent/** Copies the specified {@link $structType.Buffer} to the {@code $member} field. */") if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(${it.annotate("$structType.Buffer", it.size)} value) { $n$setter($ADDRESS, value); return this; }") - println("$indent/** Copies the specified {@link $structType} at the specified index of the {@code $field} field. */") + println("$indent/** Copies the specified {@link $structType} at the specified index of the {@code $member} field. */") } if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(int index, ${it.annotate("$structType${if (getReferenceMember(it.name) == null) "" else ".Buffer"}")} value) { $n$setter($ADDRESS, index, value); return this; }") } else if (it is StructMemberCharArray) { - println("$indent/** Copies the specified encoded string to the {@code $field} field. */") + println("$indent/** Copies the specified encoded string to the {@code $member} field. */") if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(${it.annotate("ByteBuffer", it.size)} value) { $n$setter($ADDRESS, value); return this; }") } else { val bufferType = it.primitiveMapping.toPointer.javaMethodName - println("$indent/** Copies the specified {@link $bufferType} to the {@code $field} field. */") + println("$indent/** Copies the specified {@link $bufferType} to the {@code $member} field. */") if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(${it.annotate(bufferType, it.size)} value) { $n$setter($ADDRESS, value); return this; }") - println("$indent/** Sets the specified value at the specified index of the {@code $field} field. */") + println("$indent/** Sets the specified value at the specified index of the {@code $member} field. */") if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(int index, ${it.annotate(it.nativeType.javaMethodType)} value) { $n$setter($ADDRESS, index, value); return this; }") } } else if (it.nativeType is CharSequenceType) { - println("$indent/** Sets the address of the specified encoded string to the {@code $field} field. */") + println("$indent/** Sets the address of the specified encoded string to the {@code $member} field. */") if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(${it.annotate("ByteBuffer")} value) { $n$setter($ADDRESS, value); return this; }") } else if (it.nativeType.isPointerData) { val pointerType = it.nativeType.javaMethodType if (it is StructMemberBuffer) { - println("$indent/** Sets the address of the specified {@link $pointerType.Buffer} to the {@code $field} field. */") + println("$indent/** Sets the address of the specified {@link $pointerType.Buffer} to the {@code $member} field. */") if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(${it.annotate("$pointerType.Buffer")} value) { $n$setter($ADDRESS, value); return this; }") } else { - println("$indent/** Sets the address of the specified {@link $pointerType} to the {@code $field} field. */") + println("$indent/** Sets the address of the specified {@link $pointerType} to the {@code $member} field. */") if (overrides) println("$indent@Override") println("${indent}public $returnType $setter(${it.annotate(pointerType)} value) { $n$setter($ADDRESS, value); return this; }") } @@ -1476,11 +1548,11 @@ ${validations.joinToString("\n")} private fun PrintWriter.generateStaticGetters( members: Sequence, parentStruct: Struct? = null, - parentMember: String = "", + parentGetter: String = "", parentField: String = "" ) { members.forEach { - val getter = it.field(parentMember) + val getter = it.field(parentGetter) val field = getFieldOffset(it, parentStruct, parentField) if (it.isNestedStruct) { @@ -1489,7 +1561,7 @@ ${validations.joinToString("\n")} if (structType === ANONYMOUS) generateStaticGetters( it.nestedMembers, nestedStruct, - if (it.name === ANONYMOUS) parentMember else getter, + if (it.name === ANONYMOUS) parentGetter else getter, if (it.name === ANONYMOUS) parentField else field ) else { @@ -1504,7 +1576,7 @@ ${validations.joinToString("\n")} if (it.public) println("$t/** Unsafe version of {@link #$getter}. */") if (it.nativeType is CallbackType) { - println("${t}public static ${it.nativeType.className} n$getter(long $STRUCT) { return ${it.nativeType.className}.create(memGetAddress($STRUCT + $field)); }") + println("$t${it.nullable("public")} static ${it.nativeType.className} n$getter(long $STRUCT) { return ${it.construct(it.nativeType.className)}(memGetAddress($STRUCT + $field)); }") } else { val javaType = it.nativeType.nativeMethodType val bufferMethod = getBufferMethod(it, javaType) @@ -1527,26 +1599,26 @@ ${validations.joinToString("\n")} if (it.public) println("$t/** Unsafe version of {@link #$getter}. */") - println("${t}public static PointerBuffer n$getter(long $STRUCT) { return memPointerBuffer($STRUCT + $field, $size); }") + println("$t${it.nullable("public")} static PointerBuffer n$getter(long $STRUCT) { return ${it.mem("PointerBuffer")}($STRUCT + $field, $size); }") if (it.public) println("$t/** Unsafe version of {@link #$getter(int) $getter}. */") - println("${t}public static $nestedStruct${if (autoSizeIndirect == null) "" else ".Buffer"} n$getter(long $STRUCT, int index) {") + println("$t${it.nullable("public", isArray = false)} static $nestedStruct${if (autoSizeIndirect == null) "" else ".Buffer"} n$getter(long $STRUCT, int index) {") if (Binding.CHECKS) println("$t${t}if (CHECKS) { check(index, $size); }") - println("$t${t}return $nestedStruct.create(memGetAddress($STRUCT + $field + index * POINTER_SIZE)${autoSizeIndirect.let { + println("$t${t}return ${it.construct(nestedStruct)}(memGetAddress($STRUCT + $field + index * POINTER_SIZE)${autoSizeIndirect.let { if (it == null) "" else ", n${it.name}($STRUCT)" }});") println("$t}") } else { if (it.public) println("$t/** Unsafe version of {@link #$getter}. */") - println("${t}public static $nestedStruct.Buffer n$getter(long $STRUCT) { return $nestedStruct.create($STRUCT + $field, $size); }") + println("$t${it.nullable("public")} static $nestedStruct.Buffer n$getter(long $STRUCT) { return ${it.construct(nestedStruct)}($STRUCT + $field, $size); }") if (it.public) println("$t/** Unsafe version of {@link #$getter(int) $getter}. */") - println("${t}public static $nestedStruct n$getter(long $STRUCT, int index) {") + println("$t${it.nullable("public")} static $nestedStruct n$getter(long $STRUCT, int index) {") if (Binding.CHECKS) println("$t${t}if (CHECKS) { check(index, $size); }") - println("$t${t}return $nestedStruct.create($STRUCT + $field + index * $nestedStruct.SIZEOF);") + println("$t${t}return ${it.construct(nestedStruct)}($STRUCT + $field + index * $nestedStruct.SIZEOF);") println("$t}") } } else if (it is StructMemberCharArray) { @@ -1566,7 +1638,7 @@ ${validations.joinToString("\n")} if (it.public) println("$t/** Unsafe version of {@link #$getter}. */") - println("${t}public static $bufferType n$getter(long $STRUCT) { return mem$bufferType($STRUCT + $field, ${getReferenceMember(it.name)?.autoSize ?: it.size}); }") + println("$t${it.nullable("public")} static $bufferType n$getter(long $STRUCT) { return ${it.mem(bufferType)}($STRUCT + $field, ${getReferenceMember(it.name)?.autoSize ?: it.size}); }") val javaType = it.nativeType.nativeMethodType val bytesPerElement = if (mapping === PrimitiveMapping.POINTER) "POINTER_SIZE" else mapping.bytes.toString() @@ -1585,10 +1657,10 @@ ${validations.joinToString("\n")} val mapping = it.nativeType.charMapping if (it.public) println("$t/** Unsafe version of {@link #$getter}. */") - println("${t}public static ByteBuffer n$getter(long $STRUCT) { return memByteBufferNT${mapping.bytes}(memGetAddress($STRUCT + $field)); }") + println("$t${it.nullable("public")} static ByteBuffer n$getter(long $STRUCT) { return ${it.mem("ByteBufferNT${mapping.bytes}")}(memGetAddress($STRUCT + $field)); }") if (it.public) println("$t/** Unsafe version of {@link #${getter}String}. */") - println("${t}public static String n${getter}String(long $STRUCT) { return mem${mapping.charset}(memGetAddress($STRUCT + $field)); }") + println("$t${it.nullable("public")} static String n${getter}String(long $STRUCT) { return ${it.mem(mapping.charset)}(memGetAddress($STRUCT + $field)); }") } else if (it.nativeType.isPointerData) { val returnType = it.nativeType.javaMethodType if (it.nativeType.dereference is StructType) { @@ -1597,22 +1669,22 @@ ${validations.joinToString("\n")} println(if (it is StructMemberBuffer) { val capacity = getReferenceMember(it.name) if (capacity == null) - "${t}public static $returnType.Buffer n$getter(long $STRUCT, int $BUFFER_CAPACITY_PARAM) { return $returnType.create(memGetAddress($STRUCT + $field), $BUFFER_CAPACITY_PARAM); }" + "$t${it.nullable("public")} static $returnType.Buffer n$getter(long $STRUCT, int $BUFFER_CAPACITY_PARAM) { return ${it.construct(returnType)}(memGetAddress($STRUCT + $field), $BUFFER_CAPACITY_PARAM); }" else - "${t}public static $returnType.Buffer n$getter(long $STRUCT) { return $returnType.create(memGetAddress($STRUCT + $field), ${capacity.autoSize}); }" + "$t${it.nullable("public")} static $returnType.Buffer n$getter(long $STRUCT) { return ${it.construct(returnType)}(memGetAddress($STRUCT + $field), ${capacity.autoSize}); }" } else - "${t}public static $returnType n$getter(long $STRUCT) { return $returnType.create(memGetAddress($STRUCT + $field)); }" + "$t${it.nullable("public")} static $returnType n$getter(long $STRUCT) { return ${it.construct(returnType)}(memGetAddress($STRUCT + $field)); }" ) } else { val capacity = getReferenceMember(it.name) if (capacity == null) { if (it.public) println("$t/** Unsafe version of {@link #$getter(int) $getter}. */") - println("${t}public static $returnType n$getter(long $STRUCT, int $BUFFER_CAPACITY_PARAM) { return mem$returnType(memGetAddress($STRUCT + $field), $BUFFER_CAPACITY_PARAM); }") + println("$t${it.nullable("public")} static $returnType n$getter(long $STRUCT, int $BUFFER_CAPACITY_PARAM) { return ${it.mem(returnType)}(memGetAddress($STRUCT + $field), $BUFFER_CAPACITY_PARAM); }") } else { if (it.public) println("$t/** Unsafe version of {@link #$getter() $getter}. */") - println("${t}public static $returnType n$getter(long $STRUCT) { return mem$returnType(memGetAddress($STRUCT + $field), ${capacity.autoSize}); }") + println("$t${it.nullable("public")} static $returnType n$getter(long $STRUCT) { return ${it.mem(returnType)}(memGetAddress($STRUCT + $field), ${capacity.autoSize}); }") } } } @@ -1622,6 +1694,9 @@ ${validations.joinToString("\n")} private fun PrintWriter.generateGetterAnnotations(indent: String, overrides: Boolean, member: StructMember, type: String, arraySize: String? = null) { if (overrides) println("$indent@Override") + if (arraySize == null && member.nativeType.isReference && member.isNullable) { + println("$indent@Nullable") + } member.nativeType.annotation(type, member.has(const), arraySize).let { if (it != null) println("$indent$it") @@ -1631,11 +1706,13 @@ ${validations.joinToString("\n")} private fun PrintWriter.generateGetters( accessMode: AccessMode, members: Sequence, + parentGetter: String = "", parentMember: String = "" ) { val n = if (accessMode === AccessMode.INSTANCE) "n" else "$className.n" members.forEach { - val getter = it.field(parentMember) + val getter = it.field(parentGetter) + val member = it.fieldName(parentMember) val indent = accessMode.indent val overrides = extends != null && extends.members.any { parentMember -> parentMember.name == it.name } @@ -1644,9 +1721,14 @@ ${validations.joinToString("\n")} val nestedStruct = (it.nativeType as StructType).definition val structType = nestedStruct.className if (structType === ANONYMOUS) - generateGetters(accessMode, it.nestedMembers, if (it.name === ANONYMOUS) parentMember else getter) + generateGetters( + accessMode, + it.nestedMembers, + if (it.name === ANONYMOUS) parentGetter else getter, + if (it.name === ANONYMOUS) parentMember else member + ) else { - println("$indent/** Returns a {@link $structType} view of the {@code $getter} field. */") + println("$indent/** Returns a {@link $structType} view of the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, structType) println("${indent}public $structType $getter() { return $n$getter($ADDRESS); }") } @@ -1659,7 +1741,7 @@ ${validations.joinToString("\n")} is ObjectType -> "long" else -> it.nativeType.javaMethodType } - println("$indent/** Returns the value of the {@code $getter} field. */") + println("$indent/** Returns the value of the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, returnType) println("${indent}public $returnType $getter() { return $n$getter($ADDRESS)${if (it.nativeType.mapping === PrimitiveMapping.BOOLEAN4) " != 0" else ""}; }") } @@ -1670,43 +1752,43 @@ ${validations.joinToString("\n")} if (it.nativeType.dereference is StructType) { val structType = it.nativeType.javaMethodType if (it.nativeType is PointerType) { - println("$indent/** Returns a {@link PointerBuffer} view of the {@code $getter} field. */") + println("$indent/** Returns a {@link PointerBuffer} view of the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, "PointerBuffer", it.size) println("${indent}public PointerBuffer $getter() { return $n$getter($ADDRESS); }") val retType = "$structType${if (getReferenceMember(it.name) == null) "" else ".Buffer"}" - println("$indent/** Returns a {@link $structType} view of the pointer at the specified index of the {@code $getter}. */") + println("$indent/** Returns a {@link $structType} view of the pointer at the specified index of the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, retType) println("${indent}public $retType $getter(int index) { return $n$getter($ADDRESS, index); }") } else { - println("$indent/** Returns a {@link $structType}.Buffer view of the {@code $getter} field. */") + println("$indent/** Returns a {@link $structType}.Buffer view of the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, "$structType.Buffer", it.size) println("${indent}public $structType.Buffer $getter() { return $n$getter($ADDRESS); }") - println("$indent/** Returns a {@link $structType} view of the struct at the specified index of the {@code $getter} field. */") + println("$indent/** Returns a {@link $structType} view of the struct at the specified index of the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, structType) println("${indent}public $structType $getter(int index) { return $n$getter($ADDRESS, index); }") } } else if (it is StructMemberCharArray) { - println("$indent/** Returns a {@link ByteBuffer} view of the {@code $getter} field. */") + println("$indent/** Returns a {@link ByteBuffer} view of the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, "ByteBuffer", it.size) println("${indent}public ByteBuffer $getter() { return $n$getter($ADDRESS); }") - println("$indent/** Decodes the null-terminated string stored in the {@code $getter} field. */") + println("$indent/** Decodes the null-terminated string stored in the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, "String", it.size) println("${indent}public String ${getter}String() { return $n${getter}String($ADDRESS); }") } else { val bufferType = it.primitiveMapping.toPointer.javaMethodName - println("$indent/** Returns a {@link $bufferType} view of the {@code $getter} field. */") + println("$indent/** Returns a {@link $bufferType} view of the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, bufferType, it.size) println("${indent}public $bufferType $getter() { return $n$getter($ADDRESS); }") - println("$indent/** Returns the value at the specified index of the {@code $getter} field. */") + println("$indent/** Returns the value at the specified index of the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, it.nativeType.nativeMethodType) println("${indent}public ${it.nativeType.nativeMethodType} $getter(int index) { return $n$getter($ADDRESS, index); }") } } else if (it.nativeType is CharSequenceType) { - println("$indent/** Returns a {@link ByteBuffer} view of the null-terminated string pointed to by the {@code $getter} field. */") + println("$indent/** Returns a {@link ByteBuffer} view of the null-terminated string pointed to by the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, "ByteBuffer") println("${indent}public ByteBuffer $getter() { return $n$getter($ADDRESS); }") - println("$indent/** Decodes the null-terminated string pointed to by the {@code $getter} field. */") + println("$indent/** Decodes the null-terminated string pointed to by the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, "String") println("${indent}public String ${getter}String() { return $n${getter}String($ADDRESS); }") } else if (it.nativeType.isPointerData) { @@ -1715,19 +1797,19 @@ ${validations.joinToString("\n")} if (it is StructMemberBuffer) { if (getReferenceMember(it.name) == null) { println("""$indent/** -$indent * Returns a {@link $returnType.Buffer} view of the struct array pointed to by the {@code $getter} field. +$indent * Returns a {@link $returnType.Buffer} view of the struct array pointed to by the {@code $member} field. $indent * $indent * @param $BUFFER_CAPACITY_PARAM the number of elements in the returned buffer $indent */""") generateGetterAnnotations(indent, overrides, it, "$returnType.Buffer") println("${indent}public $returnType.Buffer $getter(int $BUFFER_CAPACITY_PARAM) { return $n$getter($ADDRESS, $BUFFER_CAPACITY_PARAM); }") } else { - println("$indent/** Returns a {@link $returnType.Buffer} view of the struct array pointed to by the {@code $getter} field. */") + println("$indent/** Returns a {@link $returnType.Buffer} view of the struct array pointed to by the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, "$returnType.Buffer") println("${indent}public $returnType.Buffer $getter() { return $n$getter($ADDRESS); }") } } else { - println("$indent/** Returns a {@link $returnType} view of the struct pointed to by the {@code $getter} field. */") + println("$indent/** Returns a {@link $returnType} view of the struct pointed to by the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, returnType) println("${indent}public $returnType $getter() { return $n$getter($ADDRESS); }") } @@ -1735,14 +1817,14 @@ $indent */""") if (getReferenceMember(it.name) == null) { println( """$indent/** -$indent * Returns a {@link $returnType} view of the data pointed to by the {@code $getter} field. +$indent * Returns a {@link $returnType} view of the data pointed to by the {@code $member} field. $indent * $indent * @param $BUFFER_CAPACITY_PARAM the number of elements in the returned buffer $indent */""") generateGetterAnnotations(indent, overrides, it, returnType) println("${indent}public $returnType $getter(int $BUFFER_CAPACITY_PARAM) { return $n$getter($ADDRESS, $BUFFER_CAPACITY_PARAM); }") } else { - println("$indent/** Returns a {@link $returnType} view of the data pointed to by the {@code $getter} field. */") + println("$indent/** Returns a {@link $returnType} view of the data pointed to by the {@code $member} field. */") generateGetterAnnotations(indent, overrides, it, returnType) println("${indent}public $returnType $getter() { return $n$getter($ADDRESS); }") } From 82f02e52b8f109ab3e2313a18549027fb3dcd8fe Mon Sep 17 00:00:00 2001 From: Ioannis Tsakpinis Date: Wed, 10 Jan 2018 01:16:38 +0200 Subject: [PATCH 4/4] feat: use nullability information in tests/demos --- .../org/lwjgl/demo/assimp/HelloAssimp.java | 6 +- .../test/java/org/lwjgl/demo/glfw/Events.java | 22 +++---- .../test/java/org/lwjgl/demo/glfw/Gears.java | 6 +- .../org/lwjgl/demo/glfw/MultipleWindows.java | 4 +- .../java/org/lwjgl/demo/glfw/Threads.java | 3 +- .../test/java/org/lwjgl/demo/nanovg/Demo.java | 3 - .../org/lwjgl/demo/nanovg/ExampleFBO.java | 3 +- .../org/lwjgl/demo/nanovg/ExampleGL2.java | 4 +- .../org/lwjgl/demo/nanovg/ExampleGL3.java | 4 +- .../java/org/lwjgl/demo/nanovg/SVGDemo.java | 15 +++-- .../java/org/lwjgl/demo/nuklear/GLFWDemo.java | 64 +++++++++---------- .../java/org/lwjgl/demo/openal/EFXUtil.java | 4 +- .../java/org/lwjgl/demo/openal/HRTFDemo.java | 21 +++--- .../org/lwjgl/demo/openal/OpenALInfo.java | 6 +- .../lwjgl/demo/opencl/CLGLInteropDemo.java | 4 +- .../org/lwjgl/demo/opencl/Mandelbrot.java | 15 +++-- .../java/org/lwjgl/demo/ovr/HelloLibOVR.java | 5 +- .../java/org/lwjgl/demo/stb/FontDemo.java | 5 +- .../test/java/org/lwjgl/demo/stb/Image.java | 7 +- .../test/java/org/lwjgl/demo/stb/Vorbis.java | 5 +- .../lwjgl/demo/system/jawt/LWJGLCanvas.java | 9 ++- .../org/lwjgl/demo/util/lmdb/LMDBDemo.java | 5 +- .../java/org/lwjgl/demo/util/lmdb/MTest.java | 22 +++---- .../org/lwjgl/demo/util/nfd/HelloNFD.java | 5 +- .../lwjgl/demo/util/par/ParShapesDemo.java | 8 +-- .../lwjgl/demo/util/tinyfd/HelloTinyFD.java | 5 +- .../lwjgl/demo/util/tootle/HelloTootle.java | 21 +++--- .../lwjgl/demo/util/xxhash/XXHashDemo.java | 2 +- .../org/lwjgl/demo/util/yoga/HolyGrail.java | 7 +- .../org/lwjgl/demo/vulkan/HelloVulkan.java | 55 ++++++++-------- .../test/java/org/lwjgl/opencl/CLTest.java | 2 + .../java/org/lwjgl/system/MemoryUtilTest.java | 10 +-- .../test/java/org/lwjgl/system/StackTest.java | 60 +++++++---------- .../test/java/org/lwjgl/util/par/ParTest.java | 61 ++++++++---------- .../org/lwjgl/util/yoga/YogaNodeTest.java | 4 +- 35 files changed, 240 insertions(+), 242 deletions(-) diff --git a/modules/core/src/test/java/org/lwjgl/demo/assimp/HelloAssimp.java b/modules/core/src/test/java/org/lwjgl/demo/assimp/HelloAssimp.java index 36a7ff9490..a89da74d30 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/assimp/HelloAssimp.java +++ b/modules/core/src/test/java/org/lwjgl/demo/assimp/HelloAssimp.java @@ -6,6 +6,8 @@ import org.lwjgl.assimp.*; +import java.util.*; + import static org.lwjgl.assimp.Assimp.*; public final class HelloAssimp { @@ -24,7 +26,7 @@ public static void main(String[] args) { System.out.println("\nImport formats:"); for (int i = 0; i < c; i++) { - AIImporterDesc desc = aiGetImportFormatDescription(i); + AIImporterDesc desc = Objects.requireNonNull(aiGetImportFormatDescription(i)); System.out.println("\t" + (i + 1) + ". " + desc.mNameString() + " (" + desc.mFileExtensionsString() + ")"); } @@ -32,7 +34,7 @@ public static void main(String[] args) { System.out.println("\nExport formats:"); for (int i = 0; i < c; i++) { - AIExportFormatDesc desc = aiGetExportFormatDescription(i); + AIExportFormatDesc desc = Objects.requireNonNull(aiGetExportFormatDescription(i)); System.out.println("\t" + (i + 1) + ". " + desc.descriptionString() + " (" + desc.fileExtensionString() + ")"); } } diff --git a/modules/core/src/test/java/org/lwjgl/demo/glfw/Events.java b/modules/core/src/test/java/org/lwjgl/demo/glfw/Events.java index 122edcd14a..313262df65 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/glfw/Events.java +++ b/modules/core/src/test/java/org/lwjgl/demo/glfw/Events.java @@ -53,7 +53,7 @@ public static void main(String[] args) { demo(); } finally { glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } } @@ -96,7 +96,7 @@ private static void demo() { long primaryMonitor = glfwGetPrimaryMonitor(); - PointerBuffer monitors = glfwGetMonitors(); + PointerBuffer monitors = Objects.requireNonNull(glfwGetMonitors()); for (int i = 0; i < monitors.remaining(); i++) { long monitor = monitors.get(i); @@ -122,8 +122,7 @@ private static void demo() { double MM_TO_INCH = 0.0393701; - GLFWVidMode mode = glfwGetVideoMode(monitor); - + GLFWVidMode mode = Objects.requireNonNull(glfwGetVideoMode(monitor)); System.out.format("\tCurrent mode : %d x %d @ %d Hz (%s, R%dG%dB%d)%n", mode.width(), mode.height(), mode.refreshRate(), @@ -188,14 +187,13 @@ private static void demo() { throw new RuntimeException(e); } - ByteBuffer pixels = stbi_load_from_memory(png, w, h, comp, 0); - + ByteBuffer pixels = Objects.requireNonNull(stbi_load_from_memory(png, w, h, comp, 0)); try (GLFWImage img = GLFWImage.malloc().set(w.get(0), h.get(0), pixels)) { cursor = glfwCreateCursor(img, 0, 8); glfwSetCursor(window, cursor); + } finally { + stbi_image_free(pixels); } - - stbi_image_free(pixels); } // Icons @@ -210,14 +208,14 @@ private static void demo() { } try (GLFWImage.Buffer icons = GLFWImage.malloc(2)) { - ByteBuffer pixels16 = stbi_load_from_memory(icon16, w, h, comp, 4); + ByteBuffer pixels16 = Objects.requireNonNull(stbi_load_from_memory(icon16, w, h, comp, 4)); icons .position(0) .width(w.get(0)) .height(h.get(0)) .pixels(pixels16); - ByteBuffer pixels32 = stbi_load_from_memory(icon32, w, h, comp, 4); + ByteBuffer pixels32 = Objects.requireNonNull(stbi_load_from_memory(icon32, w, h, comp, 4)); icons .position(1) .width(w.get(0)) @@ -319,8 +317,8 @@ private static void demo() { } glfwFreeCallbacks(window); - glfwSetJoystickCallback(null).free(); - glfwSetMonitorCallback(null).free(); + Objects.requireNonNull(glfwSetJoystickCallback(null)).free(); + Objects.requireNonNull(glfwSetMonitorCallback(null)).free(); glfwDestroyCursor(cursor); glfwDestroyWindow(window); diff --git a/modules/core/src/test/java/org/lwjgl/demo/glfw/Gears.java b/modules/core/src/test/java/org/lwjgl/demo/glfw/Gears.java index b441b31da7..4eded4b353 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/glfw/Gears.java +++ b/modules/core/src/test/java/org/lwjgl/demo/glfw/Gears.java @@ -10,6 +10,7 @@ import org.lwjgl.system.*; import java.nio.*; +import java.util.*; import static org.lwjgl.demo.glfw.GLFWUtil.*; import static org.lwjgl.glfw.Callbacks.*; @@ -106,8 +107,7 @@ private void init() { long monitor = glfwGetPrimaryMonitor(); - GLFWVidMode vidmode = glfwGetVideoMode(monitor); - + GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(monitor)); // Center window glfwSetWindowPos( window, @@ -223,7 +223,7 @@ private void destroy() { } glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/lwjgl/demo/glfw/MultipleWindows.java b/modules/core/src/test/java/org/lwjgl/demo/glfw/MultipleWindows.java index 7ba0c7456e..e5cc035c81 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/glfw/MultipleWindows.java +++ b/modules/core/src/test/java/org/lwjgl/demo/glfw/MultipleWindows.java @@ -32,7 +32,7 @@ public static void main(String[] args) { demo(); } finally { glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } } @@ -63,7 +63,7 @@ private static void demo() { glfwSetKeyCallback(handle, (windowHnd, key, scancode, action, mods) -> { if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) { Arrays.stream(windows) - .filter(w -> w != null) + .filter(Objects::nonNull) .forEach(w -> glfwSetWindowShouldClose(w.handle, true)); } }); diff --git a/modules/core/src/test/java/org/lwjgl/demo/glfw/Threads.java b/modules/core/src/test/java/org/lwjgl/demo/glfw/Threads.java index 545b5e7328..cba07e2d81 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/glfw/Threads.java +++ b/modules/core/src/test/java/org/lwjgl/demo/glfw/Threads.java @@ -7,6 +7,7 @@ import org.lwjgl.glfw.*; import org.lwjgl.opengl.*; +import java.util.*; import java.util.concurrent.*; import static org.lwjgl.glfw.Callbacks.*; @@ -87,7 +88,7 @@ public static void main(String[] args) { } glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } private static class GLFWThread extends Thread { diff --git a/modules/core/src/test/java/org/lwjgl/demo/nanovg/Demo.java b/modules/core/src/test/java/org/lwjgl/demo/nanovg/Demo.java index a63ec074c2..7b849a4007 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/nanovg/Demo.java +++ b/modules/core/src/test/java/org/lwjgl/demo/nanovg/Demo.java @@ -1338,9 +1338,6 @@ private static void flipHorizontal(ByteBuffer image, int w, int h, int stride) { static void saveScreenShot(int w, int h, boolean premult, String name) { ByteBuffer image = memAlloc(w * h * 4); - if (image == null) { - return; - } // TODO: Make this work for GLES glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image); diff --git a/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleFBO.java b/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleFBO.java index 6aa5c5a137..46b7c64798 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleFBO.java +++ b/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleFBO.java @@ -11,6 +11,7 @@ import org.lwjgl.system.*; import java.nio.*; +import java.util.*; import static java.lang.Math.*; import static org.lwjgl.glfw.Callbacks.*; @@ -253,7 +254,7 @@ public static void main(String[] args) { glfwFreeCallbacks(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleGL2.java b/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleGL2.java index 585bf1d95a..a0b59d7731 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleGL2.java +++ b/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleGL2.java @@ -7,6 +7,8 @@ import org.lwjgl.glfw.*; import org.lwjgl.opengl.*; +import java.util.*; + import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.nanovg.NanoVG.*; @@ -148,7 +150,7 @@ public static void main(String[] args) { glfwFreeCallbacks(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleGL3.java b/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleGL3.java index 02b80b746c..9287cb0f90 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleGL3.java +++ b/modules/core/src/test/java/org/lwjgl/demo/nanovg/ExampleGL3.java @@ -8,6 +8,8 @@ import org.lwjgl.opengl.*; import org.lwjgl.system.*; +import java.util.*; + import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.nanovg.NanoVG.*; @@ -189,7 +191,7 @@ public static void main(String[] args) { glfwFreeCallbacks(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/lwjgl/demo/nanovg/SVGDemo.java b/modules/core/src/test/java/org/lwjgl/demo/nanovg/SVGDemo.java index d40f747a67..e0169102d9 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/nanovg/SVGDemo.java +++ b/modules/core/src/test/java/org/lwjgl/demo/nanovg/SVGDemo.java @@ -13,6 +13,7 @@ import java.net.*; import java.nio.*; import java.nio.channels.*; +import java.util.*; import static java.lang.Math.*; import static org.lwjgl.demo.glfw.GLFWUtil.*; @@ -59,9 +60,6 @@ private SVGDemo(ByteBuffer svgData) { this.h = (int)svg.height(); image = memAlloc(w * h * 4); - if (image == null) { - throw new IllegalStateException("Failed to allocate image buffer."); - } System.out.format("Rasterizing image %d x %d...", w, h); long t = System.nanoTime(); @@ -93,7 +91,11 @@ public static void main(String[] args) { int c; while ((c = rbc.read(svgData)) != -1) { if (c == 0) { - svgData = memRealloc(svgData, (svgData.capacity() * 3) >> 1); + ByteBuffer newData = memRealloc(svgData, (svgData.capacity() * 3) >> 1); + if (newData == null) { + throw new OutOfMemoryError(); + } + svgData = newData; } } } @@ -157,8 +159,7 @@ private void init() { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); - GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); - + GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor())); ww = max(800, min(w, vidmode.width() - 160)); wh = max(600, min(h, vidmode.height() - 120)); @@ -350,7 +351,7 @@ private void destroy() { glfwFreeCallbacks(window); glfwDestroyWindow(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } } diff --git a/modules/core/src/test/java/org/lwjgl/demo/nuklear/GLFWDemo.java b/modules/core/src/test/java/org/lwjgl/demo/nuklear/GLFWDemo.java index 4b9379a5ec..84d01287a1 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/nuklear/GLFWDemo.java +++ b/modules/core/src/test/java/org/lwjgl/demo/nuklear/GLFWDemo.java @@ -12,6 +12,7 @@ import java.io.*; import java.nio.*; +import java.util.*; import static org.lwjgl.demo.util.IOUtil.*; import static org.lwjgl.glfw.Callbacks.*; @@ -49,15 +50,7 @@ public class GLFWDemo { static { ALLOCATOR = NkAllocator.create(); - ALLOCATOR.alloc((handle, old, size) -> { - long mem = nmemAlloc(size); - if (mem == NULL) { - throw new OutOfMemoryError(); - } - - return mem; - - }); + ALLOCATOR.alloc((handle, old, size) -> nmemAllocChecked(size)); ALLOCATOR.mfree((handle, ptr) -> nmemFree(ptr)); VERTEX_LAYOUT = NkDrawVertexLayoutElement.create(4) @@ -289,7 +282,7 @@ private void run() { debugProc.free(); } glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } private void setupContext() { @@ -487,25 +480,26 @@ private NkContext setupWindow(long win) { }); nk_init(ctx, ALLOCATOR, null); - ctx.clip().copy((handle, text, len) -> { - if (len == 0) { - return; - } + ctx.clip() + .copy((handle, text, len) -> { + if (len == 0) { + return; + } - try (MemoryStack stack = stackPush()) { - ByteBuffer str = stack.malloc(len + 1); - memCopy(text, memAddress(str), len); - str.put(len, (byte)0); + try (MemoryStack stack = stackPush()) { + ByteBuffer str = stack.malloc(len + 1); + memCopy(text, memAddress(str), len); + str.put(len, (byte)0); - glfwSetClipboardString(win, str); - } - }); - ctx.clip().paste((handle, edit) -> { - long text = nglfwGetClipboardString(win); - if (text != NULL) { - nnk_textedit_paste(edit, text, nnk_strlen(text)); - } - }); + glfwSetClipboardString(win, str); + } + }) + .paste((handle, edit) -> { + long text = nglfwGetClipboardString(win); + if (text != NULL) { + nnk_textedit_paste(edit, text, nnk_strlen(text)); + } + }); setupContext(); return ctx; } @@ -578,8 +572,8 @@ private void render(int AA, int max_vertex_buffer, int max_element_buffer) { glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, GL_STREAM_DRAW); // load draw vertices & elements directly into vertex + element buffer - ByteBuffer vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY, max_vertex_buffer, null); - ByteBuffer elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY, max_element_buffer, null); + ByteBuffer vertices = Objects.requireNonNull(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY, max_vertex_buffer, null)); + ByteBuffer elements = Objects.requireNonNull(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY, max_element_buffer, null)); try (MemoryStack stack = stackPush()) { // fill convert configuration NkConvertConfig config = NkConvertConfig.callocStack(stack) @@ -650,17 +644,17 @@ private void destroy() { } private void shutdown() { - ctx.clip().copy().free(); - ctx.clip().paste().free(); + Objects.requireNonNull(ctx.clip().copy()).free(); + Objects.requireNonNull(ctx.clip().paste()).free(); nk_free(ctx); destroy(); - default_font.query().free(); - default_font.width().free(); + Objects.requireNonNull(default_font.query()).free(); + Objects.requireNonNull(default_font.width()).free(); calc.numberFilter.free(); - ALLOCATOR.alloc().free(); - ALLOCATOR.mfree().free(); + Objects.requireNonNull(ALLOCATOR.alloc()).free(); + Objects.requireNonNull(ALLOCATOR.mfree()).free(); } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/lwjgl/demo/openal/EFXUtil.java b/modules/core/src/test/java/org/lwjgl/demo/openal/EFXUtil.java index a4498ed83a..f79cf5bc9e 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/openal/EFXUtil.java +++ b/modules/core/src/test/java/org/lwjgl/demo/openal/EFXUtil.java @@ -4,8 +4,6 @@ */ package org.lwjgl.demo.openal; -import org.lwjgl.openal.*; - import static org.lwjgl.openal.AL10.*; import static org.lwjgl.openal.EXTEfx.*; @@ -182,7 +180,7 @@ private static boolean testSupportGeneric(int objectType, int typeValue) { } } else if (genError == AL_OUT_OF_MEMORY) { - throw new RuntimeException(AL10.alGetString(genError)); + throw new RuntimeException(alGetString(AL_OUT_OF_MEMORY)); } return supported; diff --git a/modules/core/src/test/java/org/lwjgl/demo/openal/HRTFDemo.java b/modules/core/src/test/java/org/lwjgl/demo/openal/HRTFDemo.java index 4e265878fa..d2b418578a 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/openal/HRTFDemo.java +++ b/modules/core/src/test/java/org/lwjgl/demo/openal/HRTFDemo.java @@ -9,6 +9,7 @@ import org.lwjgl.stb.*; import java.nio.*; +import java.util.*; import static java.lang.Math.*; import static org.lwjgl.openal.AL10.*; @@ -60,7 +61,7 @@ public static void main(String[] args) { soundname = args[0]; } - /* Enumerate available HRTFs, and reset the device using one. */ + /* Enumerate available HRTFs, and reset the device using one. */ int num_hrtf = alcGetInteger(device, ALC_NUM_HRTF_SPECIFIERS_SOFT); if (num_hrtf == 0) { System.out.println("No HRTFs found"); @@ -69,10 +70,10 @@ public static void main(String[] args) { System.out.println("Available HRTFs:"); for (int i = 0; i < num_hrtf; i++) { - String name = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i); + String name = Objects.requireNonNull(alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i)); System.out.format(" %d: %s\n", i, name); - /* Check if this is the HRTF the user requested. */ + /* Check if this is the HRTF the user requested. */ if (hrtfname != null && name.equals(hrtfname)) { index = i; } @@ -100,7 +101,7 @@ public static void main(String[] args) { System.out.format("Failed to reset device: %s\n", alcGetString(device, alcGetError(device))); } - /* Check if HRTF is enabled, and show which is being used. */ + /* Check if HRTF is enabled, and show which is being used. */ int hrtf_state = alcGetInteger(device, ALC_HRTF_SOFT); if (hrtf_state == 0) { System.out.format("HRTF not enabled!\n"); @@ -109,14 +110,14 @@ public static void main(String[] args) { System.out.format("HRTF enabled, using %s\n", name); } - /* Load the sound into a buffer. */ + /* Load the sound into a buffer. */ int buffer = alGenBuffers(); try (STBVorbisInfo info = STBVorbisInfo.malloc()) { ShortBuffer pcm = ALCDemo.readVorbis(soundname, 32 * 1024, info); alBufferData(buffer, AL_FORMAT_MONO16, pcm, info.sample_rate()); } - /* Create the source to play the sound with. */ + /* Create the source to play the sound with. */ int source = alGenSources(); alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); alSource3f(source, AL_POSITION, 0.0f, 0.0f, -1.0f); @@ -126,7 +127,7 @@ public static void main(String[] args) { throw new IllegalStateException("Failed to setup sound source"); } - /* Play the sound until it finishes. */ + /* Play the sound until it finishes. */ double angle = 0.0; alSourcePlay(source); int state; @@ -137,16 +138,16 @@ public static void main(String[] args) { e.printStackTrace(); } - /* Rotate the source around the listener by about 1/8th cycle per second. + /* Rotate the source around the listener by about 1/8th cycle per second. * Only affects mono sounds. - */ + */ angle += (Math.PI / 4 / 100); alSource3f(source, AL_POSITION, (float)sin(angle), 0.0f, -(float)cos(angle)); state = alGetSourcei(source, AL_SOURCE_STATE); } while (alGetError() == AL_NO_ERROR && state == AL_PLAYING); - /* All done. Delete resources, and close OpenAL. */ + /* All done. Delete resources, and close OpenAL. */ alDeleteSources(source); alDeleteBuffers(buffer); diff --git a/modules/core/src/test/java/org/lwjgl/demo/openal/OpenALInfo.java b/modules/core/src/test/java/org/lwjgl/demo/openal/OpenALInfo.java index f14e48e73e..59781fba1d 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/openal/OpenALInfo.java +++ b/modules/core/src/test/java/org/lwjgl/demo/openal/OpenALInfo.java @@ -77,7 +77,7 @@ private static void printALCInfo(long device, ALCCapabilities caps) { System.out.println("ALC version: " + majorVersion + "." + minorVersion); System.out.println("ALC extensions:"); - String[] extensions = alcGetString(device, ALC_EXTENSIONS).split(" "); + String[] extensions = Objects.requireNonNull(alcGetString(device, ALC_EXTENSIONS)).split(" "); checkALCError(device); for (String extension : extensions) { if (extension.trim().isEmpty()) { @@ -92,7 +92,7 @@ private static void printALInfo() { System.out.println("OpenAL renderer string: " + alGetString(AL_RENDERER)); System.out.println("OpenAL version string: " + alGetString(AL_VERSION)); System.out.println("AL extensions:"); - String[] extensions = alGetString(AL_EXTENSIONS).split(" "); + String[] extensions = Objects.requireNonNull(alGetString(AL_EXTENSIONS)).split(" "); for (String extension : extensions) { if (extension.trim().isEmpty()) { continue; @@ -146,7 +146,7 @@ private static void printEFXInfo(long device) { } private static void printDevices(int which, String kind) { - List devices = ALUtil.getStringList(NULL, which); + List devices = Objects.requireNonNull(ALUtil.getStringList(NULL, which)); System.out.println("Available " + kind + " devices: "); for (String d : devices) { System.out.println(" " + d); diff --git a/modules/core/src/test/java/org/lwjgl/demo/opencl/CLGLInteropDemo.java b/modules/core/src/test/java/org/lwjgl/demo/opencl/CLGLInteropDemo.java index e138e477b1..cc21eca231 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/opencl/CLGLInteropDemo.java +++ b/modules/core/src/test/java/org/lwjgl/demo/opencl/CLGLInteropDemo.java @@ -127,7 +127,7 @@ public static void main(String... args) { throw new IllegalStateException("No OpenCL platform found that supports OpenGL context sharing."); } - Collections.sort(platforms, (p1, p2) -> { + platforms.sort((p1, p2) -> { // Prefer platforms that support GPU devices boolean gpu1 = !getDevices(p1, CL_DEVICE_TYPE_GPU).isEmpty(); boolean gpu2 = !getDevices(p2, CL_DEVICE_TYPE_GPU).isEmpty(); @@ -224,7 +224,7 @@ public void run() { CL.destroy(); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); System.out.println("GAME OVER!"); } diff --git a/modules/core/src/test/java/org/lwjgl/demo/opencl/Mandelbrot.java b/modules/core/src/test/java/org/lwjgl/demo/opencl/Mandelbrot.java index d56bef3012..b05b6c3932 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/opencl/Mandelbrot.java +++ b/modules/core/src/test/java/org/lwjgl/demo/opencl/Mandelbrot.java @@ -267,13 +267,14 @@ public Mandelbrot(long platform, CLCapabilities platformCaps, GLFWWindow window, vbo = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, stackPush().floats( - 0.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f - ), GL_STATIC_DRAW); - stackPop(); + try (MemoryStack stack = stackPush()) { + glBufferData(GL_ARRAY_BUFFER, stack.floats( + 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f + ), GL_STATIC_DRAW); + } vsh = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vsh, diff --git a/modules/core/src/test/java/org/lwjgl/demo/ovr/HelloLibOVR.java b/modules/core/src/test/java/org/lwjgl/demo/ovr/HelloLibOVR.java index a7eadf7376..881a257b7a 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/ovr/HelloLibOVR.java +++ b/modules/core/src/test/java/org/lwjgl/demo/ovr/HelloLibOVR.java @@ -24,13 +24,12 @@ public static void main(String[] args) { System.out.println("OVRDetectResult.IsOculusServiceRunning = " + detect.IsOculusServiceRunning()); } - OVRLogCallback callback; + OVRLogCallback callback = OVRLogCallback.create((userData, level, message) -> System.out.println("LibOVR [" + level + "] " + memASCII(message))); try ( OVRInitParams initParams = OVRInitParams.calloc() - .LogCallback((userData, level, message) -> System.out.println("LibOVR [" + level + "] " + memASCII(message))) + .LogCallback(callback) .Flags(ovrInit_Debug) ) { - callback = initParams.LogCallback(); System.out.println("ovr_Initialize = " + ovr_Initialize(initParams)); } diff --git a/modules/core/src/test/java/org/lwjgl/demo/stb/FontDemo.java b/modules/core/src/test/java/org/lwjgl/demo/stb/FontDemo.java index a6a72c7f14..a41c4e198e 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/stb/FontDemo.java +++ b/modules/core/src/test/java/org/lwjgl/demo/stb/FontDemo.java @@ -10,6 +10,7 @@ import java.io.*; import java.nio.*; +import java.util.*; import java.util.regex.*; import static java.lang.Math.*; @@ -242,7 +243,7 @@ private void init(String title) { }); // Center window - GLFWVidMode vidmode = glfwGetVideoMode(monitor); + GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(monitor)); glfwSetWindowPos( window, @@ -285,7 +286,7 @@ private void destroy() { glfwFreeCallbacks(window); glfwDestroyWindow(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/lwjgl/demo/stb/Image.java b/modules/core/src/test/java/org/lwjgl/demo/stb/Image.java index 47ab3d3091..de5ab4ff2b 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/stb/Image.java +++ b/modules/core/src/test/java/org/lwjgl/demo/stb/Image.java @@ -10,6 +10,7 @@ import java.io.*; import java.nio.*; +import java.util.*; import static java.lang.Math.*; import static org.lwjgl.demo.glfw.GLFWUtil.*; @@ -59,6 +60,8 @@ private Image(String imagePath) { // We don't need this for this demo, just testing the API. if (!stbi_info_from_memory(imageBuffer, w, h, comp)) { throw new RuntimeException("Failed to read image information: " + stbi_failure_reason()); + } else { + System.out.println("OK with reason: " + stbi_failure_reason()); } System.out.println("Image width: " + w.get(0)); @@ -129,7 +132,7 @@ private void init() { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); - GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor())); ww = max(800, min(w, vidmode.width() - 160)); wh = max(600, min(h, vidmode.height() - 120)); @@ -331,7 +334,7 @@ private void destroy() { glfwFreeCallbacks(window); glfwDestroyWindow(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/lwjgl/demo/stb/Vorbis.java b/modules/core/src/test/java/org/lwjgl/demo/stb/Vorbis.java index 67deeadd91..a05e6ae176 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/stb/Vorbis.java +++ b/modules/core/src/test/java/org/lwjgl/demo/stb/Vorbis.java @@ -13,6 +13,7 @@ import java.io.*; import java.nio.*; +import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; @@ -143,7 +144,7 @@ private Vorbis(String filePath) { } // Center window - GLFWVidMode vidmode = glfwGetVideoMode(monitor); + GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(monitor)); glfwSetWindowPos( window, (vidmode.width() - framebufferW) / 2, @@ -257,7 +258,7 @@ public void close() { } glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } public static void main(String[] args) { diff --git a/modules/core/src/test/java/org/lwjgl/demo/system/jawt/LWJGLCanvas.java b/modules/core/src/test/java/org/lwjgl/demo/system/jawt/LWJGLCanvas.java index f2c3d82f99..37f23eeb93 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/system/jawt/LWJGLCanvas.java +++ b/modules/core/src/test/java/org/lwjgl/demo/system/jawt/LWJGLCanvas.java @@ -38,7 +38,7 @@ public LWJGLCanvas() { awt = JAWT.calloc(); awt.version(JAWT_VERSION_1_4); if (!JAWT_GetAWT(awt)) { - throw new RuntimeException("GetAWT failed"); + throw new IllegalStateException("GetAWT failed"); } gears = new AbstractGears(); @@ -54,19 +54,22 @@ public void paint(Graphics g) { // Get the drawing surface JAWTDrawingSurface ds = JAWT_GetDrawingSurface(awt.GetDrawingSurface(), this); if (ds == null) { - throw new RuntimeException("awt->GetDrawingSurface() failed"); + throw new IllegalStateException("awt->GetDrawingSurface() failed"); } try { // Lock the drawing surface int lock = JAWT_DrawingSurface_Lock(ds.Lock(), ds); if ((lock & JAWT_LOCK_ERROR) != 0) { - throw new RuntimeException("ds->Lock() failed"); + throw new IllegalStateException("ds->Lock() failed"); } try { // Get the drawing surface info JAWTDrawingSurfaceInfo dsi = JAWT_DrawingSurface_GetDrawingSurfaceInfo(ds.GetDrawingSurfaceInfo(), ds); + if (dsi == null) { + throw new IllegalStateException("ds->GetDrawingSurfaceInfo() failed"); + } try { // Get the platform-specific drawing info diff --git a/modules/core/src/test/java/org/lwjgl/demo/util/lmdb/LMDBDemo.java b/modules/core/src/test/java/org/lwjgl/demo/util/lmdb/LMDBDemo.java index 5794738fd9..fda680f9c5 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/util/lmdb/LMDBDemo.java +++ b/modules/core/src/test/java/org/lwjgl/demo/util/lmdb/LMDBDemo.java @@ -9,6 +9,7 @@ import org.lwjgl.util.lmdb.*; import java.io.*; +import java.util.*; import static org.lwjgl.demo.util.lmdb.LMDBUtil.*; import static org.lwjgl.system.MemoryStack.*; @@ -80,7 +81,7 @@ private static void putZeroCopy(long env, int dbi, int key, String value) { // no copy, LMDB updates dv.mv_data with a pointer to the database E(mdb_put(txn, dbi, kv, dv, MDB_RESERVE)); // value is encoded directly to the memory-mapped file - memUTF8(value, false, dv.mv_data()); + memUTF8(value, false, Objects.requireNonNull(dv.mv_data())); return null; }); @@ -94,7 +95,7 @@ private static String get(long env, int dbi, int key) { MDBVal dv = MDBVal.callocStack(stack); E(mdb_get(txn, dbi, kv, dv)); - return memUTF8(dv.mv_data()); + return memUTF8(Objects.requireNonNull(dv.mv_data())); }); } diff --git a/modules/core/src/test/java/org/lwjgl/demo/util/lmdb/MTest.java b/modules/core/src/test/java/org/lwjgl/demo/util/lmdb/MTest.java index 8faaac1b15..400669d160 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/util/lmdb/MTest.java +++ b/modules/core/src/test/java/org/lwjgl/demo/util/lmdb/MTest.java @@ -50,16 +50,16 @@ private static void CHECK(int rc, boolean test, String msg) { private static void print_p(MDBVal key, MDBVal data) { System.out.printf( "key: %X %s, data: %X %s\n", - memGetAddress(key.address() + MDBVal.MV_DATA), memASCII(key.mv_data()), - memGetAddress(data.address() + MDBVal.MV_DATA), memASCII(data.mv_data()) + memGetAddress(key.address() + MDBVal.MV_DATA), memASCII(Objects.requireNonNull(key.mv_data())), + memGetAddress(data.address() + MDBVal.MV_DATA), memASCII(Objects.requireNonNull(data.mv_data())) ); } private static void print(MDBVal key, MDBVal data) { System.out.printf( "key: %s, data: %s\n", - memASCII(key.mv_data()), - memASCII(data.mv_data()) + memASCII(Objects.requireNonNull(key.mv_data())), + memASCII(Objects.requireNonNull(data.mv_data())) ); } @@ -162,20 +162,20 @@ public static void main(String[] args) { txn = pp.get(0); E(mdb_cursor_open(txn, dbi, pp)); cursor = pp.get(0); - System.out.printf("Cursor next\n"); + System.out.print("Cursor next\n"); while ((rc = mdb_cursor_get(cursor, key, data, MDB_NEXT)) == 0) { print(key, data); } CHECK(rc, rc == MDB_NOTFOUND, "mdb_cursor_get"); - System.out.printf("Cursor last\n"); + System.out.print("Cursor last\n"); E(mdb_cursor_get(cursor, key, data, MDB_LAST)); print(key, data); - System.out.printf("Cursor prev\n"); + System.out.print("Cursor prev\n"); while ((rc = mdb_cursor_get(cursor, key, data, MDB_PREV)) == 0) { print(key, data); } CHECK(rc, rc == MDB_NOTFOUND, "mdb_cursor_get"); - System.out.printf("Cursor last/prev\n"); + System.out.print("Cursor last/prev\n"); E(mdb_cursor_get(cursor, key, data, MDB_LAST)); print(key, data); E(mdb_cursor_get(cursor, key, data, MDB_PREV)); @@ -184,7 +184,7 @@ public static void main(String[] args) { mdb_cursor_close(cursor); mdb_txn_abort(txn); - System.out.printf("Deleting with cursor\n"); + System.out.print("Deleting with cursor\n"); E(mdb_txn_begin(env, NULL, 0, pp)); txn = pp.get(0); E(mdb_cursor_open(txn, dbi, pp)); @@ -197,7 +197,7 @@ public static void main(String[] args) { E(mdb_del(txn, dbi, key, null)); } - System.out.printf("Restarting cursor in txn\n"); + System.out.print("Restarting cursor in txn\n"); for (int op = MDB_FIRST, i = 0; i <= 32; op = MDB_NEXT, i++) { if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, key, data, op))) { break; @@ -207,7 +207,7 @@ public static void main(String[] args) { mdb_cursor_close(cur2); E(mdb_txn_commit(txn)); - System.out.printf("Restarting cursor outside txn\n"); + System.out.print("Restarting cursor outside txn\n"); E(mdb_txn_begin(env, NULL, 0, pp)); txn = pp.get(0); E(mdb_cursor_open(txn, dbi, pp)); diff --git a/modules/core/src/test/java/org/lwjgl/demo/util/nfd/HelloNFD.java b/modules/core/src/test/java/org/lwjgl/demo/util/nfd/HelloNFD.java index ae303c8d3e..a23cb8dc52 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/util/nfd/HelloNFD.java +++ b/modules/core/src/test/java/org/lwjgl/demo/util/nfd/HelloNFD.java @@ -11,6 +11,7 @@ import org.lwjgl.util.nfd.*; import java.nio.*; +import java.util.*; import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.GLFW.*; @@ -73,7 +74,7 @@ public static void main(String[] args) { }); // Center window - GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor())); glfwSetWindowPos( window, (vidmode.width() - 300) / 2, @@ -100,7 +101,7 @@ public static void main(String[] args) { glfwDestroyWindow(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } private static void openSingle() { diff --git a/modules/core/src/test/java/org/lwjgl/demo/util/par/ParShapesDemo.java b/modules/core/src/test/java/org/lwjgl/demo/util/par/ParShapesDemo.java index 7bf2a06004..7c55229f4e 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/util/par/ParShapesDemo.java +++ b/modules/core/src/test/java/org/lwjgl/demo/util/par/ParShapesDemo.java @@ -11,6 +11,7 @@ import org.lwjgl.util.par.*; import java.nio.*; +import java.util.*; import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.GLFW.*; @@ -171,10 +172,7 @@ private void init() { }); // center window - long monitor = glfwGetPrimaryMonitor(); - - GLFWVidMode vidmode = glfwGetVideoMode(monitor); - + GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor())); glfwSetWindowPos( window, (vidmode.width() - width) / 2, @@ -484,7 +482,7 @@ private void cleanup() { glfwFreeCallbacks(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); if (debugCB != null) { debugCB.free(); diff --git a/modules/core/src/test/java/org/lwjgl/demo/util/tinyfd/HelloTinyFD.java b/modules/core/src/test/java/org/lwjgl/demo/util/tinyfd/HelloTinyFD.java index 80773fcf2d..d6c7674aa7 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/util/tinyfd/HelloTinyFD.java +++ b/modules/core/src/test/java/org/lwjgl/demo/util/tinyfd/HelloTinyFD.java @@ -10,6 +10,7 @@ import org.lwjgl.system.*; import java.nio.*; +import java.util.*; import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.GLFW.*; @@ -96,7 +97,7 @@ public static void main(String[] args) { }); // Center window - GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor())); glfwSetWindowPos( window, (vidmode.width() - 300) / 2, @@ -127,7 +128,7 @@ public static void main(String[] args) { glfwDestroyWindow(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/lwjgl/demo/util/tootle/HelloTootle.java b/modules/core/src/test/java/org/lwjgl/demo/util/tootle/HelloTootle.java index d27cab5f26..a40c36a0d8 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/util/tootle/HelloTootle.java +++ b/modules/core/src/test/java/org/lwjgl/demo/util/tootle/HelloTootle.java @@ -15,6 +15,7 @@ import java.nio.*; import java.nio.file.*; +import java.util.*; import java.util.concurrent.*; import static java.lang.Math.*; @@ -276,10 +277,7 @@ private HelloTootle() { }); // center window - long monitor = glfwGetPrimaryMonitor(); - - GLFWVidMode vidmode = glfwGetVideoMode(monitor); - + GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor())); glfwSetWindowPos( window, (vidmode.width() - width) / 2, @@ -379,6 +377,9 @@ private HelloTootle() { gpuTimer = new GPUTimer(); propertyStore = aiCreatePropertyStore(); + if (propertyStore == null) { + throw new OutOfMemoryError(); + } aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_PTV_NORMALIZE, 1); } @@ -425,7 +426,7 @@ private void cleanup() { glfwFreeCallbacks(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); if (debugCB != null) { debugCB.free(); @@ -952,7 +953,7 @@ private void importMesh() { if (scene != null) { try { PointerBuffer meshes = scene.mMeshes(); - for (int i = 0; i < meshes.remaining(); i++) { + for (int i = 0; i < (meshes == null ? 0 : meshes.remaining()); i++) { AIMesh aiMesh = AIMesh.create(meshes.get(i)); try { ParShapesMesh mesh = copyAssimpToParShapes(aiMesh); @@ -978,13 +979,17 @@ private void importMesh() { private static ParShapesMesh copyAssimpToParShapes(AIMesh aiMesh) { ParShapesMesh mesh = par_shapes_create_empty(); + if (mesh == null) { + throw new OutOfMemoryError(); + } memPutInt(mesh.address() + ParShapesMesh.NPOINTS, aiMesh.mNumVertices()); memPutInt(mesh.address() + ParShapesMesh.NTRIANGLES, aiMesh.mNumFaces()); memPutAddress(mesh.address() + ParShapesMesh.POINTS, memAddress(copyAssimpToParShapes(aiMesh.mVertices()))); - if (aiMesh.mNormals() != null) { - memPutAddress(mesh.address() + ParShapesMesh.NORMALS, memAddress(copyAssimpToParShapes(aiMesh.mNormals()))); + AIVector3D.Buffer normals = aiMesh.mNormals(); + if (normals != null) { + memPutAddress(mesh.address() + ParShapesMesh.NORMALS, memAddress(copyAssimpToParShapes(normals))); } AIFace.Buffer faces = aiMesh.mFaces(); diff --git a/modules/core/src/test/java/org/lwjgl/demo/util/xxhash/XXHashDemo.java b/modules/core/src/test/java/org/lwjgl/demo/util/xxhash/XXHashDemo.java index 8b343d22d0..eef1d94071 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/util/xxhash/XXHashDemo.java +++ b/modules/core/src/test/java/org/lwjgl/demo/util/xxhash/XXHashDemo.java @@ -46,7 +46,7 @@ public static void main(String[] args) { String resource = "lwjgl32.png"; try { // Allocate and free using the API - XXH64State state = XXH64_createState(); + XXH64State state = Objects.requireNonNull(XXH64_createState()); try { hash64 = streamingHash(buffer, resource, state, SEED); System.out.format("streaming 64-bit hash: 0x%X (%s, malloc)\n", hash64, resource); diff --git a/modules/core/src/test/java/org/lwjgl/demo/util/yoga/HolyGrail.java b/modules/core/src/test/java/org/lwjgl/demo/util/yoga/HolyGrail.java index 9cbfdbbc74..01e333878f 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/util/yoga/HolyGrail.java +++ b/modules/core/src/test/java/org/lwjgl/demo/util/yoga/HolyGrail.java @@ -11,6 +11,7 @@ import org.lwjgl.util.yoga.*; import java.nio.*; +import java.util.*; import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.GLFW.*; @@ -64,10 +65,8 @@ private HolyGrail() { throw new RuntimeException("Failed to create the GLFW window"); } - long monitor = glfwGetPrimaryMonitor(); - GLFWVidMode vidmode = glfwGetVideoMode(monitor); - // Center window + GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor())); glfwSetWindowPos( window, (vidmode.width() - width) / 2, @@ -266,7 +265,7 @@ private void destroy() { } glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); } public static void main(String[] args) { diff --git a/modules/core/src/test/java/org/lwjgl/demo/vulkan/HelloVulkan.java b/modules/core/src/test/java/org/lwjgl/demo/vulkan/HelloVulkan.java index 8692816780..65c5a11ba8 100644 --- a/modules/core/src/test/java/org/lwjgl/demo/vulkan/HelloVulkan.java +++ b/modules/core/src/test/java/org/lwjgl/demo/vulkan/HelloVulkan.java @@ -10,6 +10,7 @@ import org.lwjgl.vulkan.*; import java.nio.*; +import java.util.*; import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.GLFW.*; @@ -299,13 +300,13 @@ private HelloVulkan() { type, memASCII(pLayerPrefix), messageCode, VkDebugReportCallbackEXT.getString(pMessage) ); - /* + /* * false indicates that layer should not bail-out of an - * API call that had validation failures. This may mean that the - * app dies inside the driver due to invalid parameter(s). - * That's what would happen without validation layers, so we'll - * keep that behavior here. - */ + * API call that had validation failures. This may mean that the + * app dies inside the driver due to invalid parameter(s). + * That's what would happen without validation layers, so we'll + * keep that behavior here. + */ return VK_FALSE; } ); @@ -454,20 +455,20 @@ private void demo_init_vk() { inst = new VkInstance(pp.get(0), inst_info); - /* Make initial call to query gpu_count, then second call for gpu info */ + /* Make initial call to query gpu_count, then second call for gpu info */ check(vkEnumeratePhysicalDevices(inst, ip, null)); if (ip.get(0) > 0) { PointerBuffer physical_devices = stack.mallocPointer(ip.get(0)); check(vkEnumeratePhysicalDevices(inst, ip, physical_devices)); - /* For tri demo we just grab the first physical device */ + /* For tri demo we just grab the first physical device */ gpu = new VkPhysicalDevice(physical_devices.get(0), inst); } else { throw new IllegalStateException("vkEnumeratePhysicalDevices reported zero accessible devices."); } - /* Look for device extensions */ + /* Look for device extensions */ boolean swapchainExtFound = false; check(vkEnumerateDeviceExtensionProperties(gpu, (String)null, ip, null)); @@ -912,15 +913,15 @@ private void demo_prepare_depth() { .height(height) .depth(1); - /* create image */ + /* create image */ check(vkCreateImage(device, image, null, lp)); depth.image = lp.get(0); - /* get memory requirements for this object */ + /* get memory requirements for this object */ VkMemoryRequirements mem_reqs = VkMemoryRequirements.mallocStack(stack); vkGetImageMemoryRequirements(device, depth.image, mem_reqs); - /* select memory size and type */ + /* select memory size and type */ VkMemoryAllocateInfo mem_alloc = VkMemoryAllocateInfo.mallocStack(stack) .sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO) .pNext(NULL) @@ -931,11 +932,11 @@ private void demo_prepare_depth() { mem_alloc); assert (pass); - /* allocate memory */ + /* allocate memory */ check(vkAllocateMemory(device, mem_alloc, null, lp)); depth.mem = lp.get(0); - /* bind memory */ + /* bind memory */ check(vkBindImageMemory(device, depth.image, depth.mem, 0)); demo_set_image_layout(depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, @@ -943,7 +944,7 @@ private void demo_prepare_depth() { VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 0); - /* create image view */ + /* create image view */ VkImageViewCreateInfo view = VkImageViewCreateInfo.callocStack(stack) .sType(VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO) .pNext(NULL) @@ -1020,11 +1021,11 @@ private void demo_prepare_texture_image( pass = memory_type_from_properties(mem_reqs.memoryTypeBits(), required_props, mem_alloc); assert (pass); - /* allocate memory */ + /* allocate memory */ check(vkAllocateMemory(device, mem_alloc, null, lp)); tex_obj.mem = lp.get(0); - /* bind memory */ + /* bind memory */ check(vkBindImageMemory(device, tex_obj.image, tex_obj.mem, 0)); if ((required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) { @@ -1050,13 +1051,13 @@ private void demo_prepare_texture_image( tex_obj.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; demo_set_image_layout(tex_obj.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, tex_obj.imageLayout, VK_ACCESS_HOST_WRITE_BIT); - /* setting the image layout does not reference the actual memory so no need - * to add a mem ref */ + /* setting the image layout does not reference the actual memory so no need + * to add a mem ref */ } } private void demo_destroy_texture_image(TextureObject tex_obj) { - /* clean up staging resources */ + /* clean up staging resources */ vkDestroyImage(device, tex_obj.image, null); vkFreeMemory(device, tex_obj.mem, null); } @@ -1093,13 +1094,13 @@ private void demo_prepare_textures() { for (int i = 0; i < DEMO_TEXTURE_COUNT; i++) { if ((props.linearTilingFeatures() & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0 && !USE_STAGING_BUFFER) { - /* Device can texture using linear textures */ + /* Device can texture using linear textures */ demo_prepare_texture_image( tex_colors[i], textures[i], VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); } else if ((props.optimalTilingFeatures() & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0) { - /* Must use staging buffer to copy linear texture to optimized */ + /* Must use staging buffer to copy linear texture to optimized */ TextureObject staging_texture = new TextureObject(); demo_prepare_texture_image( @@ -1165,7 +1166,7 @@ private void demo_prepare_textures() { demo_destroy_texture_image(staging_texture); } else { - /* Can't support VK_FORMAT_B8G8R8A8_UNORM !? */ + /* Can't support VK_FORMAT_B8G8R8A8_UNORM !? */ throw new IllegalStateException("No support for B8G8R8A8_UNORM as texture image format"); } @@ -1187,7 +1188,7 @@ private void demo_prepare_textures() { .borderColor(VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE) .unnormalizedCoordinates(false); - /* create sampler */ + /* create sampler */ check(vkCreateSampler(device, sampler, null, lp)); textures[i].sampler = lp.get(0); @@ -1210,7 +1211,7 @@ private void demo_prepare_textures() { .baseArrayLayer(0) .layerCount(1); - /* create image view */ + /* create image view */ view.image(textures[i].image); check(vkCreateImageView(device, view, null, lp)); textures[i].view = lp.get(0); @@ -1229,7 +1230,7 @@ private static class Vertices { private void demo_prepare_vertices() { float[][] vb = { - /* position texcoord */ + /* position texcoord */ {-1.0f, -1.0f, 0.25f, 0.0f, 0.0f}, {1.0f, -1.0f, 0.25f, 1.0f, 0.0f}, {0.0f, 1.0f, 1.0f, 0.5f, 1.0f}, @@ -1915,7 +1916,7 @@ private void demo_cleanup() { glfwFreeCallbacks(window); glfwDestroyWindow(window); glfwTerminate(); - glfwSetErrorCallback(null).free(); + Objects.requireNonNull(glfwSetErrorCallback(null)).free(); memFree(extension_names); diff --git a/modules/core/src/test/java/org/lwjgl/opencl/CLTest.java b/modules/core/src/test/java/org/lwjgl/opencl/CLTest.java index 682b3be24d..4dd90fad32 100644 --- a/modules/core/src/test/java/org/lwjgl/opencl/CLTest.java +++ b/modules/core/src/test/java/org/lwjgl/opencl/CLTest.java @@ -9,6 +9,7 @@ import org.testng.*; import org.testng.annotations.*; +import javax.annotation.*; import java.nio.*; import java.util.concurrent.*; @@ -23,6 +24,7 @@ @Test(singleThreaded = true) public class CLTest { + @Nullable private static CLContextCallback CONTEXT_CALLBACK; @BeforeClass diff --git a/modules/core/src/test/java/org/lwjgl/system/MemoryUtilTest.java b/modules/core/src/test/java/org/lwjgl/system/MemoryUtilTest.java index a7d89d3598..73eb04c8d4 100644 --- a/modules/core/src/test/java/org/lwjgl/system/MemoryUtilTest.java +++ b/modules/core/src/test/java/org/lwjgl/system/MemoryUtilTest.java @@ -234,21 +234,23 @@ public void testUTF8() { } // encode null returns null - assertNull(memUTF8((String)null)); - assertNull(memUTF8(null, false)); + assertNull(memUTF8Safe((String)null)); + assertNull(memUTF8Safe(null, false)); // decode null/NULL returns null - assertNull(memUTF8(NULL)); - assertNull(memUTF8((ByteBuffer)null)); + assertNull(memUTF8Safe(NULL)); + assertNull(memUTF8Safe((ByteBuffer)null)); // decode null/NULL with specific length/offset throws NPE try { + //noinspection ConstantConditions memUTF8(null, 0); fail(); } catch (NullPointerException ignored) { } try { + //noinspection ConstantConditions memUTF8(null, 0, 0); fail(); } catch (NullPointerException ignored) { diff --git a/modules/core/src/test/java/org/lwjgl/system/StackTest.java b/modules/core/src/test/java/org/lwjgl/system/StackTest.java index 464f098e05..44ac91cb2a 100644 --- a/modules/core/src/test/java/org/lwjgl/system/StackTest.java +++ b/modules/core/src/test/java/org/lwjgl/system/StackTest.java @@ -22,13 +22,11 @@ public void testPushPop() { assertEquals(stack.getPointer(), size); assertEquals(stack.getFrameIndex(), 0); - stack.push(); - { - stack.malloc(8); - assertEquals(stack.getPointer(), size - 8); - assertEquals(stack.getFrameIndex(), 1); + try (MemoryStack frame = stack.push()) { + frame.malloc(8); + assertEquals(frame.getPointer(), size - 8); + assertEquals(frame.getFrameIndex(), 1); } - stack.pop(); assertEquals(stack.getPointer(), size); assertEquals(stack.getFrameIndex(), 0); @@ -41,39 +39,27 @@ public void testAlignment() { assertEquals(stack.getPointer(), size); - stack.push(); - try { - stack.malloc(1); - assertEquals(stack.getPointer(), size - 1); - } finally { - stack.pop(); + try (MemoryStack frame = stack.push()) { + frame.malloc(1); + assertEquals(frame.getPointer(), size - 1); } - stack.push(); - try { - stack.malloc(1); - stack.mallocShort(1); - assertEquals(stack.getPointer(), size - 4); - } finally { - stack.pop(); + try (MemoryStack frame = stack.push()) { + frame.malloc(1); + frame.mallocShort(1); + assertEquals(frame.getPointer(), size - 4); } - stack.push(); - try { - stack.malloc(1); - stack.mallocInt(1); - assertEquals(stack.getPointer(), size - 8); - } finally { - stack.pop(); + try (MemoryStack frame = stack.push()) { + frame.malloc(1); + frame.mallocInt(1); + assertEquals(frame.getPointer(), size - 8); } - stack.push(); - try { - stack.malloc(1); - stack.mallocLong(1); - assertEquals(stack.getPointer(), size - 16); - } finally { - stack.pop(); + try (MemoryStack frame = stack.push()) { + frame.malloc(1); + frame.mallocLong(1); + assertEquals(frame.getPointer(), size - 16); } } @@ -85,10 +71,10 @@ public void testOOME() { expectThrows(OutOfMemoryError.class, () -> { MemoryStack stack = new MemoryStack(8); - stack.push(); - stack.malloc(8); - stack.malloc(1); - stack.pop(); + try (MemoryStack frame = stack.push()) { + frame.malloc(8); + frame.malloc(1); + } }); } diff --git a/modules/core/src/test/java/org/lwjgl/util/par/ParTest.java b/modules/core/src/test/java/org/lwjgl/util/par/ParTest.java index 0a5c741dbf..bd9c112173 100644 --- a/modules/core/src/test/java/org/lwjgl/util/par/ParTest.java +++ b/modules/core/src/test/java/org/lwjgl/util/par/ParTest.java @@ -7,6 +7,7 @@ import org.testng.annotations.*; import java.nio.*; +import java.util.*; import static org.lwjgl.system.MemoryUtil.*; import static org.lwjgl.util.par.ParShapes.*; @@ -16,17 +17,11 @@ public class ParTest { public void testCylindersAndSpheres() { - ParShapesMesh bad1 = par_shapes_create_cylinder(1, 1); - ParShapesMesh bad2 = par_shapes_create_cylinder(1, 3); - ParShapesMesh good = par_shapes_create_cylinder(3, 1); + assertEquals(par_shapes_create_cylinder(1, 1), null); // bad + assertEquals(par_shapes_create_cylinder(1, 3), null); // bad + par_shapes_free_mesh(Objects.requireNonNull(par_shapes_create_cylinder(3, 1))); // good - assertEquals(bad1, null); - assertEquals(bad2, null); - assertNotEquals(good, null); - - par_shapes_free_mesh(good); - - ParShapesMesh m = par_shapes_create_cylinder(5, 6); + ParShapesMesh m = Objects.requireNonNull(par_shapes_create_cylinder(5, 6)); assertEquals(m.npoints(), 42); par_shapes_free_mesh(m); @@ -34,32 +29,32 @@ public void testCylindersAndSpheres() { slices = 5; stacks = 6; - m = par_shapes_create_cylinder(slices, stacks); + m = Objects.requireNonNull(par_shapes_create_cylinder(slices, stacks)); assertEquals(m.ntriangles(), slices * stacks * 2); par_shapes_free_mesh(m); slices = 5; stacks = 6; - m = par_shapes_create_parametric_sphere(slices, stacks); + m = Objects.requireNonNull(par_shapes_create_parametric_sphere(slices, stacks)); assertEquals(m.ntriangles(), slices * 2 + (stacks - 2) * slices * 2); par_shapes_free_mesh(m); slices = 12; stacks = 13; - m = par_shapes_create_parametric_sphere(slices, stacks); + m = Objects.requireNonNull(par_shapes_create_parametric_sphere(slices, stacks)); assertEquals(m.ntriangles(), slices * 2 + (stacks - 2) * slices * 2); par_shapes_free_mesh(m); slices = 16; stacks = 16; - m = par_shapes_create_parametric_sphere(slices, stacks); + m = Objects.requireNonNull(par_shapes_create_parametric_sphere(slices, stacks)); assertEquals(m.ntriangles(), slices * 2 + (stacks - 2) * slices * 2); par_shapes_free_mesh(m); } public void testMerge() { - ParShapesMesh a = par_shapes_create_klein_bottle(10, 20); - ParShapesMesh b = par_shapes_create_plane(3, 3); + ParShapesMesh a = Objects.requireNonNull(par_shapes_create_klein_bottle(10, 20)); + ParShapesMesh b = Objects.requireNonNull(par_shapes_create_plane(3, 3)); int npts = a.npoints(); int ntris = a.ntriangles(); @@ -77,8 +72,8 @@ public void testTransforms() { ParShapesMesh a, b; // should support translation - a = par_shapes_create_cylinder(20, 3); - b = par_shapes_create_cylinder(4, 3); + a = Objects.requireNonNull(par_shapes_create_cylinder(20, 3)); + b = Objects.requireNonNull(par_shapes_create_cylinder(4, 3)); par_shapes_translate(a, 0.5f, 0.5f, 0.25f); par_shapes_merge(a, b); @@ -88,8 +83,8 @@ public void testTransforms() { // should support rotation - a = par_shapes_create_cylinder(20, 3); - b = par_shapes_create_cylinder(4, 3); + a = Objects.requireNonNull(par_shapes_create_cylinder(20, 3)); + b = Objects.requireNonNull(par_shapes_create_cylinder(4, 3)); FloatBuffer axis1 = memAllocFloat(3); axis1 @@ -116,7 +111,7 @@ public void testTransforms() { // should support non-uniform scale - a = par_shapes_create_cylinder(15, 3); + a = Objects.requireNonNull(par_shapes_create_cylinder(15, 3)); par_shapes_scale(a, 1.0f, 1.0f, 5.0f); @@ -142,7 +137,7 @@ public void testMiscShapes() { ParShapesMesh a, b; - a = par_shapes_create_disk(1.0f, slices, center, normal); + a = Objects.requireNonNull(par_shapes_create_disk(1.0f, slices, center, normal)); normal .put(0, 0.0f) @@ -153,7 +148,7 @@ public void testMiscShapes() { .put(1, 0.0f) .put(2, 0.2f); - b = par_shapes_create_disk(0.2f, slices, center, normal); + b = Objects.requireNonNull(par_shapes_create_disk(0.2f, slices, center, normal)); par_shapes_merge(a, b); @@ -168,8 +163,8 @@ public void testMiscShapes() { .put(1, 0.0f) .put(2, 0.0f); - a = par_shapes_create_disk(2.0f, slices, center, normal); - b = par_shapes_create_rock(1, 2); + a = Objects.requireNonNull(par_shapes_create_disk(2.0f, slices, center, normal)); + b = Objects.requireNonNull(par_shapes_create_rock(1, 2)); FloatBuffer aabb = memAllocFloat(6); par_shapes_compute_aabb(b, aabb); @@ -185,8 +180,8 @@ public void testMiscShapes() { // create a polyhedron on the Y plane - a = par_shapes_create_disk(2.0f, slices, center, normal); - b = par_shapes_create_dodecahedron(); + a = Objects.requireNonNull(par_shapes_create_disk(2.0f, slices, center, normal)); + b = Objects.requireNonNull(par_shapes_create_dodecahedron()); par_shapes_translate(b, 0, 0.934f, 0); @@ -233,10 +228,10 @@ public void testMiscShapes() { int tess = 30; - a = par_shapes_create_disk(2.5f, tess, O, J); - b = par_shapes_create_cylinder(tess, 3); - ParShapesMesh c = par_shapes_create_torus(15, tess, 0.1f); - ParShapesMesh d = par_shapes_create_disk(1, tess, top_center, J); + a = Objects.requireNonNull(par_shapes_create_disk(2.5f, tess, O, J)); + b = Objects.requireNonNull(par_shapes_create_cylinder(tess, 3)); + ParShapesMesh c = Objects.requireNonNull(par_shapes_create_torus(15, tess, 0.1f)); + ParShapesMesh d = Objects.requireNonNull(par_shapes_create_disk(1, tess, top_center, J)); par_shapes_rotate(c, (float)(Math.PI / tess), K); par_shapes_translate(c, 0, 0, 1); @@ -304,8 +299,8 @@ public void testLSystems() { .put(1, 1.0f) .put(2, 0.0f); - ParShapesMesh mesh = par_shapes_create_lsystem(program, 5, 60); - ParShapesMesh disk = par_shapes_create_disk(10, 30, O, J); + ParShapesMesh mesh = Objects.requireNonNull(par_shapes_create_lsystem(program, 5, 60)); + ParShapesMesh disk = Objects.requireNonNull(par_shapes_create_disk(10, 30, O, J)); par_shapes_merge(mesh, disk); diff --git a/modules/core/src/test/java/org/lwjgl/util/yoga/YogaNodeTest.java b/modules/core/src/test/java/org/lwjgl/util/yoga/YogaNodeTest.java index 0d198dd524..0b4b604e40 100644 --- a/modules/core/src/test/java/org/lwjgl/util/yoga/YogaNodeTest.java +++ b/modules/core/src/test/java/org/lwjgl/util/yoga/YogaNodeTest.java @@ -11,6 +11,8 @@ import org.lwjgl.system.*; import org.testng.annotations.*; +import java.util.*; + import static org.lwjgl.system.MemoryStack.*; import static org.lwjgl.util.yoga.Yoga.*; import static org.lwjgl.util.yoga.YogaNode.*; @@ -49,7 +51,7 @@ public void testBaseline() { assertEquals(0, (int)child1.getLayoutY()); assertEquals(40, (int)child2.getLayoutY()); - YGNodeGetBaselineFunc(child2.node).free(); + Objects.requireNonNull(YGNodeGetBaselineFunc(child2.node)).free(); } private static YGMeasureFunc getTestMeasureFunc(float testWidth, float testHeight) {