/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ /* * $Id: Mode.java,v 1.2.4.1 2005/09/19 05:18:11 pvedula Exp $ */ package com.sun.org.apache.xalan.internal.xsltc.compiler; import com.sun.org.apache.bcel.internal.generic.BranchHandle; import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; import com.sun.org.apache.bcel.internal.generic.DUP; import com.sun.org.apache.bcel.internal.generic.GOTO_W; import com.sun.org.apache.bcel.internal.generic.IFLT; import com.sun.org.apache.bcel.internal.generic.ILOAD; import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE; import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; import com.sun.org.apache.bcel.internal.generic.ISTORE; import com.sun.org.apache.bcel.internal.generic.Instruction; import com.sun.org.apache.bcel.internal.generic.InstructionHandle; import com.sun.org.apache.bcel.internal.generic.InstructionList; import com.sun.org.apache.bcel.internal.generic.LocalVariableGen; import com.sun.org.apache.bcel.internal.generic.SWITCH; import com.sun.org.apache.bcel.internal.generic.TargetLostException; import com.sun.org.apache.bcel.internal.util.InstructionFinder; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NamedMethodGenerator; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util; import com.sun.org.apache.xml.internal.dtm.Axis; import com.sun.org.apache.xml.internal.dtm.DTM; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.Vector; /** * Mode gathers all the templates belonging to a given mode; * it is responsible for generating an appropriate * applyTemplates + (mode name) method in the translet. * @author Jacek Ambroziak * @author Santiago Pericas-Geertsen * @author Morten Jorgensen * @author Erwin Bolwidt * @author G. Todd Miller */ final class Mode implements Constants { /** * The name of this mode as defined in the stylesheet. */ private final QName _name; /** * A reference to the stylesheet object that owns this mode. */ private final Stylesheet _stylesheet; /** * The name of the method in which this mode is compiled. */ private final String _methodName; /** * A vector of all the templates in this mode. */ private Vector _templates; /** * Group for patterns with node()-type kernel and child axis. */ private Vector _childNodeGroup = null; /** * Test sequence for patterns with node()-type kernel and child axis. */ private TestSeq _childNodeTestSeq = null; /** * Group for patterns with node()-type kernel and attribute axis. */ private Vector _attribNodeGroup = null; /** * Test sequence for patterns with node()-type kernel and attribute axis. */ private TestSeq _attribNodeTestSeq = null; /** * Group for patterns with id() or key()-type kernel. */ private Vector _idxGroup = null; /** * Test sequence for patterns with id() or key()-type kernel. */ private TestSeq _idxTestSeq = null; /** * Group for patterns with any other kernel type. */ private Vector[] _patternGroups; /** * Test sequence for patterns with any other kernel type. */ private TestSeq[] _testSeq; /** * A mapping between templates and test sequences. */ private Map _neededTemplates = new HashMap<>(); /** * A mapping between named templates and Mode objects. */ private Map _namedTemplates = new HashMap<>(); /** * A mapping between templates and instruction handles. */ private Map _templateIHs = new HashMap<>(); /** * A mapping between templates and instruction lists. */ private Map _templateILs = new HashMap<>(); /** * A reference to the pattern matching the root node. */ private LocationPathPattern _rootPattern = null; /** * Stores ranges of template precendences for the compilation * of apply-imports. */ private Map _importLevels = null; /** * A mapping between key names and keys. */ private Map _keys = null; /** * Variable index for the current node used in code generation. */ private int _currentIndex; /** * Creates a new Mode. * * @param name A textual representation of the mode's QName * @param stylesheet The Stylesheet in which the mode occured * @param suffix A suffix to append to the method name for this mode * (normally a sequence number - still in a String). */ public Mode(QName name, Stylesheet stylesheet, String suffix) { _name = name; _stylesheet = stylesheet; _methodName = APPLY_TEMPLATES + suffix; _templates = new Vector(); _patternGroups = new Vector[32]; } /** * Returns the name of the method (_not_ function) that will be * compiled for this mode. Normally takes the form 'applyTemplates()' * or * 'applyTemplates2()'. * * @return Method name for this mode */ public String functionName() { return _methodName; } public String functionName(int min, int max) { if (_importLevels == null) { _importLevels = new HashMap<>(); } _importLevels.put(max, min); return _methodName + '_' + max; } /** * Shortcut to get the class compiled for this mode (will be inlined). */ private String getClassName() { return _stylesheet.getClassName(); } public Stylesheet getStylesheet() { return _stylesheet; } public void addTemplate(Template template) { _templates.addElement(template); } private Vector quicksort(Vector templates, int p, int r) { if (p < r) { final int q = partition(templates, p, r); quicksort(templates, p, q); quicksort(templates, q + 1, r); } return templates; } private int partition(Vector templates, int p, int r) { final Template x = (Template)templates.elementAt(p); int i = p - 1; int j = r + 1; while (true) { while (x.compareTo((Template)templates.elementAt(--j)) > 0); while (x.compareTo((Template)templates.elementAt(++i)) < 0); if (i < j) { templates.set(j, templates.set(i, templates.elementAt(j))); } else { return j; } } } /** * Process all the test patterns in this mode */ public void processPatterns(Map keys) { _keys = keys; /* System.out.println("Before Sort " + _name); for (int i = 0; i < _templates.size(); i++) { System.out.println("name = " + ((Template)_templates.elementAt(i)).getName()); System.out.println("pattern = " + ((Template)_templates.elementAt(i)).getPattern()); System.out.println("priority = " + ((Template)_templates.elementAt(i)).getPriority()); System.out.println("position = " + ((Template)_templates.elementAt(i)).getPosition()); } */ _templates = quicksort(_templates, 0, _templates.size() - 1); /* System.out.println("\n After Sort " + _name); for (int i = 0; i < _templates.size(); i++) { System.out.println("name = " + ((Template)_templates.elementAt(i)).getName()); System.out.println("pattern = " + ((Template)_templates.elementAt(i)).getPattern()); System.out.println("priority = " + ((Template)_templates.elementAt(i)).getPriority()); System.out.println("position = " + ((Template)_templates.elementAt(i)).getPosition()); } */ // Traverse all templates final Enumeration templates = _templates.elements(); while (templates.hasMoreElements()) { // Get the next template final Template template = (Template)templates.nextElement(); /* * Add this template to a table of named templates if it has a name. * If there are multiple templates with the same name, all but one * (the one with highest priority) will be disabled. */ if (template.isNamed() && !template.disabled()) { _namedTemplates.put(template, this); } // Add this template to a test sequence if it has a pattern final Pattern pattern = template.getPattern(); if (pattern != null) { flattenAlternative(pattern, template, keys); } } prepareTestSequences(); } /** * This method will break up alternative patterns (ie. unions of patterns, * such as match="A/B | C/B") and add the basic patterns to their * respective pattern groups. */ private void flattenAlternative(Pattern pattern, Template template, Map keys) { // Patterns on type id() and key() are special since they do not have // any kernel node type (it can be anything as long as the node is in // the id's or key's index). if (pattern instanceof IdKeyPattern) { final IdKeyPattern idkey = (IdKeyPattern)pattern; idkey.setTemplate(template); if (_idxGroup == null) _idxGroup = new Vector(); _idxGroup.add(pattern); } // Alternative patterns are broken up and re-processed recursively else if (pattern instanceof AlternativePattern) { final AlternativePattern alt = (AlternativePattern)pattern; flattenAlternative(alt.getLeft(), template, keys); flattenAlternative(alt.getRight(), template, keys); } // Finally we have a pattern that can be added to a test sequence! else if (pattern instanceof LocationPathPattern) { final LocationPathPattern lpp = (LocationPathPattern)pattern; lpp.setTemplate(template); addPatternToGroup(lpp); } } /** * Group patterns by NodeTests of their last Step * Keep them sorted by priority within group */ private void addPatternToGroup(final LocationPathPattern lpp) { // id() and key()-type patterns do not have a kernel type if (lpp instanceof IdKeyPattern) { addPattern(-1, lpp); } // Otherwise get the kernel pattern from the LPP else { // kernel pattern is the last (maybe only) Step final StepPattern kernel = lpp.getKernelPattern(); if (kernel != null) { addPattern(kernel.getNodeType(), lpp); } else if (_rootPattern == null || lpp.noSmallerThan(_rootPattern)) { _rootPattern = lpp; } } } /** * Adds a pattern to a pattern group */ private void addPattern(int kernelType, LocationPathPattern pattern) { // Make sure the array of pattern groups is long enough final int oldLength = _patternGroups.length; if (kernelType >= oldLength) { Vector[] newGroups = new Vector[kernelType * 2]; System.arraycopy(_patternGroups, 0, newGroups, 0, oldLength); _patternGroups = newGroups; } // Find the vector to put this pattern into Vector patterns; if (kernelType == DOM.NO_TYPE) { if (pattern.getAxis() == Axis.ATTRIBUTE) { patterns = (_attribNodeGroup == null) ? (_attribNodeGroup = new Vector(2)) : _attribNodeGroup; } else { patterns = (_childNodeGroup == null) ? (_childNodeGroup = new Vector(2)) : _childNodeGroup; } } else { patterns = (_patternGroups[kernelType] == null) ? (_patternGroups[kernelType] = new Vector(2)) : _patternGroups[kernelType]; } if (patterns.size() == 0) { patterns.addElement(pattern); } else { boolean inserted = false; for (int i = 0; i < patterns.size(); i++) { final LocationPathPattern lppToCompare = (LocationPathPattern)patterns.elementAt(i); if (pattern.noSmallerThan(lppToCompare)) { inserted = true; patterns.insertElementAt(pattern, i); break; } } if (inserted == false) { patterns.addElement(pattern); } } } /** * Complete test sequences of a given type by adding all patterns * from a given group. */ private void completeTestSequences(int nodeType, Vector patterns) { if (patterns != null) { if (_patternGroups[nodeType] == null) { _patternGroups[nodeType] = patterns; } else { final int m = patterns.size(); for (int j = 0; j < m; j++) { addPattern(nodeType, (LocationPathPattern) patterns.elementAt(j)); } } } } /** * Build test sequences. The first step is to complete the test sequences * by including patterns of "*" and "node()" kernel to all element test * sequences, and of "@*" to all attribute test sequences. */ private void prepareTestSequences() { final Vector starGroup = _patternGroups[DTM.ELEMENT_NODE]; final Vector atStarGroup = _patternGroups[DTM.ATTRIBUTE_NODE]; // Complete test sequence for "text()" with "child::node()" completeTestSequences(DTM.TEXT_NODE, _childNodeGroup); // Complete test sequence for "*" with "child::node()" completeTestSequences(DTM.ELEMENT_NODE, _childNodeGroup); // Complete test sequence for "pi()" with "child::node()" completeTestSequences(DTM.PROCESSING_INSTRUCTION_NODE, _childNodeGroup); // Complete test sequence for "comment()" with "child::node()" completeTestSequences(DTM.COMMENT_NODE, _childNodeGroup); // Complete test sequence for "@*" with "attribute::node()" completeTestSequences(DTM.ATTRIBUTE_NODE, _attribNodeGroup); final Vector names = _stylesheet.getXSLTC().getNamesIndex(); if (starGroup != null || atStarGroup != null || _childNodeGroup != null || _attribNodeGroup != null) { final int n = _patternGroups.length; // Complete test sequence for user-defined types for (int i = DTM.NTYPES; i < n; i++) { if (_patternGroups[i] == null) continue; final String name = (String) names.elementAt(i - DTM.NTYPES); if (isAttributeName(name)) { // If an attribute then copy "@*" to its test sequence completeTestSequences(i, atStarGroup); // And also copy "attribute::node()" to its test sequence completeTestSequences(i, _attribNodeGroup); } else { // If an element then copy "*" to its test sequence completeTestSequences(i, starGroup); // And also copy "child::node()" to its test sequence completeTestSequences(i, _childNodeGroup); } } } _testSeq = new TestSeq[DTM.NTYPES + names.size()]; final int n = _patternGroups.length; for (int i = 0; i < n; i++) { final Vector patterns = _patternGroups[i]; if (patterns != null) { final TestSeq testSeq = new TestSeq(patterns, i, this); // System.out.println("testSeq[" + i + "] = " + testSeq); testSeq.reduce(); _testSeq[i] = testSeq; testSeq.findTemplates(_neededTemplates); } } if (_childNodeGroup != null && _childNodeGroup.size() > 0) { _childNodeTestSeq = new TestSeq(_childNodeGroup, -1, this); _childNodeTestSeq.reduce(); _childNodeTestSeq.findTemplates(_neededTemplates); } /* if (_attribNodeGroup != null && _attribNodeGroup.size() > 0) { _attribNodeTestSeq = new TestSeq(_attribNodeGroup, -1, this); _attribNodeTestSeq.reduce(); _attribNodeTestSeq.findTemplates(_neededTemplates); } */ if (_idxGroup != null && _idxGroup.size() > 0) { _idxTestSeq = new TestSeq(_idxGroup, this); _idxTestSeq.reduce(); _idxTestSeq.findTemplates(_neededTemplates); } if (_rootPattern != null) { // doesn't matter what is 'put', only key matters _neededTemplates.put(_rootPattern.getTemplate(), this); } } private void compileNamedTemplate(Template template, ClassGenerator classGen) { final ConstantPoolGen cpg = classGen.getConstantPool(); final InstructionList il = new InstructionList(); String methodName = Util.escape(template.getName().toString()); int numParams = 0; if (template.isSimpleNamedTemplate()) { Vector parameters = template.getParameters(); numParams = parameters.size(); } // Initialize the types and names arrays for the NamedMethodGenerator. com.sun.org.apache.bcel.internal.generic.Type[] types = new com.sun.org.apache.bcel.internal.generic.Type[4 + numParams]; String[] names = new String[4 + numParams]; types[0] = Util.getJCRefType(DOM_INTF_SIG); types[1] = Util.getJCRefType(NODE_ITERATOR_SIG); types[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG); types[3] = com.sun.org.apache.bcel.internal.generic.Type.INT; names[0] = DOCUMENT_PNAME; names[1] = ITERATOR_PNAME; names[2] = TRANSLET_OUTPUT_PNAME; names[3] = NODE_PNAME; // For simple named templates, the signature of the generated method // is not fixed. It depends on the number of parameters declared in the // template. for (int i = 4; i < 4 + numParams; i++) { types[i] = Util.getJCRefType(OBJECT_SIG); names[i] = "param" + String.valueOf(i-4); } NamedMethodGenerator methodGen = new NamedMethodGenerator(ACC_PUBLIC, com.sun.org.apache.bcel.internal.generic.Type.VOID, types, names, methodName, getClassName(), il, cpg); il.append(template.compile(classGen, methodGen)); il.append(RETURN); classGen.addMethod(methodGen); } private void compileTemplates(ClassGenerator classGen, MethodGenerator methodGen, InstructionHandle next) { Set