DATACMNS-1402 - Fix invocation of default Kotlin constructor.
We now correctly calculate the number of defaulting masks used to represent constructor arguments. Previously, we've been one off which caused that Kotlin classes with 32/33 parameters weren't able to be instantiated. We also now reuse KotlinDefaultMask to apply defaulting calculation and removed code duplicates.
This commit is contained in:
committed by
Oliver Gierke
parent
42ee9448d9
commit
fc4b2d1deb
@@ -28,6 +28,7 @@ import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PreferredConstructor;
|
||||
import org.springframework.data.mapping.PreferredConstructor.Parameter;
|
||||
import org.springframework.data.mapping.model.KotlinDefaultMask;
|
||||
import org.springframework.data.mapping.model.MappingInstantiationException;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
import org.springframework.data.util.ReflectionUtils;
|
||||
@@ -114,7 +115,7 @@ public class KotlinClassGeneratingEntityInstantiator extends ClassGeneratingEnti
|
||||
|
||||
// candidates must contain at least two additional parameters (int, DefaultConstructorMarker).
|
||||
// Number of defaulting masks derives from the original constructor arg count
|
||||
int syntheticParameters = (constructor.getParameterCount() / Integer.SIZE) + 1
|
||||
int syntheticParameters = KotlinDefaultMask.getMaskCount(constructor.getParameterCount())
|
||||
+ /* DefaultConstructorMarker */ 1;
|
||||
|
||||
if (constructor.getParameterCount() + syntheticParameters != candidate.getParameterCount()) {
|
||||
@@ -172,6 +173,7 @@ public class KotlinClassGeneratingEntityInstantiator extends ClassGeneratingEnti
|
||||
static class DefaultingKotlinClassInstantiatorAdapter implements EntityInstantiator {
|
||||
|
||||
private final ObjectInstantiator instantiator;
|
||||
private final KFunction<?> constructor;
|
||||
private final List<KParameter> kParameters;
|
||||
private final Constructor<?> synthetic;
|
||||
|
||||
@@ -185,6 +187,7 @@ public class KotlinClassGeneratingEntityInstantiator extends ClassGeneratingEnti
|
||||
}
|
||||
|
||||
this.instantiator = instantiator;
|
||||
this.constructor = kotlinConstructor;
|
||||
this.kParameters = kotlinConstructor.getParameters();
|
||||
this.synthetic = constructor.getConstructor();
|
||||
}
|
||||
@@ -214,10 +217,8 @@ public class KotlinClassGeneratingEntityInstantiator extends ClassGeneratingEnti
|
||||
throw new IllegalArgumentException("PreferredConstructor must not be null!");
|
||||
}
|
||||
|
||||
int[] defaulting = new int[(synthetic.getParameterCount() / Integer.SIZE) + 1];
|
||||
|
||||
Object[] params = allocateArguments(
|
||||
synthetic.getParameterCount() + defaulting.length + /* DefaultConstructorMarker */1);
|
||||
synthetic.getParameterCount() + KotlinDefaultMask.getMaskCount(synthetic.getParameterCount()) + /* DefaultConstructorMarker */1);
|
||||
int userParameterCount = kParameters.size();
|
||||
|
||||
List<Parameter<Object, P>> parameters = preferredConstructor.getParameters();
|
||||
@@ -225,28 +226,30 @@ public class KotlinClassGeneratingEntityInstantiator extends ClassGeneratingEnti
|
||||
// Prepare user-space arguments
|
||||
for (int i = 0; i < userParameterCount; i++) {
|
||||
|
||||
int slot = i / Integer.SIZE;
|
||||
int offset = slot * Integer.SIZE;
|
||||
|
||||
Parameter<Object, P> parameter = parameters.get(i);
|
||||
Class<Object> type = parameter.getType().getType();
|
||||
Object param = provider.getParameterValue(parameter);
|
||||
|
||||
KParameter kParameter = kParameters.get(i);
|
||||
|
||||
// what about null and parameter is mandatory? What if parameter is non-null?
|
||||
if (kParameter.isOptional() && param == null) {
|
||||
|
||||
defaulting[slot] = defaulting[slot] | (1 << (i - offset));
|
||||
|
||||
if (type.isPrimitive()) {
|
||||
param = ReflectionUtils.getPrimitiveDefault(type);
|
||||
}
|
||||
}
|
||||
|
||||
params[i] = param;
|
||||
params[i] = provider.getParameterValue(parameter);
|
||||
}
|
||||
|
||||
KotlinDefaultMask defaultMask = KotlinDefaultMask.from(constructor, it -> {
|
||||
|
||||
int index = kParameters.indexOf(it);
|
||||
|
||||
Parameter<Object, P> parameter = parameters.get(index);
|
||||
Class<Object> type = parameter.getType().getType();
|
||||
|
||||
if (it.isOptional() && params[index] == null) {
|
||||
if (type.isPrimitive()) {
|
||||
|
||||
// apply primitive defaulting to prevent NPE on primitive downcast
|
||||
params[index] = ReflectionUtils.getPrimitiveDefault(type);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
int[] defaulting = defaultMask.getDefaulting();
|
||||
// append nullability masks to creation arguments
|
||||
for (int i = 0; i < defaulting.length; i++) {
|
||||
params[userParameterCount + i] = defaulting[i];
|
||||
|
||||
@@ -24,14 +24,19 @@ import kotlin.reflect.KFunction;
|
||||
import kotlin.reflect.KParameter;
|
||||
import kotlin.reflect.KParameter.Kind;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Value object representing defaulting masks used for Kotlin methods applying parameter defaulting.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
class KotlinDefaultMask {
|
||||
public class KotlinDefaultMask {
|
||||
|
||||
@Getter
|
||||
private final int[] defaulting;
|
||||
|
||||
/**
|
||||
@@ -46,6 +51,16 @@ class KotlinDefaultMask {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of defaulting masks required to represent the number of {@code arguments}.
|
||||
*
|
||||
* @param arguments number of method arguments.
|
||||
* @return the number of defaulting masks required.
|
||||
*/
|
||||
public static int getMaskCount(int arguments) {
|
||||
return ((arguments - 1) / Integer.SIZE) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates defaulting mask(s) used to invoke Kotlin {@literal default} methods that conditionally apply parameter
|
||||
* values.
|
||||
|
||||
Reference in New Issue
Block a user