/* * 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.api.interop.java.test; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Test; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.interop.MessageResolution; import com.oracle.truffle.api.interop.Resolve; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnknownIdentifierException; import com.oracle.truffle.api.interop.java.JavaInterop; import com.oracle.truffle.api.nodes.Node; @SuppressWarnings({"rawtypes", "unchecked"}) public class AsCollectionsTest { @Test public void testAsList() { List origList = Arrays.asList(new String[]{"a", "b", "c"}); TruffleObject to = new ListBasedTO(origList); assertTrue(JavaInterop.isArray(to)); List interopList = JavaInterop.asJavaObject(List.class, to); assertEquals(origList.size(), interopList.size()); assertEquals(origList.toString(), interopList.toString()); // Test get out of bounds try { interopList.get(1000); fail(); } catch (IndexOutOfBoundsException ioobex) { // O.K. } // Test set out of bounds try { interopList.set(1000, "1000"); fail(); } catch (IndexOutOfBoundsException ioobex) { // O.K. } Object old = interopList.set(1, "bbb"); assertEquals("b", old); assertEquals("bbb", interopList.get(1)); } @Test public void testAsMap() { Map<String, String> origMap = new LinkedHashMap<>(); for (int i = 10; i <= 100; i += 10) { origMap.put(Integer.toString(i), Integer.toHexString(i)); } TruffleObject to = new MapBasedTO(origMap); assertFalse(JavaInterop.isArray(to)); Map interopMap = JavaInterop.asJavaObject(Map.class, to); assertEquals(origMap.size(), interopMap.size()); assertEquals(origMap.toString(), interopMap.toString()); assertNull(interopMap.get("unknown")); Object old = interopMap.put("10", "101010"); assertEquals("a", old); assertEquals("101010", interopMap.get("10")); old = interopMap.put("new", "news"); assertNull(old); assertEquals("news", interopMap.get("new")); } static final class ListBasedTO implements TruffleObject { private final List list; ListBasedTO(List list) { this.list = list; } @Override public ForeignAccess getForeignAccess() { return ListBasedMessageResolutionForeign.ACCESS; } public static boolean isInstance(TruffleObject obj) { return obj instanceof ListBasedTO; } @MessageResolution(receiverType = ListBasedTO.class) static class ListBasedMessageResolution { @Resolve(message = "HAS_SIZE") abstract static class ListBasedHasSizeNode extends Node { @SuppressWarnings("unused") public Object access(ListBasedTO lbto) { return true; } } @Resolve(message = "GET_SIZE") abstract static class ListBasedGetSizeNode extends Node { public Object access(ListBasedTO lbto) { return lbto.list.size(); } } @Resolve(message = "READ") abstract static class ListBasedReadNode extends Node { @TruffleBoundary public Object access(ListBasedTO lbto, int index) { try { return lbto.list.get(index); } catch (IndexOutOfBoundsException ioob) { throw UnknownIdentifierException.raise(Integer.toString(index)); } } } @Resolve(message = "WRITE") abstract static class ListBasedWriteNode extends Node { @TruffleBoundary public Object access(ListBasedTO lbto, int index, Object value) { try { lbto.list.set(index, value); return value; } catch (IndexOutOfBoundsException ioob) { throw UnknownIdentifierException.raise(Integer.toString(index)); } } } } } static final class MapBasedTO implements TruffleObject { private final Map map; MapBasedTO(Map map) { this.map = map; } @Override public ForeignAccess getForeignAccess() { return MapBasedMessageResolutionForeign.ACCESS; } public static boolean isInstance(TruffleObject obj) { return obj instanceof MapBasedTO; } @MessageResolution(receiverType = MapBasedTO.class) static class MapBasedMessageResolution { @Resolve(message = "KEYS") abstract static class MapBasedKeysNode extends Node { public Object access(MapBasedTO mbto) { return new MapKeysTO(mbto.map.keySet()); } } @Resolve(message = "READ") abstract static class MapBasedReadNode extends Node { @TruffleBoundary public Object access(MapBasedTO mbto, String name) { Object value = mbto.map.get(name); if (value == null) { throw UnknownIdentifierException.raise(name); } else { return value; } } } @Resolve(message = "WRITE") abstract static class MapBasedWriteNode extends Node { @TruffleBoundary public Object access(MapBasedTO mbto, String name, Object value) { mbto.map.put(name, value); return value; } } } } static final class MapKeysTO implements TruffleObject { private final List keys; private MapKeysTO(Set keys) { this.keys = new ArrayList(keys); } @Override public ForeignAccess getForeignAccess() { return MapKeysMessageResolutionForeign.ACCESS; } public static boolean isInstance(TruffleObject obj) { return obj instanceof MapKeysTO; } @MessageResolution(receiverType = MapKeysTO.class) static class MapKeysMessageResolution { @Resolve(message = "HAS_SIZE") abstract static class MapKeysHasSizeNode extends Node { @SuppressWarnings("unused") public Object access(MapKeysTO lbto) { return true; } } @Resolve(message = "GET_SIZE") abstract static class MapKeysGetSizeNode extends Node { public Object access(MapKeysTO mkto) { return mkto.keys.size(); } } @Resolve(message = "READ") abstract static class MapKeysReadNode extends Node { @TruffleBoundary public Object access(MapKeysTO mkto, int index) { try { return mkto.keys.get(index); } catch (IndexOutOfBoundsException ioob) { throw UnknownIdentifierException.raise(Integer.toString(index)); } } } } } }