use of io.quarkus.runtime.annotations.RelaxedValidation in project quarkus by quarkusio.
the class BytecodeRecorderImpl method loadComplexObject.
/**
* Created a {@link DeferredParameter} to load a complex object, such as a javabean or collection. This is basically
* just an extension of {@link #loadObjectInstanceImpl(Object, Map, Class, boolean)} but it removes some of the more complex
* code from that method.
*
* @param param The object to load
* @param existing The existing object map
* @param expectedType The expected type of the object
* @param relaxedValidation
* @return
*/
private DeferredParameter loadComplexObject(Object param, Map<Object, DeferredParameter> existing, Class<?> expectedType, boolean relaxedValidation) {
// a list of steps that are performed on the object after it has been created
// we need to create all these first, to ensure the required objects have already
// been deserialized
List<SerializationStep> setupSteps = new ArrayList<>();
List<SerializationStep> ctorSetupSteps = new ArrayList<>();
boolean relaxedOk = false;
if (param instanceof Collection) {
// if this is a collection we want to serialize every element
for (Object i : (Collection) param) {
DeferredParameter val = i != null ? loadObjectInstance(i, existing, i.getClass(), relaxedValidation) : loadObjectInstance(null, existing, Object.class, relaxedValidation);
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
// each step can happen in a new method, so it is safe to do this
method.invokeInterfaceMethod(COLLECTION_ADD, context.loadDeferred(out), context.loadDeferred(val));
}
@Override
public void prepare(MethodContext context) {
// handle the value serialization
val.prepare(context);
}
});
}
relaxedOk = true;
}
if (param instanceof Map) {
// map works the same as collection
for (Map.Entry<?, ?> i : ((Map<?, ?>) param).entrySet()) {
DeferredParameter key = loadObjectInstance(i.getKey(), existing, i.getKey().getClass(), relaxedValidation);
DeferredParameter val = i.getValue() != null ? loadObjectInstance(i.getValue(), existing, i.getValue().getClass(), relaxedValidation) : loadObjectInstance(null, existing, Object.class, relaxedValidation);
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
method.invokeInterfaceMethod(MAP_PUT, context.loadDeferred(out), context.loadDeferred(key), context.loadDeferred(val));
}
@Override
public void prepare(MethodContext context) {
key.prepare(context);
val.prepare(context);
}
});
}
relaxedOk = true;
}
// check how the object is constructed
NonDefaultConstructorHolder nonDefaultConstructorHolder = null;
DeferredParameter[] nonDefaultConstructorHandles = null;
// used to resolve the parameter position for @RecordableConstructor
Map<String, Integer> constructorParamNameMap = new HashMap<>();
if (nonDefaultConstructors.containsKey(param.getClass())) {
nonDefaultConstructorHolder = nonDefaultConstructors.get(param.getClass());
List<Object> params = nonDefaultConstructorHolder.paramGenerator.apply(param);
if (params.size() != nonDefaultConstructorHolder.constructor.getParameterCount()) {
throw new RuntimeException("Unable to serialize " + param + " as the wrong number of parameters were generated for " + nonDefaultConstructorHolder.constructor);
}
int count = 0;
nonDefaultConstructorHandles = new DeferredParameter[params.size()];
Class<?>[] parameterTypes = nonDefaultConstructorHolder.constructor.getParameterTypes();
for (int i = 0; i < params.size(); i++) {
Object obj = params.get(i);
nonDefaultConstructorHandles[i] = loadObjectInstance(obj, existing, parameterTypes[count++], relaxedValidation);
}
} else if (classesToUseRecorableConstructor.contains(param.getClass())) {
Constructor<?> current = null;
int count = 0;
for (var c : param.getClass().getConstructors()) {
if (current == null || current.getParameterCount() < c.getParameterCount()) {
current = c;
count = 0;
} else if (current != null && current.getParameterCount() == c.getParameterCount()) {
count++;
}
}
if (current == null || count > 0) {
throw new RuntimeException("Unable to determine the recordable constructor to use for " + param.getClass());
}
nonDefaultConstructorHolder = new NonDefaultConstructorHolder(current, null);
nonDefaultConstructorHandles = new DeferredParameter[current.getParameterCount()];
if (current.getParameterCount() > 0) {
Parameter[] parameters = current.getParameters();
for (int i = 0; i < current.getParameterCount(); ++i) {
String name = parameters[i].getName();
constructorParamNameMap.put(name, i);
}
}
} else {
for (Constructor<?> ctor : param.getClass().getConstructors()) {
if (ctor.isAnnotationPresent(RecordableConstructor.class)) {
nonDefaultConstructorHolder = new NonDefaultConstructorHolder(ctor, null);
nonDefaultConstructorHandles = new DeferredParameter[ctor.getParameterCount()];
if (ctor.getParameterCount() > 0) {
Parameter[] ctorParameters = ctor.getParameters();
for (int i = 0; i < ctor.getParameterCount(); ++i) {
String name = ctorParameters[i].getName();
constructorParamNameMap.put(name, i);
}
}
break;
}
}
}
Set<String> handledProperties = new HashSet<>();
Property[] desc = PropertyUtils.getPropertyDescriptors(param);
for (Property i : desc) {
if (!i.getDeclaringClass().getPackageName().startsWith("java.")) {
// check if the getter is ignored
if ((i.getReadMethod() != null) && (i.getReadMethod().getAnnotation(IgnoreProperty.class) != null)) {
continue;
}
// check if the matching field is ignored
try {
if (param.getClass().getDeclaredField(i.getName()).getAnnotation(IgnoreProperty.class) != null) {
continue;
}
} catch (NoSuchFieldException ignored) {
}
}
Integer ctorParamIndex = constructorParamNameMap.remove(i.name);
if (i.getReadMethod() != null && i.getWriteMethod() == null && ctorParamIndex == null) {
try {
// read only prop, we may still be able to do stuff with it if it is a collection
if (Collection.class.isAssignableFrom(i.getPropertyType())) {
// special case, a collection with only a read method
// we assume we can just add to the connection
handledProperties.add(i.getName());
Collection propertyValue = (Collection) i.read(param);
if (propertyValue != null && !propertyValue.isEmpty()) {
List<DeferredParameter> params = new ArrayList<>();
for (Object c : propertyValue) {
DeferredParameter toAdd = loadObjectInstance(c, existing, Object.class, relaxedValidation);
params.add(toAdd);
}
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
// get the collection
ResultHandle prop = method.invokeVirtualMethod(MethodDescriptor.ofMethod(i.getReadMethod()), context.loadDeferred(out));
for (DeferredParameter i : params) {
// add the parameter
// TODO: this is not guareded against large collections, probably not an issue in practice
method.invokeInterfaceMethod(COLLECTION_ADD, prop, context.loadDeferred(i));
}
}
@Override
public void prepare(MethodContext context) {
for (DeferredParameter i : params) {
i.prepare(context);
}
}
});
}
} else if (Map.class.isAssignableFrom(i.getPropertyType())) {
// special case, a map with only a read method
// we assume we can just add to the map
// similar to how collection works above
handledProperties.add(i.getName());
Map<Object, Object> propertyValue = (Map<Object, Object>) i.read(param);
if (propertyValue != null && !propertyValue.isEmpty()) {
Map<DeferredParameter, DeferredParameter> def = new LinkedHashMap<>();
for (Map.Entry<Object, Object> entry : propertyValue.entrySet()) {
DeferredParameter key = loadObjectInstance(entry.getKey(), existing, Object.class, relaxedValidation);
DeferredParameter val = loadObjectInstance(entry.getValue(), existing, Object.class, relaxedValidation);
def.put(key, val);
}
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
ResultHandle prop = method.invokeVirtualMethod(MethodDescriptor.ofMethod(i.getReadMethod()), context.loadDeferred(out));
for (Map.Entry<DeferredParameter, DeferredParameter> e : def.entrySet()) {
method.invokeInterfaceMethod(MAP_PUT, prop, context.loadDeferred(e.getKey()), context.loadDeferred(e.getValue()));
}
}
@Override
public void prepare(MethodContext context) {
for (Map.Entry<DeferredParameter, DeferredParameter> e : def.entrySet()) {
e.getKey().prepare(context);
e.getValue().prepare(context);
}
}
});
}
} else if (!relaxedValidation && !i.getName().equals("class") && !relaxedOk && nonDefaultConstructorHolder == null) {
// check if there is actually a field with the name
try {
i.getReadMethod().getDeclaringClass().getDeclaredField(i.getName());
throw new RuntimeException("Cannot serialise field '" + i.getName() + "' on object '" + param + "' as the property is read only");
} catch (NoSuchFieldException e) {
// if there is no underlying field then we ignore the property
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
} else if (i.getReadMethod() != null && (i.getWriteMethod() != null || ctorParamIndex != null)) {
// normal javabean property
try {
handledProperties.add(i.getName());
Object propertyValue = i.read(param);
if (propertyValue == null && ctorParamIndex == null) {
// TODO: is this a valid assumption? Should we check this by creating an instance?
continue;
}
Class propertyType = i.getPropertyType();
if (ctorParamIndex == null) {
Class<?> getterReturnType = i.getReadMethod().getReturnType();
Class<?> setterParameterType = i.getWriteMethod().getParameterTypes()[0];
if (getterReturnType != setterParameterType) {
if (relaxedValidation) {
for (Method m : param.getClass().getMethods()) {
if (m.getName().equals(i.getWriteMethod().getName())) {
if (m.getParameterCount() > 0) {
Class<?>[] parameterTypes = m.getParameterTypes();
if (parameterTypes[0].isAssignableFrom(param.getClass())) {
propertyType = parameterTypes[0];
break;
}
}
}
}
} else {
throw new RuntimeException("Cannot serialise field '" + i.getName() + "' on object '" + param + "' of type '" + param.getClass().getName() + "' as getter and setter are of different types. Getter type is '" + getterReturnType.getName() + "' while setter type is '" + setterParameterType.getName() + "'.");
}
}
}
DeferredParameter val = loadObjectInstance(propertyValue, existing, i.getPropertyType(), relaxedValidation);
if (ctorParamIndex != null) {
nonDefaultConstructorHandles[ctorParamIndex] = val;
ctorSetupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
}
@Override
public void prepare(MethodContext context) {
val.prepare(context);
}
});
} else {
Class finalPropertyType = propertyType;
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
ResultHandle object = context.loadDeferred(out);
ResultHandle resultVal = context.loadDeferred(val);
method.invokeVirtualMethod(ofMethod(param.getClass(), i.getWriteMethod().getName(), i.getWriteMethod().getReturnType(), finalPropertyType), object, resultVal);
}
@Override
public void prepare(MethodContext context) {
val.prepare(context);
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// now handle accessible fields
for (Field field : param.getClass().getFields()) {
// check if the field is ignored
if (field.getAnnotation(IgnoreProperty.class) != null) {
continue;
}
if (!handledProperties.contains(field.getName())) {
Integer ctorParamIndex = constructorParamNameMap.remove(field.getName());
if ((ctorParamIndex != null || !Modifier.isFinal(field.getModifiers())) && !Modifier.isStatic(field.getModifiers())) {
try {
DeferredParameter val = loadObjectInstance(field.get(param), existing, field.getType(), relaxedValidation);
if (ctorParamIndex != null) {
nonDefaultConstructorHandles[ctorParamIndex] = val;
ctorSetupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
}
@Override
public void prepare(MethodContext context) {
val.prepare(context);
}
});
} else {
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
method.writeInstanceField(FieldDescriptor.of(param.getClass(), field.getName(), field.getType()), context.loadDeferred(out), context.loadDeferred(val));
}
@Override
public void prepare(MethodContext context) {
val.prepare(context);
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
if (!constructorParamNameMap.isEmpty()) {
throw new RuntimeException("Could not find parameters for constructor " + nonDefaultConstructorHolder.constructor + " could not read field values " + constructorParamNameMap.keySet());
}
NonDefaultConstructorHolder finalNonDefaultConstructorHolder = nonDefaultConstructorHolder;
DeferredParameter[] finalCtorHandles = nonDefaultConstructorHandles;
// create a deferred value to represet the object itself. This allows the creation to be split
// over multiple methods, which is important if this is a large object
DeferredArrayStoreParameter objectValue = new DeferredArrayStoreParameter(param, expectedType) {
@Override
ResultHandle createValue(MethodContext context, MethodCreator method, ResultHandle array) {
ResultHandle out;
// do the creation
if (finalNonDefaultConstructorHolder != null) {
out = method.newInstance(ofConstructor(finalNonDefaultConstructorHolder.constructor.getDeclaringClass(), finalNonDefaultConstructorHolder.constructor.getParameterTypes()), Arrays.stream(finalCtorHandles).map(m -> context.loadDeferred(m)).toArray(ResultHandle[]::new));
} else {
if (List.class.isAssignableFrom(param.getClass()) && expectedType == List.class) {
// list is a common special case, so let's handle it
List listParam = (List) param;
if (listParam.isEmpty()) {
out = method.newInstance(ofConstructor(ArrayList.class));
} else {
out = method.newInstance(ofConstructor(ArrayList.class, int.class), method.load(listParam.size()));
}
} else {
try {
param.getClass().getDeclaredConstructor();
out = method.newInstance(ofConstructor(param.getClass()));
} catch (NoSuchMethodException e) {
// fallback for collection types, such as unmodifiableMap
if (SortedMap.class.isAssignableFrom(expectedType)) {
out = method.newInstance(ofConstructor(TreeMap.class));
} else if (Map.class.isAssignableFrom(expectedType)) {
out = method.newInstance(ofConstructor(LinkedHashMap.class));
} else if (List.class.isAssignableFrom(expectedType)) {
out = method.newInstance(ofConstructor(ArrayList.class));
} else if (SortedSet.class.isAssignableFrom(expectedType)) {
out = method.newInstance(ofConstructor(TreeSet.class));
} else if (Set.class.isAssignableFrom(expectedType)) {
out = method.newInstance(ofConstructor(LinkedHashSet.class));
} else {
throw new RuntimeException("Unable to serialize objects of type " + param.getClass() + " to bytecode as it has no default constructor");
}
}
}
}
return out;
}
};
// now return the actual deferred parameter that represents the result of construction
return new DeferredArrayStoreParameter(param, expectedType) {
@Override
void doPrepare(MethodContext context) {
// first create the actial object
for (SerializationStep i : ctorSetupSteps) {
i.prepare(context);
}
objectValue.prepare(context);
for (SerializationStep i : setupSteps) {
// then prepare the steps (i.e. creating the values to be placed into this object)
i.prepare(context);
// now actually run the steps (i.e. actually stick the values into the object)
context.writeInstruction(new InstructionGroup() {
@Override
public void write(MethodContext context, MethodCreator method, ResultHandle array) {
i.handle(context, method, objectValue);
}
});
}
super.doPrepare(context);
}
@Override
ResultHandle createValue(MethodContext context, MethodCreator method, ResultHandle array) {
// just return the already created object
return context.loadDeferred(objectValue);
}
};
}
use of io.quarkus.runtime.annotations.RelaxedValidation in project quarkus by quarkusio.
the class BytecodeRecorderImpl method loadObjectInstanceImpl.
/**
* Returns a representation of a serialized parameter.
*/
private DeferredParameter loadObjectInstanceImpl(Object param, Map<Object, DeferredParameter> existing, Class<?> expectedType, boolean relaxedValidation) {
// null is easy
if (param == null) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext creator, MethodCreator method, ResultHandle array) {
return method.loadNull();
}
};
}
// check the loaded object support (i.e. config) to see if this is a config item
DeferredParameter loadedObject = findLoaded(param);
if (loadedObject != null) {
return loadedObject;
}
// Handle empty collections as returned by the Collections object
loadedObject = handleCollectionsObjects(param, existing, relaxedValidation);
if (loadedObject != null) {
return loadedObject;
}
// but some are quite complex when dealing with objects and collections
if (substitutions.containsKey(param.getClass()) || substitutions.containsKey(expectedType)) {
// check for substitution types, if present we invoke recursively on the substitution
SubstitutionHolder holder = substitutions.get(param.getClass());
if (holder == null) {
holder = substitutions.get(expectedType);
}
try {
ObjectSubstitution substitution = holder.sub.getDeclaredConstructor().newInstance();
Object res = substitution.serialize(param);
DeferredParameter serialized = loadObjectInstance(res, existing, holder.to, relaxedValidation);
SubstitutionHolder finalHolder = holder;
return new DeferredArrayStoreParameter(param, expectedType) {
@Override
void doPrepare(MethodContext context) {
serialized.prepare(context);
super.doPrepare(context);
}
@Override
ResultHandle createValue(MethodContext creator, MethodCreator method, ResultHandle array) {
ResultHandle subInstance = method.newInstance(MethodDescriptor.ofConstructor(finalHolder.sub));
return method.invokeInterfaceMethod(ofMethod(ObjectSubstitution.class, "deserialize", Object.class, Object.class), subInstance, creator.loadDeferred(serialized));
}
};
} catch (Exception e) {
throw new RuntimeException("Failed to substitute " + param, e);
}
} else if (param instanceof Optional) {
Optional val = (Optional) param;
if (val.isPresent()) {
DeferredParameter res = loadObjectInstance(val.get(), existing, Object.class, relaxedValidation);
return new DeferredArrayStoreParameter(param, expectedType) {
@Override
void doPrepare(MethodContext context) {
res.prepare(context);
super.doPrepare(context);
}
@Override
ResultHandle createValue(MethodContext context, MethodCreator method, ResultHandle array) {
// so we need to use 'ofNullable' and not 'of' here.
return method.invokeStaticMethod(ofMethod(Optional.class, "ofNullable", Optional.class, Object.class), context.loadDeferred(res));
}
};
} else {
return new DeferredArrayStoreParameter(param, expectedType) {
@Override
ResultHandle createValue(MethodContext context, MethodCreator method, ResultHandle array) {
return method.invokeStaticMethod(ofMethod(Optional.class, "empty", Optional.class));
}
};
}
} else if (param instanceof String) {
if (((String) param).length() > 65535) {
throw new RuntimeException("String too large to record: " + param);
}
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((String) param);
}
};
} else if (param instanceof URL) {
String url = ((URL) param).toExternalForm();
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
AssignableResultHandle value = method.createVariable(URL.class);
try (TryBlock et = method.tryBlock()) {
et.assign(value, et.newInstance(MethodDescriptor.ofConstructor(URL.class, String.class), et.load(url)));
try (CatchBlockCreator malformed = et.addCatch(MalformedURLException.class)) {
malformed.throwException(RuntimeException.class, "Malformed URL", malformed.getCaughtException());
}
}
return value;
}
};
} else if (param instanceof Enum) {
Enum e = (Enum) param;
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
ResultHandle nm = method.load(e.name());
return method.invokeStaticMethod(ofMethod(e.getDeclaringClass(), "valueOf", e.getDeclaringClass(), String.class), nm);
}
};
} else if (param instanceof ReturnedProxy) {
// if this is a proxy we just grab the value from the StartupContext
ReturnedProxy rp = (ReturnedProxy) param;
if (!rp.__static$$init() && staticInit) {
throw new RuntimeException("Invalid proxy passed to recorder. " + rp + " was created in a runtime recorder method, while this recorder is for a static init method. The object will not have been created at the time this method is run.");
}
String proxyId = rp.__returned$proxy$key();
// we just load it from the startup context
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.invokeVirtualMethod(ofMethod(StartupContext.class, "getValue", Object.class, String.class), method.getMethodParam(0), method.load(proxyId));
}
};
} else if (param instanceof Duration) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.invokeStaticMethod(ofMethod(Duration.class, "parse", Duration.class, CharSequence.class), method.load(param.toString()));
}
};
} else if (param instanceof Class<?>) {
if (!((Class) param).isPrimitive()) {
// Only try to load the class by name if it is not a primitive class
String name = classProxies.get(param);
if (name == null) {
name = ((Class) param).getName();
}
String finalName = name;
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
ResultHandle currentThread = method.invokeStaticMethod(ofMethod(Thread.class, "currentThread", Thread.class));
ResultHandle tccl = method.invokeVirtualMethod(ofMethod(Thread.class, "getContextClassLoader", ClassLoader.class), currentThread);
return method.invokeStaticMethod(ofMethod(Class.class, "forName", Class.class, String.class, boolean.class, ClassLoader.class), method.load(finalName), method.load(true), tccl);
}
};
} else {
// Else load the primitive type by reference; double.class => Class var9 = Double.TYPE;
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.loadClassFromTCCL((Class) param);
}
};
}
} else if (expectedType == boolean.class || expectedType == Boolean.class || param instanceof Boolean) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((boolean) param);
}
};
} else if (expectedType == int.class || expectedType == Integer.class || param instanceof Integer) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((int) param);
}
};
} else if (expectedType == short.class || expectedType == Short.class || param instanceof Short) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((short) param);
}
};
} else if (expectedType == byte.class || expectedType == Byte.class || param instanceof Byte) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((byte) param);
}
};
} else if (expectedType == char.class || expectedType == Character.class || param instanceof Character) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((char) param);
}
};
} else if (expectedType == long.class || expectedType == Long.class || param instanceof Long) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((long) param);
}
};
} else if (expectedType == float.class || expectedType == Float.class || param instanceof Float) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((float) param);
}
};
} else if (expectedType == double.class || expectedType == Double.class || param instanceof Double) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((double) param);
}
};
} else if (expectedType.isArray()) {
int length = Array.getLength(param);
DeferredParameter[] components = new DeferredParameter[length];
for (int i = 0; i < length; ++i) {
DeferredParameter component = loadObjectInstance(Array.get(param, i), existing, expectedType.getComponentType(), relaxedValidation);
components[i] = component;
}
return new DeferredArrayStoreParameter(param, expectedType) {
@Override
void doPrepare(MethodContext context) {
for (int i = 0; i < length; ++i) {
components[i].prepare(context);
}
super.doPrepare(context);
}
@Override
ResultHandle createValue(MethodContext context, MethodCreator method, ResultHandle array) {
// TODO large arrays can still generate a fair bit of bytecode, and there appears to be a gizmo issue that prevents casting to an array
// fix this later
ResultHandle out = method.newArray(expectedType.getComponentType(), length);
for (int i = 0; i < length; ++i) {
method.writeArrayValue(out, i, context.loadDeferred(components[i]));
}
return out;
}
};
} else if (param instanceof AnnotationProxy) {
// new com.foo.MyAnnotation_Proxy_AnnotationLiteral("foo")
AnnotationProxy annotationProxy = (AnnotationProxy) param;
List<MethodInfo> constructorParams = annotationProxy.getAnnotationClass().methods().stream().filter(m -> !m.name().equals("<clinit>") && !m.name().equals("<init>")).collect(Collectors.toList());
Map<String, AnnotationValue> annotationValues = annotationProxy.getAnnotationInstance().values().stream().collect(Collectors.toMap(AnnotationValue::name, Function.identity()));
DeferredParameter[] constructorParamsHandles = new DeferredParameter[constructorParams.size()];
for (ListIterator<MethodInfo> iterator = constructorParams.listIterator(); iterator.hasNext(); ) {
MethodInfo valueMethod = iterator.next();
Object explicitValue = annotationProxy.getValues().get(valueMethod.name());
if (explicitValue != null) {
constructorParamsHandles[iterator.previousIndex()] = loadObjectInstance(explicitValue, existing, explicitValue.getClass(), relaxedValidation);
} else {
AnnotationValue value = annotationValues.get(valueMethod.name());
if (value == null) {
// method.invokeInterfaceMethod(MAP_PUT, valuesHandle, method.load(entry.getKey()), loadObjectInstance(method, entry.getValue(),
// returnValueResults, entry.getValue().getClass()));
Object defaultValue = annotationProxy.getDefaultValues().get(valueMethod.name());
if (defaultValue != null) {
constructorParamsHandles[iterator.previousIndex()] = loadObjectInstance(defaultValue, existing, defaultValue.getClass(), relaxedValidation);
continue;
}
if (value == null) {
value = valueMethod.defaultValue();
}
}
if (value == null) {
throw new NullPointerException("Value not set for " + param);
}
DeferredParameter retValue = loadValue(value, annotationProxy.getAnnotationClass(), valueMethod);
constructorParamsHandles[iterator.previousIndex()] = retValue;
}
}
return new DeferredArrayStoreParameter(annotationProxy.getAnnotationLiteralType()) {
@Override
ResultHandle createValue(MethodContext context, MethodCreator method, ResultHandle array) {
MethodDescriptor constructor = MethodDescriptor.ofConstructor(annotationProxy.getAnnotationLiteralType(), constructorParams.stream().map(m -> m.returnType().name().toString()).toArray());
ResultHandle[] args = new ResultHandle[constructorParamsHandles.length];
for (int i = 0; i < constructorParamsHandles.length; i++) {
DeferredParameter deferredParameter = constructorParamsHandles[i];
if (deferredParameter instanceof DeferredArrayStoreParameter) {
DeferredArrayStoreParameter arrayParam = (DeferredArrayStoreParameter) deferredParameter;
arrayParam.doPrepare(context);
}
args[i] = context.loadDeferred(deferredParameter);
}
return method.newInstance(constructor, args);
}
};
} else {
return loadComplexObject(param, existing, expectedType, relaxedValidation);
}
}
Aggregations