001/*
002 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.nfi.test;
024
025import static java.io.File.*;
026import static java.lang.System.*;
027import static jdk.internal.jvmci.common.UnsafeAccess.*;
028import static org.junit.Assert.*;
029import static org.junit.Assume.*;
030
031import java.io.*;
032import java.util.*;
033
034import org.junit.*;
035
036import com.oracle.nfi.*;
037import com.oracle.nfi.api.*;
038
039public class NativeFunctionInterfaceTest {
040
041    public final NativeFunctionInterface nfi;
042
043    public NativeFunctionInterfaceTest() {
044        nfi = NativeFunctionInterfaceRuntime.getNativeFunctionInterface();
045    }
046
047    private List<Long> allocations = new ArrayList<>();
048
049    protected long malloc(int length) {
050        long buf = unsafe.allocateMemory(length);
051        allocations.add(buf);
052        return buf;
053    }
054
055    @Before
056    public void setUp() {
057        // Ignore on SPARC
058        Assume.assumeFalse(System.getProperty("os.arch").toUpperCase().contains("SPARC"));
059    }
060
061    @After
062    public void cleanup() {
063        for (long buf : allocations) {
064            unsafe.freeMemory(buf);
065        }
066    }
067
068    private static void assertCStringEquals(long cString, String s) {
069        for (int i = 0; i < s.length(); i++) {
070            assertEquals(unsafe.getByte(cString + i) & 0xFF, (byte) s.charAt(i));
071        }
072        assertEquals(unsafe.getByte(cString + s.length()) & 0xFF, (byte) '\0');
073    }
074
075    @Test
076    public void test1() {
077        assumeTrue(nfi.isDefaultLibrarySearchSupported());
078        NativeFunctionHandle malloc = nfi.getFunctionHandle("malloc", long.class, int.class);
079        NativeFunctionHandle snprintf = nfi.getFunctionHandle("snprintf", int.class, long.class, int.class, long.class);
080        NativeFunctionHandle free = nfi.getFunctionHandle("free", void.class, long.class);
081
082        String string = "GRAAL";
083        int bufferLength = string.length() + 1;
084        long cString = (long) malloc.call(bufferLength);
085        writeCString(string, cString);
086
087        long cStringCopy = malloc(bufferLength);
088        int result = (int) snprintf.call(cStringCopy, bufferLength, cString);
089        Assert.assertEquals(string.length(), result);
090        assertCStringEquals(cString, string);
091        assertCStringEquals(cStringCopy, string);
092
093        free.call(cString);
094    }
095
096    @Test
097    public void test2() {
098        assumeTrue(nfi.isDefaultLibrarySearchSupported());
099        String formatString = "AB %f%f";
100        long formatCString = writeCString("AB %f%f", malloc(formatString.length() + 1));
101
102        String referenceString = "AB 1.0000001.000000";
103        int bufferLength = referenceString.length() + 1;
104        long buffer = malloc(bufferLength);
105
106        NativeFunctionHandle snprintf = nfi.getFunctionHandle("snprintf", int.class, long.class, int.class, long.class, double.class, double.class);
107        int result = (int) snprintf.call(buffer, bufferLength, formatCString, 1.0D, 1.0D);
108
109        assertCStringEquals(buffer, referenceString);
110        Assert.assertEquals(referenceString.length(), result);
111    }
112
113    @Test
114    public void test3() {
115        assumeTrue(nfi.isDefaultLibrarySearchSupported());
116        String format = "%i%i%i%i%i%i%i%i%i%i%i%i";
117        long formatCString = writeCString(format, malloc(format.length() + 1));
118        String referenceString = "01234567891011";
119
120        int bufferLength = referenceString.length() + 1;
121        long buffer = malloc(bufferLength);
122
123        NativeFunctionHandle snprintf = nfi.getFunctionHandle("snprintf", int.class, long.class, int.class, long.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class,
124                        int.class, int.class, int.class, int.class, int.class);
125
126        int result = (int) snprintf.call(buffer, bufferLength, formatCString, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
127        assertCStringEquals(buffer, referenceString);
128        Assert.assertEquals(referenceString.length(), result);
129    }
130
131    @Test
132    public void test4() {
133        assumeTrue(nfi.isDefaultLibrarySearchSupported());
134        long str = malloc(49);
135        int[] val = new int[12];
136        for (int i = 0; i < 12; i++) {
137            unsafe.putByte(str + 2 * i, (byte) '%');
138            unsafe.putByte(str + 2 * i + 1, (byte) 'i');
139            val[i] = i;
140        }
141        double[] dval = new double[12];
142        for (int i = 12; i < 24; i++) {
143            unsafe.putByte(str + 2 * i, (byte) '%');
144            unsafe.putByte(str + 2 * i + 1, (byte) 'f');
145            dval[i - 12] = i + 0.5;
146        }
147        unsafe.putByte(str + 48, (byte) '\0');
148
149        String referenceString = "0123456789101112.50000013.50000014.50000015.50000016.50000017.50000018.50000019.50000020.500000" + "21.50000022.50000023.500000";
150        int bufferLength = referenceString.length() + 1;
151
152        long buffer = malloc(bufferLength);
153
154        NativeFunctionHandle snprintf = nfi.getFunctionHandle("snprintf", int.class, long.class, int.class, long.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class,
155                        int.class, int.class, int.class, int.class, int.class, double.class, double.class, double.class, double.class, double.class, double.class, double.class, double.class,
156                        double.class, double.class, double.class, double.class);
157
158        int result = (int) snprintf.call(buffer, bufferLength, str, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], val[9], val[10], val[11], dval[0], dval[1], dval[2],
159                        dval[3], dval[4], dval[5], dval[6], dval[7], dval[8], dval[9], dval[10], dval[11]);
160        assertCStringEquals(buffer, referenceString);
161        Assert.assertEquals(referenceString.length(), result);
162    }
163
164    @Test
165    public void test5() {
166        assumeTrue(nfi.isDefaultLibrarySearchSupported());
167        long str = malloc(73);
168        int[] val = new int[12];
169        for (int i = 0; i < 12; i++) {
170            unsafe.putByte(str + 2 * i, (byte) '%');
171            unsafe.putByte(str + 2 * i + 1, (byte) 'i');
172            val[i] = i;
173        }
174        double[] dval = new double[12];
175        for (int i = 12; i < 24; i++) {
176            unsafe.putByte(str + 2 * i, (byte) '%');
177            unsafe.putByte(str + 2 * i + 1, (byte) 'f');
178            dval[i - 12] = i + 0.5;
179        }
180        char[] cval = new char[12];
181        for (int i = 24; i < 36; i++) {
182            unsafe.putByte(str + 2 * i, (byte) '%');
183            unsafe.putByte(str + 2 * i + 1, (byte) 'c');
184            cval[i - 24] = (char) ('a' + (i - 24));
185        }
186        unsafe.putByte(str + 72, (byte) '\0');
187
188        String referenceString = "0123456789101112.50000013.50000014.50000015.50000016.50000017.50000018.50000019.50000020.50000021.50000022.50000023.500000abcdefghijkl";
189        int bufferLength = referenceString.length() + 1;
190
191        long buffer = malloc(bufferLength);
192
193        NativeFunctionHandle snprintf = nfi.getFunctionHandle("snprintf", int.class, long.class, int.class, long.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class,
194                        int.class, int.class, int.class, int.class, int.class, double.class, double.class, double.class, double.class, double.class, double.class, double.class, double.class,
195                        double.class, double.class, double.class, double.class, char.class, char.class, char.class, char.class, char.class, char.class, char.class, char.class, char.class, char.class,
196                        char.class, char.class);
197
198        int result = (int) snprintf.call(buffer, bufferLength, str, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], val[9], val[10], val[11], dval[0], dval[1], dval[2],
199                        dval[3], dval[4], dval[5], dval[6], dval[7], dval[8], dval[9], dval[10], dval[11], cval[0], cval[1], cval[2], cval[3], cval[4], cval[5], cval[6], cval[7], cval[8], cval[9],
200                        cval[10], cval[11]);
201        assertCStringEquals(buffer, referenceString);
202        Assert.assertEquals(referenceString.length(), result);
203    }
204
205    @Test
206    public void test6() {
207        assumeTrue(nfi.isDefaultLibrarySearchSupported());
208        NativeFunctionHandle handle = nfi.getFunctionHandle("pow", double.class, double.class, double.class);
209        double result = (double) handle.call(3D, 5.5D);
210        assertEquals(Math.pow(3D, 5.5D), result, 0);
211    }
212
213    @Test
214    public void test7() {
215        assumeTrue(nfi.isDefaultLibrarySearchSupported());
216        double result = 0;
217        NativeFunctionHandle handle = nfi.getFunctionHandle("pow", double.class, double.class, double.class);
218        for (int i = 0; i < 10; i++) {
219            result = (double) handle.call(3D, 5.5D);
220        }
221        assertEquals(Math.pow(3D, 5.5D), result, 0);
222    }
223
224    @Test
225    public void test8() {
226        assumeTrue(nfi.isDefaultLibrarySearchSupported());
227        String formatString = "AB %f%f";
228        long formatCString = writeCString("AB %f%f", malloc(formatString.length() + 1));
229
230        String expected = "AB 1.0000001.000000";
231        int bufferLength = expected.length() + 1;
232        byte[] buffer = new byte[bufferLength];
233
234        NativeFunctionHandle snprintf = nfi.getFunctionHandle("snprintf", int.class, byte[].class, int.class, long.class, double.class, double.class);
235        int result = (int) snprintf.call(buffer, bufferLength, formatCString, 1.0D, 1.0D);
236
237        // trim trailing '\0'
238        String actual = new String(buffer, 0, expected.length());
239
240        assertEquals(expected, actual);
241        Assert.assertEquals(expected.length(), result);
242    }
243
244    private static double[] someDoubles = {2454.346D, 98789.22D, Double.MAX_VALUE, Double.MIN_NORMAL, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY};
245
246    @Test
247    public void test9() {
248        assumeTrue(nfi.isDefaultLibrarySearchSupported());
249        double[] src = someDoubles.clone();
250        double[] dst = new double[src.length];
251
252        NativeFunctionHandle memcpy = nfi.getFunctionHandle("memcpy", void.class, double[].class, double[].class, int.class);
253        memcpy.call(dst, src, src.length * (Double.SIZE / Byte.SIZE));
254
255        assertArrayEquals(src, dst, 0.0D);
256    }
257
258    private static String getVMName() {
259        String vmName = System.getProperty("java.vm.name").toLowerCase();
260        String vm = null;
261        if (vmName.contains("server")) {
262            vm = "server";
263        } else if (vmName.contains("graal")) {
264            vm = "graal";
265        } else if (vmName.contains("client")) {
266            vm = "client";
267        }
268
269        Assume.assumeTrue(vm != null);
270        return vm;
271    }
272
273    private static String getVMLibPath() {
274        String vm = getVMName();
275
276        String path = String.format("%s%c%s%c%s", getProperty("sun.boot.library.path"), separatorChar, vm, separatorChar, mapLibraryName("jvm"));
277        // Only continue if the library file exists
278        Assume.assumeTrue(new File(path).exists());
279        return path;
280    }
281
282    @Test
283    public void test10() {
284        NativeLibraryHandle vmLib = nfi.getLibraryHandle(getVMLibPath());
285        NativeFunctionHandle currentTimeMillis = nfi.getFunctionHandle(vmLib, "JVM_CurrentTimeMillis", long.class);
286        long time1 = (long) currentTimeMillis.call();
287        long time2 = System.currentTimeMillis();
288        long delta = time2 - time1;
289
290        // The 2 calls to get the current time should not differ by more than
291        // 100 milliseconds at the very most
292        assertTrue(String.valueOf(delta), delta >= 0);
293        assertTrue(String.valueOf(delta), delta < 100);
294    }
295
296    private static String getJavaLibPath() {
297        String path = String.format("%s%c%s", getProperty("sun.boot.library.path"), separatorChar, mapLibraryName("java"));
298        Assume.assumeTrue(new File(path).exists());
299        return path;
300    }
301
302    private static void testD2L(NativeFunctionHandle d2l) {
303        for (double d : someDoubles) {
304            long expected = Double.doubleToRawLongBits(d);
305            long actual = (long) d2l.call(0L, 0L, d);
306            assertEquals(Double.toString(d), expected, actual);
307        }
308    }
309
310    @Test
311    public void test11() {
312        NativeLibraryHandle javaLib = nfi.getLibraryHandle(getJavaLibPath());
313        NativeFunctionHandle d2l = nfi.getFunctionHandle(javaLib, "Java_java_lang_Double_doubleToRawLongBits", long.class, long.class, long.class, double.class);
314        testD2L(d2l);
315    }
316
317    @Test
318    public void test12() {
319        NativeLibraryHandle[] libs = {nfi.getLibraryHandle(getVMLibPath()), nfi.getLibraryHandle(getJavaLibPath())};
320        NativeFunctionHandle d2l = nfi.getFunctionHandle(libs, "Java_java_lang_Double_doubleToRawLongBits", long.class, long.class, long.class, double.class);
321        testD2L(d2l);
322
323        NativeLibraryHandle[] libsReveresed = {libs[1], libs[0]};
324        d2l = nfi.getFunctionHandle(libsReveresed, "Java_java_lang_Double_doubleToRawLongBits", long.class, long.class, long.class, double.class);
325        testD2L(d2l);
326    }
327
328    @Test
329    public void test13() {
330        NativeLibraryHandle[] libs = {nfi.getLibraryHandle(getVMLibPath()), nfi.getLibraryHandle(getJavaLibPath())};
331        NativeFunctionPointer functionPointer = nfi.getFunctionPointer(libs, "Java_java_lang_Double_doubleToRawLongBits");
332        NativeFunctionHandle d2l = nfi.getFunctionHandle(functionPointer, long.class, long.class, long.class, double.class);
333        testD2L(d2l);
334
335        NativeLibraryHandle[] libsReveresed = {libs[1], libs[0]};
336        functionPointer = nfi.getFunctionPointer(libsReveresed, "Java_java_lang_Double_doubleToRawLongBits");
337        d2l = nfi.getFunctionHandle(functionPointer, long.class, long.class, long.class, double.class);
338        testD2L(d2l);
339    }
340
341    @Test
342    public void test14() {
343        if (!nfi.isDefaultLibrarySearchSupported()) {
344            try {
345                nfi.getFunctionHandle("snprintf", int.class);
346                fail();
347            } catch (UnsatisfiedLinkError e) {
348            }
349        }
350    }
351
352    @Test
353    public void test15() {
354        assumeTrue(nfi.isDefaultLibrarySearchSupported());
355        NativeFunctionHandle functionHandle = nfi.getFunctionHandle("an invalid function name", int.class);
356        if (functionHandle != null) {
357            fail();
358        }
359    }
360
361    @Test
362    public void test16() {
363        NativeLibraryHandle javaLib = nfi.getLibraryHandle(getJavaLibPath());
364        NativeFunctionHandle functionHandle = nfi.getFunctionHandle(javaLib, "an invalid function name", int.class);
365        if (functionHandle != null) {
366            fail();
367        }
368    }
369
370    @Test
371    public void test17() {
372        NativeLibraryHandle[] libs = {nfi.getLibraryHandle(getVMLibPath()), nfi.getLibraryHandle(getJavaLibPath())};
373        NativeFunctionHandle functionHandle = nfi.getFunctionHandle(libs, "an invalid function name", int.class);
374        if (functionHandle != null) {
375            fail();
376        }
377    }
378
379    @Test
380    public void test18() {
381        NativeLibraryHandle[] libs = {nfi.getLibraryHandle(getVMLibPath()), nfi.getLibraryHandle(getJavaLibPath())};
382        NativeFunctionHandle functionHandle = nfi.getFunctionHandle(libs, "an invalid function name", int.class);
383        if (functionHandle != null) {
384            fail();
385        }
386    }
387
388    @Test
389    public void test19() {
390        NativeLibraryHandle[] libs = {nfi.getLibraryHandle(getVMLibPath()), nfi.getLibraryHandle(getJavaLibPath())};
391        NativeFunctionPointer functionPointer = nfi.getFunctionPointer(libs, "an invalid function name");
392        if (functionPointer != null) {
393            fail();
394        }
395    }
396
397    @Test
398    public void test20() {
399        try {
400            nfi.getLibraryHandle("an invalid library name");
401            fail();
402        } catch (UnsatisfiedLinkError e) {
403        }
404    }
405
406}