1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Oct 2017 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xalan.internal.xsltc.compiler; 23 24 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; 25 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; 26 import com.sun.org.apache.bcel.internal.generic.InstructionList; 27 import com.sun.org.apache.bcel.internal.generic.PUSH; 28 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; 29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; 30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType; 31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; 32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; 33 import java.util.List; 34 35 /** 36 * @author Morten Jorgensen 37 * @author Santiago Pericas-Geertsen 38 */ 39 final class KeyCall extends FunctionCall { 40 41 /** 42 * The name of the key. 43 */ 44 private Expression _name; 45 46 /** 47 * The value to look up in the key/index. 48 */ 49 private Expression _value; 50 51 /** 52 * The value's data type. 53 */ 54 private Type _valueType; // The value's data type 55 56 /** 57 * Expanded qname when name is literal. 58 */ 59 private QName _resolvedQName = null; 60 61 /** 62 * Get the parameters passed to function: 63 * key(String name, String value) 64 * key(String name, NodeSet value) 65 * The 'arguments' vector should contain two parameters for key() calls, 66 * one holding the key name and one holding the value(s) to look up. The 67 * vector has only one parameter for id() calls (the key name is always 68 * "##id" for id() calls). 69 * 70 * @param fname The function name (should be 'key' or 'id') 71 * @param arguments A vector containing the arguments the the function 72 */ 73 public KeyCall(QName fname, List<Expression> arguments) { 74 super(fname, arguments); 75 switch(argumentCount()) { 76 case 1: 77 _name = null; 78 _value = argument(0); 79 break; 80 case 2: 81 _name = argument(0); 82 _value = argument(1); 83 break; 84 default: 85 _name = _value = null; 86 break; 87 } 88 } 89 90 /** 91 * If this call to key() is in a top-level element like another variable 92 * or param, add a dependency between that top-level element and the 93 * referenced key. For example, 94 * 95 * <xsl:key name="x" .../> 96 * <xsl:variable name="y" select="key('x', 1)"/> 97 * 98 * and assuming this class represents "key('x', 1)", add a reference 99 * between variable y and key x. Note that if 'x' is unknown statically 100 * in key('x', 1), there's nothing we can do at this point. 101 */ 102 public void addParentDependency() { 103 // If name unknown statically, there's nothing we can do 104 if (_resolvedQName == null) return; 105 106 SyntaxTreeNode node = this; 107 while (node != null && node instanceof TopLevelElement == false) { 108 node = node.getParent(); 109 } 110 111 TopLevelElement parent = (TopLevelElement) node; 112 if (parent != null) { 113 parent.addDependency(getSymbolTable().getKey(_resolvedQName)); 114 } 115 } 116 117 /** 118 * Type check the parameters for the id() or key() function. 119 * The index name (for key() call only) must be a string or convertable 120 * to a string, and the lookup-value must be a string or a node-set. 121 * @param stable The parser's symbol table 122 * @throws TypeCheckError When the parameters have illegal type 123 */ 124 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 125 final Type returnType = super.typeCheck(stable); 126 127 // Run type check on the key name (first argument) - must be a string, 128 // and if it is not it must be converted to one using string() rules. 129 if (_name != null) { 130 final Type nameType = _name.typeCheck(stable); 131 132 if (_name instanceof LiteralExpr) { 133 final LiteralExpr literal = (LiteralExpr) _name; 134 _resolvedQName = 135 getParser().getQNameIgnoreDefaultNs(literal.getValue()); 136 } 137 else if (nameType instanceof StringType == false) { 138 _name = new CastExpr(_name, Type.String); 139 } 140 } 141 142 // Run type check on the value for this key. This value can be of 143 // any data type, so this should never cause any type-check errors. 144 // If the value is a reference, then we have to defer the decision 145 // of how to process it until run-time. 146 // If the value is known not to be a node-set, then it should be 147 // converted to a string before the lookup is done. If the value is 148 // known to be a node-set then this process (convert to string, then 149 // do lookup) should be applied to every node in the set, and the 150 // result from all lookups should be added to the resulting node-set. 151 _valueType = _value.typeCheck(stable); 152 153 if (_valueType != Type.NodeSet 154 && _valueType != Type.Reference 155 && _valueType != Type.String) { 156 _value = new CastExpr(_value, Type.String); 157 _valueType = _value.typeCheck(stable); 158 } 159 160 // If in a top-level element, create dependency to the referenced key 161 addParentDependency(); 162 163 return returnType; 164 } 165 166 /** 167 * This method is called when the constructor is compiled in 168 * Stylesheet.compileConstructor() and not as the syntax tree is traversed. 169 * <p>This method will generate byte code that produces an iterator 170 * for the nodes in the node set for the key or id function call. 171 * @param classGen The Java class generator 172 * @param methodGen The method generator 173 */ 174 public void translate(ClassGenerator classGen, 175 MethodGenerator methodGen) { 176 final ConstantPoolGen cpg = classGen.getConstantPool(); 177 final InstructionList il = methodGen.getInstructionList(); 178 179 // Returns the KeyIndex object of a given name 180 final int getKeyIndex = cpg.addMethodref(TRANSLET_CLASS, 181 "getKeyIndex", 182 "(Ljava/lang/String;)"+ 183 KEY_INDEX_SIG); 184 185 // KeyIndex.setDom(Dom, node) => void 186 final int keyDom = cpg.addMethodref(KEY_INDEX_CLASS, 187 "setDom", 188 "(" + DOM_INTF_SIG + "I)V"); 189 190 // Initialises a KeyIndex to return nodes with specific values 191 final int getKeyIterator = 192 cpg.addMethodref(KEY_INDEX_CLASS, 193 "getKeyIndexIterator", 194 "(" + _valueType.toSignature() + "Z)" 195 + KEY_INDEX_ITERATOR_SIG); 196 197 // Initialise the index specified in the first parameter of key() 198 il.append(classGen.loadTranslet()); 199 if (_name == null) { 200 il.append(new PUSH(cpg,"##id")); 201 } else if (_resolvedQName != null) { 202 il.append(new PUSH(cpg, _resolvedQName.toString())); 203 } else { 204 _name.translate(classGen, methodGen); 205 } 206 207 // Generate following byte code: 208 // 209 // KeyIndex ki = translet.getKeyIndex(_name) 210 // ki.setDom(translet.dom); 211 // ki.getKeyIndexIterator(_value, true) - for key() 212 // OR 213 // ki.getKeyIndexIterator(_value, false) - for id() 214 il.append(new INVOKEVIRTUAL(getKeyIndex)); 215 il.append(DUP); 216 il.append(methodGen.loadDOM()); 217 il.append(methodGen.loadCurrentNode()); 218 il.append(new INVOKEVIRTUAL(keyDom)); 219 220 _value.translate(classGen, methodGen); 221 il.append((_name != null) ? ICONST_1: ICONST_0); 222 il.append(new INVOKEVIRTUAL(getKeyIterator)); 223 } 224 }