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

com.facebook.presto.bytecode.control.SwitchStatement Maven / Gradle / Ivy

/*
 * 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.facebook.presto.bytecode.control;

import com.facebook.presto.bytecode.BytecodeBlock;
import com.facebook.presto.bytecode.BytecodeNode;
import com.facebook.presto.bytecode.BytecodeVisitor;
import com.facebook.presto.bytecode.MethodGenerationContext;
import com.facebook.presto.bytecode.expression.BytecodeExpression;
import com.facebook.presto.bytecode.instruction.LabelNode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;

import static com.google.common.base.Preconditions.checkState;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;

public class SwitchStatement
        implements FlowControl
{
    public static SwitchBuilder switchBuilder()
    {
        return new SwitchBuilder();
    }

    private final LabelNode endLabel = new LabelNode("switchEnd");
    private final LabelNode defaultLabel = new LabelNode("switchDefault");
    private final String comment;
    private final BytecodeExpression expression;
    private final SortedSet cases;
    private final BytecodeNode defaultBody;

    private SwitchStatement(
            String comment,
            BytecodeExpression expression,
            Iterable cases,
            BytecodeNode defaultBody)
    {
        this.comment = comment;
        this.expression = requireNonNull(expression, "expression is null");
        this.cases = ImmutableSortedSet.copyOf(comparing(CaseStatement::getKey), cases);
        this.defaultBody = defaultBody;
    }

    @Override
    public String getComment()
    {
        return comment;
    }

    public BytecodeExpression expression()
    {
        return expression;
    }

    public SortedSet cases()
    {
        return cases;
    }

    public LabelNode getDefaultLabel()
    {
        return defaultLabel;
    }

    public BytecodeNode getDefaultBody()
    {
        return defaultBody;
    }

    public LabelNode getEndLabel()
    {
        return endLabel;
    }

    @Override
    public void accept(MethodVisitor visitor, MethodGenerationContext generationContext)
    {
        // build switch table
        int[] keys = new int[cases.size()];
        Label[] labels = new Label[cases.size()];

        int index = 0;
        for (CaseStatement caseStatement : cases) {
            keys[index] = caseStatement.getKey();
            labels[index] = caseStatement.getLabel().getLabel();
            index++;
        }

        // build case blocks
        BytecodeBlock block = new BytecodeBlock();

        for (CaseStatement caseStatement : cases) {
            block.visitLabel(caseStatement.getLabel())
                    .append(caseStatement.getBody())
                    .gotoLabel(endLabel);
        }

        // build default block
        block.visitLabel(defaultLabel);

        if (defaultBody != null) {
            block.append(defaultBody);
        }

        block.visitLabel(endLabel);

        // emit code
        expression.accept(visitor, generationContext);
        visitor.visitLookupSwitchInsn(defaultLabel.getLabel(), keys, labels);
        block.accept(visitor, generationContext);
    }

    @Override
    public List getChildNodes()
    {
        return ImmutableList.of();
    }

    @Override
    public  T accept(BytecodeNode parent, BytecodeVisitor visitor)
    {
        return visitor.visitSwitch(parent, this);
    }

    public static class SwitchBuilder
    {
        private final Set cases = new HashSet<>();
        private String comment;
        private BytecodeExpression expression;
        private LabelNode defaultLabel;
        private BytecodeNode defaultBody;

        public SwitchBuilder comment(String format, Object... args)
        {
            this.comment = String.format(format, args);
            return this;
        }

        public SwitchBuilder expression(BytecodeExpression expression)
        {
            this.expression = expression;
            return this;
        }

        public SwitchBuilder addCase(int key, BytecodeNode body)
        {
            LabelNode label = new LabelNode("switchCase:" + key);
            CaseStatement statement = new CaseStatement(key, body, label);
            checkState(cases.add(statement), "case already exists for value [%s]", key);
            return this;
        }

        public SwitchBuilder defaultCase(BytecodeNode body)
        {
            checkState(defaultBody == null, "default case already set");
            this.defaultBody = requireNonNull(body, "body is null");
            return this;
        }

        public SwitchStatement build()
        {
            checkState(expression != null, "expression is not set");
            return new SwitchStatement(comment, expression, cases, defaultBody);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy