Package org.formulacompiler.compiler.internal.bytecode

Source Code of org.formulacompiler.compiler.internal.bytecode.ExpressionCompilerForNumbers_Base$TestCompilerBranchingWhenFalse

/*
* Copyright (c) 2006-2009 by Abacus Research AG, Switzerland.
* All rights reserved.
*
* This file is part of the Abacus Formula Compiler (AFC).
*
* For commercial licensing, please contact sales(at)formulacompiler.com.
*
* AFC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AFC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AFC.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.formulacompiler.compiler.internal.bytecode;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;

import org.formulacompiler.compiler.CompilerException;
import org.formulacompiler.compiler.Function;
import org.formulacompiler.compiler.NumericType;
import org.formulacompiler.compiler.Operator;
import org.formulacompiler.compiler.internal.expressions.DataType;
import org.formulacompiler.compiler.internal.expressions.ExpressionNode;
import org.formulacompiler.compiler.internal.expressions.ExpressionNodeForArrayReference;
import org.formulacompiler.compiler.internal.expressions.ExpressionNodeForConstantValue;
import org.formulacompiler.compiler.internal.expressions.ExpressionNodeForFunction;
import org.formulacompiler.compiler.internal.expressions.ExpressionNodeForOperator;
import org.formulacompiler.compiler.internal.expressions.InnerExpressionException;
import org.formulacompiler.compiler.internal.model.ExpressionNodeForCount;
import org.formulacompiler.compiler.internal.model.SectionModel;
import org.formulacompiler.runtime.Milliseconds;
import org.formulacompiler.runtime.MillisecondsSinceUTC1970;
import org.formulacompiler.runtime.ScaledLong;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;


abstract class ExpressionCompilerForNumbers_Base extends ExpressionCompilerForAll_Generated
{
  protected final static Type NUMBER_CLASS = Type.getType( Number.class );
  protected final static String N = NUMBER_CLASS.getDescriptor();

  static ExpressionCompilerForNumbers compilerFor( MethodCompiler _methodCompiler, NumericType _numericType )
  {
    if (Double.TYPE == _numericType.valueType()) {
      return new ExpressionCompilerForDoubles( _methodCompiler, _numericType );
    }
    else if (Long.TYPE == _numericType.valueType()) {
      return new ExpressionCompilerForScaledLongs( _methodCompiler, _numericType );
    }
    else if (BigDecimal.class == _numericType.valueType()) {
      if (null != _numericType.mathContext()) {
        return new ExpressionCompilerForPrecisionBigDecimals( _methodCompiler, _numericType );
      }
      else {
        return new ExpressionCompilerForScaledBigDecimals( _methodCompiler, _numericType );
      }
    }
    else {
      throw new IllegalArgumentException( "Unsupported data type " + _numericType + " for byte code compilation." );
    }
  }


  private final NumericType numericType;

  public ExpressionCompilerForNumbers_Base( MethodCompiler _methodCompiler, NumericType _numericType )
  {
    super( _methodCompiler );
    this.numericType = _numericType;
  }


  NumericType numericType()
  {
    return this.numericType;
  }

  @Override
  protected TypeCompiler typeCompiler()
  {
    return section().engineCompiler().numberCompiler();
  }


  protected abstract boolean compileConversionFrom( ScaledLong _scale ) throws CompilerException;
  protected abstract boolean compileConversionTo( ScaledLong _scale ) throws CompilerException;


  protected final void compileConversionFromInt() throws CompilerException
  {
    compile_util_fromInt();
    compileScaleUp();
  }

  private final void compileConversionToInt() throws CompilerException
  {
    compileScaleDown();
    compile_util_toInt();
  }

  protected final void compileConversionToNumber() throws CompilerException
  {
    if (isScaled()) {
      compile_util_toNumber_Scaled();
    }
    else {
      compile_util_toNumber();
    }
  }

  protected final void compileInt( ExpressionNode _node ) throws CompilerException
  {
    compile( _node );
    if (!(_node instanceof ExpressionNodeForFunction)
        || !((ExpressionNodeForFunction) _node).getFunction().returnsInt()) {
      compileConversionToInt();
    }
  }


  @Override
  protected void compileConversionTo( Class _class ) throws CompilerException
  {
    if (_class == Number.class) {
      compileConversionToNumber();
    }
    else {
      final Class unboxed = unboxed( _class );
      if (null == unboxed) {
        compileConversionToUnboxed( _class );
      }
      else {
        compileConversionToUnboxed( unboxed );
        compileBoxing( _class );
      }
    }
  }


  protected abstract boolean isScaled();

  @SuppressWarnings( "unused" )
  protected void compileScaleUp() throws CompilerException
  {
    if (isScaled()) throw new IllegalStateException( "No scaling for " + toString() );
  }

  @SuppressWarnings( "unused" )
  protected void compileScaleDown() throws CompilerException
  {
    if (isScaled()) throw new IllegalStateException( "No scaling for " + toString() );
  }


  protected void compileConversionToUnboxed( Class _class ) throws CompilerException
  {
    if (_class == Long.TYPE) {
      compileScaleDown();
      compile_util_toLong();
    }

    else if (_class == Integer.TYPE) {
      compileScaleDown();
      compile_util_toInt();
    }

    else if (_class == Short.TYPE) {
      compileScaleDown();
      compile_util_toShort();
    }

    else if (_class == Byte.TYPE) {
      compileScaleDown();
      compile_util_toByte();
    }

    else if (_class == Boolean.TYPE) {
      compileScaleDown();
      compile_util_toBoolean();
    }

    else if (_class == Character.TYPE) {
      compileScaleDown();
      compile_util_toCharacter();
    }

    else if (_class == Double.TYPE) {
      if (isScaled()) {
        compile_util_toDouble_Scaled();
      }
      else {
        compile_util_toDouble();
      }
    }

    else if (_class == Float.TYPE) {
      if (isScaled()) {
        compile_util_toFloat_Scaled();
      }
      else {
        compile_util_toFloat();
      }
    }

    else if (_class == BigInteger.class) {
      compileScaleDown();
      compile_util_toBigInteger();
    }

    else if (_class == BigDecimal.class) {
      if (isScaled()) {
        compile_util_toBigDecimal_Scaled();
      }
      else {
        compile_util_toBigDecimal();
      }
    }

    else if (_class == Date.class) {
      compile_util_toDate();
    }

    else if (_class == String.class) {
      compile_util_toString();
    }

    else {
      super.compileConversionTo( _class );
    }
  }

  protected abstract void compile_util_toByte() throws CompilerException;
  protected abstract void compile_util_toShort() throws CompilerException;
  protected abstract void compile_util_toInt() throws CompilerException;
  protected abstract void compile_util_toLong() throws CompilerException;
  protected abstract void compile_util_toDouble() throws CompilerException;
  protected abstract void compile_util_toFloat() throws CompilerException;
  protected abstract void compile_util_toBigDecimal() throws CompilerException;
  protected abstract void compile_util_toBigInteger() throws CompilerException;
  protected abstract void compile_util_toBoolean() throws CompilerException;
  protected abstract void compile_util_toCharacter() throws CompilerException;
  protected abstract void compile_util_toDate() throws CompilerException;
  protected abstract void compile_util_toString() throws CompilerException;
  protected abstract void compile_util_toNumber() throws CompilerException;

  @SuppressWarnings( "unused" )
  protected void compile_util_toDouble_Scaled() throws CompilerException
  {
    throw new IllegalStateException( "No scaling for " + toString() );
  }

  @SuppressWarnings( "unused" )
  protected void compile_util_toFloat_Scaled() throws CompilerException
  {
    throw new IllegalStateException( "No scaling for " + toString() );
  }

  @SuppressWarnings( "unused" )
  protected void compile_util_toBigDecimal_Scaled() throws CompilerException
  {
    throw new IllegalStateException( "No scaling for " + toString() );
  }

  @SuppressWarnings( "unused" )
  protected void compile_util_toNumber_Scaled() throws CompilerException
  {
    throw new IllegalStateException( "No scaling for " + toString() );
  }


  private Class unboxed( Class _class )
  {
    if (Byte.class == _class) {
      return Byte.TYPE;
    }
    else if (Short.class == _class) {
      return Short.TYPE;
    }
    else if (Integer.class == _class) {
      return Integer.TYPE;
    }
    else if (Long.class == _class) {
      return Long.TYPE;
    }
    else if (Float.class == _class) {
      return Float.TYPE;
    }
    else if (Double.class == _class) {
      return Double.TYPE;
    }
    else if (Character.class == _class) {
      return Character.TYPE;
    }
    else if (Boolean.class == _class) {
      return Boolean.TYPE;
    }
    return null;
  }


  private void compileBoxing( Class _class ) throws CompilerException
  {
    if (Byte.class == _class) {
      compile_util_boxByte();
    }
    else if (Short.class == _class) {
      compile_util_boxShort();
    }
    else if (Integer.class == _class) {
      compile_util_boxInteger();
    }
    else if (Long.class == _class) {
      compile_util_boxLong();
    }
    else if (Float.class == _class) {
      compile_util_boxFloat();
    }
    else if (Double.class == _class) {
      compile_util_boxDouble();
    }
    else if (Character.class == _class) {
      compile_util_boxCharacter();
    }
    else if (Boolean.class == _class) {
      compile_util_boxBoolean();
    }
  }


  protected void compileConversionToString() throws CompilerException
  {
    compile_util_toString();
  }


  @Override
  protected void compileConversionFrom( Class _class ) throws CompilerException
  {
    compileConversionFromUnboxed( compileUnboxing( _class ) );
  }


  protected void compileConversionFromUnboxed( Class _class ) throws CompilerException
  {
    if (_class == Integer.TYPE || _class == Short.TYPE || _class == Byte.TYPE) {
      compile_util_fromInt();
      compileScaleUp();
    }

    else if (_class == Long.TYPE) {
      compile_util_fromLong();
      compileScaleUp();
    }

    else if (_class == Double.TYPE) {
      if (isScaled()) {
        compile_util_fromDouble_Scaled();
      }
      else {
        compile_util_fromDouble();
      }
    }

    else if (_class == Float.TYPE) {
      if (isScaled()) {
        compile_util_fromFloat_Scaled();
      }
      else {
        compile_util_fromFloat();
      }
    }

    else if (BigDecimal.class.isAssignableFrom( _class )) {
      if (isScaled()) {
        compile_util_fromBigDecimal_Scaled();
      }
      else {
        compile_util_fromBigDecimal();
      }
    }

    else if (BigInteger.class.isAssignableFrom( _class )) {
      compile_util_fromBigInteger();
      compileScaleUp();
    }

    else if (Number.class.isAssignableFrom( _class )) {
      compile_util_fromNumber();
      compileScaleUp();
    }

    else if (_class == Boolean.TYPE) {
      compile_util_fromBoolean();
    }

    else if (Date.class.isAssignableFrom( _class )) {
      compile_util_fromDate();
    }

    else {
      super.compileConversionFrom( _class );
    }
  }

  protected abstract void compile_util_fromInt() throws CompilerException;
  protected abstract void compile_util_fromLong() throws CompilerException;
  protected abstract void compile_util_fromDouble() throws CompilerException;
  protected abstract void compile_util_fromFloat() throws CompilerException;
  protected abstract void compile_util_fromNumber() throws CompilerException;
  protected abstract void compile_util_fromBoolean() throws CompilerException;
  protected abstract void compile_util_fromDate() throws CompilerException;

  protected void compile_util_fromBigDecimal() throws CompilerException
  {
    compile_util_fromNumber();
  }

  protected void compile_util_fromBigInteger() throws CompilerException
  {
    compile_util_fromNumber();
  }

  @SuppressWarnings( "unused" )
  protected void compile_util_fromDouble_Scaled() throws CompilerException
  {
    throw new IllegalStateException( "No scaling for " + toString() );
  }

  @SuppressWarnings( "unused" )
  protected void compile_util_fromFloat_Scaled() throws CompilerException
  {
    throw new IllegalStateException( "No scaling for " + toString() );
  }

  @SuppressWarnings( "unused" )
  protected void compile_util_fromBigDecimal_Scaled() throws CompilerException
  {
    throw new IllegalStateException( "No scaling for " + toString() );
  }


  Class compileUnboxing( Class _class ) throws CompilerException
  {
    if (Byte.class.isAssignableFrom( _class )) {
      compile_util_unboxByte();
      return Byte.TYPE;
    }
    else if (Short.class.isAssignableFrom( _class )) {
      compile_util_unboxShort();
      return Short.TYPE;
    }
    else if (Integer.class.isAssignableFrom( _class )) {
      compile_util_unboxInteger();
      return Integer.TYPE;
    }
    else if (Long.class.isAssignableFrom( _class )) {
      compile_util_unboxLong();
      return Long.TYPE;
    }
    else if (Float.class.isAssignableFrom( _class )) {
      compile_util_unboxFloat();
      return Float.TYPE;
    }
    else if (Double.class.isAssignableFrom( _class )) {
      compile_util_unboxDouble();
      return Double.TYPE;
    }
    else if (Character.class.isAssignableFrom( _class )) {
      compile_util_unboxCharacter();
      return Character.TYPE;
    }
    else if (Boolean.class.isAssignableFrom( _class )) {
      compile_util_unboxBoolean();
      return Boolean.TYPE;
    }
    return _class;
  }


  protected abstract void compile_util_fromMs() throws CompilerException;
  protected abstract void compile_util_fromMsSinceUTC1970() throws CompilerException;

  @Override
  protected void innerCompileConversionFromResultOf( Method _method ) throws CompilerException
  {
    final Class returnType = _method.getReturnType();
    if (returnType == Long.TYPE || returnType == Long.class) {

      if ((_method.getAnnotation( Milliseconds.class ) != null)) {
        if (returnType == Long.class) {
          compile_util_unboxLong();
        }
        compile_util_fromMs();
        return;
      }

      if ((_method.getAnnotation( MillisecondsSinceUTC1970.class ) != null)) {
        if (returnType == Long.class) {
          compile_util_unboxLong();
        }
        compile_util_fromMsSinceUTC1970();
        return;
      }

      final ScaledLong scale = scaleOf( _method );
      if (scale != null && scale.value() != 0) {
        if (returnType == Long.class) {
          compile_util_unboxLong();
        }
        if (!compileConversionFrom( scale )) {
          throw new CompilerException.UnsupportedDataType( "Scaled long inputs not supported by " + this + "." );
        }
        return;
      }

    }
    compileConversionFrom( returnType );
  }


  protected abstract void compile_util_toMs() throws CompilerException;
  protected abstract void compile_util_toMsSinceUTC1970() throws CompilerException;

  @Override
  protected void innerCompileConversionToResultOf( Method _method ) throws CompilerException
  {
    final Class returnType = _method.getReturnType();
    if (returnType == Long.TYPE || returnType == Long.class) {

      if ((_method.getAnnotation( Milliseconds.class ) != null)) {
        compile_util_toMs();
        if (returnType == Long.class) {
          compile_util_boxLong();
        }
        return;
      }

      if ((_method.getAnnotation( MillisecondsSinceUTC1970.class ) != null)) {
        compile_util_toMsSinceUTC1970();
        if (returnType == Long.class) {
          compile_util_boxLong();
        }
        return;
      }

      final ScaledLong scale = scaleOf( _method );
      if (scale != null && scale.value() != 0) {
        if (compileConversionTo( scale )) {
          if (returnType == Long.class) {
            mv().visitMethodInsn( Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", J2LONG );
          }
        }
        else {
          throw new CompilerException.UnsupportedDataType( "Scaled long results not supported by " + this + "." );
        }
        return;
      }

    }
    if (!isNativeType( returnType )) {
      compileConversionTo( returnType );
    }
  }

  protected abstract boolean isNativeType( Class _type );


  protected final ScaledLong scaleOf( Method _method )
  {
    final ScaledLong typeScale = _method.getDeclaringClass().getAnnotation( ScaledLong.class );
    final ScaledLong mtdScale = _method.getAnnotation( ScaledLong.class );
    final ScaledLong scale = (mtdScale != null) ? mtdScale : typeScale;
    return scale;
  }


  protected final void compileInstructions( int... _conversionOpcodes )
  {
    final GeneratorAdapter mv = mv();
    for (int conv : _conversionOpcodes) {
      mv.visitInsn( conv );
    }
  }


  @Override
  protected void compileConst( Object _value ) throws CompilerException
  {
    if (_value instanceof Date) {
      mv().push( ((Date) _value).getTime() );
      compile_util_fromMsSinceUTC1970();
    }
    else {
      super.compileConst( _value );
    }
  }


  @Override
  protected void compileFunction( ExpressionNodeForFunction _node ) throws CompilerException
  {
    switch (_node.getFunction()) {

      case MATCH:
      case INTERNAL_MATCH_INT:
        compileMatch( _node );
        return;

    }
    super.compileFunction( _node );
  }


  private void compileMatch( ExpressionNodeForFunction _node ) throws CompilerException
  {
    switch (_node.cardinality()) {

      case 2: {
        compileMatch( _node, 1 );
        return;
      }

      case 3: {
        final ExpressionNode typeArg = _node.arguments().get( 2 );
        if (typeArg instanceof ExpressionNodeForConstantValue) {
          final ExpressionNodeForConstantValue constTypeArg = (ExpressionNodeForConstantValue) typeArg;
          final Object typeVal = constTypeArg.value();
          if (typeVal instanceof Number) {
            compileMatch( _node, ((Number) typeVal).intValue() );
          }
          else if (typeVal instanceof Boolean) {
            compileMatch( _node, (Boolean) typeVal ? 1 : 0 );
          }
          else {
            compileMatch( _node, 1 );
          }
          return;
        }
        throw new InnerExpressionException( typeArg, new CompilerException.UnsupportedExpression(
            "The last argument to MATCH, the match type, must be constant, but is " + typeArg.describe() + "." ) );
      }

    }
    throw new CompilerException.UnsupportedExpression( "MATCH must have two or three arguments." );
  }

  private static final String[] TYPE_SUFFIXES = { "Descending", "Exact", "Ascending" };

  private void compileMatch( ExpressionNodeForFunction _node, int _type ) throws CompilerException
  {
    final ExpressionNode valNode = _node.argument( 0 );
    final ExpressionNodeForArrayReference arrayNode = (ExpressionNodeForArrayReference) _node.argument( 1 );
    if (valNode.getDataType() != arrayNode.getDataType()) {
      throw new CompilerException.UnsupportedExpression(
          "MATCH must have the same type of argument in the first and second slot." );
    }
    final int type = (_type > 0) ? +1 : (_type < 0) ? -1 : 0;

    final ExpressionCompilerForNumbers numCompiler = method().numericCompiler();
    final ExpressionCompiler valCompiler = method().expressionCompiler( valNode.getDataType() );
    final ArrayAccessorCompiler acc = section().getArrayAccessorForFullData( arrayNode );

    // return Runtime.fun_MATCH_xy( val, vals [, env] );
    final boolean needEnv = (valNode.getDataType() == DataType.STRING && type != 0);
    final String envDescriptor = needEnv ? ByteCodeEngineCompiler.ENV_DESC : "";
    valCompiler.compile( valNode );
    mv().loadThis();
    acc.compileCall( mv() );
    if (needEnv) {
      compile_environment();
    }
    valCompiler.compileRuntimeMethod( "fun_MATCH_" + TYPE_SUFFIXES[ type + 1 ], "("
        + acc.elementDescriptor() + acc.arrayDescriptor() + envDescriptor + ")I" );

    if (_node.getFunction() != Function.INTERNAL_MATCH_INT) {
      numCompiler.compileConversionFromInt();
    }
  }


  protected void compileRuntimeFunction( ExpressionNodeForFunction _node ) throws CompilerException
  {
    final boolean needsContext = doesRuntimeFunctionNeedContext( _node );
    final StringBuilder descriptorBuilder = new StringBuilder();
    final String typeDescriptor = typeDescriptor();
    descriptorBuilder.append( '(' );
    for (ExpressionNode arg : _node.arguments()) {
      compile( arg );
      descriptorBuilder.append( typeDescriptor );
    }
    if (needsContext) appendRuntimeFunctionContext( descriptorBuilder );
    descriptorBuilder.append( ')' );
    descriptorBuilder.append( typeDescriptor );
    if (needsContext) {
      compileRuntimeMethodWithContext( "std" + _node.getFunction().getName(), descriptorBuilder.toString() );
    }
    else {
      compileRuntimeMethod( "std" + _node.getFunction().getName(), descriptorBuilder.toString() );
    }
  }

  protected boolean doesRuntimeFunctionNeedContext( ExpressionNodeForFunction _node )
  {
    return false;
  }

  protected void appendRuntimeFunctionContext( StringBuilder _descriptorBuilder )
  {
    throw new IllegalStateException( "Internal error: appendStdFunctionContext() is not applicable for " + this + "." );
  }

  protected void compileRuntimeMethodWithContext( String _string, String _string2 )
  {
    throw new IllegalStateException( "Internal error: compileRuntimeMethodWithContext() is not applicable for "
        + this + "." );
  }


  protected abstract void compile_util_round( int _maxFractionalDigits ) throws CompilerException;


  @Override
  protected final void compileCount( ExpressionNodeForCount _node ) throws CompilerException
  {
    final GeneratorAdapter mv = mv();
    mv.push( _node.staticValueCount() );

    final SectionModel[] subs = _node.subSectionModels();
    final int[] cnts = _node.subSectionStaticValueCounts();
    for (int i = 0; i < subs.length; i++) {
      SubSectionCompiler sub = sectionInContext().subSectionCompiler( subs[ i ] );
      mv.visitVarInsn( Opcodes.ALOAD, method().objectInContext() );
      sectionInContext().compileCallToGetterFor( mv, sub );
      mv.arrayLength();
      if (cnts[ i ] != 1) {
        mv.push( cnts[ i ] );
        mv.visitInsn( Opcodes.IMUL );
      }
      mv.visitInsn( Opcodes.IADD );
    }

    compileConversionFromInt();
  }


  final void compileTest( ExpressionNode _test, Label _notMet ) throws CompilerException
  {
    new TestCompilerBranchingWhenFalse( _test, _notMet ).compileTest();
  }


  private abstract class TestCompiler
  {
    protected ExpressionNode node;
    protected Label branchTo;

    TestCompiler( ExpressionNode _node, Label _branchTo )
    {
      super();
      this.node = _node;
      this.branchTo = _branchTo;
    }

    final void compileTest() throws CompilerException
    {
      if (this.node instanceof ExpressionNodeForOperator) {
        final ExpressionNodeForOperator opNode = (ExpressionNodeForOperator) this.node;
        final Operator operator = opNode.getOperator();

        switch (operator) {

          case EQUAL:
          case NOTEQUAL:
          case GREATER:
          case GREATEROREQUAL:
          case LESS:
          case LESSOREQUAL:
            final List<ExpressionNode> args = this.node.arguments();
            compileComparison( operator, args.get( 0 ), args.get( 1 ) );
            return;
        }
      }

      else if (this.node instanceof ExpressionNodeForFunction) {
        final ExpressionNodeForFunction fnNode = (ExpressionNodeForFunction) this.node;
        final Function fn = fnNode.getFunction();

        switch (fn) {

          case AND:
            compileAnd();
            return;

          case OR:
            compileOr();
            return;

          case NOT:
            compileNot();
            return;

        }
      }

      compileValue();
    }


    private final void compileComparison( Operator _operator, ExpressionNode _left, ExpressionNode _right )
        throws CompilerException
    {
      final boolean leftIsString = _left.getDataType() == DataType.STRING;
      final boolean rightIsString = _right.getDataType() == DataType.STRING;

      // String is always greater than number in Excel:
      if (leftIsString && !rightIsString) {
        compileStaticComparisonResult( _operator, +1 );
      }
      else if (!leftIsString && rightIsString) {
        compileStaticComparisonResult( _operator, -1 );
      }
      else {
        final ExpressionCompiler comparisonCompiler = (leftIsString || rightIsString) ? method().stringCompiler()
            : method().numericCompiler();
        comparisonCompiler.compile( _left );
        comparisonCompiler.compile( _right );
        compileComparison( _operator, comparisonCompiler );
      }
    }

    private final void compileStaticComparisonResult( Operator _operator, int _result ) throws CompilerException
    {
      switch (_operator) {
        case EQUAL:
          compileStaticConditionResult( _result == 0 );
          break;
        case NOTEQUAL:
          compileStaticConditionResult( _result != 0 );
          break;
        case GREATER:
          compileStaticConditionResult( _result > 0 );
          break;
        case GREATEROREQUAL:
          compileStaticConditionResult( _result >= 0 );
          break;
        case LESS:
          compileStaticConditionResult( _result < 0 );
          break;
        case LESSOREQUAL:
          compileStaticConditionResult( _result <= 0 );
          break;
        default:
          throw new IllegalArgumentException( "Comparison expected" );
      }
    }

    protected abstract TestCompiler newInverseCompiler( ExpressionNode _node, Label _branchTo );
    protected abstract void compileAnd() throws CompilerException;
    protected abstract void compileOr() throws CompilerException;
    protected abstract void compileComparison( Operator _comparison, ExpressionCompiler _comparisonCompiler )
        throws CompilerException;
    protected abstract void compileStaticConditionResult( boolean _result ) throws CompilerException;

    protected final void compileComparison( ExpressionCompiler _comparisonCompiler, int _ifOpcode,
        int _comparisonOpcode ) throws CompilerException
    {
      final int effectiveIfOpcode = _comparisonCompiler.compileComparison( _ifOpcode, _comparisonOpcode );
      mv().visitJumpInsn( effectiveIfOpcode, this.branchTo );
    }

    private final void compileNot() throws CompilerException
    {
      final List<ExpressionNode> args = this.node.arguments();
      if (1 == args.size()) {
        newInverseCompiler( args.get( 0 ), this.branchTo ).compileTest();
      }
      else {
        throw new CompilerException.UnsupportedExpression( "NOT must have exactly one argument." );
      }
    }

    final void compileValue() throws CompilerException
    {
      compile( this.node );
      compileZero();
      compileComparison( Operator.NOTEQUAL, method().numericCompiler() );
    }

    protected abstract void compileBooleanTest() throws CompilerException;
  }


  private class TestCompilerBranchingWhenFalse extends TestCompiler
  {

    TestCompilerBranchingWhenFalse( ExpressionNode _node, Label _branchTo )
    {
      super( _node, _branchTo );
    }

    @Override
    protected void compileComparison( Operator _comparison, ExpressionCompiler _comparisonCompiler )
        throws CompilerException
    {
      switch (_comparison) {

        case EQUAL:
          compileComparison( _comparisonCompiler, Opcodes.IFNE, Opcodes.DCMPL );
          return;

        case NOTEQUAL:
          compileComparison( _comparisonCompiler, Opcodes.IFEQ, Opcodes.DCMPL );
          return;

        case GREATER:
          compileComparison( _comparisonCompiler, Opcodes.IFLE, Opcodes.DCMPL );
          return;

        case GREATEROREQUAL:
          compileComparison( _comparisonCompiler, Opcodes.IFLT, Opcodes.DCMPL );
          return;

        case LESS:
          compileComparison( _comparisonCompiler, Opcodes.IFGE, Opcodes.DCMPG );
          return;

        case LESSOREQUAL:
          compileComparison( _comparisonCompiler, Opcodes.IFGT, Opcodes.DCMPG );
          return;

      }
    }

    @Override
    protected void compileStaticConditionResult( boolean _result ) throws CompilerException
    {
      if (!_result) {
        mv().visitJumpInsn( Opcodes.GOTO, this.branchTo );
      }
    }

    @Override
    protected TestCompiler newInverseCompiler( ExpressionNode _node, Label _branchTo )
    {
      return new TestCompilerBranchingWhenTrue( _node, _branchTo );
    }

    @Override
    protected void compileOr() throws CompilerException
    {
      final Label met = mv().newLabel();
      final int nArg = this.node.arguments().size();
      int iArg = 0;
      while (iArg < nArg - 1) {
        final ExpressionNode arg = this.node.arguments().get( iArg );
        new TestCompilerBranchingWhenTrue( arg, met ).compileTest();
        iArg++;
      }
      final ExpressionNode lastArg = this.node.arguments().get( iArg );
      new TestCompilerBranchingWhenFalse( lastArg, this.branchTo ).compileTest();
      mv().mark( met );
    }

    @Override
    protected void compileAnd() throws CompilerException
    {
      for (ExpressionNode arg : this.node.arguments()) {
        new TestCompilerBranchingWhenFalse( arg, this.branchTo ).compileTest();
      }
    }

    @Override
    protected void compileBooleanTest() throws CompilerException
    {
      mv().visitJumpInsn( Opcodes.IFEQ, this.branchTo );
    }
  }


  private class TestCompilerBranchingWhenTrue extends TestCompiler
  {

    TestCompilerBranchingWhenTrue( ExpressionNode _node, Label _branchTo )
    {
      super( _node, _branchTo );
    }

    @Override
    protected void compileComparison( Operator _comparison, ExpressionCompiler _comparisonCompiler )
        throws CompilerException
    {
      switch (_comparison) {

        case EQUAL:
          compileComparison( _comparisonCompiler, Opcodes.IFEQ, Opcodes.DCMPL );
          return;

        case NOTEQUAL:
          compileComparison( _comparisonCompiler, Opcodes.IFNE, Opcodes.DCMPL );
          return;

        case GREATER:
          compileComparison( _comparisonCompiler, Opcodes.IFGT, Opcodes.DCMPG );
          return;

        case GREATEROREQUAL:
          compileComparison( _comparisonCompiler, Opcodes.IFGE, Opcodes.DCMPG );
          return;

        case LESS:
          compileComparison( _comparisonCompiler, Opcodes.IFLT, Opcodes.DCMPL );
          return;

        case LESSOREQUAL:
          compileComparison( _comparisonCompiler, Opcodes.IFLE, Opcodes.DCMPL );
          return;

      }
    }

    @Override
    protected void compileStaticConditionResult( boolean _result ) throws CompilerException
    {
      if (_result) {
        mv().visitJumpInsn( Opcodes.GOTO, this.branchTo );
      }
    }

    @Override
    protected TestCompiler newInverseCompiler( ExpressionNode _node, Label _branchTo )
    {
      return new TestCompilerBranchingWhenFalse( _node, _branchTo );
    }

    @Override
    protected void compileOr() throws CompilerException
    {
      for (ExpressionNode arg : this.node.arguments()) {
        new TestCompilerBranchingWhenTrue( arg, this.branchTo ).compileTest();
      }
    }

    @Override
    protected void compileAnd() throws CompilerException
    {
      final Label notMet = mv().newLabel();
      final int nArg = this.node.arguments().size();
      int iArg = 0;
      while (iArg < nArg - 1) {
        final ExpressionNode arg = this.node.arguments().get( iArg );
        new TestCompilerBranchingWhenFalse( arg, notMet ).compileTest();
        iArg++;
      }
      final ExpressionNode lastArg = this.node.arguments().get( iArg );
      new TestCompilerBranchingWhenTrue( lastArg, this.branchTo ).compileTest();
      mv().mark( notMet );
    }

    @Override
    protected void compileBooleanTest() throws CompilerException
    {
      mv().visitJumpInsn( Opcodes.IFNE, this.branchTo );
    }
  }


  @Override
  public String toString()
  {
    return numericType().toString();
  }

}
TOP

Related Classes of org.formulacompiler.compiler.internal.bytecode.ExpressionCompilerForNumbers_Base$TestCompilerBranchingWhenFalse

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.