blob: 175b2127b4e98eb599fde97ac7fd8571960151cb [file] [log] [blame]
// Copyright 2008 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.gwtorm.server;
import com.google.gwtorm.schema.Util;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/** Generates a factory to efficiently create new Schema instances. */
public class SchemaConstructorGen<T extends Schema> implements Opcodes {
private static final String CTX = "schemaArg";
private final GeneratedClassLoader classLoader;
private final Class<T> schemaImpl;
private final Object schemaArg;
private ClassWriter cw;
private String implClassName;
private String implTypeName;
public SchemaConstructorGen(final GeneratedClassLoader loader, final Class<T> c, final Object f) {
classLoader = loader;
schemaImpl = c;
schemaArg = f;
}
public void defineClass() throws OrmException {
init();
declareFactoryField();
implementConstructor();
implementNewInstance();
cw.visitEnd();
classLoader.defineClass(implClassName, cw.toByteArray());
}
public SchemaFactory<T> create() throws OrmException {
defineClass();
try {
final Class<?> c = Class.forName(implClassName, true, classLoader);
final Constructor<?> n = c.getDeclaredConstructors()[0];
return cast(n.newInstance(new Object[] {schemaArg}));
} catch (InstantiationException e) {
throw new OrmException("Cannot create schema factory", e);
} catch (IllegalAccessException e) {
throw new OrmException("Cannot create schema factory", e);
} catch (ClassNotFoundException e) {
throw new OrmException("Cannot create schema factory", e);
} catch (IllegalArgumentException e) {
throw new OrmException("Cannot create schema factory", e);
} catch (InvocationTargetException e) {
throw new OrmException("Cannot create schema factory", e);
}
}
@SuppressWarnings("unchecked")
private SchemaFactory<T> cast(final Object newInstance) {
return (SchemaFactory<T>) newInstance;
}
private void init() {
implClassName = schemaImpl.getName() + "_Factory_" + Util.createRandomName();
implTypeName = implClassName.replace('.', '/');
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(
V1_3,
ACC_PUBLIC | ACC_FINAL | ACC_SUPER,
implTypeName,
null,
Type.getInternalName(Object.class),
new String[] {Type.getInternalName(SchemaFactory.class)});
}
private void declareFactoryField() {
cw.visitField(
ACC_PRIVATE | ACC_FINAL,
CTX,
Type.getType(schemaArg.getClass()).getDescriptor(),
null,
null)
.visitEnd();
}
private void implementConstructor() {
final Type ft = Type.getType(schemaArg.getClass());
final String consName = "<init>";
final String consDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {ft});
final MethodVisitor mv;
mv = cw.visitMethod(ACC_PUBLIC, consName, consDesc, null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(
INVOKESPECIAL,
Type.getInternalName(Object.class),
consName,
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, implTypeName, CTX, ft.getDescriptor());
mv.visitInsn(RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private void implementNewInstance() {
final Type ft = Type.getType(schemaArg.getClass());
final String typeName = Type.getType(schemaImpl).getInternalName();
final MethodVisitor mv =
cw.visitMethod(
ACC_PUBLIC | ACC_FINAL,
"open",
Type.getMethodDescriptor(Type.getType(Schema.class), new Type[] {}),
null,
null);
mv.visitCode();
Constructor<?> c = schemaImpl.getDeclaredConstructors()[0];
Type argType = Type.getType(c.getParameterTypes()[0]);
mv.visitTypeInsn(NEW, typeName);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, implTypeName, CTX, ft.getDescriptor());
mv.visitMethodInsn(
INVOKESPECIAL,
typeName,
"<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {argType}));
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
}