diff --git a/src/share/classes/com/sun/crypto/provider/GCTR.java b/src/share/classes/com/sun/crypto/provider/GCTR.java index d4cd740af7bea80f35b0f532ff981b69110a4d44..f8a3eaa0a4cd11707f655319d6581e283f3f582f 100644 --- a/src/share/classes/com/sun/crypto/provider/GCTR.java +++ b/src/share/classes/com/sun/crypto/provider/GCTR.java @@ -38,7 +38,17 @@ import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; * under section 6.5. It needs to be constructed w/ an initialized * cipher object, and initial counter block(ICB). Given an input X * of arbitrary length, it processes and returns an output which has - * the same length as X. + * the same length as X. The invariants of this class are: + * + * (1) The length of intialCounterBlk (and also of its clones, e.g., + * fields counter and counterSave) is equal to AES_BLOCK_SIZE. + * + * (2) After construction, the field counter never becomes null, it + * always contains a byte array of length AES_BLOCK_SIZE. + * + * If any invariant is broken, failures can occur because the + * AESCrypt.encryptBlock method can be intrinsified on the HotSpot VM + * (see JDK-8067648 for details). * *
This function is used in the implementation of GCM mode.
*
@@ -59,6 +69,10 @@ final class GCTR {
// NOTE: cipher should already be initialized
GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
this.aes = cipher;
+ if (initialCounterBlk.length != AES_BLOCK_SIZE) {
+ throw new RuntimeException("length of initial counter block (" + initialCounterBlk.length +
+ ") not equal to AES_BLOCK_SIZE (" + AES_BLOCK_SIZE + ")");
+ }
this.icb = initialCounterBlk;
this.counter = icb.clone();
}
@@ -137,6 +151,8 @@ final class GCTR {
* Restores the content of this object to the previous saved one.
*/
void restore() {
- this.counter = this.counterSave;
+ if (this.counterSave != null) {
+ this.counter = this.counterSave;
+ }
}
}
diff --git a/src/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
index e9d2526cd8d336b9f082065973f4345ff7d57278..cabd0716341678ae1b0fbe9f8471669e2d09c8e9 100644
--- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java
+++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
@@ -31,7 +31,6 @@ import java.util.Arrays;
import sun.invoke.util.VerifyAccess;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.LambdaForm.*;
-import static java.lang.invoke.LambdaForm.BasicType.*;
import static java.lang.invoke.MethodTypeForm.*;
import static java.lang.invoke.MethodHandleStatics.*;
import java.lang.ref.WeakReference;
diff --git a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
index 6e8d091a6c5afbd6cdbec879d93eb6ed19543c3a..d9c6f0ffbdfdb2954672a47248522cfad8cc4ab1 100644
--- a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
+++ b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
@@ -56,9 +56,11 @@ class InvokerBytecodeGenerator {
private static final String OBJ = "java/lang/Object";
private static final String OBJARY = "[Ljava/lang/Object;";
+ private static final String MH_SIG = "L" + MH + ";";
private static final String LF_SIG = "L" + LF + ";";
private static final String LFN_SIG = "L" + LFN + ";";
private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
+ private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V";
private static final String CLL_SIG = "(L" + CLS + ";L" + OBJ + ";)L" + OBJ + ";";
/** Name of its super class*/
@@ -616,6 +618,15 @@ class InvokerBytecodeGenerator {
return g.loadMethod(g.generateCustomizedCodeBytes());
}
+ /** Generates code to check that actual receiver and LambdaForm matches */
+ private boolean checkActualReceiver() {
+ // Expects MethodHandle on the stack and actual receiver MethodHandle in slot #0
+ mv.visitInsn(Opcodes.DUP);
+ mv.visitVarInsn(Opcodes.ALOAD, localsMap[0]);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "assertSame", LLV_SIG, false);
+ return true;
+ }
+
/**
* Generate an invoker method for the passed {@link LambdaForm}.
*/
@@ -635,6 +646,16 @@ class InvokerBytecodeGenerator {
mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true);
}
+ if (lambdaForm.customized != null) {
+ // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute
+ // receiver MethodHandle (at slot #0) with an embedded constant and use it instead.
+ // It enables more efficient code generation in some situations, since embedded constants
+ // are compile-time constants for JIT compiler.
+ mv.visitLdcInsn(constantPlaceholder(lambdaForm.customized));
+ mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+ assert(checkActualReceiver()); // expects MethodHandle on top of the stack
+ mv.visitVarInsn(Opcodes.ASTORE, localsMap[0]);
+ }
// iterate over the form's names, generating bytecode instructions for each
// start iterating at the first name following the arguments
diff --git a/src/share/classes/java/lang/invoke/Invokers.java b/src/share/classes/java/lang/invoke/Invokers.java
index 14aef1bd7b46284b2209727dca4c511dcbd82fb0..e09dc3686c4fecce346a9e2f1716add5a1e105b0 100644
--- a/src/share/classes/java/lang/invoke/Invokers.java
+++ b/src/share/classes/java/lang/invoke/Invokers.java
@@ -247,6 +247,7 @@ class Invokers {
int nameCursor = OUTARG_LIMIT;
final int MTYPE_ARG = customized ? -1 : nameCursor++; // might be last in-argument
final int CHECK_TYPE = nameCursor++;
+ final int CHECK_CUSTOM = (CUSTOMIZE_THRESHOLD >= 0) ? nameCursor++ : -1;
final int LINKER_CALL = nameCursor++;
MethodType invokerFormType = mtype.invokerType();
if (isLinker) {
@@ -279,6 +280,9 @@ class Invokers {
// mh.invokeGeneric(a*):R => checkGenericType(mh, TYPEOF(a*:R)).invokeBasic(a*)
outArgs[0] = names[CHECK_TYPE];
}
+ if (CHECK_CUSTOM != -1) {
+ names[CHECK_CUSTOM] = new Name(NF_checkCustomized, names[CALL_MH]);
+ }
names[LINKER_CALL] = new Name(outCallType, outArgs);
lform = new LambdaForm(debugName, INARG_LIMIT, names);
if (isLinker)
@@ -386,11 +390,32 @@ class Invokers {
return ((CallSite)site).getTarget();
}
+ /*non-public*/ static
+ @ForceInline
+ void checkCustomized(Object o) {
+ MethodHandle mh = (MethodHandle)o;
+ if (mh.form.customized == null) {
+ maybeCustomize(mh);
+ }
+ }
+
+ /*non-public*/ static
+ @DontInline
+ void maybeCustomize(MethodHandle mh) {
+ byte count = mh.customizationCount;
+ if (count >= CUSTOMIZE_THRESHOLD) {
+ mh.customize();
+ } else {
+ mh.customizationCount = (byte)(count+1);
+ }
+ }
+
// Local constant functions:
private static final NamedFunction
NF_checkExactType,
NF_checkGenericType,
- NF_getCallSiteTarget;
+ NF_getCallSiteTarget,
+ NF_checkCustomized;
static {
try {
NamedFunction nfs[] = {
@@ -399,7 +424,9 @@ class Invokers {
NF_checkGenericType = new NamedFunction(Invokers.class
.getDeclaredMethod("checkGenericType", Object.class, Object.class)),
NF_getCallSiteTarget = new NamedFunction(Invokers.class
- .getDeclaredMethod("getCallSiteTarget", Object.class))
+ .getDeclaredMethod("getCallSiteTarget", Object.class)),
+ NF_checkCustomized = new NamedFunction(Invokers.class
+ .getDeclaredMethod("checkCustomized", Object.class))
};
for (NamedFunction nf : nfs) {
// Each nf must be statically invocable or we get tied up in our bootstraps.
diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java
index 2129f8618bdb2273ab73a39380f6e57a5a5d58f4..5c441071b922302039b034c518d26d46153bdccc 100644
--- a/src/share/classes/java/lang/invoke/LambdaForm.java
+++ b/src/share/classes/java/lang/invoke/LambdaForm.java
@@ -120,12 +120,14 @@ class LambdaForm {
final int arity;
final int result;
final boolean forceInline;
+ final MethodHandle customized;
@Stable final Name[] names;
final String debugName;
MemberName vmentry; // low-level behavior, or null if not yet prepared
private boolean isCompiled;
- volatile Object transformCache; // managed by LambdaFormEditor
+ // Either a LambdaForm cache (managed by LambdaFormEditor) or a link to uncustomized version (for customized LF)
+ volatile Object transformCache;
public static final int VOID_RESULT = -1, LAST_RESULT = -2;
@@ -244,16 +246,17 @@ class LambdaForm {
LambdaForm(String debugName,
int arity, Name[] names, int result) {
- this(debugName, arity, names, result, true);
+ this(debugName, arity, names, result, /*forceInline=*/true, /*customized=*/null);
}
LambdaForm(String debugName,
- int arity, Name[] names, int result, boolean forceInline) {
+ int arity, Name[] names, int result, boolean forceInline, MethodHandle customized) {
assert(namesOK(arity, names));
this.arity = arity;
this.result = fixResult(result, names);
this.names = names.clone();
this.debugName = fixDebugName(debugName);
this.forceInline = forceInline;
+ this.customized = customized;
int maxOutArity = normalize();
if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) {
// Cannot use LF interpreter on very high arity expressions.
@@ -263,21 +266,21 @@ class LambdaForm {
}
LambdaForm(String debugName,
int arity, Name[] names) {
- this(debugName, arity, names, LAST_RESULT, true);
+ this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true, /*customized=*/null);
}
LambdaForm(String debugName,
int arity, Name[] names, boolean forceInline) {
- this(debugName, arity, names, LAST_RESULT, forceInline);
+ this(debugName, arity, names, LAST_RESULT, forceInline, /*customized=*/null);
}
LambdaForm(String debugName,
Name[] formals, Name[] temps, Name result) {
this(debugName,
- formals.length, buildNames(formals, temps, result), LAST_RESULT, true);
+ formals.length, buildNames(formals, temps, result), LAST_RESULT, /*forceInline=*/true, /*customized=*/null);
}
LambdaForm(String debugName,
Name[] formals, Name[] temps, Name result, boolean forceInline) {
this(debugName,
- formals.length, buildNames(formals, temps, result), LAST_RESULT, forceInline);
+ formals.length, buildNames(formals, temps, result), LAST_RESULT, forceInline, /*customized=*/null);
}
private static Name[] buildNames(Name[] formals, Name[] temps, Name result) {
@@ -291,10 +294,6 @@ class LambdaForm {
}
private LambdaForm(String sig) {
- this(sig, true);
- }
-
- private LambdaForm(String sig, boolean forceInline) {
// Make a blank lambda form, which returns a constant zero or null.
// It is used as a template for managing the invocation of similar forms that are non-empty.
// Called only from getPreparedForm.
@@ -303,7 +302,8 @@ class LambdaForm {
this.result = (signatureReturn(sig) == V_TYPE ? -1 : arity);
this.names = buildEmptyNames(arity, sig);
this.debugName = "LF.zero";
- this.forceInline = forceInline;
+ this.forceInline = true;
+ this.customized = null;
assert(nameRefsAreLegal());
assert(isEmpty());
assert(sig.equals(basicTypeSignature())) : sig + " != " + basicTypeSignature();
@@ -375,6 +375,31 @@ class LambdaForm {
return true;
}
+ /** Customize LambdaForm for a particular MethodHandle */
+ LambdaForm customize(MethodHandle mh) {
+ LambdaForm customForm = new LambdaForm(debugName, arity, names, result, forceInline, mh);
+ if (COMPILE_THRESHOLD > 0 && isCompiled) {
+ // If shared LambdaForm has been compiled, compile customized version as well.
+ customForm.compileToBytecode();
+ }
+ customForm.transformCache = this; // LambdaFormEditor should always use uncustomized form.
+ return customForm;
+ }
+
+ /** Get uncustomized flavor of the LambdaForm */
+ LambdaForm uncustomize() {
+ if (customized == null) {
+ return this;
+ }
+ assert(transformCache != null); // Customized LambdaForm should always has a link to uncustomized version.
+ LambdaForm uncustomizedForm = (LambdaForm)transformCache;
+ if (COMPILE_THRESHOLD > 0 && isCompiled) {
+ // If customized LambdaForm has been compiled, compile uncustomized version as well.
+ uncustomizedForm.compileToBytecode();
+ }
+ return uncustomizedForm;
+ }
+
/** Renumber and/or replace params so that they are interned and canonically numbered.
* @return maximum argument list length among the names (since we have to pass over them anyway)
*/
@@ -417,8 +442,8 @@ class LambdaForm {
for (int i = arity; i < names.length; i++) {
names[i].internArguments();
}
- assert(nameRefsAreLegal());
}
+ assert(nameRefsAreLegal());
return maxOutArity;
}
diff --git a/src/share/classes/java/lang/invoke/LambdaFormEditor.java b/src/share/classes/java/lang/invoke/LambdaFormEditor.java
index 1c23e94995ac7c5818741fe40c6a9cfa27bfa4de..7bc2dfbebade51557bb3269c61c5580e88b1f7c4 100644
--- a/src/share/classes/java/lang/invoke/LambdaFormEditor.java
+++ b/src/share/classes/java/lang/invoke/LambdaFormEditor.java
@@ -51,7 +51,10 @@ class LambdaFormEditor {
static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) {
// TO DO: Consider placing intern logic here, to cut down on duplication.
// lambdaForm = findPreexistingEquivalent(lambdaForm)
- return new LambdaFormEditor(lambdaForm);
+
+ // Always use uncustomized version for editing.
+ // It helps caching and customized LambdaForms reuse transformCache field to keep a link to uncustomized version.
+ return new LambdaFormEditor(lambdaForm.uncustomize());
}
/** A description of a cached transform, possibly associated with the result of the transform.
diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java
index 9b586a3179cce393c3fd09c23c07f1782f2e9816..79730644a232729a99b673e536ab50db780fd326 100644
--- a/src/share/classes/java/lang/invoke/MethodHandle.java
+++ b/src/share/classes/java/lang/invoke/MethodHandle.java
@@ -434,6 +434,8 @@ public abstract class MethodHandle {
// form is not private so that invokers can easily fetch it
/*private*/ MethodHandle asTypeCache;
// asTypeCache is not private so that invokers can easily fetch it
+ /*non-public*/ byte customizationCount;
+ // customizationCount should be accessible from invokers
/**
* Reports the type of this method handle.
@@ -454,9 +456,9 @@ public abstract class MethodHandle {
type.getClass(); // explicit NPE
form.getClass(); // explicit NPE
this.type = type;
- this.form = form;
+ this.form = form.uncustomize();
- form.prepare(); // TO DO: Try to delay this step until just before invocation.
+ this.form.prepare(); // TO DO: Try to delay this step until just before invocation.
}
/**
@@ -1425,12 +1427,24 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
*/
/*non-public*/
void updateForm(LambdaForm newForm) {
+ assert(newForm.customized == null || newForm.customized == this);
if (form == newForm) return;
newForm.prepare(); // as in MethodHandle.