Skip to content

Commit

Permalink
improve dynamic invocation performance
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeloffner committed Dec 20, 2024
1 parent fc4dd1b commit a29534a
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 110 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/lucee/runtime/reflection/Reflector.java
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ private static Object _clean(ObjectIdentityHashSet done, Collection coll) {
}

public static MethodInstance getMethodInstance(Class clazz, final FunctionMember fm, Object[] args, boolean nameCaseSensitive, boolean exactMatchOnly) {
return new MethodInstance(clazz, fm, args, nameCaseSensitive, !exactMatchOnly);
return new MethodInstance(clazz, (Method) fm, args, nameCaseSensitive, !exactMatchOnly);
}

public static MethodInstance getMethodInstance(Class clazz, final Collection.Key methodName, Object[] args, boolean nameCaseSensitive, boolean exactMatchOnly) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,13 @@
import lucee.commons.io.log.Log;
import lucee.commons.io.log.LogUtil;
import lucee.commons.lang.ExceptionUtil;
import lucee.commons.lang.Pair;
import lucee.runtime.exp.PageException;
import lucee.runtime.op.Caster;
import lucee.runtime.type.Collection.Key;
import lucee.transformer.dynamic.DynamicInvoker;
import lucee.transformer.dynamic.meta.Clazz;
import lucee.transformer.dynamic.meta.Constructor;
import lucee.transformer.dynamic.meta.FunctionMember;
import lucee.transformer.dynamic.meta.LegacyConstuctor;
import lucee.transformer.dynamic.meta.dynamic.ClazzDynamic;

/**
* class holds a Constructor and the parameter to call it
Expand All @@ -41,7 +39,9 @@ public final class ConstructorInstance {

private Class clazz;
private Object[] args;
private Pair<FunctionMember, Object> result;
private Constructor fm;
private Object instance;

private boolean convertComparsion;

/**
Expand All @@ -60,7 +60,7 @@ public Object invoke()
throws PageException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {

try {
return ((BiFunction<Object, Object, Object>) getResult().getValue()).apply(null, args);
return ((BiFunction<Object, Object, Object>) getInstance()).apply(null, args);
}
catch (IncompatibleClassChangeError | ClassFormatError | IllegalStateException e) {
if (!Clazz.allowReflection()) throw e;
Expand Down Expand Up @@ -90,28 +90,39 @@ public Object[] getArgs() {
return args;
}

public Constructor getConstructor() throws PageException {
return (Constructor) getResult().getName();
}

public Constructor getConstructor(Constructor defaultValue) {
try {
return (Constructor) getResult().getName();
return getConstructor();
}
catch (Exception e) {
return defaultValue;
}
}

private Pair<FunctionMember, Object> getResult() throws PageException {
if (result == null) {
private Constructor getConstructor() throws PageException {
getInstance();
return fm;
}

private Object getInstance() throws PageException {
if (instance == null) {
try {
result = DynamicInvoker.getExistingInstance().getInstance(clazz, (Key) null, args, true, convertComparsion);
DynamicInvoker di = DynamicInvoker.getExistingInstance();
ClazzDynamic clazzz = di.toClazzDynamic(clazz);
if (fm == null) {
fm = clazzz.getConstructor(args, true, convertComparsion);
}
instance = di.getInstance(clazzz, fm, args);
}
catch (Exception e) {
throw Caster.toPageException(e);
catch (Throwable t) {
ExceptionUtil.rethrowIfNecessary(t);
throw Caster.toPageException(t);
}
}
return result;
return instance;
}

public lucee.transformer.dynamic.meta.FunctionMember getConstructor(ClazzDynamic clazzz, Object[] arguments, boolean convertComparsion) throws NoSuchMethodException {
return clazzz.getConstructor(arguments, true, convertComparsion);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@

import lucee.commons.io.log.LogUtil;
import lucee.commons.lang.ExceptionUtil;
import lucee.commons.lang.Pair;
import lucee.runtime.exp.PageException;
import lucee.runtime.op.Caster;
import lucee.runtime.type.Collection.Key;
import lucee.transformer.dynamic.DynamicInvoker;
import lucee.transformer.dynamic.meta.Clazz;
import lucee.transformer.dynamic.meta.FunctionMember;
import lucee.transformer.dynamic.meta.LegacyMethod;
import lucee.transformer.dynamic.meta.Method;
import lucee.transformer.dynamic.meta.dynamic.ClazzDynamic;

/**
* class holds a Method and the parameter to call it
Expand All @@ -41,14 +40,14 @@ public final class MethodInstance {
private Class clazz;
private Key methodName;
private Object[] args;
private Pair<FunctionMember, Object> result;
private boolean convertComparsion;
private boolean nameCaseSensitive;
private FunctionMember fm;
private Method method;
private Object instance;

public MethodInstance(Class clazz, FunctionMember fm, Object[] args, boolean nameCaseSensitive, boolean convertComparsion) {
public MethodInstance(Class clazz, Method method, Object[] args, boolean nameCaseSensitive, boolean convertComparsion) {
this.clazz = clazz;
this.fm = fm;
this.method = method;
this.args = args;
this.convertComparsion = convertComparsion;
this.nameCaseSensitive = nameCaseSensitive;
Expand All @@ -65,7 +64,7 @@ public MethodInstance(Class clazz, Key methodName, Object[] args, boolean nameCa
public Object invoke(Object o) throws PageException {
// if (Clazz.allowReflection()) print.e(Clazz.allowReflection());
try {
return ((BiFunction<Object, Object, Object>) getResult().getValue()).apply(o, args);
return ((BiFunction<Object, Object, Object>) getInstance()).apply(o, args);
}
catch (IncompatibleClassChangeError | ClassFormatError | ClassCastException e) { // java.lang.ClassCastException
if (!Clazz.allowReflection()) throw e;
Expand All @@ -90,8 +89,10 @@ public Object invoke(Object o) throws PageException {
public static Object invoke(Object obj, Key methodName, Object[] args, boolean nameCaseSensitive, boolean convertComparsion) throws PageException {
// if (Clazz.allowReflection()) print.e(Clazz.allowReflection());
try {
return ((BiFunction<Object, Object, Object>) DynamicInvoker.getExistingInstance().getInstance(obj.getClass(), methodName, args, nameCaseSensitive, convertComparsion)
.getValue()).apply(obj, args);
DynamicInvoker di = DynamicInvoker.getExistingInstance();
ClazzDynamic clazzz = di.toClazzDynamic(obj.getClass());
return ((BiFunction<Object, Object, Object>) di.getInstance(clazzz, clazzz.getMethod(methodName.getString(), args, nameCaseSensitive, true, convertComparsion), args))
.apply(obj, args);
}
catch (IncompatibleClassChangeError | ClassFormatError | ClassCastException e) { // java.lang.ClassCastException
if (!Clazz.allowReflection()) throw e;
Expand Down Expand Up @@ -123,13 +124,9 @@ public Object[] getArgs() {
return args;
}

public Method getMethod() throws PageException {
return (Method) getResult().getName();
}

public Method getMethod(Method defaultValue) {
try {
return (Method) getResult().getName();
return getMethod();
}
catch (PageException e) {
return defaultValue;
Expand All @@ -145,28 +142,38 @@ else if (args.length == 1 && "equals".equals(methodName.getString())) {
}

try {
FunctionMember fm = getResult().getName();
return fm != null;
return getMethod() != null;
}
catch (PageException e) {
return false;
}
}

private Pair<FunctionMember, Object> getResult() throws PageException {
if (result == null) {
try {
result = fm != null ?

DynamicInvoker.getExistingInstance().getInstance(clazz, fm, args, nameCaseSensitive, convertComparsion) :
public Method getMethod() throws PageException {
getInstance();
return method;
}

DynamicInvoker.getExistingInstance().getInstance(clazz, methodName, args, nameCaseSensitive, convertComparsion);
private Object getInstance() throws PageException {
if (instance == null) {
try {
DynamicInvoker di = DynamicInvoker.getExistingInstance();
ClazzDynamic clazzz = di.toClazzDynamic(clazz);
if (method == null) {
method = clazzz.getMethod(methodName.getString(), args, nameCaseSensitive, true, convertComparsion);
}
instance = di.getInstance(clazzz, method, args);
}
catch (Throwable t) {
ExceptionUtil.rethrowIfNecessary(t);
throw Caster.toPageException(t);
}
}
return result;
return instance;
}

public lucee.transformer.dynamic.meta.FunctionMember getMethod(ClazzDynamic clazzz, Key methodName, Object[] arguments, boolean nameCaseSensitive, boolean convertComparsion)
throws NoSuchMethodException {
return clazzz.getMethod(methodName.getString(), arguments, nameCaseSensitive, true, convertComparsion);
}
}
86 changes: 19 additions & 67 deletions core/src/main/java/lucee/transformer/dynamic/DynamicInvoker.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,10 @@ public Object invokeConstructor(Class<?> clazz, Object[] arguments, boolean conv
*/
private Object invoke(Object objMaybeNull, Class<?> objClass, Key methodName, Object[] arguments, boolean nameCaseSensitive, boolean convertComparsion) throws Exception {
try {
return ((BiFunction<Object, Object[], Object>) getInstance(objClass, methodName, arguments, nameCaseSensitive, convertComparsion).getValue()).apply(objMaybeNull,
arguments);
ClazzDynamic clazzz = toClazzDynamic(objClass);

return ((BiFunction<Object, Object[], Object>) getInstance(clazzz, getFunctionMember(clazzz, methodName, arguments, nameCaseSensitive, convertComparsion), arguments))
.apply(objMaybeNull, arguments);
}
catch (IncompatibleClassChangeError | IllegalStateException e) {
if (log != null) log.error("dynamic", e);
Expand All @@ -154,87 +156,38 @@ public Clazz getClazz(Class<?> clazz, boolean useReflection) {
private static double clsLoader = 0;
private static double loadInstance = 0;

public Pair<FunctionMember, Object> getInstance(Class<?> clazz, Key methodName, Object[] arguments, boolean nameCaseSensitive, boolean convertComparsion)
throws NoSuchMethodException, IOException, UnmodifiableClassException, InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, SecurityException {

// double start = SystemUtil.millis();
boolean isConstr = methodName == null;
// Clazz clazzz = getClazz(clazz);
ClazzDynamic clazzz = ClazzDynamic.getInstance(clazz, root, log);
// Clazz clazzz = new ClazzReflection(clazz);
// getClass -= start;
// start = SystemUtil.millis();
// getClass += start;
public ClazzDynamic toClazzDynamic(Class<?> clazz) throws IOException {
return ClazzDynamic.getInstance(clazz, root, log);
}

lucee.transformer.dynamic.meta.FunctionMember fm = isConstr ? clazzz.getConstructor(arguments, true, convertComparsion)
private lucee.transformer.dynamic.meta.FunctionMember getFunctionMember(ClazzDynamic clazzz, Key methodName, Object[] arguments, boolean nameCaseSensitive,
boolean convertComparsion) throws NoSuchMethodException {
return (methodName == null) ? clazzz.getConstructor(arguments, true, convertComparsion)
: clazzz.getMethod(methodName.getString(), arguments, nameCaseSensitive, true, convertComparsion);

// match -= start;
// start = SystemUtil.millis();
// match += start;
// clazz = fm.getDeclaringClass(); // we wanna go as low as possible, to be as open as possible also
// this avoid not allow to access

// getDeclaringClass -= start;
// start = SystemUtil.millis();
// getDeclaringClass += start;

String className = fm.getClassName();

// pathName -= start;
// start = SystemUtil.millis();
// pathName += start;

DynamicClassLoader loader = clazzz.getDynamicClassLoader(this);

// clsLoader -= start;
// start = SystemUtil.millis();
// clsLoader += start;

if (loader.hasClass(className)) {
try {
return new Pair<FunctionMember, Object>(fm, loader.loadInstance(className));

}
catch (Exception e) {
// simply ignore when fail
}
// finally {
// loadInstance -= start;
// start = SystemUtil.millis();
// loadInstance += start;
// }
}

return createInstance(clazzz, fm, isConstr, className, loader);

}

public Pair<FunctionMember, Object> getInstance(Class<?> clazz, final lucee.transformer.dynamic.meta.FunctionMember fm, Object[] arguments, boolean nameCaseSensitive,
boolean convertComparsion) throws NoSuchMethodException, IOException, UnmodifiableClassException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException, SecurityException {
public Object getInstance(ClazzDynamic clazzz, final lucee.transformer.dynamic.meta.FunctionMember fm, Object[] arguments) throws InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, UnmodifiableClassException, IOException {

boolean isConstr = fm instanceof lucee.transformer.dynamic.meta.Constructor;
ClazzDynamic clazzz = ClazzDynamic.getInstance(clazz, root, log);
// ClazzDynamic clazzz = ClazzDynamic.getInstance(clazz, root, log);
String className = fm.getClassName();
DynamicClassLoader loader = clazzz.getDynamicClassLoader(this);

if (loader.hasClass(className)) {
try {
return new Pair<FunctionMember, Object>(fm, loader.loadInstance(className));
return loader.loadInstance(className);

}
catch (Exception e) {
// simply ignore when fail
}
}
return createInstance(clazzz, fm, isConstr, className, loader);
return createInstance(clazzz, fm, fm instanceof lucee.transformer.dynamic.meta.Constructor, className, loader);
}

private Pair<FunctionMember, Object> createInstance(ClazzDynamic clazzz, lucee.transformer.dynamic.meta.FunctionMember fm, boolean isConstr, String className,
DynamicClassLoader loader) throws NoSuchMethodException, IOException, UnmodifiableClassException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException, SecurityException {
private Object createInstance(ClazzDynamic clazzz, lucee.transformer.dynamic.meta.FunctionMember fm, boolean isConstr, String className, DynamicClassLoader loader)
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException,
UnmodifiableClassException, IOException {

synchronized (SystemUtil.createToken("dyninvocer", className)) {
Class[] parameterClasses = fm.getArgumentClasses();
Expand Down Expand Up @@ -348,8 +301,7 @@ private Pair<FunctionMember, Object> createInstance(ClazzDynamic clazzz, lucee.t

cw.visitEnd();
byte[] barr = cw.toByteArray();
Object result = loader.loadInstance(className, barr);
return new Pair<FunctionMember, Object>(fm, result);
return loader.loadInstance(className, barr);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,9 @@ public Method getMethod(String methodName, Object[] args, boolean nameCaseSensit
}
}

// in case there are no arguments the code below will not find any match, nothing to convert
if (args.length == 0) return defaultValue;

// cache
StringBuilder sb = new StringBuilder(100).append(methodName).append(';'); // append(id()).
for (Object arg: args) {
Expand Down Expand Up @@ -468,6 +471,9 @@ public Constructor getConstructor(Object[] args, boolean convertArgument, boolea
}
}

// in case there are no arguments the code below will not find any match, nothing to convert
if (args.length == 0) return defaultValue;

// convert comparsion
Pair<Constructor, Object[]> result = null;
int _rating = 0;
Expand Down
2 changes: 1 addition & 1 deletion loader/build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<project default="core" basedir="." name="Lucee"
xmlns:resolver="antlib:org.apache.maven.resolver.ant">

<property name="version" value="6.2.0.230-SNAPSHOT"/>
<property name="version" value="6.2.0.231-SNAPSHOT"/>

<taskdef uri="antlib:org.apache.maven.resolver.ant" resource="org/apache/maven/resolver/ant/antlib.xml">
<classpath>
Expand Down
2 changes: 1 addition & 1 deletion loader/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>org.lucee</groupId>
<artifactId>lucee</artifactId>
<version>6.2.0.230-SNAPSHOT</version>
<version>6.2.0.231-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Lucee Loader Build</name>
Expand Down

0 comments on commit a29534a

Please sign in to comment.