blob: d95a683bf5c9e53dba58932255b22ea8d57893e9 [file] [log] [blame]
/*
* Copyright (c) 2015, 2018, 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.
*
* 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 org.graalvm.compiler.core.test;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.WRITE;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Files;
import java.nio.file.Path;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.phases.common.CanonicalizerPhase;
import org.graalvm.compiler.phases.common.inlining.InliningPhase;
import org.graalvm.compiler.phases.common.inlining.policy.InlineEverythingPolicy;
import org.graalvm.compiler.phases.tiers.HighTierContext;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import jdk.vm.ci.code.InstalledCode;
import jdk.vm.ci.code.InvalidInstalledCodeException;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import sun.misc.Unsafe;
public class MarkUnsafeAccessTest extends GraalCompilerTest {
public static Unsafe unsafe;
public void getRaw() {
unsafe.getInt(0L);
}
public void get() {
unsafe.getInt(null, 0L);
}
public void putRaw() {
unsafe.putInt(0L, 0);
}
public void put() {
unsafe.putInt(null, 0L, 0);
}
public void cas() {
unsafe.compareAndSwapInt(null, 0, 0, 0);
}
public void noAccess() {
unsafe.addressSize();
unsafe.pageSize();
}
private void assertHasUnsafe(String name, boolean hasUnsafe) {
Assert.assertEquals(hasUnsafe, compile(getResolvedJavaMethod(name), null).hasUnsafeAccess());
}
@Test
public void testGet() {
assertHasUnsafe("get", true);
assertHasUnsafe("getRaw", true);
}
@Test
public void testPut() {
assertHasUnsafe("put", true);
assertHasUnsafe("putRaw", true);
}
@Test
public void testCas() {
assertHasUnsafe("cas", true);
}
@Test
public void testNoAcces() {
assertHasUnsafe("noAccess", false);
}
@FunctionalInterface
private interface MappedByteBufferGetter {
byte get(MappedByteBuffer mbb);
}
@Test
public void testStandard() throws IOException {
testMappedByteBuffer(MappedByteBuffer::get);
}
@Test
public void testCompiled() throws IOException {
Assume.assumeFalse("Crashes on AArch64 (GR-8351)", System.getProperty("os.arch").equalsIgnoreCase("aarch64"));
ResolvedJavaMethod getMethod = asResolvedJavaMethod(getMethod(ByteBuffer.class, "get", new Class<?>[]{}));
ResolvedJavaType mbbClass = getMetaAccess().lookupJavaType(MappedByteBuffer.class);
ResolvedJavaMethod getMethodImpl = mbbClass.findUniqueConcreteMethod(getMethod).getResult();
Assert.assertNotNull(getMethodImpl);
StructuredGraph graph = parseForCompile(getMethodImpl);
HighTierContext highContext = getDefaultHighTierContext();
new CanonicalizerPhase().apply(graph, highContext);
new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase()).apply(graph, highContext);
InstalledCode compiledCode = getCode(getMethodImpl, graph);
testMappedByteBuffer(mbb -> {
try {
return (byte) compiledCode.executeVarargs(mbb);
} catch (InvalidInstalledCodeException e) {
Assert.fail();
return 0;
}
});
}
private static final int BLOCK_SIZE = 512;
private static final int BLOCK_COUNT = 16;
public void testMappedByteBuffer(MappedByteBufferGetter getter) throws IOException {
Path tmp = Files.createTempFile(null, null);
tmp.toFile().deleteOnExit();
FileChannel tmpFileChannel = FileChannel.open(tmp, READ, WRITE);
ByteBuffer bb = ByteBuffer.allocate(BLOCK_SIZE);
while (bb.remaining() >= 4) {
bb.putInt(0xA8A8A8A8);
}
for (int i = 0; i < BLOCK_COUNT; ++i) {
bb.flip();
while (bb.hasRemaining()) {
tmpFileChannel.write(bb);
}
}
tmpFileChannel.force(true);
MappedByteBuffer mbb = tmpFileChannel.map(MapMode.READ_WRITE, 0, BLOCK_SIZE * BLOCK_COUNT);
Assert.assertEquals((byte) 0xA8, mbb.get());
mbb.position(mbb.position() + BLOCK_SIZE);
Assert.assertEquals((byte) 0xA8, mbb.get());
boolean truncated = false;
try {
tmpFileChannel.truncate(0);
tmpFileChannel.force(true);
truncated = true;
} catch (IOException e) {
// not all platforms support truncating memory-mapped files
}
Assume.assumeTrue(truncated);
try {
mbb.position(BLOCK_SIZE);
getter.get(mbb);
// Make a call that goes into native code to materialize async exception
new File("").exists();
} catch (InternalError e) {
return;
}
Assert.fail("Expected exception");
}
}