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

org.glassfish.pfl.dynamic.codegen.impl.ByteCodeUtility Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://oss.oracle.com/licenses/CDDL+GPL-1.1
 * or LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.pfl.dynamic.codegen.impl ;

import java.io.PrintStream ;

import java.lang.reflect.Modifier ;

import java.util.List ;
import java.util.Map ;
import java.util.HashMap ;
import java.util.Collections ;
import java.util.ArrayList ;
import java.util.Comparator ;

import org.glassfish.pfl.dynamic.codegen.spi.Type ;
import org.glassfish.pfl.dynamic.codegen.spi.Signature ;
import org.glassfish.pfl.dynamic.codegen.spi.Variable ;
import org.glassfish.pfl.dynamic.codegen.spi.ClassInfo ;
import org.glassfish.pfl.dynamic.codegen.spi.MethodInfo ;

import org.glassfish.pfl.objectweb.asm.MethodVisitor ;
import org.glassfish.pfl.objectweb.asm.ClassWriter ;
import org.glassfish.pfl.objectweb.asm.Label ;
import org.glassfish.pfl.objectweb.asm.util.TraceMethodVisitor ;

import static java.lang.reflect.Modifier.* ;
import static org.glassfish.pfl.objectweb.asm.Opcodes.* ;

/** Class that is responsible for low-level bytecode generation using ASM.
 * It provides methods that directly generate bytecode in a MethodVisitor.
 * This will often be accompanied by type analysis, such as in determining 
 * the correct sort of return instruction to use, or how to generate code
 * for a constant.
 * 
 */
public final class ByteCodeUtility {
    private ClassWriter cw ;
    private MethodVisitor mv ;
    private boolean debug ;
    private PrintStream ps ;
    private String methodName ;
    private String methodSignature ;

    /** Construct an instance of ByteCodeUtility from an ASM ClassWriter
     * and a codegen ClassGeneratorImpl.  If debug is true, debugText() can
     * be called to get a text representation of the generated code for
     * debugging purposes.
     */
    public ByteCodeUtility( ClassWriter cw, ClassGeneratorImpl cg,
	boolean debug, PrintStream ps ) {

	this.cw = cw ;
	this.mv = null ;
	this.debug = debug ;
	this.ps = ps ;

	String[] interfaces = new String[cg.impls().size()] ;
	int ctr=0 ;
	for (Type impl : cg.impls()) {
            interfaces[ctr++] =
                ASMUtil.bcName(impl);
        }

	int modifiers = cg.modifiers() ;
	if (cg.isInterface()) {
            modifiers |= ACC_INTERFACE;
        }
	String superType = (cg.superType() == null) ? 
	    ASMUtil.bcName( Type._Object() ) :
	    ASMUtil.bcName( cg.superType() ) ;

	cw.visit( V1_5, modifiers, ASMUtil.bcName( cg.thisType() ), 
	    null, superType, interfaces ) ;
	cw.visitSource( cg.name().replace( '.', '/' ) + ".java", null ) ;
    }

    public void addField( FieldGenerator arg ) {
	String descriptor = arg.type().signature() ;
	cw.visitField( arg.modifiers(), arg.name(), descriptor, null, null ) ;
    }

    public ByteCodeUtility( ClassWriter cw, ClassGeneratorImpl cg ) {
	this( cw, cg, false, System.out ) ;
    }

    private void dump() {
	if (debug) {
	    List data = TraceMethodVisitor.class.cast( mv ).getText() ;
	    ps.printf( "MethodVisitor calls for method %s%s\n", 
		methodName, methodSignature ) ;
	    for (String str : data) {
		ps.print( str ) ;
	    }
	}
    }

    public void emitMethodStart( MethodGenerator mg ) {
	
	// Get a List for parameter types
	List types = new ArrayList() ;
	for (Variable var : mg.arguments()) {
	    types.add( ((VariableInternal)var).type() ) ;
	}

	methodName = mg.name() ;
	methodSignature = 
	    Signature.make( mg.returnType(), types ).signature() ;
	   
	// Get a String[] representation of the exception types
	String[] strs = new String[mg.exceptions().size()] ;
	int ctr = 0 ;
	for (Type exception : mg.exceptions()) {
	    strs[ctr++] = ASMUtil.bcName( exception ) ;
	}
	
	mv = cw.visitMethod( mg.modifiers(), methodName, methodSignature, null,
	    strs ) ;

	if (debug) {
            mv = new TraceMethodVisitor(mv);
        }

	mv.visitCode() ;
    }
   
    private void emitLineNumberTable( MethodGenerator mg ) {
	ASMUtil.LineNumberTable lnt = ASMUtil.lineNumberTable.get( mg ) ;
	if (lnt != null) {
	    List labels = new ArrayList( lnt.keySet() ) ;
	    Collections.sort( labels, 
		new Comparator() {
                    @Override
		    public int compare( MyLabel l1, MyLabel l2 ) {
			return l1.getOffset() - l2.getOffset() ;
		    }
		}
	    ) ;

	    int currentLineNum = -1 ;
	    for (MyLabel label : labels) {
		int lineNum = lnt.get( label ) ;
		if (lineNum != currentLineNum) {
		    currentLineNum = lineNum ;
		    mv.visitLineNumber( lineNum, label ) ;
		}
	    }
	}
    }

    private void emitLocalVariableTable( MethodGenerator mg ) {
	ASMUtil.VariablesInMethod vm = ASMUtil.variablesInMethod.get( mg ) ;

	for (Variable var : vm) {
	    Label start ;
	    Label end ;

	    Statement stmt = ((VariableInternal)var).getAncestor(
                Statement.class ) ;
	    if (stmt == null) { // We must have a method argument
		end = ASMUtil.returnLabel.get( mg ) ;

		BlockStatement bs = mg.body() ;
		if (bs.isEmpty()) {
		    // No statements, so set start=end.
		    start = end ;
		} else {
		    // Start is the start of the first statement 
		    // in the method body.
		    List stmts = bs.body() ;
		    Statement first = stmts.iterator().next() ;
		    start = ASMUtil.statementStartLabel.get( first ) ;
		}
	    } else {
		start = ASMUtil.statementStartLabel.get( stmt ) ;
		end = ASMUtil.statementEndLabel.get( stmt ) ;
	    }

            VariableInternal ivar = (VariableInternal)var ;
	    int index = ASMUtil.stackFrameSlot.get( ivar ) ;
	    mv.visitLocalVariable( ivar.ident(), ivar.type().signature(), null,
		start, end, index ) ;
	}
    }

    public void emitMethodEnd( MethodGenerator mg, Label returnLabel, 
	Variable returnVariable, boolean dump ) {

	// Only generate return for non-abstract methods.
	if ((mg.modifiers() & ABSTRACT) == 0) {
	    // Generate the return at the end of the method.
	    // This is just a label, followed by pushing any return value
	    // onto the stack, and then the appropriately typed xRETURN
	    // instruction.  Return instructions are basically just 
	    // setting the return variable and then branching to this
	    // location, EXCEPT that we also need to handle finally
	    // blocks (the real reason for using a variable here).
	    mv.visitLabel( returnLabel ) ;
	    
	    if (returnVariable != null) {
		// We need the get emitter here.
		EmitterFactory.Emitter emitter =
		    ASMUtil.getEmitter.get( (VariableInternal)returnVariable ) ;
		assert emitter != null ;
		emitter.evaluate( mv ) ;
	    }

	    emitReturn( 
		returnVariable == null
                    ? Type._void()
                    : ((VariableInternal)returnVariable).type() ) ;

	    // Emit debug information, if present
	    emitLineNumberTable( mg ) ;
	    emitLocalVariableTable( mg ) ;
	}

	mv.visitMaxs(0,0) ;
	mv.visitEnd() ;

	if (dump) {
            dump();
        }

	mv = null ;
    }

    // Emit the appropriately typed return bytecode.
    private void emitReturn( Type returnType ) {

	if (returnType.equals(Type._void())) {
	    mv.visitInsn( RETURN ) ;
	} else if (returnType.isPrimitive()) {
	    // ILFD
	    if (returnType.equals(Type._long())) {
		mv.visitInsn( LRETURN ) ;
	    } else if (returnType.equals(Type._float())) {
		mv.visitInsn( FRETURN ) ;
	    } else if (returnType.equals(Type._double())) {
		mv.visitInsn( DRETURN ) ;
	    } else {
		mv.visitInsn( IRETURN ) ;
	    }
	} else { // must be a reference 
	    mv.visitInsn( ARETURN ) ;
	} 
    }

    public void emitRet( Variable var ) {
        VariableInternal ivar = (VariableInternal)var ;
	// We always use Object here, so check this.
	assert ivar.type().equals( Type._Object() ) ;

	Integer slot = ASMUtil.stackFrameSlot.get( ivar ) ;
	assert slot != null ;

	mv.visitVarInsn( RET, slot ) ;
    }

    public void emitConstantExpression( Type type, Object value ) {

	// This nasty little piece of code handles all of the special
	// variations in loading numberic constants, as well as 
	// Class, String, and null values.
	if (type.equals(Type._null())) {
	    mv.visitInsn( ACONST_NULL ) ;
	} else if (type.equals(Type._Class())) {
	    // value is a Type, so get a corresponding ASM type.
	    Type vtype = Type.class.cast( value ) ;
	    org.glassfish.pfl.objectweb.asm.Type atype =
		org.glassfish.pfl.objectweb.asm.Type.getType( vtype.signature() ) ;
	    mv.visitLdcInsn( atype ) ;
	} else if (type.equals( Type._String())) {
	    mv.visitLdcInsn( value ) ;
	} else if (type.equals( Type._float() )) {
	    float val = Float.class.cast( value ).floatValue() ;
	    if (val == 0.0) {
                mv.visitInsn(FCONST_0);
            } else if (val == 1.0) {
                mv.visitInsn(FCONST_1);
            } else if (val == 2.0) {
                mv.visitInsn(FCONST_2);
            } else {
                mv.visitLdcInsn(value);
            }
	} else if (type.equals( Type._double() )) {
	    double val = Double.class.cast( value ).doubleValue() ;
	    if (val == 0.0) {
                mv.visitInsn(DCONST_0);
            } else if (val == 1.0) {
                mv.visitInsn(DCONST_1);
            } else {
                mv.visitLdcInsn(value);
            }
	} else if (type.equals( Type._long() )) {
	    long val = Long.class.cast( value ).longValue() ;
	    if (val == 0) {
                mv.visitInsn(LCONST_0);
            } else if (val == 1) {
                mv.visitInsn(LCONST_1);
            } else {
                mv.visitLdcInsn(value);
            }
	} else if (type.equals( Type._boolean() )) {
	    mv.visitInsn(  
		Boolean.class.cast( value ).booleanValue() 
		    ? ICONST_1 : ICONST_0 ) ;
	} else { // byte, char, short, or int
	    int val = Integer.class.cast( value ).intValue() ;
	    if (val == -1) {
                mv.visitInsn(ICONST_M1);
            } else if (val == 0) {
		mv.visitInsn( ICONST_0);
            } else if (val == 1) {
		mv.visitInsn( ICONST_1);
            } else if (val == 2) {
		mv.visitInsn( ICONST_2);
            } else if (val == 3) {
		mv.visitInsn( ICONST_3);
            } else if (val == 4) {
		mv.visitInsn( ICONST_4);
            } else if (val == 5) {
		mv.visitInsn( ICONST_5);
            } else if ((val >= Byte.MIN_VALUE) && (val <= Byte.MAX_VALUE)) {
		mv.visitIntInsn( BIPUSH, val ) ;
            } else if ((val >= Short.MIN_VALUE) && (val <= Short.MAX_VALUE)) {
		mv.visitIntInsn( SIPUSH, val ) ;
            } else {
		mv.visitLdcInsn( value ) ;
            }
	}
    }

    public void emitThisExpression() {
	mv.visitIntInsn( ALOAD, 0 ) ;
    }

    // Used for generating the required branch for booleans.
    // Note that true=1, false=0.
    // Stack Map: (boolean) -> ().
    public void emitConditionalBranch( MyLabel falseBranch ) {

	mv.visitJumpInsn( IFEQ, falseBranch ) ;
    }

    public void emitBranch( MyLabel target ) {
	mv.visitJumpInsn( GOTO, target ) ;
    }

    // Visit the label given by the attribute on node if the
    // attribute is set, and has not already been visited.
    // Unfortunately it is possible to call emitLabel twice
    // in some cases (see try statements and preStatement in 
    // ASMByteCodeVisitor).
    public void emitLabel( Attribute attr, Node node ) {
	if (attr.isSet( node )) {
	    MyLabel label = attr.get( node ) ;

	    if (label.emitted()) {
		if (debug) {
                    TraceMethodVisitor.class.cast(mv).getText().
                        add("    Already emitted label " + label);
                }
	    } else {
		int lineNumber = CodegenPrinter.lineNumberAttribute.get( node ) ;
		if (lineNumber > 0) {
		    MethodGenerator mg = node.getAncestor( MethodGenerator.class ) ;
		    ASMUtil.LineNumberTable lnt = ASMUtil.lineNumberTable.get( mg ) ;
		    lnt.put( label, lineNumber ) ;
		}
		label.emitted( true ) ;
		mv.visitLabel( label ) ;
	    }
	}
    }

    /** Emit the NEW, DUP sequence required at the start of a new 
     * call.
     */
    public void emitNewCall( Type type ) {
	String typeName = ASMUtil.bcName( type ) ;

	mv.visitTypeInsn( NEW, typeName ) ;
	mv.visitInsn( DUP ) ;
    }

    public void emitInstanceof( Type type ) {
	String typeName = ASMUtil.bcName( type ) ;

	mv.visitTypeInsn( INSTANCEOF, typeName ) ;
    }

    public void emitCast( Type from, Type to ) {
	// Assume that the cast conversion check has already occurred, so
	// we are only calling emitCast if it is necessary to emit some
	// instructions.
	// Possibilties:
	// from, to are both numeric types: emit appropriate x2y instruction
	// from, to are both non-array reference types: emit CHECKCAST
	// from, to are both array types: emit CHECKCAST?
	
	if (from.isPrimitive()) {
	    if (to.isPrimitive()) {
		if (from.isNumber() && to.isNumber()) {
                    emitConversion(from, to);
                } else {
                    throw new IllegalArgumentException("No conversion is possible from " +
                        from.name() + " to " + to.name());
                }
	    } else {
		throw new IllegalArgumentException( "Type " + from.name() +
		    " is a primitive type, but type " + to.name() +
		    " is a reference type: no conversion is possible" ) ;
	    }
	} else {
	    if (to.isPrimitive()) {
		throw new IllegalArgumentException( "Type " + from.name() +
		    " is a reference type, but type " + to.name() +
		    " is a primitive type: no conversion is possible" ) ;
	    } else {
		String sig = to.isArray() ? 
		    to.signature() :
		    ASMUtil.bcName(to) ;
		
		// mv.visitTypeInsn( CHECKCAST, ASMUtil.bcName(to) ) ;
		mv.visitTypeInsn( CHECKCAST, sig ) ;
	    }
	}
    }
    
    public void emitDup() {
	mv.visitInsn( DUP ) ;
    }

    public void emitArrayStore() {
	mv.visitInsn( AASTORE ) ;
    }

    // return the type code for a primitive type in a
    // NEWARRAY instruction.
    public int typeCode( Type type ) {
	if (type.equals(Type._boolean())) {
            return T_BOOLEAN;
        }
	if (type.equals(Type._byte())) {
            return T_BYTE;
        }
	if (type.equals(Type._char())) {
            return T_CHAR;
        }
	if (type.equals(Type._short())) {
            return T_SHORT;
        }
	if (type.equals(Type._int())) {
            return T_INT;
        }
	if (type.equals(Type._long())) {
            return T_LONG;
        }
	if (type.equals(Type._float())) {
            return T_FLOAT;
        }
	if (type.equals(Type._double())) {
            return T_DOUBLE;
        }

	throw new IllegalArgumentException( 
	    "Can only get a NEWARRAY typecode for a primitive type" ) ;
    }

    public void emitNewArrayCall( Type type ) {
	if (type.isPrimitive()) {
            mv.visitIntInsn(NEWARRAY, typeCode(type));
        } else {
            mv.visitTypeInsn(ANEWARRAY,
                ASMUtil.bcName(type));
        }
    }

    /** Emit a static INVOKE instruction. 
     */
    public void emitStaticInvoke( Type type, 
	String name, Signature sig ) {

	mv.visitMethodInsn( INVOKESTATIC, ASMUtil.bcName(type), name, 
	    sig.signature() ) ;
    }

    /** Emit the appropriate non-static INVOKE instruction as follows:
     * 
    *
  1. If type is an interface, emit INVOKEINTERFACE. *
  2. If name/sig has private access in type, emit INVOKESPECIAL. Note * that the target must be "this" in this case. *
  3. Otherwise emit INVOKEVIRTUAL. *
*/ public void emitInvoke( Type type, String name, Signature sig ) { String sigString = sig.signature() ; ClassInfo targetInfo ; targetInfo = type.classInfo() ; MethodInfo minfo = targetInfo.findMethodInfo( name, sig ) ; if (minfo == null) { throw new IllegalArgumentException("Could not find a method " + name + " with signature " + sig + " in class " + targetInfo.name()); } ClassInfo mcinfo = minfo.myClassInfo() ; boolean privateMethod = Modifier.isPrivate( minfo.modifiers() ) ; int opcode ; if (mcinfo.isInterface()) { opcode = INVOKEINTERFACE; } else if (privateMethod) { opcode = INVOKESPECIAL; } else { opcode = INVOKEVIRTUAL; } String typeName = ASMUtil.bcName(mcinfo.thisType()) ; mv.visitMethodInsn( opcode, typeName, name, sigString ) ; } /** Emit the INVOKESPECIAL instruction for calling a constructor * with the given signature. This is used for new Foo() calls, * and for super() and this() calls at the start of a constructor. */ public void emitNewInvoke( Type type, Signature sig ) { String typeName = ASMUtil.bcName(type) ; mv.visitMethodInsn( INVOKESPECIAL, typeName, CodeGeneratorUtil.CONSTRUCTOR_METHOD_NAME, sig.signature()) ; } /** Emit the INVOKESPECIAL instruction for calling a method * with the given signature. This is used for * for super.name() and this.name() method calls. */ public void emitSpecialInvoke( Type type, String name, Signature sig ) { String typeName = ASMUtil.bcName(type) ; mv.visitMethodInsn( INVOKESPECIAL, typeName, name, sig.signature() ) ; } public void emitThrow() { mv.visitInsn( ATHROW ) ; } public void emitExceptionTableEntry( Label start, Label end, Label handler, Type exceptionType ) { String exceptionTypeName = exceptionType == null ? null : ASMUtil.bcName(exceptionType) ; mv.visitTryCatchBlock( start, end, handler, exceptionTypeName ) ; } public void emitJsr( Label label ) { mv.visitJumpInsn( JSR, label ) ; } public void callEmitter( EmitterFactory.Emitter emitter ) { emitter.evaluate( mv ) ; } public void emitPop() { mv.visitInsn( POP ) ; } private static Map typeIndex = new HashMap() ; static { typeIndex.put( Type._byte(), 0 ) ; typeIndex.put( Type._short(), 1 ) ; typeIndex.put( Type._char(), 2 ) ; typeIndex.put( Type._int(), 3 ) ; typeIndex.put( Type._long(), 4 ) ; typeIndex.put( Type._float(), 5 ) ; typeIndex.put( Type._double(), 6 ) ; } private static final EmitterFactory.Emitter E_NOP = new EmitterFactory.NullEmitter() ; private static final EmitterFactory.Emitter E_I2B = new EmitterFactory.SimpleEmitter( I2B ) ; private static final EmitterFactory.Emitter E_I2C = new EmitterFactory.SimpleEmitter( I2C ) ; private static final EmitterFactory.Emitter E_I2S = new EmitterFactory.SimpleEmitter( I2S ) ; private static final EmitterFactory.Emitter E_I2L = new EmitterFactory.SimpleEmitter( I2L ) ; private static final EmitterFactory.Emitter E_I2F = new EmitterFactory.SimpleEmitter( I2F ) ; private static final EmitterFactory.Emitter E_I2D = new EmitterFactory.SimpleEmitter( I2D ) ; private static final EmitterFactory.Emitter E_L2I = new EmitterFactory.SimpleEmitter( L2I ) ; private static final EmitterFactory.Emitter E_F2I = new EmitterFactory.SimpleEmitter( F2I ) ; private static final EmitterFactory.Emitter E_D2I = new EmitterFactory.SimpleEmitter( D2I ) ; private static final EmitterFactory.Emitter E_L2F = new EmitterFactory.SimpleEmitter( L2F ) ; private static final EmitterFactory.Emitter E_L2D = new EmitterFactory.SimpleEmitter( L2D ) ; private static final EmitterFactory.Emitter E_F2L = new EmitterFactory.SimpleEmitter( F2L ) ; private static final EmitterFactory.Emitter E_D2L = new EmitterFactory.SimpleEmitter( D2L ) ; private static final EmitterFactory.Emitter E_F2D = new EmitterFactory.SimpleEmitter( F2D ) ; private static final EmitterFactory.Emitter E_D2F = new EmitterFactory.SimpleEmitter( D2F ) ; private static final EmitterFactory.Emitter E_L2B = new EmitterFactory.CompoundEmitter( E_L2I, E_I2B ) ; private static final EmitterFactory.Emitter E_F2B = new EmitterFactory.CompoundEmitter( E_F2I, E_I2B ) ; private static final EmitterFactory.Emitter E_D2B = new EmitterFactory.CompoundEmitter( E_D2I, E_I2B ) ; private static final EmitterFactory.Emitter E_L2S = new EmitterFactory.CompoundEmitter( E_L2I, E_I2S ) ; private static final EmitterFactory.Emitter E_F2S = new EmitterFactory.CompoundEmitter( E_F2I, E_I2S ) ; private static final EmitterFactory.Emitter E_D2S = new EmitterFactory.CompoundEmitter( E_D2I, E_I2S ) ; private static final EmitterFactory.Emitter E_L2C = new EmitterFactory.CompoundEmitter( E_L2I, E_I2C ) ; private static final EmitterFactory.Emitter E_F2C = new EmitterFactory.CompoundEmitter( E_F2I, E_I2C ) ; private static final EmitterFactory.Emitter E_D2C = new EmitterFactory.CompoundEmitter( E_D2I, E_I2C ) ; // First index: from type, second index: to type EmitterFactory.Emitter[][] numericConversions = new EmitterFactory.Emitter[][] { // to: // byte short char int long float double { E_I2B, E_I2S, E_I2C, E_NOP, E_I2L, E_I2F, E_I2D }, // from byte { E_I2B, E_I2S, E_I2C, E_NOP, E_I2L, E_I2F, E_I2D }, // from short { E_I2B, E_I2S, E_I2C, E_NOP, E_I2L, E_I2F, E_I2D }, // from char { E_I2B, E_I2S, E_I2C, E_NOP, E_I2L, E_I2F, E_I2D }, // from int { E_L2B, E_L2S, E_L2C, E_L2I, E_NOP, E_L2F, E_L2D }, // from long { E_F2B, E_F2S, E_F2C, E_F2I, E_F2L, E_NOP, E_F2D }, // from float { E_D2B, E_D2S, E_D2C, E_D2I, E_D2L, E_D2F, E_NOP }} ; // from double public void emitConversion( Type from, Type to ) { if (!from.isNumber()) { throw new IllegalArgumentException("From type " + from.name() + " is not a numeric type"); } if (!to.isNumber()) { throw new IllegalArgumentException("To type " + to.name() + " is not a numeric type"); } int fromIndex = typeIndex.get( from ) ; int toIndex = typeIndex.get( to ) ; EmitterFactory.Emitter emitter = numericConversions[fromIndex][toIndex] ; emitter.evaluate( mv ) ; } // Operator and type emitter tables private static Map> opInstructions = new HashMap>() ; private static Map ifOpInstructions = new HashMap() ; static { Map map = new HashMap() ; map.put( Type._boolean(), ISUB ) ; map.put( Type._byte(), ISUB ) ; map.put( Type._char(), ISUB ) ; map.put( Type._short(), ISUB ) ; map.put( Type._int(), ISUB ) ; map.put( Type._long(), LCMP ) ; map.put( Type._float(), FCMPG ) ; map.put( Type._double(), DCMPG ) ; opInstructions.put( ExpressionFactory.BinaryOperator.EQ, map ) ; opInstructions.put( ExpressionFactory.BinaryOperator.NE, map ) ; opInstructions.put( ExpressionFactory.BinaryOperator.GT, map ) ; opInstructions.put( ExpressionFactory.BinaryOperator.GE, map ) ; opInstructions.put( ExpressionFactory.BinaryOperator.LT, map ) ; opInstructions.put( ExpressionFactory.BinaryOperator.LE, map ) ; map.clear() ; map.put( Type._int(), IADD ) ; map.put( Type._long(), LADD ) ; map.put( Type._float(), FADD ) ; map.put( Type._double(), DADD ) ; opInstructions.put( ExpressionFactory.BinaryOperator.PLUS, map ) ; map.clear() ; map.put( Type._int(), ISUB ) ; map.put( Type._long(), LSUB ) ; map.put( Type._float(), FSUB ) ; map.put( Type._double(), DSUB ) ; opInstructions.put( ExpressionFactory.BinaryOperator.MINUS, map ) ; map.clear() ; map.put( Type._int(), IMUL ) ; map.put( Type._long(), LMUL ) ; map.put( Type._float(), FMUL ) ; map.put( Type._double(), DMUL ) ; opInstructions.put( ExpressionFactory.BinaryOperator.TIMES, map ) ; map.clear() ; map.put( Type._int(), IDIV ) ; map.put( Type._long(), LDIV ) ; map.put( Type._float(), FDIV ) ; map.put( Type._double(), DDIV ) ; opInstructions.put( ExpressionFactory.BinaryOperator.DIV, map ) ; map.clear() ; map.put( Type._int(), IREM ) ; map.put( Type._long(), LREM ) ; map.put( Type._float(), FREM ) ; map.put( Type._double(), DREM ) ; opInstructions.put( ExpressionFactory.BinaryOperator.REM, map ) ; ifOpInstructions.put( ExpressionFactory.BinaryOperator.EQ, IFEQ ) ; ifOpInstructions.put( ExpressionFactory.BinaryOperator.NE, IFNE ) ; ifOpInstructions.put( ExpressionFactory.BinaryOperator.GT, IFGT ) ; ifOpInstructions.put( ExpressionFactory.BinaryOperator.GE, IFGE ) ; ifOpInstructions.put( ExpressionFactory.BinaryOperator.LT, IFLT ) ; ifOpInstructions.put( ExpressionFactory.BinaryOperator.LE, IFLE ) ; } private void emitBooleanCodeForPrimitive( ExpressionFactory.BinaryOperatorExpression arg ) { MyLabel internalLabel = new MyLabel() ; MyLabel exitLabel = ASMByteCodeVisitor.nextLabel( arg ) ; if (!ifOpInstructions.containsKey( arg.operator() )) { throw new IllegalStateException( "emitBooleanCode called with operator " + arg + ", which is not a relational operator"); } mv.visitJumpInsn( ifOpInstructions.get(arg.operator()), internalLabel ) ; mv.visitInsn( ICONST_0 ) ; mv.visitJumpInsn( GOTO, exitLabel ) ; mv.visitLabel( internalLabel ) ; mv.visitInsn( ICONST_1 ) ; } private void emitBooleanCodeForReference( ExpressionFactory.BinaryOperatorExpression arg ) { MyLabel internalLabel = new MyLabel() ; MyLabel exitLabel = new MyLabel() ; if (arg.operator() == ExpressionFactory.BinaryOperator.EQ) { mv.visitJumpInsn( IF_ACMPEQ, internalLabel ) ; } else if (arg.operator() == ExpressionFactory.BinaryOperator.NE) { mv.visitJumpInsn( IF_ACMPNE, internalLabel ) ; } mv.visitInsn( ICONST_0 ) ; mv.visitJumpInsn( GOTO, exitLabel ) ; mv.visitLabel( internalLabel ) ; mv.visitInsn( ICONST_1 ) ; mv.visitLabel( exitLabel ) ; } // public void emitBinaryOperator( ExpressionFactory.BinaryOperatorExpression arg ) { // This is complicated due to the rather ugly structure of the JVM bytecodes // for comparisons. For now, we will not attempt to generate optimal code. // Rather we will try to keep this clean and simple. Unfortunately all of // the relational operators require emitting code that contains branches. // Cases: // 1. EQ, NE: these are the only operators that apply to reference types. // We need to use if_acmpeq and if_acmpne to compile these. // Each of these is followed by: // ifOP L1 // iconst_0 // GOTO next label // L1: iconst_1 // 2. relational operators: these only apply to numeric arguments. We // can assume that both expressions have the same type here, since that // is guaranteed by binary promotions in the ExpressionFactory code. // The basic pattern here is to compare, then use the appropriate branch // on an integer value as follows: // int: isub // long: lcmp // float: fcmp // double: dcmp // Each of these is followed by: // ifOP L1 // iconst_0 // GOTO next label // L1: iconst_1 // 3. Arithmetic. These ops simply emit (i,l,f,d)(add,sub,mul,div,rem) // instructions. // Type type = ((ExpressionInternal)arg.left()).type() ; ExpressionFactory.BinaryOperator op = arg.operator() ; if (type.isPrimitive()) { mv.visitInsn( opInstructions.get(op).get(type) ) ; if (op.kind() == ExpressionFactory.BinaryOperatorKind.RELATIONAL) { emitBooleanCodeForPrimitive( arg ) ; } } else { // Only ops for reference types are EQ and NE if ((op == ExpressionFactory.BinaryOperator.EQ) || (op == ExpressionFactory.BinaryOperator.NE)) { emitBooleanCodeForReference( arg ) ; } else { throw new IllegalStateException( "Binary operator argument types are " + type.name() + " but operator is not EQ or NE" ) ; } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy