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 
display(int indent)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 
hasWithParams()67     public boolean hasWithParams() {
68         return elementCount() > 0;
69     }
70 
parseContents(Parser parser)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      */
typeCheck(SymbolTable stable)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 
translate(ClassGenerator classGen, MethodGenerator methodGen)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      */
getCalleeTemplate()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      */
buildParameterList()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 }
245