Painless: Add a simple cache for whitelist methods and fields. (#28142)
With support for multiple contexts we are adding some caching to the whitelist to keep the memory footprint for definitions from exploding.
This commit is contained in:
parent
06d1ed8ba8
commit
1d1dcd4ae7
|
@ -41,6 +41,9 @@ import java.util.regex.Pattern;
|
||||||
*/
|
*/
|
||||||
public final class Definition {
|
public final class Definition {
|
||||||
|
|
||||||
|
private static final Map<String, Method> methodCache = new HashMap<>();
|
||||||
|
private static final Map<String, Field> fieldCache = new HashMap<>();
|
||||||
|
|
||||||
private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$");
|
private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$");
|
||||||
|
|
||||||
public static final String[] DEFINITION_FILES = new String[] {
|
public static final String[] DEFINITION_FILES = new String[] {
|
||||||
|
@ -533,6 +536,22 @@ public final class Definition {
|
||||||
return simpleTypesMap.values();
|
return simpleTypesMap.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String buildMethodCacheKey(String structName, String methodName, List<Type> arguments) {
|
||||||
|
StringBuilder key = new StringBuilder();
|
||||||
|
key.append(structName);
|
||||||
|
key.append(methodName);
|
||||||
|
|
||||||
|
for (Type argument : arguments) {
|
||||||
|
key.append(argument.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return key.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildFieldCacheKey(String structName, String fieldName, String typeName) {
|
||||||
|
return structName + fieldName + typeName;
|
||||||
|
}
|
||||||
|
|
||||||
// INTERNAL IMPLEMENTATION:
|
// INTERNAL IMPLEMENTATION:
|
||||||
|
|
||||||
private final Map<Class<?>, RuntimeClass> runtimeMap;
|
private final Map<Class<?>, RuntimeClass> runtimeMap;
|
||||||
|
@ -836,8 +855,10 @@ public final class Definition {
|
||||||
" with constructor parameters " + whitelistConstructor.painlessParameterTypeNames);
|
" with constructor parameters " + whitelistConstructor.painlessParameterTypeNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
painlessConstructor = new Method("<init>", ownerStruct, null, getTypeInternal("void"), painlessParametersTypes,
|
painlessConstructor = methodCache.computeIfAbsent(buildMethodCacheKey(ownerStruct.name, "<init>", painlessParametersTypes),
|
||||||
asmConstructor, javaConstructor.getModifiers(), javaHandle);
|
key -> new Method("<init>", ownerStruct, null, getTypeInternal("void"), painlessParametersTypes,
|
||||||
|
asmConstructor, javaConstructor.getModifiers(), javaHandle));
|
||||||
|
|
||||||
ownerStruct.constructors.put(painlessMethodKey, painlessConstructor);
|
ownerStruct.constructors.put(painlessMethodKey, painlessConstructor);
|
||||||
} else if (painlessConstructor.arguments.equals(painlessParametersTypes) == false){
|
} else if (painlessConstructor.arguments.equals(painlessParametersTypes) == false){
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
|
@ -859,7 +880,7 @@ public final class Definition {
|
||||||
" [" + whitelistMethod.javaMethodName + "] for owner struct [" + ownerStructName + "].");
|
" [" + whitelistMethod.javaMethodName + "] for owner struct [" + ownerStructName + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> javaAugmentedClass = null;
|
Class<?> javaAugmentedClass;
|
||||||
|
|
||||||
if (whitelistMethod.javaAugmentedClassName != null) {
|
if (whitelistMethod.javaAugmentedClassName != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -869,6 +890,8 @@ public final class Definition {
|
||||||
"not found for method with name [" + whitelistMethod.javaMethodName + "] " +
|
"not found for method with name [" + whitelistMethod.javaMethodName + "] " +
|
||||||
"and parameters " + whitelistMethod.painlessParameterTypeNames, cnfe);
|
"and parameters " + whitelistMethod.painlessParameterTypeNames, cnfe);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
javaAugmentedClass = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int augmentedOffset = javaAugmentedClass == null ? 0 : 1;
|
int augmentedOffset = javaAugmentedClass == null ? 0 : 1;
|
||||||
|
@ -939,8 +962,10 @@ public final class Definition {
|
||||||
"[" + whitelistMethod.javaMethodName + "] and parameters " + whitelistMethod.painlessParameterTypeNames);
|
"[" + whitelistMethod.javaMethodName + "] and parameters " + whitelistMethod.painlessParameterTypeNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
painlessMethod = new Method(whitelistMethod.javaMethodName, ownerStruct, null, painlessReturnType,
|
painlessMethod = methodCache.computeIfAbsent(
|
||||||
painlessParametersTypes, asmMethod, javaMethod.getModifiers(), javaMethodHandle);
|
buildMethodCacheKey(ownerStruct.name, whitelistMethod.javaMethodName, painlessParametersTypes),
|
||||||
|
key -> new Method(whitelistMethod.javaMethodName, ownerStruct, null, painlessReturnType, painlessParametersTypes,
|
||||||
|
asmMethod, javaMethod.getModifiers(), javaMethodHandle));
|
||||||
ownerStruct.staticMethods.put(painlessMethodKey, painlessMethod);
|
ownerStruct.staticMethods.put(painlessMethodKey, painlessMethod);
|
||||||
} else if ((painlessMethod.name.equals(whitelistMethod.javaMethodName) && painlessMethod.rtn.equals(painlessReturnType) &&
|
} else if ((painlessMethod.name.equals(whitelistMethod.javaMethodName) && painlessMethod.rtn.equals(painlessReturnType) &&
|
||||||
painlessMethod.arguments.equals(painlessParametersTypes)) == false) {
|
painlessMethod.arguments.equals(painlessParametersTypes)) == false) {
|
||||||
|
@ -963,8 +988,10 @@ public final class Definition {
|
||||||
"[" + whitelistMethod.javaMethodName + "] and parameters " + whitelistMethod.painlessParameterTypeNames);
|
"[" + whitelistMethod.javaMethodName + "] and parameters " + whitelistMethod.painlessParameterTypeNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
painlessMethod = new Method(whitelistMethod.javaMethodName, ownerStruct, javaAugmentedClass, painlessReturnType,
|
painlessMethod = methodCache.computeIfAbsent(
|
||||||
painlessParametersTypes, asmMethod, javaMethod.getModifiers(), javaMethodHandle);
|
buildMethodCacheKey(ownerStruct.name, whitelistMethod.javaMethodName, painlessParametersTypes),
|
||||||
|
key -> new Method(whitelistMethod.javaMethodName, ownerStruct, javaAugmentedClass, painlessReturnType,
|
||||||
|
painlessParametersTypes, asmMethod, javaMethod.getModifiers(), javaMethodHandle));
|
||||||
ownerStruct.methods.put(painlessMethodKey, painlessMethod);
|
ownerStruct.methods.put(painlessMethodKey, painlessMethod);
|
||||||
} else if ((painlessMethod.name.equals(whitelistMethod.javaMethodName) && painlessMethod.rtn.equals(painlessReturnType) &&
|
} else if ((painlessMethod.name.equals(whitelistMethod.javaMethodName) && painlessMethod.rtn.equals(painlessReturnType) &&
|
||||||
painlessMethod.arguments.equals(painlessParametersTypes)) == false) {
|
painlessMethod.arguments.equals(painlessParametersTypes)) == false) {
|
||||||
|
@ -1016,33 +1043,40 @@ public final class Definition {
|
||||||
Field painlessField = ownerStruct.staticMembers.get(whitelistField.javaFieldName);
|
Field painlessField = ownerStruct.staticMembers.get(whitelistField.javaFieldName);
|
||||||
|
|
||||||
if (painlessField == null) {
|
if (painlessField == null) {
|
||||||
painlessField = new Field(whitelistField.javaFieldName, javaField.getName(),
|
painlessField = fieldCache.computeIfAbsent(
|
||||||
ownerStruct, painlessFieldType, javaField.getModifiers(), null, null);
|
buildFieldCacheKey(ownerStruct.name, whitelistField.javaFieldName, painlessFieldType.name),
|
||||||
|
key -> new Field(whitelistField.javaFieldName, javaField.getName(),
|
||||||
|
ownerStruct, painlessFieldType, javaField.getModifiers(), null, null));
|
||||||
ownerStruct.staticMembers.put(whitelistField.javaFieldName, painlessField);
|
ownerStruct.staticMembers.put(whitelistField.javaFieldName, painlessField);
|
||||||
} else if (painlessField.type.equals(painlessFieldType) == false) {
|
} else if (painlessField.type.equals(painlessFieldType) == false) {
|
||||||
throw new IllegalArgumentException("illegal duplicate static fields [" + whitelistField.javaFieldName + "] " +
|
throw new IllegalArgumentException("illegal duplicate static fields [" + whitelistField.javaFieldName + "] " +
|
||||||
"found within the struct [" + ownerStruct.name + "] with type [" + whitelistField.painlessFieldTypeName + "]");
|
"found within the struct [" + ownerStruct.name + "] with type [" + whitelistField.painlessFieldTypeName + "]");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MethodHandle javaMethodHandleGetter = null;
|
MethodHandle javaMethodHandleGetter;
|
||||||
MethodHandle javaMethodHandleSetter = null;
|
MethodHandle javaMethodHandleSetter;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (Modifier.isStatic(javaField.getModifiers()) == false) {
|
if (Modifier.isStatic(javaField.getModifiers()) == false) {
|
||||||
javaMethodHandleGetter = MethodHandles.publicLookup().unreflectGetter(javaField);
|
javaMethodHandleGetter = MethodHandles.publicLookup().unreflectGetter(javaField);
|
||||||
javaMethodHandleSetter = MethodHandles.publicLookup().unreflectSetter(javaField);
|
javaMethodHandleSetter = MethodHandles.publicLookup().unreflectSetter(javaField);
|
||||||
|
} else {
|
||||||
|
javaMethodHandleGetter = null;
|
||||||
|
javaMethodHandleSetter = null;
|
||||||
}
|
}
|
||||||
} catch (IllegalAccessException exception) {
|
} catch (IllegalAccessException exception) {
|
||||||
throw new IllegalArgumentException("getter/setter [" + whitelistField.javaFieldName + "]" +
|
throw new IllegalArgumentException("getter/setter [" + whitelistField.javaFieldName + "]" +
|
||||||
" not found for class [" + ownerStruct.clazz.getName() + "].");
|
" not found for class [" + ownerStruct.clazz.getName() + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
Field painlessField = ownerStruct.staticMembers.get(whitelistField.javaFieldName);
|
Field painlessField = ownerStruct.members.get(whitelistField.javaFieldName);
|
||||||
|
|
||||||
if (painlessField == null) {
|
if (painlessField == null) {
|
||||||
painlessField = new Field(whitelistField.javaFieldName, javaField.getName(),
|
painlessField = fieldCache.computeIfAbsent(
|
||||||
ownerStruct, painlessFieldType, javaField.getModifiers(), javaMethodHandleGetter, javaMethodHandleSetter);
|
buildFieldCacheKey(ownerStruct.name, whitelistField.javaFieldName, painlessFieldType.name),
|
||||||
ownerStruct.staticMembers.put(whitelistField.javaFieldName, painlessField);
|
key -> new Field(whitelistField.javaFieldName, javaField.getName(),
|
||||||
|
ownerStruct, painlessFieldType, javaField.getModifiers(), javaMethodHandleGetter, javaMethodHandleSetter));
|
||||||
|
ownerStruct.members.put(whitelistField.javaFieldName, painlessField);
|
||||||
} else if (painlessField.type.equals(painlessFieldType) == false) {
|
} else if (painlessField.type.equals(painlessFieldType) == false) {
|
||||||
throw new IllegalArgumentException("illegal duplicate member fields [" + whitelistField.javaFieldName + "] " +
|
throw new IllegalArgumentException("illegal duplicate member fields [" + whitelistField.javaFieldName + "] " +
|
||||||
"found within the struct [" + ownerStruct.name + "] with type [" + whitelistField.painlessFieldTypeName + "]");
|
"found within the struct [" + ownerStruct.name + "] with type [" + whitelistField.painlessFieldTypeName + "]");
|
||||||
|
|
Loading…
Reference in New Issue