RFR: 8324651: Compiler Implementation for Derived Record Creation (Preview) [v5]

ExE Boss duke at openjdk.org
Tue Apr 2 12:57:08 UTC 2024


On Thu, 28 Mar 2024 14:08:44 GMT, Jan Lahoda <jlahoda at openjdk.org> wrote:

>> This is a patch for javac, that adds the Derived Record Creation expressions. The current draft specification for the feature is:
>> https://cr.openjdk.org/~gbierman/jep468/jep468-20240326/specs/derived-record-creation-jls.html
>> 
>> The current CSR is here:
>> https://bugs.openjdk.org/browse/JDK-8328637
>> 
>> The patch is mostly straightforward, with two notable changes:
>>  - there is a new `ElementKind.COMPONENT_LOCAL_VARIABLE`, as the specification introduces this term, and it seems consistent with `ElementKind.BINDING_VARIABLE` that was introduced some time ago.
>>  - there are a bit broader changes in `Flow`, to facilitate the introduction of variables without an explicit declaration for definite assignment and effectively final computation.
>
> Jan Lahoda has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Fixing tests.

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java line 1415:

> 1413:                                                      canonicalConstructorTypes,
> 1414:                                                      List.nil());
> 1415:         createNew.constructor = init;

Maybe instead of hardcoding the constructor that is canonical at compile time (in case new components get added), it might be better to go through some sort of `indy` callsite like (at least when not in the same compilation unit):

DynamicCallSiteDesc[java.lang.runtime.RecordSupport::derivedConstructor(modified component names...):(T extends Record, Class<?>... /* modified component types */)T]


with the `derivedConstructor` bootstrap method:

public static MethodHandle derivedConstructor(MethodHandles.Lookup lookup, String unused, MethodType type, String... modifiedComponents) throws ReflectiveOperationException {
	requireNonNull(lookup);
	requireNonNull(type);
	// implicit null-check:
	List<String> modifiedComponentNames = List.of(modifiedComponents);

	Class<?> rtype = type.returnType();
	if (
		!rtype.isRecord()
		|| type.parameterCount() != modifiedComponents.length + 1
		|| type.parameterType(0) != rtype
	) {
		throw new IllegalArgumentException("...");
	}

	Set<String> remainingComponentNames = new HashSet(modifiedComponentNames);
	if (remainingComponentNames.size() != modifiedComponentNames.size()) {
		throw new IllegalArgumentException("Duplicate component names in modifiedComponents");
	}

	RecordComponent[] recordComponents = rtype.getRecordComponents();

	var componentTypes = new Class<?>[recordComponents.length];
	var filters = new MethodHandle[recordComponents.length];
	var reorder = new int[recordComponents.length];

	for (int i = 0, j = 1; i < recordComponents.length; i++) {
		var component = recordComponents[i];
		componentTypes[i] = component.getType();

		var getter = lookup.findVirtual(rtype, component.getName(), MethodType.methodType(component.getType()));
		if (modifiedComponentNames.contains(component.getName())) {
			remainingComponentNames.remove(component.getName());
			filters[i] = null;
			reorder[i] = j++;
		} else {
			filters[i] = getter;
			reorder[i] = 0;
		}
	}

	if (!remainingComponentNames.isEmpty()) {
		throw new IllegalArgumentException("Components " + remainingComponentNames + " are not present in the record " + rtype);
	}

	var canonicalConstructor = lookup.findConstructor(rtype, MethodType.methodType(rtype, componentTypes);
	var filteredConstructor = MethodHandles.filterArguments(canonicalConstructor, 0, filters);
	// implicitly verifies that type's parameter types match the actual component types
	var permutedConstructor = MethodHandles.permuteArguments(filterArguments, type, reorder);

	return permutedConstructor;
}

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/18509#discussion_r1547819538


More information about the core-libs-dev mailing list