Skip to content
Permalink
Browse files
8247663: [lworld] LambdaMetaFactory should allow an inline value proj…
…ection type to convert to its reference projection type

Reviewed-by: rriggs
  • Loading branch information
Mandy Chung committed Jul 8, 2020
1 parent 701567c commit 83d6a2281a95fcda0cad5a45850da7dcdb7ade58
@@ -567,13 +567,16 @@ public boolean isInlineClass() {

/**
* Returns a {@code Class} object representing the <em>value projection</em>
* type of this class if this {@code Class} is the reference projection type
* of an {@linkplain #isInlineClass() inline class}. Otherwise an empty
* {@link Optional} is returned.
* type of this class if this {@code Class} represents the reference projection
* type of an {@linkplain #isInlineClass() inline class}.
* If this {@code Class} represents the value projection type
* of an inline class, then this method returns this class.
* Otherwise an empty {@link Optional} is returned.
*
* @return the {@code Class} object representing the value projection type of
* this class if this class is the reference projection type of an
* inline class; an empty {@link Optional} otherwise
* this class if this class is the value projection type
* or the reference projection type of an inline class;
* an empty {@link Optional} otherwise
* @since Valhalla
*/
public Optional<Class<T>> valueType() {
@@ -582,15 +585,18 @@ public boolean isInlineClass() {

/**
* Returns a {@code Class} object representing the <em>reference projection</em>
* type of this class if this class is an {@linkplain #isInlineClass() inline class}
* with a reference projection.
* If this class is an {@linkplain #isInlineClass() inline class}
* without a reference projection or this class is not an inline class,
* type of this class if this {@code Class} represents an
* {@linkplain #isInlineClass() inline class} with a reference projection
* type. If this {@code Class} represents the reference projection type
* of an inline class, then this method returns this class.
* If this class is not an {@linkplain #isInlineClass() inline class}
* or this class is an inline class without a reference projection,
* then this method returns an empty {@link Optional}.
*
* @return the {@code Class} object representing the value projection type of
* this class if this class is the reference projection type of an
* inline class; an empty {@link Optional} otherwise
* this class if this class is an inline class with a reference
* projection type or this class is the reference projection type;
* an empty {@link Optional} otherwise
* @since Valhalla
*/
public Optional<Class<T>> referenceType() {
@@ -366,12 +366,47 @@ private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict
return !strict;
}
} else {
// both are reference types: fromType should be a superclass of toType.
return !strict || toType.isAssignableFrom(fromType);
// inline types: fromType and toType are projection types of the same inline class
// identity types: fromType should be a superclass of toType.
return !strict || canConvert(fromType, toType);
}
}
}

/**
* Tests if {@code fromType} can be converted to {@code toType}
* via an identity conversion, via a widening reference conversion or
* via inline narrowing and widening conversions.
* <p>
* If {@code fromType} represents a class or interface, this method
* returns {@code true} if {@code toType} is the same as,
* or is a superclass or superinterface of, {@code fromType}.
* <p>
* If {@code fromType} is an inline class, this method returns {@code true}
* if {@code toType} is the {@linkplain Class#referenceType() reference projection type}
* of {@code fromType}.
* If {@code toType} is an inline class, this method returns {@code true}
* if {@code toType} is the {@linkplain Class#valueType() value projection type}
* of {@code fromType}.
* <p>
* Otherwise, this method returns {@code false}.
*
* @param fromType the {@code Class} object to be converted from
* @param toType the {@code Class} object to be converted to
* @return {@code true} if {@code fromType} can be converted to {@code toType}
*/
private boolean canConvert(Class<?> fromType, Class<?> toType) {
if (toType.isAssignableFrom(fromType)) {
return true;
}

if (!fromType.isInlineClass() && !toType.isInlineClass()) {
return false;
}

return fromType.valueType().equals(toType.valueType());
}

/**
* Check type adaptability for return types --
* special handling of void type) and parameterized fromType
@@ -55,7 +55,7 @@
* @see LambdaMetafactory
*/
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
private static final int CLASSFILE_VERSION = V15;
private static final int CLASSFILE_VERSION = V16;
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
private static final String NAME_CTOR = "<init>";
@@ -0,0 +1,159 @@
/*
* Copyright (c) 2020, 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.
*/


/*
* @test
* @summary test method handles with inline type conversion
* @run testng/othervm InlineTypeConversionTest
*/

import java.lang.invoke.*;

import static java.lang.invoke.MethodType.*;

import org.testng.annotations.Test;
import static org.testng.Assert.*;

public class InlineTypeConversionTest {
static inline class Value {
Point val;
Point.ref ref;
Value(Point p1, Point.ref p2) {
this.val = p1;
this.ref = p2;
}
}

static Value narrow(Value.ref v) {
return v;
}

static Value.ref widen(Value v) {
if (((Object)v) == null) {
throw new Error("should never reach here: should be caught by runtime");
}
return null;
}

static final Value VALUE = new Value(new Point(10,10), new Point(20, 20));

@Test
public static void inlineWidening() throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh1 = lookup.findStatic(InlineTypeConversionTest.class, "narrow", methodType(Value.class, Value.ref.class));
MethodHandle mh2 = mh1.asType(methodType(Value.class, Value.class));
Object v = mh1.invoke(VALUE);
assertEquals(v, VALUE);
try {
Object v1 = mh1.invoke((Object)null);
fail("Expected NullPointerException but not thrown");
} catch (NullPointerException e) {}

try {
Object v2 = mh2.invoke((Object)null);
fail("Expected NullPointerException but not thrown");
} catch (NullPointerException e) {}
}

@Test
public static void inlineNarrowing() throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findStatic(InlineTypeConversionTest.class, "widen", methodType(Value.ref.class, Value.class));
Object v = mh.invoke(VALUE);
assertTrue(v == null);
try {
Object v1 = mh.invoke((Object)null);
fail("Expected NullPointerException but not thrown");
} catch (NullPointerException e) {
e.printStackTrace();
}
MethodHandle mh2 = mh.asType(methodType(Value.class, Value.ref.class));
try {
Value v2 = (Value) mh2.invoke((Value.ref)null);
fail("Expected NullPointerException but not thrown");
} catch (NullPointerException e) {
e.printStackTrace();
}
}

@Test
public static void valToRef() throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh1 = lookup.findGetter(Value.class, "val", Point.class);
MethodHandle mh2 = mh1.asType(methodType(Point.ref.class, Value.class));
Value v = new Value(new Point(10,10), null);

Point p1 = (Point) mh1.invokeExact(VALUE);
Point.ref p2 = (Point.ref) mh2.invokeExact(VALUE);
assertEquals(p1, p2);
}

@Test
public static void refToVal() throws Throwable {
MethodHandle mh1 = MethodHandles.lookup().findGetter(Value.class, "ref", Point.ref.class);
MethodHandle mh2 = mh1.asType(methodType(Point.class, Value.class));
Point.ref p1 = (Point.ref) mh1.invokeExact(VALUE);
Point p2 = (Point) mh2.invokeExact(VALUE);
assertEquals(p1, p2);
}

@Test
public static void valToRef1() throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh1 = lookup.findGetter(Value.class, "val", Point.class);
MethodHandle mh2 = mh1.asType(methodType(Point.class, Value.ref.class));

Point p1 = (Point) mh1.invokeExact(VALUE);
Point p2 = (Point) mh2.invoke(VALUE);
Point p3 = (Point) mh2.invokeExact((Value.ref)VALUE);
assertEquals(p1, p2);
assertEquals(p1, p3);
}

@Test
public static void refToVal1() throws Throwable {
MethodHandle mh1 = MethodHandles.lookup().findGetter(Value.class, "ref", Point.ref.class);
MethodHandle mh2 = mh1.asType(methodType(Point.ref.class, Value.ref.class));
Value v = new Value(new Point(10,10), null);

Point.ref p1 = (Point.ref) mh1.invokeExact(v);
Point.ref p2 = (Point.ref) mh2.invoke(v);
Point.ref p3 = (Point.ref) mh2.invokeExact((Value.ref)v);
assertEquals(p1, p2);
assertEquals(p1, p3);
}

@Test
public static void refToVal2() throws Throwable {
MethodHandle mh1 = MethodHandles.lookup().findGetter(Value.class, "ref", Point.ref.class);
MethodHandle mh2 = mh1.asType(methodType(Point.class, Value.class));
Value v = new Value(new Point(10,10), null);

Point.ref p1 = (Point.ref) mh1.invokeExact(v);
try {
Point p2 = (Point) mh2.invokeExact(v);
fail("Expected NullPointerException but not thrown");
} catch (NullPointerException e) {}
}
}
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2020, 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.
*/

/*
* @test
* @run testng/othervm LambdaConversion
* @summary test lambda type conversion of inline type
*/

import java.util.List;
import java.util.function.ToIntFunction;
import java.util.stream.Stream;

import org.testng.annotations.Test;
import static org.testng.Assert.*;

public class LambdaConversion {

static class c_int { }
static inline class Pointer<X> {
final long addr;

public Pointer(long addr) {
this.addr = addr;
}

long address() { return addr; }
}

@Test
public static void test() {
Pointer<c_int> p_int = new Pointer<>(12);
assertTrue(doAction(p_int, LambdaConversion::one) == 1);
assertTrue(doAction(p_int, LambdaConversion::two) == 2);
}

static <Z> int doAction(Pointer<Z> pointer, ToIntFunction<Pointer.ref<Z>> action) {
return action.applyAsInt(pointer);
}

static int one(Pointer<c_int> pointer) {
return 1;
}

static int two(Pointer<c_int> p_int) {
return 2;
}
}

@@ -77,7 +77,8 @@ public static void testMirrors() throws Exception {
assertFalse(Point.ref.class.isInlineClass());
assertEquals(inlineClass.valueType().get(), Point.class);
assertEquals(inlineClass.referenceType().get(), Point.ref.class);

assertEquals(Point.ref.class.valueType().get(), Point.class);
assertEquals(Point.ref.class.referenceType().get(), Point.ref.class);

Point o = Point.makePoint(10, 20);
assertTrue(Point.class.isInstance(o));
@@ -28,10 +28,11 @@
* @run testng StreamTest
*/

import org.testng.annotations.Test;

import java.util.Arrays;
import java.util.stream.*;
import java.util.stream.Stream;

import org.testng.annotations.Test;
import static org.testng.Assert.*;

public class StreamTest {
@@ -64,6 +65,14 @@ public void testInlineType() {
.forEach(System.out::println);
}

@Test
public void mapToInt() {
Stream<Point.ref> stream = Arrays.stream(values)
.filter(v -> (v.getI() % 2) == 0)
.map(Value.ref::point);
stream.forEach(p -> assertTrue((p.x % 2) == 0));
}

static inline class Value {
int i;
Point p;
@@ -81,5 +90,7 @@ Point point() {
Point.ref nullablePoint() {
return nullable;
}

int getI() { return i; }
}
}

0 comments on commit 83d6a22

Please sign in to comment.