Java tutorial
/* * 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()); } }