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) {
}
Assume.assumeTrue(truncated);
try {
mbb.position(BLOCK_SIZE);
getter.get(mbb);
new File("").exists();
} catch (InternalError e) {
return;
}
Assert.fail("Expected exception");
}
}