private void createWriteReplace() { MethodIdentifier identifier = MethodIdentifier.getIdentifier(Object.class, "writeReplace"); ClassMethod method = classFile.addMethod(AccessFlag.PROTECTED, "writeReplace", "Ljava/lang/Object;"); method.addCheckedExceptions(ObjectStreamException.class); overrideMethod(method, identifier, new WriteReplaceBodyCreator()); }
@Override public void overrideMethod(ClassMethod method, Method superclassMethod) { CodeAttribute ca = method.getCodeAttribute(); if (method.getName().equals("_getProxyInvocationDispatcher")) { ca.aload(0); ca.getfield(getClassName(), INVOCATION_DISPATCHER_FIELD, InvocationDispatcher.class); ca.returnInstruction(); } else if (method.getName().equals("_setProxyInvocationDispatcher")) { ca.aload(0); ca.aload(1); ca.putfield(getClassName(), INVOCATION_DISPATCHER_FIELD, InvocationDispatcher.class); ca.returnInstruction(); } else { throw new RuntimeException("Unkown method on interface " + ProxyInstance.class); } }
/** * Writes the bytecode to load an instance of Method for the given method onto the stack * * <p>If loadMethod has not already been called for the given method then a static field to hold * the method is added to the class, and code is added to the static constructor to initialize the * field to the correct Method. * * @param methodToLoad the method to load * @param method the subclass method to populate */ protected void loadMethodIdentifier(Method methodToLoad, ClassMethod method) { if (!methodIdentifiers.containsKey(methodToLoad)) { int identifierNo = identifierCount++; String fieldName = METHOD_FIELD_PREFIX + identifierNo; classFile.addField(AccessFlag.PRIVATE | AccessFlag.STATIC, fieldName, Method.class); methodIdentifiers.put(methodToLoad, identifierNo); } final Integer fieldNo = methodIdentifiers.get(methodToLoad); method .getCodeAttribute() .getstatic(getClassName(), METHOD_FIELD_PREFIX + fieldNo, METHOD_FIELD_DESCRIPTOR); }
@Override public void overrideConstructor(ClassMethod method, Constructor<?> constructor) { CodeAttribute ca = method.getCodeAttribute(); ca.aload(0); ca.iconst(0); ca.putfield(getClassName(), CONSTRUCTED_GUARD, "Z"); ca.aload(0); ca.loadMethodParameters(); ca.invokespecial(constructor); ca.aload(0); ca.iconst(1); ca.putfield(getClassName(), CONSTRUCTED_GUARD, "Z"); ca.returnInstruction(); }
@Override public void overrideMethod(ClassMethod method, Method superclassMethod) { // superClassMethod will be null CodeAttribute ca = method.getCodeAttribute(); ca.newInstruction(serializableProxyClass.getName()); ca.dup(); ca.invokespecial(serializableProxyClass.getName(), "<init>", "()V"); ca.dup(); ca.aload(0); ca.invokeinterface( SerializableProxy.class.getName(), "setProxyInstance", "(Lorg/jboss/proxy/ProxyInstance;)V"); ca.returnInstruction(); }
private void setupCachedProxyFields() { cachedMethods.addAll(methodIdentifiers.keySet()); // set the methods to be accessible AccessController.doPrivileged( new PrivilegedAction<Object>() { @Override public Object run() { for (Method method : cachedMethods) { method.setAccessible(true); } return null; } }); // store the Method objects in a thread local, so that // the proxies <clinit> method can access them // this removes the need for reflection in the proxy <clinit> method final Method[] methods = new Method[identifierCount]; for (Map.Entry<Method, Integer> entry : methodIdentifiers.entrySet()) { methods[entry.getValue()] = entry.getKey(); } MethodStore.METHODS.put(new ClassIdentifier(classFile.getName(), getClassLoader()), methods); // add the bytecode to load the cached fields in the static constructor CodeAttribute ca = staticConstructor.getCodeAttribute(); ca.getstatic(MethodStore.class.getName(), "METHODS", "Ljava/util/Map;"); ca.newInstruction(ClassIdentifier.class); ca.dup(); ca.ldc(classFile.getName()); ca.loadClass(classFile.getName()); ca.invokevirtual("java.lang.Class", "getClassLoader", "()Ljava/lang/ClassLoader;"); ca.invokespecial( ClassIdentifier.class.getName(), "<init>", "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"); ca.invokeinterface(Map.class.getName(), "remove", "(Ljava/lang/Object;)Ljava/lang/Object;"); ca.checkcast("[Ljava/lang/reflect/Method;"); for (int i = 0; i < identifierCount; ++i) { ca.dup(); ca.ldc(i); ca.aaload(); ca.putstatic(getClassName(), METHOD_FIELD_PREFIX + i, METHOD_FIELD_DESCRIPTOR); } }
// we simply want to load the corresponding identifier // and then forward it to the dispatcher @Override public void overrideMethod(ClassMethod method, Method superclassMethod) { CodeAttribute ca = method.getCodeAttribute(); // first we need to check the constructed field ca.aload(0); ca.getfield(getClassName(), CONSTRUCTED_GUARD, "Z"); // if the object has not been constructed yet invoke the superclass version of the method BranchEnd end = ca.ifne(); ca.aload(0); ca.loadMethodParameters(); ca.invokespecial(getSuperClassName(), method.getName(), method.getDescriptor()); ca.returnInstruction(); // normal invocation path begins here ca.branchEnd(end); ca.aload(0); ca.getfield(getClassName(), INVOCATION_DISPATCHER_FIELD, InvocationDispatcher.class); // now we have the dispatcher on the stack, we need to build an invocation ca.newInstruction(Invocation.class.getName()); ca.dup(); // the constructor we are using is Invocation(final Class<?> declaringClass, final // MethodIdentifier // methodIdentifier, final Object... args) ca.loadClass(getClassName()); loadMethodIdentifier(superclassMethod, method); // now we need to stick the parameters into an array, boxing if nessesary String[] params = method.getParameters(); ca.iconst(params.length); ca.anewarray("java/lang/Object"); int loadPosition = 1; for (int i = 0; i < params.length; ++i) { ca.dup(); ca.iconst(i); String type = params[i]; if (type.length() == 1) { // primitive char typeChar = type.charAt(0); switch (typeChar) { case 'I': ca.iload(loadPosition); Boxing.boxInt(ca); break; case 'S': ca.iload(loadPosition); Boxing.boxShort(ca); break; case 'B': ca.iload(loadPosition); Boxing.boxByte(ca); break; case 'Z': ca.iload(loadPosition); Boxing.boxBoolean(ca); break; case 'C': ca.iload(loadPosition); Boxing.boxChar(ca); break; case 'D': ca.dload(loadPosition); Boxing.boxDouble(ca); loadPosition++; break; case 'J': ca.lload(loadPosition); Boxing.boxLong(ca); loadPosition++; break; case 'F': ca.fload(loadPosition); Boxing.boxFloat(ca); break; default: throw new RuntimeException("Unkown primitive type descriptor: " + typeChar); } } else { ca.aload(loadPosition); } ca.aastore(); loadPosition++; } ca.invokespecial( Invocation.class.getName(), "<init>", "(Ljava/lang/Class;Lorg/jboss/invocation/MethodIdentifier;[Ljava/lang/Object;)V"); // now we have the invocation on top of the stack, with the dispatcher below it ca.invokeinterface( InvocationDispatcher.class.getName(), "dispatch", "(Lorg/jboss/invocation/Invocation;)Lorg/jboss/invocation/InvocationReply;"); ca.invokevirtual(InvocationReply.class.getName(), "getReply", "()Ljava/lang/Object;"); if (superclassMethod.getReturnType() != void.class) { if (superclassMethod.getReturnType().isPrimitive()) { Boxing.unbox(ca, method.getReturnType()); } else { ca.checkcast(superclassMethod.getReturnType().getName()); } } ca.returnInstruction(); }
/** This method must be called by subclasses after they have finished generating the class. */ protected void finalizeStaticConstructor() { setupCachedProxyFields(); staticConstructor.getCodeAttribute().returnInstruction(); }