blob: f1b861370c85e22fbf0a75f117948e3cbef93746 [file] [log] [blame]
// Copyright 2015 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.lib.syntax.compiler;
import com.google.devtools.build.lib.syntax.Operator;
import net.bytebuddy.implementation.Implementation.Context;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Implements byte code for gotos and conditional jumps.
*/
public class Jump implements StackManipulation {
protected final int opCode;
protected final Label target;
protected final Size stackSizeChange;
private Jump(int opCode, Label target, Size stackSizeChange) {
this.opCode = opCode;
this.target = target;
this.stackSizeChange = stackSizeChange;
}
@Override
public boolean isValid() {
return true;
}
@Override
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitJumpInsn(opCode, target);
return stackSizeChange;
}
@Override
public String toString() {
return "Jump(" + opCode + ", " + target + ")";
}
/**
* Builds a conditional jump for two int operands on the stack.
*/
public static JumpWithoutTarget ifIntOperands(PrimitiveComparison comparison) {
return new JumpWithoutTarget(Opcodes.IF_ICMPEQ + comparison.ordinal(), new Size(-2, 0));
}
/**
* Builds a conditional jump for one int operand from the stack compared to zero.
*/
public static JumpWithoutTarget ifIntOperandToZero(PrimitiveComparison comparison) {
return new JumpWithoutTarget(Opcodes.IFEQ + comparison.ordinal(), new Size(-1, 0));
}
/**
* Builds a conditional jump for two reference type operands from the stack.
*/
public static JumpWithoutTarget ifReferenceOperands(ReferenceComparison comparison) {
return new JumpWithoutTarget(Opcodes.IF_ACMPEQ + comparison.ordinal(), new Size(-2, 0));
}
/**
* Builds a conditional jump for one reference type operand from the stack compared to null.
*/
public static JumpWithoutTarget ifReferenceOperandToNull(ReferenceComparison comparison) {
return new JumpWithoutTarget(Opcodes.IFNULL + comparison.ordinal(), new Size(-1, 0));
}
/**
* Builds an unconditional jump to the target label.
*/
public static Jump to(Label target) {
return new Jump(Opcodes.GOTO, target, new Size(0, 0));
}
/**
* Builds an unconditional jump to the label added by the given {@link LabelAdder}.
*/
public static Jump to(LabelAdder target) {
return to(target.getLabel());
}
/**
* Builder helper class for partially built jumps from conditionals.
*
* <p>Allows adding a jump target label.
*/
public static final class JumpWithoutTarget {
protected final int opCode;
protected final Size stackSizeChange;
private JumpWithoutTarget(int opCode, Size stackSizeChange) {
this.opCode = opCode;
this.stackSizeChange = stackSizeChange;
}
/**
* Builds a jump to the given target and the previously initialized conditional.
*/
public Jump to(LabelAdder target) {
return new Jump(opCode, target.getLabel(), stackSizeChange);
}
}
/**
* All primitive comparisons for which there are byte code equivalents.
*/
public enum PrimitiveComparison {
EQUAL,
NOT_EQUAL,
LESS,
GREATER_EQUAL,
GREATER,
LESS_EQUAL;
public static PrimitiveComparison forOperator(Operator operator) {
switch (operator) {
case LESS:
return LESS;
case LESS_EQUALS:
return PrimitiveComparison.LESS_EQUAL;
case GREATER:
return GREATER;
case GREATER_EQUALS:
return GREATER_EQUAL;
default:
throw new Error("unreachable code");
}
}
}
/**
* All reference comparisons for which there are byte code equivalents.
*/
public enum ReferenceComparison {
EQUAL,
NOT_EQUAL;
}
}