/* * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.oracle.truffle.nfi.test; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.Message; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.nfi.test.interop.BoxedPrimitive; import com.oracle.truffle.nfi.test.interop.NullObject; import com.oracle.truffle.tck.TruffleRunner; import com.oracle.truffle.tck.TruffleRunner.Inject; import java.nio.ByteBuffer; import java.nio.charset.Charset; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TruffleRunner.class) public class VarargsNFITest extends NFITest { private static class FormatNode extends Node { @Child Node bind = Message.createInvoke(1).createNode(); @Child Node execute = Message.createExecute(0).createNode(); private String execute(TruffleObject formatString, Object... args) { assert args[0] == null && args[1] == null; byte[] buffer = new byte[128]; args[0] = JavaInterop.asTruffleObject(buffer); args[1] = buffer.length; int size; try { size = (Integer) ForeignAccess.sendExecute(execute, formatString, args); } catch (InteropException e) { throw new AssertionError(e); } finally { args[0] = args[1] = null; } return toString(buffer, size); } @TruffleBoundary private static String toString(byte[] array, int length) { ByteBuffer buffer = ByteBuffer.wrap(array, 0, length); return Charset.forName("utf-8").decode(buffer).toString(); } } public static class SimpleFormatRoot extends NFITestRootNode { private final TruffleObject formatString; @Child FormatNode printf = new FormatNode(); public SimpleFormatRoot() { this.formatString = lookupAndBind("format_string", "([sint8], uint64, string, ...double) : sint32"); } @Override public Object executeTest(VirtualFrame frame) { return printf.execute(formatString, null, null, "Hello, World %f", 42.0); } } @Test public void testSimpleFormat(@Inject(SimpleFormatRoot.class) CallTarget callTarget) { Object ret = callTarget.call(); Assert.assertEquals("return value", "Hello, World 42.00", ret); } private static final class FormatSpec { final TruffleObject formatString; final Object[] args; final String expectedRet; FormatSpec(String expectedRet, String signature, Object... args) { this.formatString = lookupAndBind("format_string", signature); this.expectedRet = expectedRet; this.args = new Object[args.length + 2]; System.arraycopy(args, 0, this.args, 2, args.length); } } private static class MultiFormatRoot extends NFITestRootNode { @Child FormatNode printf = new FormatNode(); @CompilationFinal(dimensions = 1) final FormatSpec[] specs; MultiFormatRoot(FormatSpec... specs) { this.specs = specs; } @Override @ExplodeLoop public Object executeTest(VirtualFrame frame) { for (int i = 0; i < specs.length; i++) { String ret = printf.execute(specs[i].formatString, specs[i].args); assertEquals(specs[i].expectedRet, ret); } return null; } } public static class MultiSignatureRoot extends MultiFormatRoot { public MultiSignatureRoot() { super( new FormatSpec("15 37 28", "([sint8], uint64, string, ...sint64, sint64, sint64) : sint32", "%d %d %d", 15, 37, 28), new FormatSpec("15 37 28.00", "([sint8], uint64, string, ...sint64, sint64, double) : sint32", "%d %d %f", 15, 37, 28), new FormatSpec("15 37.00 28", "([sint8], uint64, string, ...sint64, double, sint64) : sint32", "%d %f %d", 15, 37, 28), new FormatSpec("15 37.00 28.00", "([sint8], uint64, string, ...sint64, double, double) : sint32", "%d %f %f", 15, 37, 28), new FormatSpec("15.00 37 28", "([sint8], uint64, string, ...double, sint64, sint64) : sint32", "%f %d %d", 15, 37, 28), new FormatSpec("15.00 37 28.00", "([sint8], uint64, string, ...double, sint64, double) : sint32", "%f %d %f", 15, 37, 28), new FormatSpec("15.00 37.00 28", "([sint8], uint64, string, ...double, double, sint64) : sint32", "%f %f %d", 15, 37, 28), new FormatSpec("15.00 37.00 28.00", "([sint8], uint64, string, ...double, double, double) : sint32", "%f %f %f", 15, 37, 28)); } } @Test public void testMultiSignatureFormat(@Inject(MultiSignatureRoot.class) CallTarget callTarget) { callTarget.call(); } public static class MultiTypesRoot extends MultiFormatRoot { public MultiTypesRoot() { super( new FormatSpec("42 8472", "([sint8], uint64, string, ...sint64, sint64) : sint32", "%d %d", 42, 8472), new FormatSpec("(nil) 8472", "([sint8], uint64, string, ...pointer, sint64) : sint32", "%p %d", new NullObject(), 8472), new FormatSpec("42.00 8472", "([sint8], uint64, string, ...double, sint64) : sint32", "%f %d", 42, 8472), new FormatSpec("hello 8472", "([sint8], uint64, string, ...string, sint64) : sint32", "%s %d", "hello", 8472), new FormatSpec("42 world", "([sint8], uint64, string, ...sint64, string) : sint32", "%d %s", 42, "world"), new FormatSpec("(nil) world", "([sint8], uint64, string, ...pointer, string) : sint32", "%p %s", new NullObject(), "world"), new FormatSpec("42.00 world", "([sint8], uint64, string, ...double, string) : sint32", "%f %s", 42, "world"), new FormatSpec("hello world", "([sint8], uint64, string, ...string, string) : sint32", "%s %s", "hello", "world")); } } @Test public void testMultiTypesFormat(@Inject(MultiTypesRoot.class) CallTarget callTarget) { callTarget.call(); } public static class VariableArgCountFormatRoot extends MultiFormatRoot { public VariableArgCountFormatRoot() { super( new FormatSpec("42", "([sint8], uint64, string, ...sint64) : sint32", "%d", 42), new FormatSpec("42 x", "([sint8], uint64, string, ...sint64, string) : sint32", "%d %s", 42, "x"), new FormatSpec("42 x (nil)", "([sint8], uint64, string, ...sint64, string, pointer) : sint32", "%d %s %p", 42, "x", new NullObject()), new FormatSpec("42 x (nil) 42.00", "([sint8], uint64, string, ...sint64, string, pointer, double) : sint32", "%d %s %p %f", 42, "x", new NullObject(), 42), new FormatSpec("x", "([sint8], uint64, string, ...string) : sint32", "%s", new BoxedPrimitive("x")), new FormatSpec("x (nil)", "([sint8], uint64, string, ...string, pointer) : sint32", "%s %p", new BoxedPrimitive("x"), new NullObject()), new FormatSpec("x (nil) 42.00", "([sint8], uint64, string, ...string, pointer, double) : sint32", "%s %p %f", new BoxedPrimitive("x"), new NullObject(), 42), new FormatSpec("x (nil) 42.00 42", "([sint8], uint64, string, ...string, pointer, double, sint64) : sint32", "%s %p %f %d", new BoxedPrimitive("x"), new NullObject(), 42, 42)); } } @Test public void testVariableArgCountFormat(@Inject(VariableArgCountFormatRoot.class) CallTarget callTarget) { callTarget.call(); } }