All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.inject.internal.aop.FastClass Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
/*
 * Copyright (C) 2020 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.internal.aop;

import static com.google.inject.internal.BytecodeGen.FASTCLASS_BY_GUICE_MARKER;
import static com.google.inject.internal.aop.BytecodeTasks.box;
import static com.google.inject.internal.aop.BytecodeTasks.unpackArguments;
import static java.lang.reflect.Modifier.FINAL;
import static java.lang.reflect.Modifier.PRIVATE;
import static java.lang.reflect.Modifier.PUBLIC;
import static java.lang.reflect.Modifier.STATIC;
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.PUTSTATIC;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_8;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.Collection;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

/**
 * Generates fast-classes.
 *
 * 

Each fast-class has a single constructor that takes an index. It also has an instance method * that takes a context object and an array of argument objects which it combines with the index to * call the shared static trampoline. Each fast-class instance therefore acts like a bound invoker * to the appropriate constructor or method of the host class. * *

A handle to the fast-class constructor is used as the invoker table, mapping index to invoker. * *

Fast-classes have the following pseudo-Java structure: * *

 * public final class HostClass$$FastClassByGuice
 *   implements BiFunction // each fast-class instance represents a bound invoker
 * {
 *   private final int index; // the bound trampoline index
 *
 *   public HostClass$$FastClassByGuice(int index) {
 *     this.index = index;
 *   }
 *
 *   public Object apply(Object context, Object args) {
 *     return GUICE$TRAMPOLINE(index, context, (Object[]) args);
 *   }
 *
 *   public static Object GUICE$TRAMPOLINE(int index, Object context, Object[] args) {
 *     switch (index) {
 *       case 0: {
 *         return new HostClass(...);
 *       }
 *       case 1: {
 *         return ((HostClass) context).instanceMethod(...);
 *       }
 *       case 2: {
 *         return HostClass.staticMethod(...);
 *       }
 *     }
 *     return null;
 *   }
 * }
 * 
* * @author [email protected] (Stuart McCulloch) */ final class FastClass extends AbstractGlueGenerator { private static final String[] FAST_CLASS_API = {"java/util/function/BiFunction"}; private static final String INVOKERS_NAME = "GUICE$INVOKERS"; private static final String INVOKERS_DESCRIPTOR = "Ljava/lang/invoke/MethodHandle;"; private static final Type INDEX_TO_INVOKER_METHOD_TYPE = Type.getMethodType("(I)Ljava/util/function/BiFunction;"); private static final String RAW_INVOKER_DESCRIPTOR = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"; private static final String OBJECT_ARRAY_TYPE = Type.getInternalName(Object[].class); private final boolean hostIsInterface; FastClass(Class hostClass) { super(hostClass, FASTCLASS_BY_GUICE_MARKER); hostIsInterface = hostClass.isInterface(); } @Override protected byte[] generateGlue(Collection members) { ClassWriter cw = new ClassWriter(COMPUTE_MAXS); MethodVisitor mv; // target Java8 because that's all we need for the generated trampoline code cw.visit(V1_8, PUBLIC | FINAL | ACC_SUPER, proxyName, null, "java/lang/Object", FAST_CLASS_API); cw.visitSource(GENERATED_SOURCE, null); // this shared field contains the constructor handle adapted to look like an invoker table cw.visitField(PUBLIC | STATIC | FINAL, INVOKERS_NAME, INVOKERS_DESCRIPTOR, null, null) .visitEnd(); setupInvokerTable(cw); cw.visitField(PRIVATE | FINAL, "index", "I", null, null).visitEnd(); // fast-class constructor that takes an index and binds it mv = cw.visitMethod(PUBLIC, "", "(I)V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); mv.visitVarInsn(ILOAD, 1); mv.visitFieldInsn(PUTFIELD, proxyName, "index", "I"); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); // fast-class invoker function that takes a context object and argument array mv = cw.visitMethod(PUBLIC, "apply", RAW_INVOKER_DESCRIPTOR, null, null); mv.visitCode(); // combine bound index with context object and argument array mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, proxyName, "index", "I"); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 2); mv.visitTypeInsn(CHECKCAST, OBJECT_ARRAY_TYPE); // call into the shared trampoline mv.visitMethodInsn(INVOKESTATIC, proxyName, TRAMPOLINE_NAME, TRAMPOLINE_DESCRIPTOR, false); mv.visitInsn(ARETURN); mv.visitMaxs(0, 0); mv.visitEnd(); generateTrampoline(cw, members); cw.visitEnd(); return cw.toByteArray(); } /** Generate static initializer to setup invoker table based on the fast-class constructor. */ private void setupInvokerTable(ClassWriter cw) { MethodVisitor mv = cw.visitMethod(PRIVATE | STATIC, "", "()V", null, null); mv.visitCode(); Handle constructorHandle = new Handle(H_NEWINVOKESPECIAL, proxyName, "", "(I)V", false); mv.visitLdcInsn(constructorHandle); // adapt constructor handle to make it look like an invoker table (int -> BiFunction) mv.visitLdcInsn(INDEX_TO_INVOKER_METHOD_TYPE); mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "asType", "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false); mv.visitFieldInsn(PUTSTATIC, proxyName, INVOKERS_NAME, INVOKERS_DESCRIPTOR); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } @Override protected void generateConstructorInvoker(MethodVisitor mv, Constructor constructor) { mv.visitTypeInsn(NEW, hostName); mv.visitInsn(DUP); // fast-class constructor invokers don't use the context object unpackArguments(mv, constructor.getParameterTypes()); mv.visitMethodInsn( INVOKESPECIAL, hostName, "", Type.getConstructorDescriptor(constructor), false); } @Override protected void generateMethodInvoker(MethodVisitor mv, Method method) { int invokeOpcode; if ((method.getModifiers() & STATIC) == 0) { // context object is the instance whose method we want to call mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, hostName); invokeOpcode = hostIsInterface ? INVOKEINTERFACE : INVOKEVIRTUAL; } else { // fast-class static method invokers don't use the context object invokeOpcode = INVOKESTATIC; } unpackArguments(mv, method.getParameterTypes()); mv.visitMethodInsn( invokeOpcode, hostName, method.getName(), Type.getMethodDescriptor(method), hostIsInterface); Class returnType = method.getReturnType(); if (returnType == void.class) { mv.visitInsn(ACONST_NULL); } else if (returnType.isPrimitive()) { box(mv, Type.getType(returnType)); } } @Override protected MethodHandle lookupInvokerTable(Class glueClass) throws Throwable { return (MethodHandle) glueClass.getField(INVOKERS_NAME).get(null); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy