It turns out our invokeExact call sites in code generated by JavaAdapterBytecodeGenerator go megamorphic real fast. The reason for that is that every ScriptFunction method handle that we delegate to is also wrapped in a converter method handle using asType(), making it unique per adapter instance.
The strategy to fix this is to avoid argument conversion when possible, and to make it explicit in bytecode when avoidance is not possible. Instead of asType()ing the function method handle, we retrieve it as-is (the generic version that takes Object arguments and returns Object), and perform boxing of arguments and unboxing of the return value separately.
Conversion to primitive types, void, and String is handled with explicit static method invocations. Conversion to SAM types, Java array types, List type etc. are handled by retrieving and storing a converter method handle through LinkerServices, and invoking it in a separate invokeExact immediately after the delegate method's invokeExact.
For reasons of security, when the return value conversion needs a method handle, we need to retrieve those converter method handles during either adapter class (class-level overrides) or adapter instance creation and store them into fields. This will work exactly as Bootstrap's asType() worked before, and will respect the MethodHandles.Lookup related to the currently processed call site.