1/*
2 * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.core.test;
24
25import static java.nio.file.StandardOpenOption.READ;
26import static java.nio.file.StandardOpenOption.WRITE;
27
28import java.io.IOException;
29import java.nio.ByteBuffer;
30import java.nio.MappedByteBuffer;
31import java.nio.channels.FileChannel;
32import java.nio.channels.FileChannel.MapMode;
33import java.nio.file.Files;
34import java.nio.file.Path;
35
36import jdk.vm.ci.code.InstalledCode;
37import jdk.vm.ci.code.InvalidInstalledCodeException;
38import jdk.vm.ci.meta.ResolvedJavaMethod;
39import jdk.vm.ci.meta.ResolvedJavaType;
40
41import org.junit.Assert;
42import org.junit.Assume;
43import org.junit.Test;
44
45import sun.misc.Unsafe;
46
47import org.graalvm.compiler.nodes.StructuredGraph;
48import org.graalvm.compiler.phases.common.CanonicalizerPhase;
49import org.graalvm.compiler.phases.common.inlining.InliningPhase;
50import org.graalvm.compiler.phases.common.inlining.policy.InlineEverythingPolicy;
51import org.graalvm.compiler.phases.tiers.HighTierContext;
52
53public class MarkUnsafeAccessTest extends GraalCompilerTest {
54
55    public static Unsafe unsafe;
56
57    public void getRaw() {
58        unsafe.getInt(0L);
59    }
60
61    public void get() {
62        unsafe.getInt(null, 0L);
63    }
64
65    public void putRaw() {
66        unsafe.putInt(0L, 0);
67    }
68
69    public void put() {
70        unsafe.putInt(null, 0L, 0);
71    }
72
73    public void cas() {
74        unsafe.compareAndSwapInt(null, 0, 0, 0);
75    }
76
77    public void noAccess() {
78        unsafe.addressSize();
79        unsafe.pageSize();
80    }
81
82    private void assertHasUnsafe(String name, boolean hasUnsafe) {
83        Assert.assertEquals(hasUnsafe, compile(getResolvedJavaMethod(name), null).hasUnsafeAccess());
84    }
85
86    @Test
87    public void testGet() {
88        assertHasUnsafe("get", true);
89        assertHasUnsafe("getRaw", true);
90    }
91
92    @Test
93    public void testPut() {
94        assertHasUnsafe("put", true);
95        assertHasUnsafe("putRaw", true);
96    }
97
98    @Test
99    public void testCas() {
100        assertHasUnsafe("cas", true);
101    }
102
103    @Test
104    public void testNoAcces() {
105        assertHasUnsafe("noAccess", false);
106    }
107
108    @FunctionalInterface
109    private interface MappedByteBufferGetter {
110        byte get(MappedByteBuffer mbb);
111    }
112
113    @Test
114    public void testStandard() throws IOException {
115        testMappedByteBuffer(MappedByteBuffer::get);
116    }
117
118    @Test
119    public void testCompiled() throws IOException {
120        ResolvedJavaMethod getMethod = asResolvedJavaMethod(getMethod(ByteBuffer.class, "get", new Class<?>[]{}));
121        ResolvedJavaType mbbClass = getMetaAccess().lookupJavaType(MappedByteBuffer.class);
122        ResolvedJavaMethod getMethodImpl = mbbClass.findUniqueConcreteMethod(getMethod).getResult();
123        Assert.assertNotNull(getMethodImpl);
124        StructuredGraph graph = parseForCompile(getMethodImpl);
125        HighTierContext highContext = getDefaultHighTierContext();
126        new CanonicalizerPhase().apply(graph, highContext);
127        new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase()).apply(graph, highContext);
128        InstalledCode compiledCode = getCode(getMethodImpl, graph);
129        testMappedByteBuffer(mbb -> {
130            try {
131                return (byte) compiledCode.executeVarargs(mbb);
132            } catch (InvalidInstalledCodeException e) {
133                Assert.fail();
134                return 0;
135            }
136        });
137    }
138
139    private static final int BLOCK_SIZE = 512;
140    private static final int BLOCK_COUNT = 16;
141
142    public void testMappedByteBuffer(MappedByteBufferGetter getter) throws IOException {
143        Path tmp = Files.createTempFile(null, null);
144        tmp.toFile().deleteOnExit();
145        FileChannel tmpFileChannel = FileChannel.open(tmp, READ, WRITE);
146        ByteBuffer bb = ByteBuffer.allocate(BLOCK_SIZE);
147        while (bb.remaining() >= 4) {
148            bb.putInt(0xA8A8A8A8);
149        }
150        for (int i = 0; i < BLOCK_COUNT; ++i) {
151            bb.flip();
152            while (bb.hasRemaining()) {
153                tmpFileChannel.write(bb);
154            }
155        }
156        tmpFileChannel.force(true);
157        MappedByteBuffer mbb = tmpFileChannel.map(MapMode.READ_WRITE, 0, BLOCK_SIZE * BLOCK_COUNT);
158        Assert.assertEquals((byte) 0xA8, mbb.get());
159        mbb.position(mbb.position() + BLOCK_SIZE);
160        Assert.assertEquals((byte) 0xA8, mbb.get());
161        boolean truncated = false;
162        try {
163            tmpFileChannel.truncate(0);
164            tmpFileChannel.force(true);
165            truncated = true;
166        } catch (IOException e) {
167            // not all platforms support truncating memory-mapped files
168        }
169        Assume.assumeTrue(truncated);
170        try {
171            mbb.position(BLOCK_SIZE);
172            getter.get(mbb);
173            System.currentTimeMillis(); // materialize async exception
174        } catch (InternalError e) {
175            return;
176        }
177        Assert.fail("Expected exception");
178    }
179}
180