com.google.template.soy.jbcsrc.LocalVariable.java Source code

Java tutorial

Introduction

Here is the source code for com.google.template.soy.jbcsrc.LocalVariable.java

Source

/*
 * Copyright 2015 Google Inc.
 *
 * 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.template.soy.jbcsrc;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Optional;

import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

/**
 * A local variable representation.
 * 
 * <p>This does nothing to enforce required constraints, e.g.:
 * <ul>
 *    <li>This does not ensure that {@link #start()} and {@link #end()} are valid and exist in the
 *        method.
 *    <li>This does not ensure that the {@link #index} is otherwise unused and that only one 
 *        variable is active at a time with the index.
 * </ul>
 * 
 * <p>Note: This class does not attempt to make use of the convenience methods on generator adapter
 * such as {@link CodeBuilder#newLocal(Type)} or {@link CodeBuilder#loadArg(int)} that
 * make it easier to work with local variables (and calculating local variable indexes).  Instead
 * we push this responsibility onto our caller.  This is because CodeBuilder doesn't make it
 * possible to generate local variable debugging tables in this case (e.g. there is no way to map
 * a method parameter index to a local variable index).
 */
final class LocalVariable extends Expression {
    // TODO(lukes): the fact that you need to specify the start and end labels during construction
    // ends up being awkward... Due to the fact that it is unclear who is responsible for actually
    // visiting the labels.  Maybe this object should be label agnostic and the labels should just be
    // parameters to tableEntry?

    static LocalVariable createThisVar(TypeInfo owner, Label start, Label end) {
        return new LocalVariable("this", owner.type(), 0, start, end, Feature.NON_NULLABLE);
    }

    static LocalVariable createLocal(String name, int index, Type type, Label start, Label end) {
        checkArgument(!name.equals("this"));
        return new LocalVariable(name, type, index, start, end);
    }

    private final String variableName;
    private final int index;
    private final Label start;
    private final Label end;

    private LocalVariable(String variableName, Type type, int index, Label start, Label end, Feature... features) {
        super(type, Feature.CHEAP /* locals are always cheap */, features);
        this.variableName = checkNotNull(variableName);
        this.index = index;
        this.start = checkNotNull(start);
        this.end = checkNotNull(end);
    }

    /** The name of the variable, ends up in debugging tables. */
    String variableName() {
        return variableName;
    }

    int index() {
        return index;
    }

    /** A label defining the earliest point at which this variable is defined. */
    Label start() {
        return start;
    }

    /** A label defining the latest point at which this variable is defined. */
    Label end() {
        return end;
    }

    @Override
    LocalVariable asCheap() {
        return this;
    }

    @Override
    LocalVariable asNonNullable() {
        if (isNonNullable()) {
            return this;
        }
        return new LocalVariable(variableName, resultType(), index, start, end, Feature.NON_NULLABLE);
    }

    /**
     * Write a local variable table entry for this variable.  This informs debuggers about variable
     * names, types and lifetime.
     */
    void tableEntry(CodeBuilder mv) {
        mv.visitLocalVariable(variableName(), resultType().getDescriptor(), null /** no generic signature */
                , start(), end(), index());
    }

    @Override
    void doGen(CodeBuilder mv) {
        mv.visitVarInsn(resultType().getOpcode(Opcodes.ILOAD), index());
    }

    /**
     * Return a {@link Statement} that stores the value of the given expression into this variable.
     */
    Statement store(final Expression expr) {
        return store(expr, Optional.<Label>absent());
    }

    /**
     * Return a {@link Statement} that stores the value of the given expression into this variable.
     * 
     * @param expr The expression to store
     * @param firstVarInstruction A label to use to mark the store instruction
     */
    Statement store(final Expression expr, Label firstVarInstruction) {
        return store(expr, Optional.<Label>of(firstVarInstruction));
    }

    /** Writes the value at the top of the stack to the local variable. */
    private Statement store(final Expression expr, final Optional<Label> firstVarInstruction) {
        expr.checkAssignableTo(resultType());
        return new Statement() {
            @Override
            void doGen(CodeBuilder adapter) {
                expr.gen(adapter);
                if (firstVarInstruction.isPresent()) {
                    adapter.mark(firstVarInstruction.get());
                }
                adapter.visitVarInsn(resultType().getOpcode(Opcodes.ISTORE), index());
            }
        };
    }
}