1 /*
   2  * Copyright (c) 2015, 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  * $Id: TestSeq.java,v 1.2.4.1 2005/09/12 11:31:38 pvedula Exp $
  22  */
  23 
  24 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  25 
  26 import com.sun.org.apache.bcel.internal.generic.GOTO_W;
  27 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  28 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  31 import java.util.ArrayList;
  32 import java.util.List;
  33 import java.util.Map;
  34 
  35 /**
  36  * A test sequence is a sequence of patterns that
  37  *
  38  *  (1) occured in templates in the same mode
  39  *  (2) share the same kernel node type (e.g. A/B and C/C/B)
  40  *  (3) may also contain patterns matching "*" and "node()"
  41  *      (element sequence only) or matching "@*" (attribute
  42  *      sequence only).
  43  *
  44  * A test sequence may have a default template, which will be
  45  * instantiated if none of the other patterns match.
  46  * @author Jacek Ambroziak
  47  * @author Santiago Pericas-Geertsen
  48  * @author Erwin Bolwidt <ejb@klomp.org>
  49  * @author Morten Jorgensen <morten.jorgensen@sun.com>
  50  * @LastModified: Nov 2017
  51  */
  52 final class TestSeq {
  53 
  54     /**
  55      * Integer code for the kernel type of this test sequence
  56      */
  57     private int _kernelType;
  58 
  59     /**
  60      * ArrayList of all patterns in the test sequence. May include
  61      * patterns with "*", "@*" or "node()" kernel.
  62      */
  63     private List<LocationPathPattern> _patterns = null;
  64 
  65     /**
  66      * A reference to the Mode object.
  67      */
  68     private Mode _mode = null;
  69 
  70     /**
  71      * Default template for this test sequence
  72      */
  73     private Template _default = null;
  74 
  75     /**
  76      * Instruction list representing this test sequence.
  77      */
  78     private InstructionList _instructionList;
  79 
  80     /**
  81      * Cached handle to avoid compiling more than once.
  82      */
  83     private InstructionHandle _start = null;
  84 
  85     /**
  86      * Creates a new test sequence given a set of patterns and a mode.
  87      */
  88     public TestSeq(List<LocationPathPattern> patterns, Mode mode) {
  89         this(patterns, -2, mode);
  90     }
  91 
  92     public TestSeq(List<LocationPathPattern> patterns, int kernelType, Mode mode) {
  93         _patterns = patterns;
  94         _kernelType = kernelType;
  95         _mode = mode;
  96     }
  97 
  98     /**
  99      * Returns a string representation of this test sequence. Notice
 100      * that test sequences are mutable, so the value returned by this
 101      * method is different before and after calling reduce().
 102      */
 103     public String toString() {
 104         final int count = _patterns.size();
 105         final StringBuffer result = new StringBuffer();
 106 
 107         for (int i = 0; i < count; i++) {
 108             final LocationPathPattern pattern = _patterns.get(i);
 109 
 110             if (i == 0) {
 111                 result.append("Testseq for kernel ").append(_kernelType)
 112                       .append('\n');
 113             }
 114             result.append("   pattern ").append(i).append(": ")
 115                   .append(pattern.toString())
 116                   .append('\n');
 117         }
 118         return result.toString();
 119     }
 120 
 121     /**
 122      * Returns the instruction list for this test sequence
 123      */
 124     public InstructionList getInstructionList() {
 125         return _instructionList;
 126     }
 127 
 128     /**
 129      * Return the highest priority for a pattern in this test
 130      * sequence. This is either the priority of the first or
 131      * of the default pattern.
 132      */
 133     public double getPriority() {
 134         final Template template = (_patterns.isEmpty()) ? _default
 135             : ((Pattern) _patterns.get(0)).getTemplate();
 136         return template.getPriority();
 137     }
 138 
 139     /**
 140      * Returns the position of the highest priority pattern in
 141      * this test sequence.
 142      */
 143     public int getPosition() {
 144         final Template template = (_patterns.isEmpty()) ? _default
 145             : ((Pattern) _patterns.get(0)).getTemplate();
 146         return template.getPosition();
 147     }
 148 
 149     /**
 150      * Reduce the patterns in this test sequence. Creates a new
 151      * vector of patterns and sets the default pattern if it
 152      * finds a patterns that is fully reduced.
 153      */
 154     public void reduce() {
 155         final List<LocationPathPattern> newPatterns = new ArrayList<>();
 156 
 157         for (LocationPathPattern pattern : _patterns) {
 158             // Reduce this pattern
 159             pattern.reduceKernelPattern();
 160 
 161             // Is this pattern fully reduced?
 162             if (pattern.isWildcard()) {
 163                 _default = pattern.getTemplate();
 164                 break;          // Ignore following patterns
 165             }
 166             else {
 167                 newPatterns.add(pattern);
 168             }
 169         }
 170         _patterns = newPatterns;
 171     }
 172 
 173     /**
 174      * Returns, by reference, the templates that are included in
 175      * this test sequence. Note that a single template can occur
 176      * in several test sequences if its pattern is a union.
 177      */
 178     public void findTemplates(Map<Template, Object> templates) {
 179         if (_default != null) {
 180             templates.put(_default, this);
 181         }
 182         for (LocationPathPattern pattern : _patterns) {
 183             templates.put(pattern.getTemplate(), this);
 184         }
 185     }
 186 
 187     /**
 188      * Get the instruction handle to a template's code. This is
 189      * used when a single template occurs in several test
 190      * sequences; that is, if its pattern is a union of patterns
 191      * (e.g. match="A/B | A/C").
 192      */
 193     private InstructionHandle getTemplateHandle(Template template) {
 194         return _mode.getTemplateInstructionHandle(template);
 195     }
 196 
 197     /**
 198      * Returns pattern n in this test sequence
 199      */
 200     private LocationPathPattern getPattern(int n) {
 201         return _patterns.get(n);
 202     }
 203 
 204     /**
 205      * Compile the code for this test sequence. Compile patterns
 206      * from highest to lowest priority. Note that since patterns
 207      * can be share by multiple test sequences, instruction lists
 208      * must be copied before backpatching.
 209      */
 210     public InstructionHandle compile(ClassGenerator classGen,
 211                                      MethodGenerator methodGen,
 212                                      InstructionHandle continuation)
 213     {
 214         // Returned cached value if already compiled
 215         if (_start != null) {
 216             return _start;
 217         }
 218 
 219         // If not patterns, then return handle for default template
 220         final int count = _patterns.size();
 221         if (count == 0) {
 222             return (_start = getTemplateHandle(_default));
 223         }
 224 
 225         // Init handle to jump when all patterns failed
 226         InstructionHandle fail = (_default == null) ? continuation
 227             : getTemplateHandle(_default);
 228 
 229         // Compile all patterns in reverse order
 230         for (int n = count - 1; n >= 0; n--) {
 231             final LocationPathPattern pattern = getPattern(n);
 232             final Template template = pattern.getTemplate();
 233             final InstructionList il = new InstructionList();
 234 
 235             // Patterns expect current node on top of stack
 236             il.append(methodGen.loadCurrentNode());
 237 
 238             // Apply the test-code compiled for the pattern
 239             InstructionList ilist = methodGen.getInstructionList(pattern);
 240             if (ilist == null) {
 241                 ilist = pattern.compile(classGen, methodGen);
 242                 methodGen.addInstructionList(pattern, ilist);
 243             }
 244 
 245             // Make a copy of the instruction list for backpatching
 246             InstructionList copyOfilist = ilist.copy();
 247 
 248             FlowList trueList = pattern.getTrueList();
 249             if (trueList != null) {
 250                 trueList = trueList.copyAndRedirect(ilist, copyOfilist);
 251             }
 252             FlowList falseList = pattern.getFalseList();
 253             if (falseList != null) {
 254                 falseList = falseList.copyAndRedirect(ilist, copyOfilist);
 255             }
 256 
 257             il.append(copyOfilist);
 258 
 259             // On success branch to the template code
 260             final InstructionHandle gtmpl = getTemplateHandle(template);
 261             final InstructionHandle success = il.append(new GOTO_W(gtmpl));
 262 
 263             if (trueList != null) {
 264                 trueList.backPatch(success);
 265             }
 266             if (falseList != null) {
 267                 falseList.backPatch(fail);
 268             }
 269 
 270             // Next pattern's 'fail' target is this pattern's first instruction
 271             fail = il.getStart();
 272 
 273             // Append existing instruction list to the end of this one
 274             if (_instructionList != null) {
 275                 il.append(_instructionList);
 276             }
 277 
 278             // Set current instruction list to be this one
 279             _instructionList = il;
 280         }
 281         return (_start = fail);
 282     }
 283 }