Annotation Type Slice
-
@Target({}) @Retention(RUNTIME) public @interface SliceA Slice identifies a section of a method to search for injection points.Using slices provides for a much more expressive way of identifying target injection points in a method, which has two advantages:
- Encoding assumptions about the structure of a method makes it easier to detect (and fail-fast) when a method has changed in an unexpected way.
- Injection points using slices are less brittle than points specified with large ordinals because they allow injection points to be specified with reference to characteristics of the method rather than arbitrary ordinal values.
Consider the following example:
private void foo(Bar bar) { bar.update(); List<Thing> list = bar.getThings(); for (Thing thing : list) { thing.processStuff(); } Thing specialThing = bar.getSpecialThing(); if (specialThing.isReady()) { specialThing.processStuff(); bar.update(); } bar.notifyFoo(); }Let's assume we are interested in applying a
Redirectto the processStuff() method call within the if block. Using ordinal we can achieve this as follows:@At(value = "INVOKE", target = "processStuff", ordinal = 1)
This will work fine initially. However consider the case that in a future version of the target library the method is altered and an additional call to processStuff() is added:
private void foo(Bar bar) { bar.update(); List<Thing> list = bar.getThings(); for (Thing thing : list) { thing.processStuff(); }// A new call to processStuff, which now has the ordinal 1bar.getActiveThing().processStuff(); Thing specialThing = bar.getSpecialThing(); if (specialThing.isReady()) { specialThing.processStuff(); bar.update(); } bar.notifyFoo(); }It's still pretty easy for a human to identify the correct point since the original if hasn't changed. However the use of ordinals means that the injection is now wrong and must be fixed by hand.
We can make the injection point more expressive and reliable by using a Slice. We know in this example that the call to processStuff() we want is the one immediately after a call to isReady(). We can slice the method at the call to isReady() and modify the injector accordingly:
@Inject( slice = @Slice( from = @At(value = "INVOKE", target = "isReady") ) at = @At(value = "INVOKE", target = "processStuff", ordinal = 0) )The ordinal is specified as 0 because the scope of the injection point is now the region of the method defined by the slice .
Slices can be specified using a
from()point, ato()point, or both.Callback Injectorsusing multiple injection points can distinguish different slices usingid()and then specify the slice to use in theAt.slice()argument.
-
-
Optional Element Summary
Optional Elements Modifier and Type Optional Element Description AtfromInjection point which specifies the start of the slice region.java.lang.StringidThe identifier for this slice, specified using theAt.slice()value (if omitted, this slice becomes the default slice and applies to all undecoratedAtqueries).AttoInjection point which specifies the end of the slice region.
-
-
-
Element Detail
-
id
java.lang.String id
The identifier for this slice, specified using theAt.slice()value (if omitted, this slice becomes the default slice and applies to all undecoratedAtqueries).This value can be safely ignored for injector types which only accept a single query (eg.
Redirectand others). However sinceInjectinjectors can have multipleAtqueries, it may be desirable to have multiple slices as well. When specifying multiple slices, each should be designed a unique id which can then be referenced in the corresponding At.There are no specifications or restrictions for valid ids, however it is recommended that the id in some way describe the slice, thus allowing the At query to be read without necessarily reading the slice itself. For example, if the slice is selecting all instructions before the first call to some method init, then using an id "beforeInit makes sense. Using before, after and between prefixes as a loose standard is considered good practice.
This value defaults to an empty string, the empty string is used as a default identifier throughout the injection subsystem, and any
Atwhich doesn't specify a slice explicitly will use this identifer. Specifying id or slice for injectors which only support a single slice is ignored internally, so you may use this field to give a descriptive name to the slice if you wish.- Returns:
- The identifier for this slice
- Default:
- ""
-
-
-
from
At from
Injection point which specifies the start of the slice region.Ats supplied here should generally use aInjectionPoint.Specifierin order to identify which instruction should be used for queries which return multiple results. The specifier is supplied by appending the specifier type to the injection point type as follows:@At(value = "INVOKE:LAST", ... )
If from is not supplied then
to()must be supplied. It is allowed to specify both from and to as long as the points select a region with positive size (eg the insn returned by to must appear after that selected by from in the method body).- Returns:
- the start point of the slice
- Default:
- @org.spongepowered.asm.mixin.injection.At("HEAD")
-
-
-
to
At to
Injection point which specifies the end of the slice region. Likefrom(),Ats supplied here should generally specify aInjectionPoint.Specifierin order to identify which instruction should be used for queries which return multiple results. The specifier is supplied by appending the specifier type to the injection point type as follows:@At(value = "INVOKE:LAST", ... )
If to is not supplied then
from()must be supplied. It is allowed to specify both from and to as long as the points select a region with positive size (eg the insn returned by to must appear after that selected by from in the method body).- Returns:
- the start point of the slice
- Default:
- @org.spongepowered.asm.mixin.injection.At("TAIL")
-
-