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

Java tutorial

Introduction

Here is the source code for com.google.template.soy.jbcsrc.FieldRef.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.checkState;

import com.google.auto.value.AutoValue;
import com.google.template.soy.data.SoyValueHelper;
import com.google.template.soy.data.restricted.BooleanData;
import com.google.template.soy.jbcsrc.Expression.Feature;
import com.google.template.soy.jbcsrc.Expression.Features;
import com.google.template.soy.jbcsrc.runtime.Runtime;

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

import java.lang.reflect.Modifier;

/**
 * Representation of a field in a java class.
 */
@AutoValue
abstract class FieldRef {
    static final FieldRef BOOLEAN_DATA_FALSE = staticFieldReference(BooleanData.class, "FALSE");
    static final FieldRef BOOLEAN_DATA_TRUE = staticFieldReference(BooleanData.class, "TRUE");
    static final FieldRef NULL_PROVIDER = staticFieldReference(Runtime.class, "NULL_PROVIDER");
    static final FieldRef EMPTY_DICT = staticFieldReference(SoyValueHelper.class, "EMPTY_DICT");

    static FieldRef createFinalField(TypeInfo owner, String name, Class<?> type) {
        return createFinalField(owner, name, Type.getType(type));
    }

    static FieldRef createFinalField(TypeInfo owner, String name, Type type) {
        return new AutoValue_FieldRef(owner, name, type, Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL,
                !BytecodeUtils.isPrimitive(type));
    }

    static FieldRef instanceFieldReference(Class<?> owner, String name) {
        Class<?> fieldType;
        int modifiers = 0;
        try {
            java.lang.reflect.Field declaredField = owner.getDeclaredField(name);
            modifiers = declaredField.getModifiers();
            if (Modifier.isStatic(modifiers)) {
                throw new IllegalStateException("Field: " + declaredField + " is static");
            }
            fieldType = declaredField.getType();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return new AutoValue_FieldRef(TypeInfo.create(owner), name, Type.getType(fieldType), modifiers,
                !fieldType.isPrimitive());
    }

    static FieldRef staticFieldReference(Class<?> owner, String name) {
        java.lang.reflect.Field declaredField;
        try {
            declaredField = owner.getDeclaredField(name);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return staticFieldReference(declaredField);
    }

    static FieldRef staticFieldReference(java.lang.reflect.Field field) {
        if (!Modifier.isStatic(field.getModifiers())) {
            throw new IllegalStateException("Field: " + field + " is not static");
        }
        return new AutoValue_FieldRef(TypeInfo.create(field.getDeclaringClass()), field.getName(),
                Type.getType(field.getType()), Opcodes.ACC_STATIC,
                false /** Assume all static field refs are non-null. */
        );
    }

    static <T extends Enum<T>> FieldRef enumReference(T enumInstance) {
        return staticFieldReference(enumInstance.getDeclaringClass(), enumInstance.name());
    }

    static FieldRef createField(TypeInfo owner, String name, Class<?> type) {
        return createField(owner, name, Type.getType(type));
    }

    static FieldRef createField(TypeInfo owner, String name, Type type) {
        return new AutoValue_FieldRef(owner, name, type, Opcodes.ACC_PRIVATE, !BytecodeUtils.isPrimitive(type));
    }

    /** The type that owns this field. */
    abstract TypeInfo owner();

    abstract String name();

    abstract Type type();

    /** 
     * The field access flags.  This is a bit set of things like {@link Opcodes#ACC_STATIC}
     * and {@link Opcodes#ACC_PRIVATE}.
     */
    abstract int accessFlags();

    abstract boolean isNullable();

    final boolean isStatic() {
        return (accessFlags() & Opcodes.ACC_STATIC) != 0;
    }

    /** Defines the given field as member of the class. */
    void defineField(ClassVisitor cv) {
        cv.visitField(accessFlags(), name(), type().getDescriptor(), null /* no generic signature */,
                null /* no initializer */);
    }

    FieldRef asNonNull() {
        if (!isNullable() || BytecodeUtils.isPrimitive(type())) {
            return this;
        }
        return new AutoValue_FieldRef(owner(), name(), type(), accessFlags(), false);
    }

    /**
     * Returns an accessor that accesses this field on the given owner.
     */
    Expression accessor(final Expression owner) {
        checkState(!isStatic());
        checkArgument(owner.resultType().equals(this.owner().type()));
        Features features = Features.of();
        if (owner.isCheap()) {
            features = features.plus(Feature.CHEAP);
        }
        if (!isNullable()) {
            features = features.plus(Feature.NON_NULLABLE);
        }
        return new Expression(type(), features) {
            @Override
            void doGen(CodeBuilder mv) {
                owner.gen(mv);
                mv.getField(owner().type(), FieldRef.this.name(), resultType());
            }
        };
    }

    /**
     * Returns an expression that accesses this static field.
     */
    Expression accessor() {
        checkState(isStatic());
        Features features = Features.of(Feature.CHEAP);
        if (!isNullable()) {
            features = features.plus(Feature.NON_NULLABLE);
        }
        return new Expression(type(), features) {
            @Override
            void doGen(CodeBuilder mv) {
                mv.getStatic(owner().type(), FieldRef.this.name(), resultType());
            }
        };
    }

    /**
     * Returns a {@link Statement} that stores the {@code value} in this field on the given 
     * {@code instance}.
     * 
     * @throws IllegalStateException if this is a static field
     */
    Statement putInstanceField(final Expression instance, final Expression value) {
        checkState(!isStatic(), "This field is static!");
        instance.checkAssignableTo(owner().type());
        value.checkAssignableTo(type());
        return new Statement() {
            @Override
            void doGen(CodeBuilder adapter) {
                instance.gen(adapter);
                value.gen(adapter);
                putUnchecked(adapter);
            }

        };
    }

    /**
     * Returns a {@link Statement} that stores the {@code value} in this field on the given
     * {@code instance}.
     *
     * @throws IllegalStateException if this is a static field
     */
    Statement putStaticField(final Expression value) {
        checkState(isStatic(), "This field is not static!");
        value.checkAssignableTo(type());
        return new Statement() {
            @Override
            void doGen(CodeBuilder adapter) {
                value.gen(adapter);
                adapter.putStatic(owner().type(), name(), type());
            }
        };
    }

    /**
     * Adds code to place the top item of the stack into this field.
     * 
     * @throws IllegalStateException if this is a static field
     */
    void putUnchecked(CodeBuilder adapter) {
        checkState(!isStatic(), "This field is static!");
        adapter.putField(owner().type(), name(), type());
    }
}