Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nullability marker annotations #344

Merged
merged 4 commits into from
Jan 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .idea/libraries/jsr305.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/modules/Core.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -383,13 +383,16 @@

<target name="compile" description="Compiles the Java source code" depends="generate, -init-compile">
<lwjgl.javac destdir="${bin.core}" taskname="javac: Core">
<classpath>
<pathelement path="${lib}/jsr305.jar"/>
</classpath>

<src>
<pathelement path="${src.core}"/>
<pathelement path="${src.generated.java}"/>
</src>

<include name="**/*.java"/>
<exclude name="**/package-info.java"/>

<patternset refid="excluded.sources.withCore"/>
<patternset refid="excluded.sources"/>
Expand Down Expand Up @@ -541,6 +544,7 @@
<lwjgl.javac srcdir="${src.tests}" destdir="${bin.tests}" taskname="javac: Tests">
<classpath>
<pathelement path="${bin.core}"/>
<pathelement path="${lib}/jsr305.jar"/>
<pathelement path="${lib}/testng.jar"/>
<pathelement path="${lib}/joml.jar"/>
</classpath>
Expand Down Expand Up @@ -687,6 +691,7 @@
<classpath>
<pathelement path="${src.core}"/>
<pathelement path="${src.generated.java}"/>
<pathelement path="${lib}/jsr305.jar"/>
</classpath>

<fileset dir="${src.core}">
Expand Down
12 changes: 12 additions & 0 deletions doc/notes/3.1.6.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -40,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`.
Expand Down
5 changes: 3 additions & 2 deletions modules/core/src/main/java/org/lwjgl/PointerBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@

import org.lwjgl.system.*;

import javax.annotation.*;
import java.nio.*;

import static org.lwjgl.system.MemoryUtil.*;

/** This class is a container for architecture-independent pointer data. Its interface mirrors the {@link LongBuffer} API for convenience. */
public class PointerBuffer extends CustomBuffer<PointerBuffer> implements Comparable<PointerBuffer> {

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);
}

Expand Down Expand Up @@ -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);
}

Expand Down
32 changes: 27 additions & 5 deletions modules/core/src/main/java/org/lwjgl/egl/EGL.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import org.lwjgl.system.*;

import javax.annotation.*;
import java.nio.*;
import java.util.*;

Expand Down Expand Up @@ -35,8 +36,10 @@
*/
public final class EGL {

@Nullable
private static FunctionProvider functionProvider;

@Nullable
private static EGLCapabilities caps;

static {
Expand Down Expand Up @@ -135,6 +138,7 @@ public static void destroy() {
}

/** Returns the {@link FunctionProvider} for the EGL native library. */
@Nullable
public static FunctionProvider getFunctionProvider() {
return functionProvider;
}
Expand All @@ -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<String> ext = new HashSet<>(32);

long QueryString = functionProvider.getFunctionAddress("eglQueryString");
Expand All @@ -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);
}
}
Expand All @@ -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);
}

Expand All @@ -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<String> 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);
}
Expand Down
43 changes: 24 additions & 19 deletions modules/core/src/main/java/org/lwjgl/openal/AL.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand Down Expand Up @@ -43,8 +43,10 @@
*/
public final class AL {

@Nullable
private static FunctionProvider functionProvider;

@Nullable
private static ALCapabilities processCaps;

private static final ThreadLocal<ALCapabilities> capabilitiesTLS = new ThreadLocal<>();
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand All @@ -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" +
Expand All @@ -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 {
Expand All @@ -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;
Expand All @@ -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();
}
}
}
Expand All @@ -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();
}

/**
Expand All @@ -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)) {
Expand All @@ -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 {
Expand Down
Loading