Skip to content

Commit

Permalink
Optimize extractSetMethod(...) and findFunction(...)
Browse files Browse the repository at this point in the history
* helper method for computing all conversion weights, with fail fast
* cache computed conversion weights to speedup `preferSignature()`
* optimize setter extracting
* one pass is enough, why two
  • Loading branch information
ZZZank authored Feb 1, 2025
1 parent 0d1ca69 commit 5cc4aa1
Show file tree
Hide file tree
Showing 3 changed files with 304 additions and 139 deletions.
31 changes: 13 additions & 18 deletions rhino/src/main/java/org/mozilla/javascript/JavaMembers.java
Original file line number Diff line number Diff line change
Expand Up @@ -727,28 +727,23 @@ private static MemberBox extractSetMethod(
// instance of the target arg to determine that.
//

// Make two passes: one to find a method with direct type assignment,
// and one to find a widening conversion.
for (int pass = 1; pass <= 2; ++pass) {
for (MemberBox method : methods) {
if (!isStatic || method.isStatic()) {
Class<?>[] params = method.argTypes;
if (params.length == 1) {
if (pass == 1) {
if (params[0] == type) {
return method;
}
} else {
if (pass != 2) Kit.codeBug();
if (params[0].isAssignableFrom(type)) {
return method;
}
}
MemberBox acceptableMatch = null;
for (MemberBox method : methods) {
if (!isStatic || method.isStatic()) {
Class<?>[] params = method.argTypes;
if (params.length == 1) {
if (params[0] == type) {
// perfect match, no need to continue scanning
return method;
}
if (acceptableMatch == null && params[0].isAssignableFrom(type)) {
// do not return at this point, there can still be perfect match
acceptableMatch = method;
}
}
}
}
return null;
return acceptableMatch;
}

private static MemberBox extractSetMethod(MemberBox[] methods, boolean isStatic) {
Expand Down
267 changes: 146 additions & 121 deletions rhino/src/main/java/org/mozilla/javascript/NativeJavaMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,144 +265,115 @@ int findCachedFunction(Context cx, Object[] args) {
static int findFunction(Context cx, MemberBox[] methodsOrCtors, Object[] args) {
if (methodsOrCtors.length == 0) {
return -1;
} else if (methodsOrCtors.length == 1) {
MemberBox member = methodsOrCtors[0];
Class<?>[] argTypes = member.argTypes;
int alength = argTypes.length;

if (member.vararg) {
alength--;
if (alength > args.length) {
return -1;
}
} else {
if (alength != args.length) {
return -1;
}
}
for (int j = 0; j != alength; ++j) {
if (!NativeJavaObject.canConvert(args[j], argTypes[j])) {
if (debug) printDebug("Rejecting (args can't convert) ", member, args);
return -1;
}
}
if (methodsOrCtors.length == 1) {
if (failFastConversionWeights(args, methodsOrCtors[0]) == null) {
return -1;
}
if (debug) printDebug("Found ", member, args);
if (debug) printDebug("Found ", methodsOrCtors[0], args);
return 0;
}

int firstBestFit = -1;
int[] firstBestFitWeights = null;

int[] extraBestFits = null;
int[][] extraBestFitWeights = null;
int extraBestFitsCount = 0;

search:
for (int i = 0; i < methodsOrCtors.length; i++) {
MemberBox member = methodsOrCtors[i];
Class<?>[] argTypes = member.argTypes;
int alength = argTypes.length;
if (member.vararg) {
alength--;
if (alength > args.length) {
continue search;
}
} else {
if (alength != args.length) {
continue search;
}
}
for (int j = 0; j < alength; j++) {
if (!NativeJavaObject.canConvert(args[j], argTypes[j])) {
if (debug) printDebug("Rejecting (args can't convert) ", member, args);
continue search;
}

final var weights = failFastConversionWeights(args, member);
if (weights == null) {
continue search;
}

if (firstBestFit < 0) {
if (debug) printDebug("Found first applicable ", member, args);
firstBestFit = i;
} else {
// Compare with all currently fit methods.
// The loop starts from -1 denoting firstBestFit and proceed
// until extraBestFitsCount to avoid extraBestFits allocation
// in the most common case of no ambiguity
int betterCount = 0; // number of times member was preferred over
// best fits
int worseCount = 0; // number of times best fits were preferred
// over member
for (int j = -1; j != extraBestFitsCount; ++j) {
int bestFitIndex;
if (j == -1) {
bestFitIndex = firstBestFit;
} else {
bestFitIndex = extraBestFits[j];
}
MemberBox bestFit = methodsOrCtors[bestFitIndex];
if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
&& bestFit.isPublic() != member.isPublic()) {
// When FEATURE_ENHANCED_JAVA_ACCESS gives us access
// to non-public members, continue to prefer public
// methods in overloading
if (!bestFit.isPublic()) ++betterCount;
else ++worseCount;
firstBestFitWeights = weights;
continue search;
}

// Compare with all currently fit methods.
// The loop starts from -1 denoting firstBestFit and proceed
// until extraBestFitsCount to avoid extraBestFits allocation
// in the most common case of no ambiguity
int betterCount = 0; // number of times member was preferred over
// best fits
int worseCount = 0; // number of times best fits were preferred
// over member
for (int j = -1; j != extraBestFitsCount; ++j) {
int bestFitIndex = j < 0 ? firstBestFit : extraBestFits[j];
MemberBox bestFit = methodsOrCtors[bestFitIndex];
int[] bestFitWeights = j < 0 ? firstBestFitWeights : extraBestFitWeights[j];
if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
&& bestFit.isPublic() != member.isPublic()) {
// When FEATURE_ENHANCED_JAVA_ACCESS gives us access
// to non-public members, continue to prefer public
// methods in overloading
if (!bestFit.isPublic()) ++betterCount;
else ++worseCount;
} else {
int preference =
preferSignature(args, member, weights, bestFit, bestFitWeights);
if (preference == PREFERENCE_AMBIGUOUS) {
break;
} else if (preference == PREFERENCE_FIRST_ARG) {
++betterCount;
} else if (preference == PREFERENCE_SECOND_ARG) {
++worseCount;
} else {
int preference =
preferSignature(
args,
argTypes,
member.vararg,
bestFit.argTypes,
bestFit.vararg);
if (preference == PREFERENCE_AMBIGUOUS) {
break;
} else if (preference == PREFERENCE_FIRST_ARG) {
++betterCount;
} else if (preference == PREFERENCE_SECOND_ARG) {
++worseCount;
} else {
if (preference != PREFERENCE_EQUAL) Kit.codeBug();
// This should not happen in theory
// but on some JVMs, Class.getMethods will return all
if (preference != PREFERENCE_EQUAL) Kit.codeBug();
// This should not happen in theory
// but on some JVMs, Class.getMethods will return all
// static methods of the class hierarchy, even if
// a derived class's parameters match exactly.
// We want to call the derived class's method.
if (bestFit.isStatic()
&& bestFit.getDeclaringClass()
.isAssignableFrom(member.getDeclaringClass())) {
// On some JVMs, Class.getMethods will return all
// static methods of the class hierarchy, even if
// a derived class's parameters match exactly.
// We want to call the derived class's method.
if (bestFit.isStatic()
&& bestFit.getDeclaringClass()
.isAssignableFrom(member.getDeclaringClass())) {
// On some JVMs, Class.getMethods will return all
// static methods of the class hierarchy, even if
// a derived class's parameters match exactly.
// We want to call the derived class's method.
if (debug)
printDebug("Substituting (overridden static)", member, args);
if (j == -1) {
firstBestFit = i;
} else {
extraBestFits[j] = i;
}
if (debug) printDebug("Substituting (overridden static)", member, args);
if (j == -1) {
firstBestFit = i;
firstBestFitWeights = weights;
} else {
if (debug)
printDebug("Ignoring same signature member ", member, args);
extraBestFits[j] = i;
extraBestFitWeights[j] = weights;
}
continue search;
} else {
if (debug) printDebug("Ignoring same signature member ", member, args);
}
continue search;
}
}
if (betterCount == 1 + extraBestFitsCount) {
// member was preferred over all best fits
if (debug) printDebug("New first applicable ", member, args);
firstBestFit = i;
extraBestFitsCount = 0;
} else if (worseCount == 1 + extraBestFitsCount) {
// all best fits were preferred over member, ignore it
if (debug) printDebug("Rejecting (all current bests better) ", member, args);
} else {
// some ambiguity was present, add member to best fit set
if (debug) printDebug("Added to best fit set ", member, args);
if (extraBestFits == null) {
// Allocate maximum possible array
extraBestFits = new int[methodsOrCtors.length - 1];
}
extraBestFits[extraBestFitsCount] = i;
++extraBestFitsCount;
}
if (betterCount == 1 + extraBestFitsCount) {
// member was preferred over all best fits
if (debug) printDebug("New first applicable ", member, args);
firstBestFit = i;
firstBestFitWeights = weights;
extraBestFitsCount = 0;
} else if (worseCount == 1 + extraBestFitsCount) {
// all best fits were preferred over member, ignore it
if (debug) printDebug("Rejecting (all current bests better) ", member, args);
} else {
// some ambiguity was present, add member to best fit set
if (debug) printDebug("Added to best fit set ", member, args);
if (extraBestFits == null) {
// Allocate maximum possible array
extraBestFits = new int[methodsOrCtors.length - 1];
extraBestFitWeights = new int[methodsOrCtors.length - 1][];
}
extraBestFits[extraBestFitsCount] = i;
extraBestFitWeights[extraBestFitsCount] = weights;
++extraBestFitsCount;
}
}

Expand Down Expand Up @@ -453,24 +424,40 @@ static int findFunction(Context cx, MemberBox[] methodsOrCtors, Object[] args) {
private static final int PREFERENCE_AMBIGUOUS = 3;

/**
* Determine which of two signatures is the closer fit. Returns one of PREFERENCE_EQUAL,
* PREFERENCE_FIRST_ARG, PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS.
* Determine which of two signatures is the closer fit. Returns one of {@link
* #PREFERENCE_EQUAL}, {@link #PREFERENCE_FIRST_ARG}, {@link #PREFERENCE_SECOND_ARG}, or {@link
* #PREFERENCE_AMBIGUOUS}.
*/
private static int preferSignature(
Object[] args, Class<?>[] sig1, boolean vararg1, Class<?>[] sig2, boolean vararg2) {
Object[] args,
MemberBox member1,
int[] computedWeights1,
MemberBox member2,
int[] computedWeights2) {
final var types1 = member1.argTypes;
final var types2 = member2.argTypes;

int totalPreference = 0;
for (int j = 0; j < args.length; j++) {
Class<?> type1 = vararg1 && j >= sig1.length ? sig1[sig1.length - 1] : sig1[j];
Class<?> type2 = vararg2 && j >= sig2.length ? sig2[sig2.length - 1] : sig2[j];
final var type1 =
member1.vararg && j >= types1.length ? types1[types1.length - 1] : types1[j];
final var type2 =
member2.vararg && j >= types2.length ? types2[types2.length - 1] : types2[j];
if (type1 == type2) {
continue;
}
Object arg = args[j];
final var arg = args[j];

// Determine which of type1, type2 is easier to convert from arg.

int rank1 = NativeJavaObject.getConversionWeight(arg, type1);
int rank2 = NativeJavaObject.getConversionWeight(arg, type2);
final var rank1 =
j < computedWeights1.length
? computedWeights1[j]
: NativeJavaObject.getConversionWeight(arg, type1);
final var rank2 =
j < computedWeights2.length
? computedWeights2[j]
: NativeJavaObject.getConversionWeight(arg, type2);

int preference;
if (rank1 < rank2) {
Expand Down Expand Up @@ -501,6 +488,44 @@ private static int preferSignature(
return totalPreference;
}

/**
* 1. {@code args} is too short for {@code member} calling -> return {@code null}
*
* <p>2. at least one arg cannot be converted -> return {@code null}
*
* <p>3. otherwise -> return an int array holding all computed conversion weights, whose length
* will be {@code args.length} for non-vararg member or {@code args.length-1} for vararg member
*
* @see NativeJavaObject#getConversionWeight(Object, Class)
* @see NativeJavaObject#canConvert(Object, Class)
*/
static int[] failFastConversionWeights(Object[] args, MemberBox member) {
final var argTypes = member.argTypes;
var typeLen = argTypes.length;
if (member.vararg) {
typeLen--;
if (typeLen > args.length) {
return null;
}
} else {
if (typeLen != args.length) {
return null;
}
}
final var weights = new int[typeLen];
for (int i = 0; i < typeLen; i++) {
final var weight = NativeJavaObject.getConversionWeight(args[i], argTypes[i]);
if (weight >= NativeJavaObject.CONVERSION_NONE) {
if (debug) {
printDebug("Rejecting (args can't convert) ", member, args);
}
return null;
}
weights[i] = weight;
}
return weights;
}

private static final boolean debug = false;

private static void printDebug(String msg, MemberBox member, Object[] args) {
Expand Down
Loading

0 comments on commit 5cc4aa1

Please sign in to comment.