| // 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.ColumnModel; |
| |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.Type; |
| |
| import java.lang.reflect.Method; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| public class CodeGenSupport implements Opcodes { |
| public final MethodVisitor mv; |
| private ColumnModel col; |
| private int dupOnSet; |
| private int columnIdx; |
| private Type entityType; |
| |
| private int lastLocal = 2; |
| private List<Integer> freeLocals = new ArrayList<>(4); |
| |
| public CodeGenSupport(final MethodVisitor method) { |
| mv = method; |
| } |
| |
| public void push(final int val) { |
| switch (val) { |
| case -1: |
| mv.visitInsn(ICONST_M1); |
| break; |
| case 0: |
| mv.visitInsn(ICONST_0); |
| break; |
| case 1: |
| mv.visitInsn(ICONST_1); |
| break; |
| case 2: |
| mv.visitInsn(ICONST_2); |
| break; |
| case 3: |
| mv.visitInsn(ICONST_3); |
| break; |
| case 4: |
| mv.visitInsn(ICONST_4); |
| break; |
| case 5: |
| mv.visitInsn(ICONST_5); |
| break; |
| default: |
| if (Byte.MIN_VALUE >= val && val < Byte.MAX_VALUE) { |
| mv.visitIntInsn(BIPUSH, val); |
| } else if (Short.MIN_VALUE >= val && val < Short.MAX_VALUE) { |
| mv.visitIntInsn(SIPUSH, val); |
| } else { |
| mv.visitLdcInsn(Integer.valueOf(val)); |
| } |
| break; |
| } |
| } |
| |
| public void loadVar(final Type type, final int index) { |
| mv.visitVarInsn(type.getOpcode(ILOAD), index); |
| } |
| |
| public int newLocal() { |
| if (freeLocals.isEmpty()) { |
| return ++lastLocal; |
| } |
| return freeLocals.remove(freeLocals.size() - 1); |
| } |
| |
| public void freeLocal(final int index) { |
| freeLocals.add(index); |
| } |
| |
| public void setEntityType(final Type et) { |
| entityType = et; |
| } |
| |
| public void setFieldReference(final ColumnModel cm) { |
| col = cm; |
| dupOnSet = -1; |
| columnIdx++; |
| } |
| |
| public void resetColumnIndex(final int s) { |
| columnIdx = s; |
| } |
| |
| public int getColumnIndex() { |
| return columnIdx; |
| } |
| |
| public ColumnModel getFieldReference() { |
| return col; |
| } |
| |
| public void pushSqlHandle() { |
| mv.visitVarInsn(ALOAD, 1); |
| } |
| |
| public void pushEntity() { |
| mv.visitVarInsn(ALOAD, 2); |
| } |
| |
| public void pushColumnIndex() { |
| push(columnIdx); |
| } |
| |
| public void invokePreparedStatementSet(final String sqlTypeName) { |
| final Method m; |
| try { |
| m = |
| PreparedStatement.class.getMethod("set" + sqlTypeName, Integer.TYPE, |
| ResultSet.class.getMethod("get" + sqlTypeName, Integer.TYPE) |
| .getReturnType()); |
| } catch (SecurityException e) { |
| throw new RuntimeException("java.sql has no " + sqlTypeName); |
| } catch (NoSuchMethodException e) { |
| throw new RuntimeException("java.sql has no " + sqlTypeName, e); |
| } |
| mv.visitMethodInsn(INVOKEINTERFACE, Type |
| .getInternalName(PreparedStatement.class), m.getName(), Type |
| .getMethodDescriptor(m)); |
| } |
| |
| public void invokeResultSetGet(final String sqlTypeName) { |
| final Method m; |
| try { |
| m = ResultSet.class.getMethod("get" + sqlTypeName, Integer.TYPE); |
| } catch (SecurityException e) { |
| throw new RuntimeException("java.sql has no " + sqlTypeName); |
| } catch (NoSuchMethodException e) { |
| throw new RuntimeException("java.sql has no " + sqlTypeName, e); |
| } |
| mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(ResultSet.class), |
| m.getName(), Type.getMethodDescriptor(m)); |
| } |
| |
| public void fieldSetBegin() { |
| pushEntity(); |
| if (col.getParent() != null) { |
| appendGetField(col.getParent()); |
| } |
| } |
| |
| public void fieldSetEnd() { |
| final Type c = containerClass(col); |
| if (dupOnSet >= 0) { |
| mv.visitInsn(DUP); |
| mv.visitVarInsn(ASTORE, dupOnSet); |
| } |
| mv.visitFieldInsn(PUTFIELD, c.getInternalName(), col.getFieldName(), |
| toType(col).getDescriptor()); |
| } |
| |
| public void setDupOnFieldSetEnd(final int varIdx) { |
| dupOnSet = varIdx; |
| } |
| |
| public void pushFieldValue() { |
| pushEntity(); |
| appendGetField(col); |
| } |
| |
| protected void appendGetField(final ColumnModel c) { |
| if (c.getParent() != null) { |
| appendGetField(c.getParent()); |
| } |
| final Type t = containerClass(c); |
| mv.visitFieldInsn(GETFIELD, t.getInternalName(), c.getFieldName(), |
| toType(c).getDescriptor()); |
| } |
| |
| private Type containerClass(final ColumnModel c) { |
| if (c.getParent() == null) { |
| return entityType; |
| } |
| final String n = c.getParent().getNestedClassName(); |
| return Type.getObjectType(n.replace('.', '/')); |
| } |
| |
| public static Type toType(final ColumnModel c) { |
| if (c.isSqlPrimitive()) { |
| return Type.getType(c.getPrimitiveType()); |
| } |
| return Type.getObjectType(c.getNestedClassName().replace('.', '/')); |
| } |
| } |