/* * 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.template.soy.jbcsrc.SyntheticVarName.foreachLoopIndex; import static com.google.template.soy.jbcsrc.SyntheticVarName.foreachLoopLength; import com.google.template.soy.exprtree.AbstractReturningExprNodeVisitor; import com.google.template.soy.exprtree.ExprNode; import com.google.template.soy.exprtree.FunctionNode; import com.google.template.soy.exprtree.MapLiteralNode; import com.google.template.soy.exprtree.VarDefn; import com.google.template.soy.exprtree.VarRefNode; import com.google.template.soy.shared.internal.BuiltinFunction; import com.google.template.soy.shared.restricted.SoyFunction; import com.google.template.soy.soytree.ForeachNonemptyNode; import com.google.template.soy.soytree.SoyNode.LocalVarNode; import com.google.template.soy.soytree.defn.InjectedParam; import com.google.template.soy.soytree.defn.LocalVar; import com.google.template.soy.soytree.defn.TemplateParam; /** * An abstract base class that adds extra visitor methods for unpacking varrefs and functions based * on their subtypes. */ abstract class EnhancedAbstractExprNodeVisitor<T> extends AbstractReturningExprNodeVisitor<T> { @Override protected final T visit(ExprNode node) { try { return super.visit(node); } catch (UnexpectedCompilerFailureException e) { e.addLocation(node); throw e; } catch (Throwable t) { throw new UnexpectedCompilerFailureException(node, t); } } @Override protected final T visitVarRefNode(VarRefNode node) { VarDefn defn = node.getDefnDecl(); switch (defn.kind()) { case LOCAL_VAR: LocalVar local = (LocalVar) defn; LocalVarNode declaringNode = local.declaringNode(); switch (declaringNode.getKind()) { case FOR_NODE: return visitForLoopIndex(node, local); case FOREACH_NONEMPTY_NODE: return visitForeachLoopVar(node, local); case LET_CONTENT_NODE: case LET_VALUE_NODE: return visitLetNodeVar(node, local); default: throw new AssertionError("Unexpected local variable: " + local); } case PARAM: return visitParam(node, (TemplateParam) defn); case IJ_PARAM: return visitIjParam(node, (InjectedParam) defn); case UNDECLARED: throw new RuntimeException("undeclared params are not supported by jbcsrc"); default: throw new AssertionError(); } } @Override protected final T visitFunctionNode(FunctionNode node) { SoyFunction function = node.getSoyFunction(); if (function instanceof BuiltinFunction) { BuiltinFunction nonpluginFn = (BuiltinFunction) function; if (nonpluginFn == BuiltinFunction.QUOTE_KEYS_IF_JS) { // this function is a no-op in non JS backends, the CheckFunctionCallsVisitor ensures that // there is only one child and it is a MapLiteralNode return visitMapLiteralNode((MapLiteralNode) node.getChild(0)); } if (nonpluginFn == BuiltinFunction.CHECK_NOT_NULL) { return visitCheckNotNullFunction(node); } // the rest of the builtins all deal with indexing operations on foreach variables. VarRefNode varRef = (VarRefNode) node.getChild(0); ForeachNonemptyNode declaringNode = (ForeachNonemptyNode) ((LocalVar) varRef.getDefnDecl()).declaringNode(); switch (nonpluginFn) { case IS_FIRST: return visitIsFirstFunction(node, foreachLoopIndex(declaringNode)); case IS_LAST: return visitIsLastFunction( node, foreachLoopIndex(declaringNode), foreachLoopLength(declaringNode)); case INDEX: return visitIndexFunction(node, foreachLoopIndex(declaringNode)); case QUOTE_KEYS_IF_JS: case CHECK_NOT_NULL: // should have been handled above, before the switch statement throw new AssertionError(); default: throw new AssertionError(); } } return visitPluginFunction(node); } T visitForLoopIndex(VarRefNode varRef, LocalVar local) { return visitExprNode(varRef); } T visitForeachLoopVar(VarRefNode varRef, LocalVar local) { return visitExprNode(varRef); } T visitLetNodeVar(VarRefNode node, LocalVar local) { return visitExprNode(node); } T visitParam(VarRefNode varRef, TemplateParam param) { return visitExprNode(varRef); } T visitIjParam(VarRefNode varRef, InjectedParam ij) { return visitExprNode(varRef); } T visitIsFirstFunction(FunctionNode node, SyntheticVarName indexVar) { return visitExprNode(node); } T visitIsLastFunction(FunctionNode node, SyntheticVarName indexVar, SyntheticVarName lengthVar) { return visitExprNode(node); } T visitIndexFunction(FunctionNode node, SyntheticVarName indexVar) { return visitExprNode(node); } T visitCheckNotNullFunction(FunctionNode node) { return visitExprNode(node); } T visitPluginFunction(FunctionNode node) { return visitExprNode(node); } }