Skip to content

Commit

Permalink
Save a small amount of allocations during init
Browse files Browse the repository at this point in the history
This is done by using the known size of the List parameters of recorders
  • Loading branch information
geoand committed May 12, 2021
1 parent 5acb055 commit 22c6b70
Showing 1 changed file with 42 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public class BytecodeRecorderImpl implements RecorderContext {
private static final String PROXY_KEY = "proxykey";

private static final MethodDescriptor COLLECTION_ADD = ofMethod(Collection.class, "add", boolean.class, Object.class);
private static final MethodDescriptor COLLECTIONS_EMPTY_LIST = ofMethod(Collections.class, "emptyList", List.class);
private static final MethodDescriptor MAP_PUT = ofMethod(Map.class, "put", Object.class, Object.class, Object.class);

private final boolean staticInit;
Expand Down Expand Up @@ -1103,8 +1104,8 @@ private DeferredParameter loadComplexObject(Object param, Map<Object, DeferredPa
//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<SerialzationStep> setupSteps = new ArrayList<>();
List<SerialzationStep> ctorSetupSteps = new ArrayList<>();
List<SerializationStep> setupSteps = new ArrayList<>();
List<SerializationStep> ctorSetupSteps = new ArrayList<>();

boolean relaxedOk = false;
if (param instanceof Collection) {
Expand All @@ -1113,7 +1114,7 @@ private DeferredParameter loadComplexObject(Object param, Map<Object, DeferredPa
DeferredParameter val = i != null
? loadObjectInstance(i, existing, i.getClass(), relaxedValidation)
: loadObjectInstance(null, existing, Object.class, relaxedValidation);
setupSteps.add(new SerialzationStep() {
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
Expand All @@ -1136,7 +1137,7 @@ public void prepare(MethodContext context) {
DeferredParameter val = i.getValue() != null
? loadObjectInstance(i.getValue(), existing, i.getValue().getClass(), relaxedValidation)
: loadObjectInstance(null, existing, Object.class, relaxedValidation);
setupSteps.add(new SerialzationStep() {
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
method.invokeInterfaceMethod(MAP_PUT, context.loadDeferred(out), context.loadDeferred(key),
Expand Down Expand Up @@ -1221,7 +1222,7 @@ public void prepare(MethodContext context) {
params.add(toAdd);

}
setupSteps.add(new SerialzationStep() {
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method,
DeferredArrayStoreParameter out) {
Expand Down Expand Up @@ -1261,7 +1262,7 @@ public void prepare(MethodContext context) {
relaxedValidation);
def.put(key, val);
}
setupSteps.add(new SerialzationStep() {
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method,
DeferredArrayStoreParameter out) {
Expand Down Expand Up @@ -1337,7 +1338,7 @@ public void prepare(MethodContext context) {
i.getPropertyType(), relaxedValidation);
if (ctorParamIndex != null) {
nonDefaultConstructorHandles[ctorParamIndex] = val;
ctorSetupSteps.add(new SerialzationStep() {
ctorSetupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {

Expand All @@ -1350,7 +1351,7 @@ public void prepare(MethodContext context) {
});
} else {
Class finalPropertyType = propertyType;
setupSteps.add(new SerialzationStep() {
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
method.invokeVirtualMethod(
Expand Down Expand Up @@ -1389,7 +1390,7 @@ public void prepare(MethodContext context) {
relaxedValidation);
if (ctorParamIndex != null) {
nonDefaultConstructorHandles[ctorParamIndex] = val;
ctorSetupSteps.add(new SerialzationStep() {
ctorSetupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method,
DeferredArrayStoreParameter out) {
Expand All @@ -1402,7 +1403,7 @@ public void prepare(MethodContext context) {
}
});
} else {
setupSteps.add(new SerialzationStep() {
setupSteps.add(new SerializationStep() {
@Override
public void handle(MethodContext context, MethodCreator method,
DeferredArrayStoreParameter out) {
Expand Down Expand Up @@ -1445,24 +1446,34 @@ ResultHandle createValue(MethodContext context, MethodCreator method, ResultHand
Arrays.stream(finalCtorHandles).map(m -> context.loadDeferred(m))
.toArray(ResultHandle[]::new));
} 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));
if (List.class.isAssignableFrom(param.getClass())) {
// list is a common special case, so let's handle it
List listParam = (List) param;
if (listParam.isEmpty()) {
out = method.invokeStaticMethod(COLLECTIONS_EMPTY_LIST); // TODO: Should we do this? There is a risk of breaking recorders that don't treat the params as immutable
} else {
throw new RuntimeException("Unable to serialize objects of type " + param.getClass()
+ " to bytecode as it has no default constructor");
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");
}
}
}
}
Expand All @@ -1477,15 +1488,15 @@ ResultHandle createValue(MethodContext context, MethodCreator method, ResultHand
void doPrepare(MethodContext context) {
//this is where the object construction happens
//first create the actial object
for (SerialzationStep i : ctorSetupSteps) {
for (SerializationStep i : ctorSetupSteps) {
i.prepare(context);
}
objectValue.prepare(context);
for (SerialzationStep i : setupSteps) {
for (SerializationStep i : setupSteps) {
//then prepare the steps (i.e. creating the values to be placed into this object)
i.prepare(context);
}
for (SerialzationStep i : setupSteps) {
for (SerializationStep i : setupSteps) {
//now actually run the steps (i.e. actually stick the values into the object)
context.writeInstruction(new InstructionGroup() {
@Override
Expand Down Expand Up @@ -1792,7 +1803,7 @@ final ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHan
/**
* A step that must be executed to serialize a complex object
*/
interface SerialzationStep {
interface SerializationStep {

void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out);

Expand Down

0 comments on commit 22c6b70

Please sign in to comment.