/* * Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de> * * 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.github.rjeschke.weel; import java.lang.reflect.Modifier; import java.util.ArrayList; /** * Method writer specialized for Weel. * * @author René Jeschke <rene_jeschke@yahoo.de> */ final class JvmMethodWriter { /** The class writer. */ final JvmClassWriter classWriter; /** This methods's name. */ String methodName; /** This method's descriptor. */ final String descriptor; /** This method's code. */ ByteList code = new ByteList(); /** Maximum stack used. */ int maxStack = 0; /** Current stack used. */ int curStack = 0; /** Maximum locals used. */ int maxLocals = 0; /** The name constant pool index. */ int nameIndex; /** The descriptor constant pool index. */ int descriptorIndex; /** The access mode. */ int access = Modifier.PUBLIC | Modifier.FINAL | Modifier.STATIC; /** Encountered jumps. */ ArrayList<Integer> jumps = new ArrayList<Integer>(); /** Registered labels. */ ArrayList<Integer> labels = new ArrayList<Integer>(); /** * Constructor. * * @param classWriter * The class writer. * @param methodName * The method name. * @param descriptor * The descriptor. */ JvmMethodWriter(final JvmClassWriter classWriter, final String methodName, final String descriptor, final int access) { this.classWriter = classWriter; this.methodName = methodName; this.descriptor = descriptor; this.access = access; int i, ps = (access & Modifier.STATIC) != 0 ? 0 : 1; for(i = 1; i < descriptor.length(); i++) { if(descriptor.charAt(i) == ')') break; switch(descriptor.charAt(i)) { case 'L': while(descriptor.charAt(i) != ';') { i++; } //$FALL-THROUGH$ default: ps++; break; } } this.maxLocals = ps; } /** * Loads a constant. * * @param index * The constant pool index. */ private void ldcIndex(final int index) { if(index > 255) { this.code.add(JvmOp.LDC_W); this.code.addShort(index); } else { this.code.add(JvmOp.LDC); this.code.add(index); } } /** * Writes a invoke instruction. * * @param op * Opcode. * @param clazz * Class name. * @param method * Method name. * @param descriptor * Descriptor. */ private void invoke(final int op, final String clazz, final String method, final String descriptor) { final int c0 = this.classWriter .addConstant(new JvmConstant(JvmConstant.CONSTANT_Class, this.classWriter.addConstant(new JvmConstant(clazz .replace('.', '/'))))); final int c1 = this.classWriter.addConstant(new JvmConstant( JvmConstant.CONSTANT_Methodref, c0, this.classWriter .addConstant(new JvmConstant( JvmConstant.CONSTANT_NameAndType, this.classWriter.addConstant(new JvmConstant( method)), this.classWriter.addConstant(new JvmConstant( descriptor)))))); this.code.add(op); this.code.addShort(c1); int spp = op != JvmOp.INVOKESTATIC ? 1 : 0, spm = 0, i; for(i = 1; i < descriptor.length(); i++) { if(descriptor.charAt(i) == ')') break; switch(descriptor.charAt(i)) { case 'J': case 'D': spp += 2; break; case 'L': while(descriptor.charAt(i) != ';') { i++; } //$FALL-THROUGH$ default: spp++; break; } } switch(descriptor.charAt(i + 1)) { case 'V': break; case 'J': case 'D': spm += 2; break; default: spm++; break; } this.curStack -= spp; this.add(spm); } /** * Writes a invokevirtual instruction. * * @param clazz * Class name. * @param method * Method name. * @param descriptor * Descriptor. */ public void invokeVirtual(final String clazz, final String method, final String descriptor) { this.invoke(JvmOp.INVOKEVIRTUAL, clazz, method, descriptor); } /** * Writes a invokestatic instruction. * * @param clazz * Class name. * @param method * Method name. * @param descriptor * Descriptor. */ public void invokeStatic(final String clazz, final String method, final String descriptor) { this.invoke(JvmOp.INVOKESTATIC, clazz, method, descriptor); } /** * Writes a invokespecial instruction. * * @param clazz * Class name. * @param method * Method name. * @param descriptor * Descriptor. */ public void invokeSpecial(final String clazz, final String method, final String descriptor) { this.invoke(JvmOp.INVOKESPECIAL, clazz, method, descriptor); } /** * Writes a invokeinterface instruction. * * @param clazz * Class name. * @param method * Method name. * @param descriptor * Descriptor. */ public void invokeInterface(final String clazz, final String method, final String descriptor) { this.invoke(JvmOp.INVOKEINTERFACE, clazz, method, descriptor); } /** * Loads a String constant. * * @param value * The value. */ public void ldc(final String value) { this.ldcIndex(this.classWriter.addConstant(new JvmConstant( JvmConstant.CONSTANT_String, this.classWriter .addConstant(new JvmConstant(value))))); this.add(1); } /** * Loads a float constant. * * @param value * The value. */ public void ldc(final float value) { this.ldcIndex(this.classWriter.addConstant(new JvmConstant(value))); this.add(1); } /** * Loads a double constant. * * @param value * The value. */ public void ldc(final double value) { this.code.add(JvmOp.LDC2_W); this.code.addShort(this.classWriter.addConstant(new JvmConstant(value))); this.add(2); } /** * Loads a boolean constant. * * @param value * The value. */ public void ldc(final boolean value) { if(value) { this.code.add(JvmOp.ICONST_1); } else { this.code.add(JvmOp.ICONST_0); } this.add(1); } /** * Loads an integer constant. * * @param value * The value. */ public void ldc(final int value) { switch(value) { case -1: this.code.add(JvmOp.ICONST_M1); break; case 0: this.code.add(JvmOp.ICONST_0); break; case 1: this.code.add(JvmOp.ICONST_1); break; case 2: this.code.add(JvmOp.ICONST_2); break; case 3: this.code.add(JvmOp.ICONST_3); break; case 4: this.code.add(JvmOp.ICONST_4); break; case 5: this.code.add(JvmOp.ICONST_5); break; default: this.ldcIndex(this.classWriter.addConstant(new JvmConstant(value))); break; } this.add(1); } /** * Adds an opcode without any checking to this code. * * @param op * The opcode. */ public void addOp(final int op) { this.code.add(op); } /** * Writes an aload. * * @param i * Index. */ public void aload(final int i) { switch(i) { case 0: this.code.add(JvmOp.ALOAD_0); break; case 1: this.code.add(JvmOp.ALOAD_1); break; case 2: this.code.add(JvmOp.ALOAD_2); break; case 3: this.code.add(JvmOp.ALOAD_3); break; default: this.code.add(JvmOp.ALOAD); this.code.add(i); break; } this.maxLocals = Math.max(this.maxLocals, i + 1); this.add(1); } /** * Writes a jump instruction. * * @param op * The opcode. * @param label * The label. */ void writeJmp(final int op, final int label) { switch(op) { case JvmOp.IFNE: case JvmOp.IFGE: this.curStack--; break; default: break; } this.code.add(op); this.jumps.add(this.code.size()); this.code.addShort(label); } /** * Adds the value to the current used stack value. Checks maxStack. * * @param plus * Value to add. */ void add(int plus) { this.maxStack = Math.max(this.maxStack, this.curStack += plus); } public void resolveLabels() { for(int i : this.jumps) { final int l = this.labels.get(this.code.getShort(i)); this.code.setShort(i, l - i + 1); } } public void addLabel(final int index) { while(this.labels.size() <= index) { this.labels.add(0); } this.labels.set(index, this.code.size()); } }