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:
Jack Conradson 2018-01-08 17:54:45 -08:00 committed by GitHub
parent 06d1ed8ba8
commit 1d1dcd4ae7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 49 additions and 15 deletions

View File

@ -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 + "]");