Skip to content

Commit

Permalink
Add workaround for JDK9 AwkwardStrongEncapsulation
Browse files Browse the repository at this point in the history
We are no longer able to access the private ByteBuffer.address field to
get its offset.
See:
http://openjdk.java.net/projects/jigsaw/spec/issues/#AwkwardStrongEncapsulation

So use the same workaround used by LWJGL3.
This requires native code in order to create a direct ByteBuffer at a
given address.
  • Loading branch information
httpdigest committed Dec 18, 2016
1 parent a3595fc commit b551ede
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 3 deletions.
5 changes: 5 additions & 0 deletions native/build.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
echo "Building for x86-64..."
if not exist "build" mkdir build
cl.exe /c /I"%JAVA_HOME%\include" /I"%JAVA_HOME%\include\win32" /Zi /W3 /WX- /O2 /Oi /GL /D NDEBUG /D _WINDOWS /D _USRDLL /D JOML_EXPORTS /D _WINDLL /D _UNICODE /D UNICODE /Gm- /EHsc /MT /GS- /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"build\\" /Fd"build\\vc140.pdb" /Gd /TC /errorReport:prompt src\JNI.c
link.exe /ERRORREPORT:PROMPT /OUT:"build\joml.dll" /NODEFAULTLIB /NOENTRY /INCREMENTAL:NO /MANIFEST:NO /SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"build\joml.lib" /MACHINE:X64 /DLL build\JNI.obj
del build\*.obj build\*.pdb build\*.exp build\*.lib
7 changes: 7 additions & 0 deletions native/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh
echo "Building for x86-64..."
mkdir -p build
gcc -c -fPIC -m64 -O3 -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" src/JNI.c -o build/JNI.o
gcc -shared -nostdlib -nodefaultlibs -O3 -o build/libjoml.so -fPIC -m64 build/JNI.o
rm build/*.o
strip -x -s build/libjoml.so
Binary file added native/build/joml.dll
Binary file not shown.
Binary file added native/build/libjoml.so
Binary file not shown.
8 changes: 8 additions & 0 deletions native/src/JNI.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <jni.h>
#include <stdint.h>
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
return JNI_VERSION_1_4;
}
JNIEXPORT jobject JNICALL Java_org_joml_MemUtil_00024MemUtilUnsafe_newTestBuffer(JNIEnv *env, jclass clazz) {
return (*env)->NewDirectByteBuffer(env, (void*) (intptr_t) 0xFEEDBABEDEADBEEFL, 0);
}
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@
<build>
<sourceDirectory>src</sourceDirectory>
<testSourceDirectory>test</testSourceDirectory>
<resources>
<resource>
<directory>native/build</directory>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
Expand Down
34 changes: 31 additions & 3 deletions src/org/joml/MemUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ of this software and associated documentation files (the "Software"), to deal
*/
package org.joml;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.Buffer;
import java.nio.ByteBuffer;
Expand Down Expand Up @@ -2197,10 +2198,15 @@ public static final class MemUtilUnsafe extends MemUtil {
private static final long Quaternionf_x;
private static final long floatArrayOffset;

/**
* Used to create a direct ByteBuffer for a known address.
*/
private static final native ByteBuffer newTestBuffer();

static {
UNSAFE = getUnsafeInstance();
try {
ADDRESS = UNSAFE.objectFieldOffset(getDeclaredField(Buffer.class, "address")); //$NON-NLS-1$
ADDRESS = findBufferAddress();
Matrix4f_m00 = checkMatrix4f();
Matrix4x3f_m00 = checkMatrix4x3f();
Matrix3f_m00 = checkMatrix3f();
Expand All @@ -2219,9 +2225,31 @@ public static final class MemUtilUnsafe extends MemUtil {
sun.misc.Unsafe.class.getDeclaredMethod("getLong", new Class[] {Object.class, long.class});
sun.misc.Unsafe.class.getDeclaredMethod("putOrderedLong", new Class[] {Object.class, long.class, long.class});
} catch (NoSuchFieldException e) {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException(e);
} catch (NoSuchMethodException e) {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException(e);
}
}

private static long findBufferAddress() {
try {
return UNSAFE.objectFieldOffset(getDeclaredField(Buffer.class, "address")); //$NON-NLS-1$
} catch (Exception e) {
/* Maybe because of JDK9 AwkwardStrongEncapsulation. */
/* Try detecting the address from a known value. */
try {
SharedLibraryLoader.load();
} catch (IOException e1) {
throw new UnsupportedOperationException("Failed to load joml shared library", e1);
}
ByteBuffer bb = newTestBuffer();
long offset = 8L;
while (offset <= 32L) { // <- don't expect offset to be too large
if (UNSAFE.getLong(bb, offset) == 0xFEEDBABEDEADBEEFL)
return offset;
offset += 8L;
}
throw new UnsupportedOperationException("Could not detect ByteBuffer.address offset", e);
}
}

Expand Down
93 changes: 93 additions & 0 deletions src/org/joml/Platform.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* (C) Copyright 2016 JOML
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package org.joml;

import java.util.regex.Pattern;

/**
* This code was taken and adapted from the LWJGL 3 sources.
* The original code can be seen here: <a href="https://github.com/LWJGL/lwjgl3/blob/master/modules/core/src/main/java/org/lwjgl/system/Platform.java">Platform.java</a>
*
* @author The LWJGL authors
*/
abstract class Platform {

private static final Platform LINUX = new Platform("Linux") {
private final Pattern SO = Pattern.compile("lib\\w+[.]so(?:[.]\\d+){0,3}");

String mapLibraryName(String name) {
if (SO.matcher(name).matches())
return name;

return System.mapLibraryName(name);
}
};
private static final Platform MACOSX = new Platform("Mac OS X") {
private final Pattern DYLIB = Pattern.compile("lib\\w+[.]dylib");

String mapLibraryName(String name) {
if (DYLIB.matcher(name).matches())
return name;

return System.mapLibraryName(name);
}
};
private static final Platform WINDOWS = new Platform("Windows") {
String mapLibraryName(String name) {
if (name.endsWith(".dll"))
return name;

return System.mapLibraryName(name);
}
};

public static final Platform PLATFORM;

static {
String osName = System.getProperty("os.name");
if (osName.startsWith("Windows"))
PLATFORM = Platform.WINDOWS;
else if (osName.startsWith("Linux") || osName.startsWith("FreeBSD") || osName.startsWith("SunOS") || osName.startsWith("Unix"))
PLATFORM = Platform.LINUX;
else if (osName.startsWith("Mac OS X") || osName.startsWith("Darwin"))
PLATFORM = Platform.MACOSX;
else
throw new LinkageError("Unknown platform: " + osName);
}

private final String name;

Platform(String name) {
this.name = name;
}

/**
* Returns the platform name.
*/
public String getName() {
return name;
}

abstract String mapLibraryName(String name);

}
119 changes: 119 additions & 0 deletions src/org/joml/SharedLibraryLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* (C) Copyright 2016 JOML
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package org.joml;

import java.io.*;
import java.net.URL;
import java.util.zip.CRC32;

/**
* This code was taken and adapted from the LWJGL 3 sources.
* The original code can be seen here: <a href="https://github.com/LWJGL/lwjgl3/blob/master/modules/core/src/main/java/org/lwjgl/system/SharedLibraryLoader.java">SharedLibraryLoader.java</a>
*
* @author The LWJGL authors
*/
final class SharedLibraryLoader {

private static final int BUFFER_SIZE = 1024;

private static final String JOML_LIBRARY_NAME = System.getProperty("os.arch").lastIndexOf("64") != -1 ? "joml" : "joml32";

private static File extractPath;

private SharedLibraryLoader() {
}

static void load() throws IOException {
try {
extractPath = extractFile(Platform.PLATFORM.mapLibraryName(JOML_LIBRARY_NAME), null).getParentFile();
} catch (Exception e) {
throw new RuntimeException("Unable to extract the JOML shared library", e);
}
load(Platform.PLATFORM.mapLibraryName(JOML_LIBRARY_NAME));
}

private static void load(String library) throws IOException {
File extracted = extractFile(Platform.PLATFORM.mapLibraryName(library), extractPath);
System.load(extracted.getAbsolutePath());
}

private static File extractFile(String libraryFile, File libraryPath) throws IOException {
URL resource = SharedLibraryLoader.class.getResource("/" + libraryFile);
if (resource == null)
throw new RuntimeException("Failed to locate resource: " + libraryFile);
String libraryCRC;
InputStream input = resource.openStream();
try {
libraryCRC = crc(input);
} finally {
input.close();
}
File extractedFile = getExtractedFile(libraryPath == null ? new File(libraryCRC) : libraryPath, new File(libraryFile).getName());
extractFile(resource, libraryCRC, extractedFile);
return extractedFile;
}

private static File getExtractedFile(File libraryPath, String fileName) {
if (libraryPath.isDirectory())
return new File(libraryPath, fileName);
String tempDirectory = "joml" + System.getProperty("user.name");
File file = new File(System.getProperty("java.io.tmpdir") + "/" + tempDirectory + "/" + libraryPath, fileName);
return file;
}

private static void extractFile(URL resource, String libraryCRC, File extractedFile) throws IOException {
String extractedCrc = null;
if (extractedFile.exists()) {
InputStream input = new FileInputStream(extractedFile);
try {
extractedCrc = crc(input);
} finally {
input.close();
}
}
if (extractedCrc == null || !extractedCrc.equals(libraryCRC)) {
extractedFile.getParentFile().mkdirs();
InputStream input = resource.openStream();
FileOutputStream output = new FileOutputStream(extractedFile);
try {
byte[] buffer = new byte[BUFFER_SIZE];
int n;
while ((n = input.read(buffer)) > 0)
output.write(buffer, 0, n);
} finally {
input.close();
output.close();
}
}
}

private static String crc(InputStream input) throws IOException {
CRC32 crc = new CRC32();
byte[] buffer = new byte[BUFFER_SIZE];
int n;
while ((n = input.read(buffer)) > 0)
crc.update(buffer, 0, n);
return Long.toHexString(crc.getValue());
}

}

0 comments on commit b551ede

Please sign in to comment.