org.formulacompiler.compiler.internal.bytecode.HelperCompilerForFoldApply.java Source code

Java tutorial

Introduction

Here is the source code for org.formulacompiler.compiler.internal.bytecode.HelperCompilerForFoldApply.java

Source

/*
 * 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 org.formulacompiler.compiler.CompilerException;
import org.formulacompiler.compiler.internal.expressions.DataType;
import org.formulacompiler.compiler.internal.expressions.ExpressionNode;
import org.formulacompiler.compiler.internal.expressions.ExpressionNodeForConstantValue;
import org.formulacompiler.compiler.internal.expressions.ExpressionNodeForFoldApply;
import org.formulacompiler.compiler.internal.expressions.LetDictionary.LetEntry;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

@SuppressWarnings("unqualified-field-access")
abstract class HelperCompilerForFoldApply extends HelperCompilerForFolds {
    private final boolean mustDetectEmptiness;
    private final boolean mustCount;

    public HelperCompilerForFoldApply(SectionCompiler _section, ExpressionNodeForFoldApply _applyNode,
            Iterable<LetEntry<Compilable>> _closure) {
        super(_section, _applyNode, _closure);

        /*
         * I really hate the following snippet, but since Java does not allow me to get "vec0" before
         * the super() call in a constructor, I see no good way to solve this using inheritance.
         */
        boolean onlyDetectEmptyIf = true, onlyCountIf = true;
        if (argumentsAreVectors()) {
            final Iterable<ExpressionNode> vec0 = ChainedFoldCompiler.firstVectorOf(apply.elements());
            onlyDetectEmptyIf = (null == firstLocalElementIn(vec0));
            onlyCountIf = ExpressionCompiler.isSubSectionIn(vec0);
        }

        this.mustDetectEmptiness = (onlyDetectEmptyIf && fold.isSpecialWhenEmpty()
                && fold.getPartiallyFoldedElementCount() == 0);
        this.mustCount = (onlyCountIf && fold.isCounted());
    }

    protected abstract boolean argumentsAreVectors();

    @Override
    protected final void compileBody() throws CompilerException {
        compileSetup();
        compileTraversal();
        compileMerge();
        mv().returnValue();
    }

    private final void compileSetup() throws CompilerException {
        compileAccuSetup();
        compileIndexSetup();
    }

    private Type[] accuTypes;
    private int[] accuVars;

    private void compileAccuSetup() throws CompilerException {
        final int nAccus = fold.accuCount();
        accuTypes = new Type[nAccus];
        accuVars = new int[nAccus];
        for (int i = 0; i < nAccus; i++) {
            final ExpressionNode initNode = fold.accuInit(i);
            final DataType initType = initNode.getDataType();
            final Type accuType = section().engineCompiler().typeCompiler(initType).type();
            final int accuVar = newLocal(accuType.getSize());
            accuTypes[i] = accuType;
            accuVars[i] = accuVar;
            expressionCompiler().compile(initNode);
            compileAccumulatorStore(i);
            letDict().let(fold.accuName(i), initType, new Compilable() {

                public void compile(ExpressionCompiler _exp) throws CompilerException {
                    mv().visitVarInsn(accuType.getOpcode(Opcodes.ILOAD), accuVar);
                }

                public boolean isArray() {
                    return false;
                }
            });
        }
    }

    private void compileAccumulatorStore(int _iAccu) {
        mv().visitVarInsn(accuTypes[_iAccu].getOpcode(Opcodes.ISTORE), accuVars[_iAccu]);
    }

    private void compileAccumulatorLoad(int _iAccu) {
        mv().visitVarInsn(accuTypes[_iAccu].getOpcode(Opcodes.ILOAD), accuVars[_iAccu]);
    }

    private static final int NO_VAR = 0;
    private int indexVar = NO_VAR;
    private int staticCount = 0;

    private void compileIndexSetup() {
        staticCount = fold.getPartiallyFoldedElementCount();
        if (mustDetectEmptiness || mustCount || fold.isIndexed()) {
            this.indexVar = newLocal(Type.INT_TYPE.getSize());
            mv().push(fold.getPartiallyFoldedElementCount());
            mv().visitVarInsn(Opcodes.ISTORE, indexVar);
            letIndexVarAs(fold.indexName());
        }
    }

    private void letIndexVarAs(String _nameOrNull) {
        if (null != _nameOrNull) {
            letDict().let(_nameOrNull, numericCompiler().dataType(), new Compilable() {

                public void compile(ExpressionCompiler _exp) throws CompilerException {
                    mv().visitVarInsn(Opcodes.ILOAD, indexVar);
                    _exp.compileConversionFrom(Integer.TYPE);
                }

                public boolean isArray() {
                    return false;
                }
            });
        }
    }

    protected abstract void compileTraversal() throws CompilerException;

    protected final void compileFoldStepsWithEltsBound() throws CompilerException {
        compileIndexIncrement();
        for (int i = 0; i < fold.accuCount(); i++) {
            expressionCompiler().compile(fold.accuStep(i));
            compileAccumulatorStore(i);
        }
    }

    protected final void compileIndexIncrement() {
        staticCount++;
        if (NO_VAR != indexVar) {
            mv().visitIincInsn(indexVar, 1);
        }
    }

    private void compileMerge() throws CompilerException {
        final Label exit;
        if (mustDetectEmptiness) {
            final Label notEmpty = mv().newLabel();
            mv().visitVarInsn(Opcodes.ILOAD, indexVar);
            mv().ifZCmp(Opcodes.IFGT, notEmpty);
            expressionCompiler().compile(fold.whenEmpty());
            exit = mv().newLabel();
            mv().goTo(exit);
            mv().mark(notEmpty);
        } else {
            exit = null;
        }

        if (fold.isMergedExplicitly()) {
            if (mustCount) {
                letIndexVarAs(fold.countName());
            } else if (fold.isCounted()) {
                letDict().let(fold.countName(), DataType.NUMERIC, new CompilableExpressionNode(
                        new ExpressionNodeForConstantValue(staticCount, DataType.NUMERIC)));
            }
            expressionCompiler().compile(fold.merge());
        } else {
            assert 1 == accuVars.length;
            compileAccumulatorLoad(0);
        }

        if (null != exit)
            mv().mark(exit);
    }

}