1 /*
   2  * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  22 
  23 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  24 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  25 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  26 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  27 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  28 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  32 import com.sun.org.apache.xml.internal.utils.XML11Char;
  33 import java.util.List;
  34 
  35 /**
  36  * @author Jacek Ambroziak
  37  * @author Santiago Pericas-Geertsen
  38  * @author Erwin Bolwidt <ejb@klomp.org>
  39  * @LastModified: Oct 2017
  40  */
  41 final class CallTemplate extends Instruction {
  42 
  43     /**
  44      * Name of template to call.
  45      */
  46     private QName _name;
  47 
  48     /**
  49      * The array of effective parameters in this CallTemplate. An object in
  50      * this array can be either a WithParam or a Param if no WithParam
  51      * exists for a particular parameter.
  52      */
  53     private SyntaxTreeNode[] _parameters = null;
  54 
  55     /**
  56      * The corresponding template which this CallTemplate calls.
  57      */
  58     private Template _calleeTemplate = null;
  59 
  60     public void display(int indent) {
  61         indent(indent);
  62         System.out.print("CallTemplate");
  63         Util.println(" name " + _name);
  64         displayContents(indent + IndentIncrement);
  65     }
  66 
  67     public boolean hasWithParams() {
  68         return elementCount() > 0;
  69     }
  70 
  71     public void parseContents(Parser parser) {
  72         final String name = getAttribute("name");
  73         if (name.length() > 0) {
  74             if (!XML11Char.isXML11ValidQName(name)) {
  75                 ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this);
  76                 parser.reportError(Constants.ERROR, err);
  77             }
  78             _name = parser.getQNameIgnoreDefaultNs(name);
  79         }
  80         else {
  81             reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name");
  82         }
  83         parseChildren(parser);
  84     }
  85 
  86     /**
  87      * Verify that a template with this name exists.
  88      */
  89     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  90         final Template template = stable.lookupTemplate(_name);
  91         if (template != null) {
  92             typeCheckContents(stable);
  93         }
  94         else {
  95             ErrorMsg err = new ErrorMsg(ErrorMsg.TEMPLATE_UNDEF_ERR,_name,this);
  96             throw new TypeCheckError(err);
  97         }
  98         return Type.Void;
  99     }
 100 
 101     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
 102         final Stylesheet stylesheet = classGen.getStylesheet();
 103         final ConstantPoolGen cpg = classGen.getConstantPool();
 104         final InstructionList il = methodGen.getInstructionList();
 105 
 106         // If there are Params in the stylesheet or WithParams in this call?
 107         if (stylesheet.hasLocalParams() || hasContents()) {
 108             _calleeTemplate = getCalleeTemplate();
 109 
 110             // Build the parameter list if the called template is simple named
 111             if (_calleeTemplate != null) {
 112                 buildParameterList();
 113             }
 114             // This is only needed when the called template is not
 115             // a simple named template.
 116             else {
 117                 // Push parameter frame
 118                 final int push = cpg.addMethodref(TRANSLET_CLASS,
 119                                                   PUSH_PARAM_FRAME,
 120                                                   PUSH_PARAM_FRAME_SIG);
 121                 il.append(classGen.loadTranslet());
 122                 il.append(new INVOKEVIRTUAL(push));
 123                 translateContents(classGen, methodGen);
 124             }
 125         }
 126 
 127         // Generate a valid Java method name
 128         final String className = stylesheet.getClassName();
 129         String methodName = Util.escape(_name.toString());
 130 
 131         // Load standard arguments
 132         il.append(classGen.loadTranslet());
 133         il.append(methodGen.loadDOM());
 134         il.append(methodGen.loadIterator());
 135         il.append(methodGen.loadHandler());
 136         il.append(methodGen.loadCurrentNode());
 137 
 138         // Initialize prefix of method signature
 139         StringBuffer methodSig = new StringBuffer("(" + DOM_INTF_SIG
 140             + NODE_ITERATOR_SIG + TRANSLET_OUTPUT_SIG + NODE_SIG);
 141 
 142         // If calling a simply named template, push actual arguments
 143         if (_calleeTemplate != null) {
 144             int numParams = _parameters.length;
 145 
 146             for (int i = 0; i < numParams; i++) {
 147                 SyntaxTreeNode node = _parameters[i];
 148                 methodSig.append(OBJECT_SIG);   // append Object to signature
 149 
 150                 // Push 'null' if Param to indicate no actual parameter specified
 151                 if (node instanceof Param) {
 152                     il.append(ACONST_NULL);
 153                 }
 154                 else {  // translate WithParam
 155                     node.translate(classGen, methodGen);
 156                 }
 157             }
 158         }
 159 
 160         // Complete signature and generate invokevirtual call
 161         methodSig.append(")V");
 162         il.append(new INVOKEVIRTUAL(cpg.addMethodref(className,
 163                                                      methodName,
 164                                                      methodSig.toString())));
 165 
 166         // release temporary result trees
 167         if (_parameters != null) {
 168             for (int i = 0; i < _parameters.length; i++) {
 169                 if (_parameters[i] instanceof WithParam) {
 170                     ((WithParam)_parameters[i]).releaseResultTree(classGen, methodGen);
 171                 }
 172             }
 173         }
 174 
 175         // Do not need to call Translet.popParamFrame() if we are
 176         // calling a simple named template.
 177         if (_calleeTemplate == null && (stylesheet.hasLocalParams() || hasContents())) {
 178             // Pop parameter frame
 179             final int pop = cpg.addMethodref(TRANSLET_CLASS,
 180                                              POP_PARAM_FRAME,
 181                                              POP_PARAM_FRAME_SIG);
 182             il.append(classGen.loadTranslet());
 183             il.append(new INVOKEVIRTUAL(pop));
 184         }
 185     }
 186 
 187     /**
 188      * Return the simple named template which this CallTemplate calls.
 189      * Return false if there is no matched template or the matched
 190      * template is not a simple named template.
 191      */
 192     public Template getCalleeTemplate() {
 193         Template foundTemplate
 194             = getXSLTC().getParser().getSymbolTable().lookupTemplate(_name);
 195 
 196         return foundTemplate.isSimpleNamedTemplate() ? foundTemplate : null;
 197     }
 198 
 199     /**
 200      * Build the list of effective parameters in this CallTemplate.
 201      * The parameters of the called template are put into the array first.
 202      * Then we visit the WithParam children of this CallTemplate and replace
 203      * the Param with a corresponding WithParam having the same name.
 204      */
 205     private void buildParameterList() {
 206         // Put the parameters from the called template into the array first.
 207         // This is to ensure the order of the parameters.
 208         List<Param> defaultParams = _calleeTemplate.getParameters();
 209         int numParams = defaultParams.size();
 210         _parameters = new SyntaxTreeNode[numParams];
 211         for (int i = 0; i < numParams; i++) {
 212             _parameters[i] = defaultParams.get(i);
 213         }
 214 
 215         // Replace a Param with a WithParam if they have the same name.
 216         int count = elementCount();
 217         for (int i = 0; i < count; i++) {
 218             Object node = elementAt(i);
 219 
 220             // Ignore if not WithParam
 221             if (node instanceof WithParam) {
 222                 WithParam withParam = (WithParam)node;
 223                 QName name = withParam.getName();
 224 
 225                 // Search for a Param with the same name
 226                 for (int k = 0; k < numParams; k++) {
 227                     SyntaxTreeNode parm = _parameters[k];
 228                     if (parm instanceof Param
 229                         && ((Param)parm).getName().equals(name)) {
 230                         withParam.setDoParameterOptimization(true);
 231                         _parameters[k] = withParam;
 232                         break;
 233                     }
 234                     else if (parm instanceof WithParam
 235                         && ((WithParam)parm).getName().equals(name)) {
 236                         withParam.setDoParameterOptimization(true);
 237                         _parameters[k] = withParam;
 238                         break;
 239                     }
 240                 }
 241             }
 242         }
 243      }
 244 }