/* * 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.template.soy.jbcsrc.BytecodeUtils.ADVISING_APPENDABLE_TYPE; import static com.google.template.soy.jbcsrc.BytecodeUtils.ADVISING_BUILDER_TYPE; import com.google.template.soy.jbcsrc.api.AdvisingAppendable; import org.objectweb.asm.Label; import org.objectweb.asm.Type; /** An expression for an {@link AdvisingAppendable}. */ final class AppendableExpression extends Expression { private static final MethodRef APPEND = MethodRef.create(AdvisingAppendable.class, "append", CharSequence.class).asNonNullable(); private static final MethodRef APPEND_CHAR = MethodRef.create(AdvisingAppendable.class, "append", char.class).asNonNullable(); private static final MethodRef SOFT_LIMITED = MethodRef.create(AdvisingAppendable.class, "softLimitReached").asCheap(); static AppendableExpression forLocal(LocalVariable delegate) { return new AppendableExpression( delegate, false /* hasSideEffects*/, true /* supportsSoftLimiting */); } static AppendableExpression forStringBuilder(Expression delegate) { checkArgument(delegate.resultType().equals(ADVISING_BUILDER_TYPE)); return new AppendableExpression( ADVISING_BUILDER_TYPE, delegate, false /* hasSideEffects*/, false /* supportsSoftLimiting */); } static AppendableExpression logger() { return new AppendableExpression( MethodRef.RUNTIME_LOGGER.invoke(), false /* hasSideEffects*/, false /* supportsSoftLimiting */); } private final Expression delegate; // Whether or not the expression contains operations with side effects (e.g. appends) private final boolean hasSideEffects; // Whether or not the appendable could ever return true from softLimitReached private final boolean supportsSoftLimiting; private AppendableExpression( Expression delegate, boolean hasSideEffects, boolean supportsSoftLimiting) { this(ADVISING_APPENDABLE_TYPE, delegate, hasSideEffects, supportsSoftLimiting); } private AppendableExpression( Type resultType, Expression delegate, boolean hasSideEffects, boolean supportsSoftLimiting) { super(resultType, delegate.features()); delegate.checkAssignableTo(ADVISING_APPENDABLE_TYPE); checkArgument( delegate.isNonNullable(), "advising appendable expressions should always be non null"); this.delegate = delegate; this.hasSideEffects = hasSideEffects; this.supportsSoftLimiting = supportsSoftLimiting; } @Override void doGen(CodeBuilder adapter) { delegate.gen(adapter); } /** * Returns a similar {@link AppendableExpression} but with the given (string valued) expression * appended to it. */ AppendableExpression appendString(Expression exp) { return withNewDelegate(delegate.invoke(APPEND, exp), true); } /** * Returns a similar {@link AppendableExpression} but with the given (char valued) expression * appended to it. */ AppendableExpression appendChar(Expression exp) { return withNewDelegate(delegate.invoke(APPEND_CHAR, exp), true); } /** Returns an expression with the result of {@link AppendableExpression#softLimitReached}. */ Expression softLimitReached() { checkArgument(supportsSoftLimiting); return delegate.invoke(SOFT_LIMITED); } @Override AppendableExpression labelStart(Label label) { return withNewDelegate(delegate.labelStart(label), this.hasSideEffects); } @Override Statement toStatement() { // .toStatement() by default just generates the expression and adds a 'POP' instruction // to clear the stack. However, this is only neccesary when the expression in question has a // side effect worth preserving. If we know that it does not we can just return the empty // statement if (hasSideEffects) { return super.toStatement(); } return Statement.NULL_STATEMENT; } private AppendableExpression withNewDelegate(Expression newDelegate, boolean hasSideEffects) { return new AppendableExpression(newDelegate, hasSideEffects, supportsSoftLimiting); } /** * Returns {@code true} if this expression requires detach logic to be generated based on runtime * calls to {@link AdvisingAppendable#softLimitReached()}. */ boolean supportsSoftLimiting() { return supportsSoftLimiting; } }