Skip to content

Commit

Permalink
[Java] Use invokedynamic instead of reflection to call `Unsafe#arra…
Browse files Browse the repository at this point in the history
…yBasedOffset`.
  • Loading branch information
vyazelenko committed Feb 26, 2025
1 parent 1941eed commit 83cf401
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 39 deletions.
21 changes: 21 additions & 0 deletions agrona/src/main/java/org/agrona/UnsafeApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,27 @@ private UnsafeApi()
{
}

private static java.lang.invoke.CallSite bootstrapArrayBaseOffset(
final java.lang.invoke.MethodHandles.Lookup lookup,
final String methodName,
final java.lang.invoke.MethodType methodType) throws Throwable
{
final Class<?> clazz = methodType.parameterType(0);
final var method = clazz.getMethod("arrayBaseOffset", Class.class);
final var methodHandle = lookup.unreflect(method);
if (method.getReturnType() == int.class)
{
return new java.lang.invoke.ConstantCallSite(methodHandle);
}
else
{
final var intReturnType = methodHandle.type().changeReturnType(int.class);
final var castToIntMethodHandle =
java.lang.invoke.MethodHandles.explicitCastArguments(methodHandle, intReturnType);
return new java.lang.invoke.ConstantCallSite(castToIntMethodHandle);
}
}

/**
* See {@code jdk.internal.misc.Unsafe#addressSize}.
* @return value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,24 @@
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.implementation.bytecode.assign.TypeCasting;
import net.bytebuddy.implementation.bytecode.collection.ArrayFactory;
import net.bytebuddy.implementation.bytecode.constant.ClassConstant;
import net.bytebuddy.implementation.bytecode.constant.IntegerConstant;
import net.bytebuddy.implementation.bytecode.constant.NullConstant;
import net.bytebuddy.implementation.bytecode.constant.TextConstant;
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
import net.bytebuddy.implementation.bytecode.member.Invokedynamic;
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.utility.JavaConstant;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;

import static net.bytebuddy.matcher.ElementMatchers.*;
Expand Down Expand Up @@ -149,53 +152,37 @@ enum ArrayBasedOffsetByteCode implements ByteCodeAppender
final @NotNull Implementation.Context implementationContext,
final @NotNull MethodDescription instrumentedMethod)
{
final TypeDescription.ForLoadedType classType = new TypeDescription.ForLoadedType(Class.class);
final TypeDescription.ForLoadedType methodType = new TypeDescription.ForLoadedType(Method.class);
final TypeDescription.ForLoadedType numberType = new TypeDescription.ForLoadedType(Number.class);

final MethodDescription.InDefinedShape classForName = classType.getDeclaredMethods()
.filter(hasSignature(new MethodDescription.SignatureToken(
"forName",
new TypeDescription.ForLoadedType(Class.class),
new TypeDescription.ForLoadedType(String.class))))
.getOnly();
final MethodDescription.InDefinedShape classGetMethod = classType.getDeclaredMethods()
.filter(named("getMethod"))
.getOnly();
final MethodDescription.InDefinedShape methodInvoke = methodType.getDeclaredMethods()
.filter(named("invoke"))
.getOnly();
final MethodDescription.InDefinedShape numberIntValue =
numberType.getDeclaredMethods().filter(named("intValue")).getOnly();
final StackManipulation.Size operandStackSize = new StackManipulation.Compound(
new TextConstant(UNSAFE_CLASS.getName()),
MethodInvocation.invoke(classForName),
new TextConstant("arrayBaseOffset"),
ArrayFactory.forType(TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(Class.class))
.withValues(List.of(ClassConstant.of(classType))),
MethodInvocation.invoke(classGetMethod),
MethodVariableAccess.REFERENCE.storeAt(1),

MethodVariableAccess.REFERENCE.loadFrom(1),
FieldAccess.forField(new FieldDescription.Latent(
implementationContext.getInstrumentedType(),
"UNSAFE",
ModifierContributor.Resolver.of(Ownership.STATIC, Visibility.PRIVATE, FieldManifestation.FINAL)
.resolve(),
TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(UNSAFE_CLASS),
List.of())).read(),
ArrayFactory.forType(TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(Object.class))
.withValues(List.of(MethodVariableAccess.REFERENCE.loadFrom(0))),
MethodInvocation.invoke(methodInvoke),
MethodVariableAccess.REFERENCE.storeAt(2),
List.of())).read(), // load UNSAFE field

MethodVariableAccess.REFERENCE.loadFrom(0), // Load method argument

MethodVariableAccess.REFERENCE.loadFrom(2),
TypeCasting.to(numberType),
MethodInvocation.invoke(numberIntValue),
new Invokedynamic("arrayBaseOffset",
JavaConstant.MethodType.of(
new TypeDescription.ForLoadedType(int.class),
new TypeDescription.ForLoadedType(UNSAFE_CLASS),
new TypeDescription.ForLoadedType(Class.class)),
JavaConstant.MethodHandle.of(new MethodDescription.Latent(
implementationContext.getInstrumentedType(),
new MethodDescription.Token(
"bootstrapArrayBaseOffset",
Modifier.PRIVATE | Modifier.STATIC,
new TypeDescription.Generic.OfNonGenericType.ForLoadedType(CallSite.class),
List.of(
new TypeDescription.Generic.OfNonGenericType.ForLoadedType(MethodHandles.Lookup.class),
new TypeDescription.Generic.OfNonGenericType.ForLoadedType(String.class),
new TypeDescription.Generic.OfNonGenericType.ForLoadedType(MethodType.class))))),
List.of()),
MethodReturn.INTEGER
).apply(methodVisitor, implementationContext);

return new Size(operandStackSize.getMaximalSize(), 3);
return new Size(operandStackSize.getMaximalSize(), 1);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,27 @@ public final class UnsafeApi
private UnsafeApi()
{
}
private static java.lang.invoke.CallSite bootstrapArrayBaseOffset(
final java.lang.invoke.MethodHandles.Lookup lookup,
final String methodName,
final java.lang.invoke.MethodType methodType) throws Throwable
{
final Class<?> clazz = methodType.parameterType(0);
final var method = clazz.getMethod("arrayBaseOffset", Class.class);
final var methodHandle = lookup.unreflect(method);
if (method.getReturnType() == int.class)
{
return new java.lang.invoke.ConstantCallSite(methodHandle);
}
else
{
final var intReturnType = methodHandle.type().changeReturnType(int.class);
final var castToIntMethodHandle =
java.lang.invoke.MethodHandles.explicitCastArguments(methodHandle, intReturnType);
return new java.lang.invoke.ConstantCallSite(castToIntMethodHandle);
}
}
$body
}
Expand Down

0 comments on commit 83cf401

Please sign in to comment.