Skip to content
Permalink
Browse files
8277924: Small tweaks to foreign function and memory API
Reviewed-by: jvernee, psandoz
  • Loading branch information
mcimadamore committed Dec 2, 2021
1 parent e002bfe commit ea905bd3dad5fc1baad66e714bdd01fa679d2d46
Showing 7 changed files with 79 additions and 38 deletions.
@@ -97,7 +97,7 @@ public static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) {
* Obtain a specialized variadic function descriptor, by appending given variadic layouts to this
* function descriptor argument layouts. The resulting function descriptor can report the position
* of the {@linkplain #firstVariadicArgumentIndex() first variadic argument}, and cannot be altered
* in any way: for instance, calling {@link #withReturnLayout(MemoryLayout)} on the resulting descriptor
* in any way: for instance, calling {@link #changeReturnLayout(MemoryLayout)} on the resulting descriptor
* will throw an {@link UnsupportedOperationException}.
* @param variadicLayouts the variadic argument layouts to be appended to this descriptor argument layouts.
* @return a new variadic function descriptor, or this descriptor if {@code variadicLayouts.length == 0}.
@@ -123,10 +123,26 @@ public int firstVariadicArgumentIndex() {
* @param addedLayouts the argument layouts to append.
* @return the new function descriptor.
*/
public FunctionDescriptor withAppendedArgumentLayouts(MemoryLayout... addedLayouts) {
Objects.requireNonNull(addedLayouts);
Arrays.stream(addedLayouts).forEach(Objects::requireNonNull);
List<MemoryLayout> newLayouts = Stream.concat(argLayouts.stream(), Stream.of(addedLayouts)).toList();
public FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts) {
return insertArgumentLayouts(argLayouts.size(), addedLayouts);
}

/**
* Create a new function descriptor with the given argument layouts inserted at the given index, into the argument
* layout array of this function descriptor.
* @param index the index at which to insert the arguments
* @param addedLayouts the argument layouts to insert at given index.
* @return the new function descriptor.
* @throws IllegalArgumentException if {@code index < 0 || index > argumentLayouts().size()}.
*/
public FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts) {
if (index < 0 || index > argLayouts.size())
throw new IllegalArgumentException("Index out of bounds: " + index);
List<MemoryLayout> added = List.of(addedLayouts); // null check on array and its elements
List<MemoryLayout> newLayouts = new ArrayList<>(argLayouts.size() + addedLayouts.length);
newLayouts.addAll(argLayouts.subList(0, index));
newLayouts.addAll(added);
newLayouts.addAll(argLayouts.subList(index, argLayouts.size()));
return new FunctionDescriptor(resLayout, newLayouts);
}

@@ -135,16 +151,17 @@ public FunctionDescriptor withAppendedArgumentLayouts(MemoryLayout... addedLayou
* @param newReturn the new return layout.
* @return the new function descriptor.
*/
public FunctionDescriptor withReturnLayout(MemoryLayout newReturn) {
public FunctionDescriptor changeReturnLayout(MemoryLayout newReturn) {
Objects.requireNonNull(newReturn);
return new FunctionDescriptor(newReturn, argLayouts);
}

/**
* Create a new function descriptor with the return layout dropped.
* Create a new function descriptor with the return layout dropped. This is useful to model functions
* which return no values.
* @return the new function descriptor.
*/
public FunctionDescriptor withVoidReturnLayout() {
public FunctionDescriptor dropReturnLayout() {
return new FunctionDescriptor(null, argLayouts);
}

@@ -212,7 +229,7 @@ public Optional<DynamicConstantDesc<FunctionDescriptor>> describeConstable() {
constants.add(argLayout.describeConstable().get());
}
return Optional.of(DynamicConstantDesc.ofNamed(
ConstantDescs.BSM_INVOKE, "function", AbstractLayout.CD_FUNCTION_DESC, constants.toArray(new ConstantDesc[0])));
ConstantDescs.BSM_INVOKE, "function", AbstractLayout.CD_FUNCTION_DESC, constants.toArray(new ConstantDesc[0])));
}

static final class VariadicFunction extends FunctionDescriptor {
@@ -231,17 +248,22 @@ public int firstVariadicArgumentIndex() {
}

@Override
public FunctionDescriptor withAppendedArgumentLayouts(MemoryLayout... addedLayouts) {
public FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts) {
throw new UnsupportedOperationException();
}

@Override
public FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts) {
throw new UnsupportedOperationException();
}

@Override
public FunctionDescriptor withReturnLayout(MemoryLayout newReturn) {
public FunctionDescriptor changeReturnLayout(MemoryLayout newReturn) {
throw new UnsupportedOperationException();
}

@Override
public FunctionDescriptor withVoidReturnLayout() {
public FunctionDescriptor dropReturnLayout() {
throw new UnsupportedOperationException();
}

@@ -59,7 +59,7 @@ public final CallingSequenceBuilder addArgumentBindings(Class<?> carrier, Memory
verifyBindings(true, carrier, bindings);
inputBindings.add(bindings);
mt = mt.appendParameterTypes(carrier);
desc = desc.withAppendedArgumentLayouts(layout);
desc = desc.appendArgumentLayouts(layout);
return this;
}

@@ -68,7 +68,7 @@ public CallingSequenceBuilder setReturnBindings(Class<?> carrier, MemoryLayout l
verifyBindings(false, carrier, bindings);
this.outputBindings = bindings;
mt = mt.changeReturnType(carrier);
desc = desc.withReturnLayout(layout);
desc = desc.changeReturnLayout(layout);
return this;
}

@@ -32,10 +32,8 @@
import jdk.incubator.foreign.MemoryLayout;
import org.testng.annotations.Test;

import java.lang.constant.Constable;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
@@ -67,7 +65,7 @@ public void testOfVoid() {
@Test
public void testAppendArgumentLayouts() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG);
fd = fd.withAppendedArgumentLayouts(C_POINTER);
fd = fd.appendArgumentLayouts(C_POINTER);

assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG, C_POINTER));
Optional<MemoryLayout> returnLayoutOp = fd.returnLayout();
@@ -78,7 +76,7 @@ public void testAppendArgumentLayouts() {
@Test
public void testChangeReturnLayout() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG);
fd = fd.withReturnLayout(C_INT);
fd = fd.changeReturnLayout(C_INT);

assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG));
Optional<MemoryLayout> returnLayoutOp = fd.returnLayout();
@@ -89,7 +87,7 @@ public void testChangeReturnLayout() {
@Test
public void testDropReturnLayout() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG);
fd = fd.withVoidReturnLayout();
fd = fd.dropReturnLayout();

assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG));
Optional<MemoryLayout> returnLayoutOp = fd.returnLayout();
@@ -40,7 +40,6 @@
import java.util.ArrayList;
import java.util.List;

import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.SymbolLookup;
@@ -127,7 +126,7 @@ interface AddIdentity {
for (int i = 0; i < args.length; i++) {
NativeSymbol ma = LOOKUP.lookup("invoke_high_arity" + i).get();
MethodType mt = baseMT.changeReturnType(baseMT.parameterType(i));
FunctionDescriptor fd = baseFD.withReturnLayout(baseFD.argumentLayouts().get(i));
FunctionDescriptor fd = baseFD.changeReturnLayout(baseFD.argumentLayouts().get(i));
Object expected = args[i];
tests.add(abi.downcallHandle(ma, fd), expected, args);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, 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
@@ -22,6 +22,30 @@
*
*/

/*
* @test id=scope
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
* @modules jdk.incubator.foreign/jdk.internal.foreign
* @build NativeTestHelper CallGeneratorHelper TestUpcall
*
* @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
* --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=17
* -DUPCALL_TEST_TYPE=SCOPE
* TestUpcall
*/

/*
* @test id=no_scope
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
* @modules jdk.incubator.foreign/jdk.internal.foreign
* @build NativeTestHelper CallGeneratorHelper TestUpcall
*
* @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
* --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=17
* -DUPCALL_TEST_TYPE=NO_SCOPE
* TestUpcall
*/

/*
* @test id=async
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
@@ -34,13 +58,11 @@
* TestUpcall
*/

import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.SegmentAllocator;
import jdk.incubator.foreign.SymbolLookup;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;

@@ -104,7 +126,7 @@ void setup() {

private static void checkSelected(TestType type) {
if (UPCALL_TEST_TYPE != type)
return;//throw new SkipException("Skipping tests that were not selected");
throw new SkipException("Skipping tests that were not selected");
}

@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
@@ -62,7 +62,7 @@ public void testEmpty() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ vmStore(rax, long.class) }
@@ -89,7 +89,7 @@ public void testNestedStructs() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ dup(), bufferLoad(0, long.class), vmStore(rdi, long.class),
@@ -119,7 +119,7 @@ public void testNestedUnion() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ dup(), bufferLoad(0, long.class), vmStore(rdi, long.class),
@@ -148,7 +148,7 @@ public void testNestedStructsUnaligned() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ dup(), bufferLoad(0, long.class), vmStore(stackStorage(0), long.class),
@@ -177,7 +177,7 @@ public void testNestedUnionUnaligned() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ dup(), bufferLoad(0, long.class), vmStore(stackStorage(0), long.class),
@@ -201,7 +201,7 @@ public void testIntegerRegs() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ vmStore(rdi, int.class) },
@@ -231,7 +231,7 @@ public void testDoubleRegs() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ vmStore(xmm0, double.class) },
@@ -265,7 +265,7 @@ public void testMixed() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ vmStore(rdi, long.class) },
@@ -321,7 +321,7 @@ public void testAbiExample() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ vmStore(rdi, int.class) },
@@ -363,7 +363,7 @@ public void testMemoryAddress() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ unboxAddress(), vmStore(rdi, long.class) },
@@ -384,7 +384,7 @@ public void testStruct(MemoryLayout struct, Binding[] expectedBindings) {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
expectedBindings,
@@ -442,7 +442,7 @@ public void testReturnRegisterStruct() {
assertFalse(bindings.isInMemoryReturn);
CallingSequence callingSequence = bindings.callingSequence;
assertEquals(callingSequence.methodType(), mt.appendParameterTypes(long.class));
assertEquals(callingSequence.functionDesc(), fd.withAppendedArgumentLayouts(C_LONG));
assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG));

checkArgumentBindings(callingSequence, new Binding[][]{
{ vmStore(rax, long.class) }
@@ -124,7 +124,7 @@ public class Upcalls extends CLayouts {
static MethodHandle linkFunc(String name, FunctionDescriptor baseDesc) {
return abi.downcallHandle(
SymbolLookup.loaderLookup().lookup(name).orElseThrow(),
baseDesc.withAppendedArgumentLayouts(C_POINTER)
baseDesc.appendArgumentLayouts(C_POINTER)
);
}

1 comment on commit ea905bd

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on ea905bd Dec 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.