nashorn/src/jdk/internal/dynalink/linker/GuardedInvocation.java
changeset 24719 f726e9d67629
parent 16245 6a1c6c8bc113
child 24720 75f8388b79df
equal deleted inserted replaced
23083:8c74590d5df1 24719:f726e9d67629
    88 import java.lang.invoke.MethodType;
    88 import java.lang.invoke.MethodType;
    89 import java.lang.invoke.SwitchPoint;
    89 import java.lang.invoke.SwitchPoint;
    90 import java.lang.invoke.WrongMethodTypeException;
    90 import java.lang.invoke.WrongMethodTypeException;
    91 import java.util.List;
    91 import java.util.List;
    92 import jdk.internal.dynalink.CallSiteDescriptor;
    92 import jdk.internal.dynalink.CallSiteDescriptor;
       
    93 import jdk.internal.dynalink.support.CatchExceptionCombinator;
    93 import jdk.internal.dynalink.support.Guards;
    94 import jdk.internal.dynalink.support.Guards;
       
    95 import jdk.nashorn.internal.runtime.options.Options;
    94 
    96 
    95 /**
    97 /**
    96  * Represents a conditionally valid method handle. It is an immutable triple of an invocation method handle, a guard
    98  * Represents a conditionally valid method handle. It is an immutable triple of an invocation method handle, a guard
    97  * method handle that defines the applicability of the invocation handle, and a switch point that can be used for
    99  * method handle that defines the applicability of the invocation handle, and a switch point that can be used for
    98  * external invalidation of the invocation handle. The invocation handle is suitable for invocation if the guard
   100  * external invalidation of the invocation handle. The invocation handle is suitable for invocation if the guard
   100  * switch point are optional; neither, one, or both can be present.
   102  * switch point are optional; neither, one, or both can be present.
   101  *
   103  *
   102  * @author Attila Szegedi
   104  * @author Attila Szegedi
   103  */
   105  */
   104 public class GuardedInvocation {
   106 public class GuardedInvocation {
       
   107     private static final boolean USE_FAST_REWRITE = Options.getBooleanProperty("nashorn.fastrewrite");
       
   108 
   105     private final MethodHandle invocation;
   109     private final MethodHandle invocation;
   106     private final MethodHandle guard;
   110     private final MethodHandle guard;
       
   111     private final Class<? extends Throwable> exception;
   107     private final SwitchPoint switchPoint;
   112     private final SwitchPoint switchPoint;
       
   113 
       
   114     /**
       
   115      * Creates a new guarded invocation. This invocation is unconditional as it has no invalidations.
       
   116      *
       
   117      * @param invocation the method handle representing the invocation. Must not be null.
       
   118      * @throws NullPointerException if invocation is null.
       
   119      */
       
   120     public GuardedInvocation(MethodHandle invocation) {
       
   121         this(invocation, null, null, null);
       
   122     }
   108 
   123 
   109     /**
   124     /**
   110      * Creates a new guarded invocation.
   125      * Creates a new guarded invocation.
   111      *
   126      *
   112      * @param invocation the method handle representing the invocation. Must not be null.
   127      * @param invocation the method handle representing the invocation. Must not be null.
   114      * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null to represent
   129      * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null to represent
   115      * an unconditional invocation, although that is unusual.
   130      * an unconditional invocation, although that is unusual.
   116      * @throws NullPointerException if invocation is null.
   131      * @throws NullPointerException if invocation is null.
   117      */
   132      */
   118     public GuardedInvocation(MethodHandle invocation, MethodHandle guard) {
   133     public GuardedInvocation(MethodHandle invocation, MethodHandle guard) {
   119         this(invocation, guard, null);
   134         this(invocation, guard, null, null);
       
   135     }
       
   136 
       
   137     /**
       
   138      * Creates a new guarded invocation.
       
   139      *
       
   140      * @param invocation the method handle representing the invocation. Must not be null.
       
   141      * @param switchPoint the optional switch point that can be used to invalidate this linkage.
       
   142      * @throws NullPointerException if invocation is null.
       
   143      */
       
   144     public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint) {
       
   145         this(invocation, null, switchPoint, null);
   120     }
   146     }
   121 
   147 
   122     /**
   148     /**
   123      * Creates a new guarded invocation.
   149      * Creates a new guarded invocation.
   124      *
   150      *
   128      * and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
   154      * and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
   129      * @param switchPoint the optional switch point that can be used to invalidate this linkage.
   155      * @param switchPoint the optional switch point that can be used to invalidate this linkage.
   130      * @throws NullPointerException if invocation is null.
   156      * @throws NullPointerException if invocation is null.
   131      */
   157      */
   132     public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint) {
   158     public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint) {
       
   159         this(invocation, guard, switchPoint, null);
       
   160     }
       
   161 
       
   162     /**
       
   163      * Creates a new guarded invocation.
       
   164      *
       
   165      * @param invocation the method handle representing the invocation. Must not be null.
       
   166      * @param guard the method handle representing the guard. Must have the same method type as the invocation, except
       
   167      * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
       
   168      * and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
       
   169      * @param switchPoint the optional switch point that can be used to invalidate this linkage.
       
   170      * @param exception the optional exception type that is expected to be thrown by the invocation and that also
       
   171      * invalidates the linkage.
       
   172      * @throws NullPointerException if invocation is null.
       
   173      */
       
   174     public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint, Class<? extends Throwable> exception) {
   133         invocation.getClass(); // NPE check
   175         invocation.getClass(); // NPE check
   134         this.invocation = invocation;
   176         this.invocation = invocation;
   135         this.guard = guard;
   177         this.guard = guard;
   136         this.switchPoint = switchPoint;
   178         this.switchPoint = switchPoint;
   137     }
   179         this.exception = exception;
   138 
   180     }
   139     /**
   181 
   140      * Creates a new guarded invocation.
       
   141      *
       
   142      * @param invocation the method handle representing the invocation. Must not be null.
       
   143      * @param switchPoint the optional switch point that can be used to invalidate this linkage.
       
   144      * @param guard the method handle representing the guard. Must have the same method type as the invocation, except
       
   145      * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
       
   146      * and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
       
   147      * @throws NullPointerException if invocation is null.
       
   148      */
       
   149     public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint, MethodHandle guard) {
       
   150         this(invocation, guard, switchPoint);
       
   151     }
       
   152     /**
   182     /**
   153      * Returns the invocation method handle.
   183      * Returns the invocation method handle.
   154      *
   184      *
   155      * @return the invocation method handle. It will never be null.
   185      * @return the invocation method handle. It will never be null.
   156      */
   186      */
   172      *
   202      *
   173      * @return the switch point that can be used to invalidate the invocation handle. Can be null.
   203      * @return the switch point that can be used to invalidate the invocation handle. Can be null.
   174      */
   204      */
   175     public SwitchPoint getSwitchPoint() {
   205     public SwitchPoint getSwitchPoint() {
   176         return switchPoint;
   206         return switchPoint;
       
   207     }
       
   208 
       
   209     /**
       
   210      * Returns the exception type that if thrown should be used to invalidate the linkage.
       
   211      *
       
   212      * @return the exception type that if thrown should be used to invalidate the linkage. Can be null.
       
   213      */
       
   214     public Class<? extends Throwable> getException() {
       
   215         return exception;
   177     }
   216     }
   178 
   217 
   179     /**
   218     /**
   180      * Returns true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
   219      * Returns true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
   181      * @return true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
   220      * @return true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
   204      * @param newInvocation the new invocation
   243      * @param newInvocation the new invocation
   205      * @param newGuard the new guard
   244      * @param newGuard the new guard
   206      * @return a new guarded invocation with the replaced methods and the same switch point as this invocation.
   245      * @return a new guarded invocation with the replaced methods and the same switch point as this invocation.
   207      */
   246      */
   208     public GuardedInvocation replaceMethods(MethodHandle newInvocation, MethodHandle newGuard) {
   247     public GuardedInvocation replaceMethods(MethodHandle newInvocation, MethodHandle newGuard) {
   209         return new GuardedInvocation(newInvocation, newGuard, switchPoint);
   248         return new GuardedInvocation(newInvocation, newGuard, switchPoint, exception);
   210     }
   249     }
   211 
   250 
   212     private GuardedInvocation replaceMethodsOrThis(MethodHandle newInvocation, MethodHandle newGuard) {
   251     private GuardedInvocation replaceMethodsOrThis(MethodHandle newInvocation, MethodHandle newGuard) {
   213         if(newInvocation == invocation && newGuard == guard) {
   252         if(newInvocation == invocation && newGuard == guard) {
   214             return this;
   253             return this;
   239         return replaceMethodsOrThis(linkerServices.asType(invocation, newType), guard == null ? null :
   278         return replaceMethodsOrThis(linkerServices.asType(invocation, newType), guard == null ? null :
   240             Guards.asType(linkerServices, guard, newType));
   279             Guards.asType(linkerServices, guard, newType));
   241     }
   280     }
   242 
   281 
   243     /**
   282     /**
       
   283      * Changes the type of the invocation, as if {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)} was
       
   284      * applied to its invocation and {@link LinkerServices#asType(MethodHandle, MethodType)} applied to its guard, if it
       
   285      * has one (with return type changed to boolean, and parameter count potentially truncated for the guard). If the
       
   286      * invocation doesn't change its type, returns this object.
       
   287      * @param linkerServices the linker services to use for the conversion
       
   288      * @param newType the new type of the invocation.
       
   289      * @return a guarded invocation with the new type applied to it.
       
   290      */
       
   291     public GuardedInvocation asTypeSafeReturn(LinkerServices linkerServices, MethodType newType) {
       
   292         return replaceMethodsOrThis(linkerServices.asTypeLosslessReturn(invocation, newType), guard == null ? null :
       
   293             Guards.asType(linkerServices, guard, newType));
       
   294     }
       
   295 
       
   296     /**
   244      * Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation
   297      * Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation
   245      * and its guard, if it has one (with return type changed to boolean for guard). If the invocation already is of the
   298      * and its guard, if it has one (with return type changed to boolean for guard). If the invocation already is of the
   246      * required type, returns this object.
   299      * required type, returns this object.
   247      * @param desc a call descriptor whose method type is adapted.
   300      * @param desc a call descriptor whose method type is adapted.
   248      * @return a guarded invocation with the new type applied to it.
   301      * @return a guarded invocation with the new type applied to it.
   301      * @return a composite method handle.
   354      * @return a composite method handle.
   302      */
   355      */
   303     public MethodHandle compose(MethodHandle switchpointFallback, MethodHandle guardFallback) {
   356     public MethodHandle compose(MethodHandle switchpointFallback, MethodHandle guardFallback) {
   304         final MethodHandle guarded =
   357         final MethodHandle guarded =
   305                 guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback);
   358                 guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback);
   306         return switchPoint == null ? guarded : switchPoint.guardWithTest(guarded, switchpointFallback);
   359         final MethodHandle catchGuarded = exception == null ? guarded : catchException(guarded, exception,
   307     }
   360                 MethodHandles.dropArguments(guardFallback, 0, exception));
   308 
   361         return switchPoint == null ? catchGuarded : switchPoint.guardWithTest(catchGuarded, switchpointFallback);
       
   362     }
       
   363 
       
   364     private static MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
       
   365         if(USE_FAST_REWRITE) {
       
   366             return CatchExceptionCombinator.catchException(target, exType, handler);
       
   367         }
       
   368         return MethodHandles.catchException(target, exType, handler);
       
   369     }
   309     private static void assertType(MethodHandle mh, MethodType type) {
   370     private static void assertType(MethodHandle mh, MethodType type) {
   310         if(!mh.type().equals(type)) {
   371         if(!mh.type().equals(type)) {
   311             throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type());
   372             throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type());
   312         }
   373         }
   313     }
   374     }