1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Nov 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  * $Id: Mode.java,v 1.2.4.1 2005/09/19 05:18:11 pvedula Exp $
  23  */
  24 
  25 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  26 
  27 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  28 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  29 import com.sun.org.apache.bcel.internal.generic.DUP;
  30 import com.sun.org.apache.bcel.internal.generic.GOTO_W;
  31 import com.sun.org.apache.bcel.internal.generic.IFLT;
  32 import com.sun.org.apache.bcel.internal.generic.ILOAD;
  33 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  34 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  35 import com.sun.org.apache.bcel.internal.generic.ISTORE;
  36 import com.sun.org.apache.bcel.internal.generic.Instruction;
  37 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  38 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  39 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  40 import com.sun.org.apache.bcel.internal.generic.SWITCH;
  41 import com.sun.org.apache.bcel.internal.generic.TargetLostException;
  42 import com.sun.org.apache.bcel.internal.util.InstructionFinder;
  43 import com.sun.org.apache.xalan.internal.xsltc.DOM;
  44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NamedMethodGenerator;
  47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  48 import com.sun.org.apache.xml.internal.dtm.Axis;
  49 import com.sun.org.apache.xml.internal.dtm.DTM;
  50 import java.util.ArrayList;
  51 import java.util.HashMap;
  52 import java.util.Iterator;
  53 import java.util.List;
  54 import java.util.Map;
  55 import java.util.Set;
  56 
  57 /**
  58  * Mode gathers all the templates belonging to a given mode;
  59  * it is responsible for generating an appropriate
  60  * applyTemplates + (mode name) method in the translet.
  61  * @author Jacek Ambroziak
  62  * @author Santiago Pericas-Geertsen
  63  * @author Morten Jorgensen
  64  * @author Erwin Bolwidt <ejb@klomp.org>
  65  * @author G. Todd Miller
  66  */
  67 final class Mode implements Constants {
  68 
  69     /**
  70      * The name of this mode as defined in the stylesheet.
  71      */
  72     private final QName _name;
  73 
  74     /**
  75      * A reference to the stylesheet object that owns this mode.
  76      */
  77     private final Stylesheet _stylesheet;
  78 
  79     /**
  80      * The name of the method in which this mode is compiled.
  81      */
  82     private final String _methodName;
  83 
  84     /**
  85      * A vector of all the templates in this mode.
  86      */
  87     private List<Template> _templates;
  88 
  89     /**
  90      * Group for patterns with node()-type kernel and child axis.
  91      */
  92     private List<LocationPathPattern> _childNodeGroup = null;
  93 
  94     /**
  95      * Test sequence for patterns with node()-type kernel and child axis.
  96      */
  97     private TestSeq _childNodeTestSeq = null;
  98 
  99     /**
 100      * Group for patterns with node()-type kernel and attribute axis.
 101      */
 102     private List<LocationPathPattern> _attribNodeGroup = null;
 103 
 104     /**
 105      * Test sequence for patterns with node()-type kernel and attribute axis.
 106      */
 107     private TestSeq _attribNodeTestSeq = null;
 108 
 109     /**
 110      * Group for patterns with id() or key()-type kernel.
 111      */
 112     private List<LocationPathPattern> _idxGroup = null;
 113 
 114     /**
 115      * Test sequence for patterns with id() or key()-type kernel.
 116      */
 117     private TestSeq _idxTestSeq = null;
 118 
 119     /**
 120      * Group for patterns with any other kernel type.
 121      */
 122     private List<LocationPathPattern>[] _patternGroups;
 123 
 124     /**
 125      * Test sequence for patterns with any other kernel type.
 126      */
 127     private TestSeq[] _testSeq;
 128 
 129 
 130     /**
 131      * A mapping between templates and test sequences.
 132      */
 133     private Map<Template, Object> _neededTemplates = new HashMap<>();
 134 
 135     /**
 136      * A mapping between named templates and Mode objects.
 137      */
 138     private Map<Template, Mode> _namedTemplates = new HashMap<>();
 139 
 140     /**
 141      * A mapping between templates and instruction handles.
 142      */
 143     private Map<Template, InstructionHandle> _templateIHs = new HashMap<>();
 144 
 145     /**
 146      * A mapping between templates and instruction lists.
 147      */
 148     private Map<Template, InstructionList> _templateILs = new HashMap<>();
 149 
 150     /**
 151      * A reference to the pattern matching the root node.
 152      */
 153     private LocationPathPattern _rootPattern = null;
 154 
 155     /**
 156      * Stores ranges of template precendences for the compilation
 157      * of apply-imports.
 158      */
 159     private Map<Integer, Integer> _importLevels = null;
 160 
 161     /**
 162      * A mapping between key names and keys.
 163      */
 164     private Map<String, Key> _keys = null;
 165 
 166     /**
 167      * Variable index for the current node used in code generation.
 168      */
 169     private int _currentIndex;
 170 
 171     /**
 172      * Creates a new Mode.
 173      *
 174      * @param name A textual representation of the mode's QName
 175      * @param stylesheet The Stylesheet in which the mode occured
 176      * @param suffix A suffix to append to the method name for this mode
 177      *               (normally a sequence number - still in a String).
 178      */
 179     @SuppressWarnings({"rawtypes", "unchecked"})
 180     public Mode(QName name, Stylesheet stylesheet, String suffix) {
 181         _name = name;
 182         _stylesheet = stylesheet;
 183         _methodName = APPLY_TEMPLATES + suffix;
 184         _templates = new ArrayList<>();
 185         _patternGroups = (List<LocationPathPattern>[])new ArrayList[32];
 186     }
 187 
 188     /**
 189      * Returns the name of the method (_not_ function) that will be
 190      * compiled for this mode. Normally takes the form 'applyTemplates()'
 191      * or * 'applyTemplates2()'.
 192      *
 193      * @return Method name for this mode
 194      */
 195     public String functionName() {
 196         return _methodName;
 197     }
 198 
 199     public String functionName(int min, int max) {
 200         if (_importLevels == null) {
 201             _importLevels = new HashMap<>();
 202         }
 203         _importLevels.put(max, min);
 204         return _methodName + '_' + max;
 205     }
 206 
 207     /**
 208      * Shortcut to get the class compiled for this mode (will be inlined).
 209      */
 210     private String getClassName() {
 211         return _stylesheet.getClassName();
 212     }
 213 
 214     public Stylesheet getStylesheet() {
 215         return _stylesheet;
 216     }
 217 
 218     public void addTemplate(Template template) {
 219         _templates.add(template);
 220     }
 221 
 222     private List<Template> quicksort(List<Template> templates, int p, int r) {
 223         if (p < r) {
 224             final int q = partition(templates, p, r);
 225             quicksort(templates, p, q);
 226             quicksort(templates, q + 1, r);
 227         }
 228         return templates;
 229     }
 230 
 231     private int partition(List<Template> templates, int p, int r) {
 232         final Template x = templates.get(p);
 233         int i = p - 1;
 234         int j = r + 1;
 235         while (true) {
 236             while (x.compareTo(templates.get(--j)) > 0);
 237             while (x.compareTo(templates.get(++i)) < 0);
 238             if (i < j) {
 239                 templates.set(j, templates.set(i, templates.get(j)));
 240             }
 241             else {
 242                 return j;
 243             }
 244         }
 245     }
 246 
 247     /**
 248      * Process all the test patterns in this mode
 249      */
 250     public void processPatterns(Map<String, Key> keys) {
 251         _keys = keys;
 252         _templates = quicksort(_templates, 0, _templates.size() - 1);
 253 
 254         // Traverse all templates
 255         for (Template template : _templates) {
 256             /*
 257              * Add this template to a table of named templates if it has a name.
 258              * If there are multiple templates with the same name, all but one
 259              * (the one with highest priority) will be disabled.
 260              */
 261             if (template.isNamed() && !template.disabled()) {
 262                 _namedTemplates.put(template, this);
 263             }
 264 
 265             // Add this template to a test sequence if it has a pattern
 266             final Pattern pattern = template.getPattern();
 267             if (pattern != null) {
 268                 flattenAlternative(pattern, template, keys);
 269             }
 270         }
 271         prepareTestSequences();
 272     }
 273 
 274     /**
 275      * This method will break up alternative patterns (ie. unions of patterns,
 276      * such as match="A/B | C/B") and add the basic patterns to their
 277      * respective pattern groups.
 278      */
 279     private void flattenAlternative(Pattern pattern,
 280                                     Template template,
 281                                     Map<String, Key> keys) {
 282         // Patterns on type id() and key() are special since they do not have
 283         // any kernel node type (it can be anything as long as the node is in
 284         // the id's or key's index).
 285         if (pattern instanceof IdKeyPattern) {
 286             final IdKeyPattern idkey = (IdKeyPattern)pattern;
 287             idkey.setTemplate(template);
 288             if (_idxGroup == null) _idxGroup = new ArrayList<>();
 289             _idxGroup.add((IdKeyPattern)pattern);
 290         }
 291         // Alternative patterns are broken up and re-processed recursively
 292         else if (pattern instanceof AlternativePattern) {
 293             final AlternativePattern alt = (AlternativePattern)pattern;
 294             flattenAlternative(alt.getLeft(), template, keys);
 295             flattenAlternative(alt.getRight(), template, keys);
 296         }
 297         // Finally we have a pattern that can be added to a test sequence!
 298         else if (pattern instanceof LocationPathPattern) {
 299             final LocationPathPattern lpp = (LocationPathPattern)pattern;
 300             lpp.setTemplate(template);
 301             addPatternToGroup(lpp);
 302         }
 303     }
 304 
 305     /**
 306      * Group patterns by NodeTests of their last Step
 307      * Keep them sorted by priority within group
 308      */
 309     private void addPatternToGroup(final LocationPathPattern lpp) {
 310         // id() and key()-type patterns do not have a kernel type
 311         if (lpp instanceof IdKeyPattern) {
 312             addPattern(-1, lpp);
 313         }
 314         // Otherwise get the kernel pattern from the LPP
 315         else {
 316             // kernel pattern is the last (maybe only) Step
 317             final StepPattern kernel = lpp.getKernelPattern();
 318             if (kernel != null) {
 319                 addPattern(kernel.getNodeType(), lpp);
 320             }
 321             else if (_rootPattern == null ||
 322                      lpp.noSmallerThan(_rootPattern)) {
 323                 _rootPattern = lpp;
 324             }
 325         }
 326     }
 327 
 328     /**
 329      * Adds a pattern to a pattern group
 330      */
 331     private void addPattern(int kernelType, LocationPathPattern pattern) {
 332         // Make sure the array of pattern groups is long enough
 333         final int oldLength = _patternGroups.length;
 334         if (kernelType >= oldLength) {
 335             @SuppressWarnings({"rawtypes", "unchecked"})
 336             List<LocationPathPattern>[] newGroups =
 337                     (List<LocationPathPattern>[])new ArrayList[kernelType * 2];
 338 
 339             System.arraycopy(_patternGroups, 0, newGroups, 0, oldLength);
 340             _patternGroups = newGroups;
 341         }
 342 
 343         // Find the vector to put this pattern into
 344         List<LocationPathPattern> patterns;
 345 
 346         if (kernelType == DOM.NO_TYPE) {
 347             if (pattern.getAxis() == Axis.ATTRIBUTE) {
 348                 patterns = (_attribNodeGroup == null) ?
 349                     (_attribNodeGroup = new ArrayList<>(2)) : _attribNodeGroup;
 350             }
 351             else {
 352                 patterns = (_childNodeGroup == null) ?
 353                     (_childNodeGroup = new ArrayList<>(2)) : _childNodeGroup;
 354             }
 355         }
 356         else {
 357             patterns = (_patternGroups[kernelType] == null) ?
 358                 (_patternGroups[kernelType] = new ArrayList<>(2)) :
 359                 _patternGroups[kernelType];
 360         }
 361 
 362         if (patterns.size() == 0) {
 363             patterns.add(pattern);
 364         }
 365         else {
 366             boolean inserted = false;
 367             for (int i = 0; i < patterns.size(); i++) {
 368                 final LocationPathPattern lppToCompare =
 369                     patterns.get(i);
 370 
 371                 if (pattern.noSmallerThan(lppToCompare)) {
 372                     inserted = true;
 373                     patterns.add(i, pattern);
 374                     break;
 375                 }
 376             }
 377             if (inserted == false) {
 378                 patterns.add(pattern);
 379             }
 380         }
 381     }
 382 
 383     /**
 384      * Complete test sequences of a given type by adding all patterns
 385      * from a given group.
 386      */
 387     private void completeTestSequences(int nodeType, List<LocationPathPattern> patterns) {
 388         if (patterns != null) {
 389             if (_patternGroups[nodeType] == null) {
 390                 _patternGroups[nodeType] = patterns;
 391             }
 392             else {
 393                 final int m = patterns.size();
 394                 for (int j = 0; j < m; j++) {
 395                     addPattern(nodeType, patterns.get(j));
 396                 }
 397             }
 398         }
 399     }
 400 
 401     /**
 402      * Build test sequences. The first step is to complete the test sequences
 403      * by including patterns of "*" and "node()" kernel to all element test
 404      * sequences, and of "@*" to all attribute test sequences.
 405      */
 406     private void prepareTestSequences() {
 407         final List<LocationPathPattern> starGroup = _patternGroups[DTM.ELEMENT_NODE];
 408         final List<LocationPathPattern> atStarGroup = _patternGroups[DTM.ATTRIBUTE_NODE];
 409 
 410         // Complete test sequence for "text()" with "child::node()"
 411         completeTestSequences(DTM.TEXT_NODE, _childNodeGroup);
 412 
 413         // Complete test sequence for "*" with "child::node()"
 414         completeTestSequences(DTM.ELEMENT_NODE, _childNodeGroup);
 415 
 416         // Complete test sequence for "pi()" with "child::node()"
 417         completeTestSequences(DTM.PROCESSING_INSTRUCTION_NODE, _childNodeGroup);
 418 
 419         // Complete test sequence for "comment()" with "child::node()"
 420         completeTestSequences(DTM.COMMENT_NODE, _childNodeGroup);
 421 
 422         // Complete test sequence for "@*" with "attribute::node()"
 423         completeTestSequences(DTM.ATTRIBUTE_NODE, _attribNodeGroup);
 424 
 425         final List<String> names = _stylesheet.getXSLTC().getNamesIndex();
 426         if (starGroup != null || atStarGroup != null ||
 427             _childNodeGroup != null || _attribNodeGroup != null)
 428         {
 429             final int n = _patternGroups.length;
 430 
 431             // Complete test sequence for user-defined types
 432             for (int i = DTM.NTYPES; i < n; i++) {
 433                 if (_patternGroups[i] == null) continue;
 434 
 435                 final String name = names.get(i - DTM.NTYPES);
 436 
 437                 if (isAttributeName(name)) {
 438                     // If an attribute then copy "@*" to its test sequence
 439                     completeTestSequences(i, atStarGroup);
 440 
 441                     // And also copy "attribute::node()" to its test sequence
 442                     completeTestSequences(i, _attribNodeGroup);
 443                 }
 444                 else {
 445                     // If an element then copy "*" to its test sequence
 446                     completeTestSequences(i, starGroup);
 447 
 448                     // And also copy "child::node()" to its test sequence
 449                     completeTestSequences(i, _childNodeGroup);
 450                 }
 451             }
 452         }
 453 
 454         _testSeq = new TestSeq[DTM.NTYPES + names.size()];
 455 
 456         final int n = _patternGroups.length;
 457         for (int i = 0; i < n; i++) {
 458             final List<LocationPathPattern> patterns = _patternGroups[i];
 459             if (patterns != null) {
 460                 final TestSeq testSeq = new TestSeq(patterns, i, this);
 461 // System.out.println("testSeq[" + i + "] = " + testSeq);
 462                 testSeq.reduce();
 463                 _testSeq[i] = testSeq;
 464                 testSeq.findTemplates(_neededTemplates);
 465             }
 466         }
 467 
 468         if (_childNodeGroup != null && _childNodeGroup.size() > 0) {
 469             _childNodeTestSeq = new TestSeq(_childNodeGroup, -1, this);
 470             _childNodeTestSeq.reduce();
 471             _childNodeTestSeq.findTemplates(_neededTemplates);
 472         }
 473 
 474 /*
 475         if (_attribNodeGroup != null && _attribNodeGroup.size() > 0) {
 476             _attribNodeTestSeq = new TestSeq(_attribNodeGroup, -1, this);
 477             _attribNodeTestSeq.reduce();
 478             _attribNodeTestSeq.findTemplates(_neededTemplates);
 479         }
 480 */
 481 
 482         if (_idxGroup != null && _idxGroup.size() > 0) {
 483             _idxTestSeq = new TestSeq(_idxGroup, this);
 484             _idxTestSeq.reduce();
 485             _idxTestSeq.findTemplates(_neededTemplates);
 486         }
 487 
 488         if (_rootPattern != null) {
 489             // doesn't matter what is 'put', only key matters
 490             _neededTemplates.put(_rootPattern.getTemplate(), this);
 491         }
 492     }
 493 
 494     private void compileNamedTemplate(Template template,
 495                                       ClassGenerator classGen) {
 496         final ConstantPoolGen cpg = classGen.getConstantPool();
 497         final InstructionList il = new InstructionList();
 498         String methodName = Util.escape(template.getName().toString());
 499 
 500         int numParams = 0;
 501         if (template.isSimpleNamedTemplate()) {
 502             List<Param> parameters = template.getParameters();
 503             numParams = parameters.size();
 504         }
 505 
 506         // Initialize the types and names arrays for the NamedMethodGenerator.
 507         com.sun.org.apache.bcel.internal.generic.Type[] types =
 508             new com.sun.org.apache.bcel.internal.generic.Type[4 + numParams];
 509         String[] names = new String[4 + numParams];
 510         types[0] = Util.getJCRefType(DOM_INTF_SIG);
 511         types[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
 512         types[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
 513         types[3] = com.sun.org.apache.bcel.internal.generic.Type.INT;
 514         names[0] = DOCUMENT_PNAME;
 515         names[1] = ITERATOR_PNAME;
 516         names[2] = TRANSLET_OUTPUT_PNAME;
 517         names[3] = NODE_PNAME;
 518 
 519         // For simple named templates, the signature of the generated method
 520         // is not fixed. It depends on the number of parameters declared in the
 521         // template.
 522         for (int i = 4; i < 4 + numParams; i++) {
 523             types[i] = Util.getJCRefType(OBJECT_SIG);
 524             names[i] = "param" + String.valueOf(i-4);
 525         }
 526 
 527         NamedMethodGenerator methodGen =
 528                 new NamedMethodGenerator(ACC_PUBLIC,
 529                                      com.sun.org.apache.bcel.internal.generic.Type.VOID,
 530                                      types, names, methodName,
 531                                      getClassName(), il, cpg);
 532 
 533         il.append(template.compile(classGen, methodGen));
 534         il.append(RETURN);
 535 
 536         classGen.addMethod(methodGen);
 537     }
 538 
 539     private void compileTemplates(ClassGenerator classGen,
 540                                   MethodGenerator methodGen,
 541                                   InstructionHandle next)
 542     {
 543         Set<Template> templates = _namedTemplates.keySet();
 544         for (Template template : templates) {
 545             compileNamedTemplate(template, classGen);
 546         }
 547 
 548         templates = _neededTemplates.keySet();
 549         for (Template template : templates) {
 550             if (template.hasContents()) {
 551                 // !!! TODO templates both named and matched
 552                 InstructionList til = template.compile(classGen, methodGen);
 553                 til.append(new GOTO_W(next));
 554                 _templateILs.put(template, til);
 555                 _templateIHs.put(template, til.getStart());
 556             }
 557             else {
 558                 // empty template
 559                 _templateIHs.put(template, next);
 560             }
 561         }
 562     }
 563 
 564     private void appendTemplateCode(InstructionList body) {
 565         for (Template template : _neededTemplates.keySet()) {
 566             final InstructionList iList = _templateILs.get(template);
 567             if (iList != null) {
 568                 body.append(iList);
 569             }
 570 
 571         }
 572     }
 573 
 574     private void appendTestSequences(InstructionList body) {
 575         final int n = _testSeq.length;
 576         for (int i = 0; i < n; i++) {
 577             final TestSeq testSeq = _testSeq[i];
 578             if (testSeq != null) {
 579                 InstructionList il = testSeq.getInstructionList();
 580                 if (il != null)
 581                     body.append(il);
 582                 // else trivial TestSeq
 583             }
 584         }
 585     }
 586 
 587     public static void compileGetChildren(ClassGenerator classGen,
 588                                           MethodGenerator methodGen,
 589                                           int node) {
 590         final ConstantPoolGen cpg = classGen.getConstantPool();
 591         final InstructionList il = methodGen.getInstructionList();
 592         final int git = cpg.addInterfaceMethodref(DOM_INTF,
 593                                                   GET_CHILDREN,
 594                                                   GET_CHILDREN_SIG);
 595         il.append(methodGen.loadDOM());
 596         il.append(new ILOAD(node));
 597         il.append(new INVOKEINTERFACE(git, 2));
 598     }
 599 
 600     /**
 601      * Compiles the default handling for DOM elements: traverse all children
 602      */
 603     private InstructionList compileDefaultRecursion(ClassGenerator classGen,
 604                                                     MethodGenerator methodGen,
 605                                                     InstructionHandle next) {
 606         final ConstantPoolGen cpg = classGen.getConstantPool();
 607         final InstructionList il = new InstructionList();
 608         final String applyTemplatesSig = classGen.getApplyTemplatesSig();
 609         final int git = cpg.addInterfaceMethodref(DOM_INTF,
 610                                                   GET_CHILDREN,
 611                                                   GET_CHILDREN_SIG);
 612         final int applyTemplates = cpg.addMethodref(getClassName(),
 613                                                     functionName(),
 614                                                     applyTemplatesSig);
 615         il.append(classGen.loadTranslet());
 616         il.append(methodGen.loadDOM());
 617 
 618         il.append(methodGen.loadDOM());
 619         il.append(new ILOAD(_currentIndex));
 620         il.append(new INVOKEINTERFACE(git, 2));
 621         il.append(methodGen.loadHandler());
 622         il.append(new INVOKEVIRTUAL(applyTemplates));
 623         il.append(new GOTO_W(next));
 624         return il;
 625     }
 626 
 627     /**
 628      * Compiles the default action for DOM text nodes and attribute nodes:
 629      * output the node's text value
 630      */
 631     private InstructionList compileDefaultText(ClassGenerator classGen,
 632                                                MethodGenerator methodGen,
 633                                                InstructionHandle next) {
 634         final ConstantPoolGen cpg = classGen.getConstantPool();
 635         final InstructionList il = new InstructionList();
 636 
 637         final int chars = cpg.addInterfaceMethodref(DOM_INTF,
 638                                                     CHARACTERS,
 639                                                     CHARACTERS_SIG);
 640         il.append(methodGen.loadDOM());
 641         il.append(new ILOAD(_currentIndex));
 642         il.append(methodGen.loadHandler());
 643         il.append(new INVOKEINTERFACE(chars, 3));
 644         il.append(new GOTO_W(next));
 645         return il;
 646     }
 647 
 648     private InstructionList compileNamespaces(ClassGenerator classGen,
 649                                               MethodGenerator methodGen,
 650                                               boolean[] isNamespace,
 651                                               boolean[] isAttribute,
 652                                               boolean attrFlag,
 653                                               InstructionHandle defaultTarget) {
 654         final XSLTC xsltc = classGen.getParser().getXSLTC();
 655         final ConstantPoolGen cpg = classGen.getConstantPool();
 656 
 657         // Append switch() statement - namespace test dispatch loop
 658         final List<String> namespaces = xsltc.getNamespaceIndex();
 659         final List<String> names = xsltc.getNamesIndex();
 660         final int namespaceCount = namespaces.size() + 1;
 661         final int namesCount = names.size();
 662 
 663         final InstructionList il = new InstructionList();
 664         final int[] types = new int[namespaceCount];
 665         final InstructionHandle[] targets = new InstructionHandle[types.length];
 666 
 667         if (namespaceCount > 0) {
 668             boolean compiled = false;
 669 
 670             // Initialize targets for namespace() switch statement
 671             for (int i = 0; i < namespaceCount; i++) {
 672                 targets[i] = defaultTarget;
 673                 types[i] = i;
 674             }
 675 
 676             // Add test sequences for known namespace types
 677             for (int i = DTM.NTYPES; i < (DTM.NTYPES+namesCount); i++) {
 678                 if ((isNamespace[i]) && (isAttribute[i] == attrFlag)) {
 679                     String name = names.get(i-DTM.NTYPES);
 680                     String namespace = name.substring(0,name.lastIndexOf(':'));
 681                     final int type = xsltc.registerNamespace(namespace);
 682 
 683                     if ((i < _testSeq.length) &&
 684                         (_testSeq[i] != null)) {
 685                         targets[type] =
 686                             (_testSeq[i]).compile(classGen,
 687                                                        methodGen,
 688                                                        defaultTarget);
 689                         compiled = true;
 690                     }
 691                 }
 692             }
 693 
 694             // Return "null" if no test sequences were compiled
 695             if (!compiled) return(null);
 696 
 697             // Append first code in applyTemplates() - get type of current node
 698             final int getNS = cpg.addInterfaceMethodref(DOM_INTF,
 699                                                         "getNamespaceType",
 700                                                         "(I)I");
 701             il.append(methodGen.loadDOM());
 702             il.append(new ILOAD(_currentIndex));
 703             il.append(new INVOKEINTERFACE(getNS, 2));
 704             il.append(new SWITCH(types, targets, defaultTarget));
 705             return(il);
 706         }
 707         else {
 708             return(null);
 709         }
 710     }
 711 
 712    /**
 713      * Compiles the applyTemplates() method and adds it to the translet.
 714      * This is the main dispatch method.
 715      */
 716     public void compileApplyTemplates(ClassGenerator classGen) {
 717         final XSLTC xsltc = classGen.getParser().getXSLTC();
 718         final ConstantPoolGen cpg = classGen.getConstantPool();
 719         final List<String> names = xsltc.getNamesIndex();
 720 
 721         // Create the applyTemplates() method
 722         final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
 723             new com.sun.org.apache.bcel.internal.generic.Type[3];
 724         argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
 725         argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
 726         argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
 727 
 728         final String[] argNames = new String[3];
 729         argNames[0] = DOCUMENT_PNAME;
 730         argNames[1] = ITERATOR_PNAME;
 731         argNames[2] = TRANSLET_OUTPUT_PNAME;
 732 
 733         final InstructionList mainIL = new InstructionList();
 734         final MethodGenerator methodGen =
 735             new MethodGenerator(ACC_PUBLIC | ACC_FINAL,
 736                                 com.sun.org.apache.bcel.internal.generic.Type.VOID,
 737                                 argTypes, argNames, functionName(),
 738                                 getClassName(), mainIL,
 739                                 classGen.getConstantPool());
 740         methodGen.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");
 741         // Insert an extra NOP just to keep "current" from appearing as if it
 742         // has a value before the start of the loop.
 743         mainIL.append(NOP);
 744 
 745 
 746         // Create a local variable to hold the current node
 747         final LocalVariableGen current;
 748         current = methodGen.addLocalVariable2("current",
 749                                               com.sun.org.apache.bcel.internal.generic.Type.INT,
 750                                               null);
 751         _currentIndex = current.getIndex();
 752 
 753         // Create the "body" instruction list that will eventually hold the
 754         // code for the entire method (other ILs will be appended).
 755         final InstructionList body = new InstructionList();
 756         body.append(NOP);
 757 
 758         // Create an instruction list that contains the default next-node
 759         // iteration
 760         final InstructionList ilLoop = new InstructionList();
 761         ilLoop.append(methodGen.loadIterator());
 762         ilLoop.append(methodGen.nextNode());
 763         ilLoop.append(DUP);
 764         ilLoop.append(new ISTORE(_currentIndex));
 765 
 766         // The body of this code can get very large - large than can be handled
 767         // by a single IFNE(body.getStart()) instruction - need workaround:
 768         final BranchHandle ifeq = ilLoop.append(new IFLT(null));
 769         final BranchHandle loop = ilLoop.append(new GOTO_W(null));
 770         ifeq.setTarget(ilLoop.append(RETURN));  // applyTemplates() ends here!
 771         final InstructionHandle ihLoop = ilLoop.getStart();
 772 
 773         current.setStart(mainIL.append(new GOTO_W(ihLoop)));
 774 
 775         // Live range of "current" ends at end of loop
 776         current.setEnd(loop);
 777 
 778         // Compile default handling of elements (traverse children)
 779         InstructionList ilRecurse =
 780             compileDefaultRecursion(classGen, methodGen, ihLoop);
 781         InstructionHandle ihRecurse = ilRecurse.getStart();
 782 
 783         // Compile default handling of text/attribute nodes (output text)
 784         InstructionList ilText =
 785             compileDefaultText(classGen, methodGen, ihLoop);
 786         InstructionHandle ihText = ilText.getStart();
 787 
 788         // Distinguish attribute/element/namespace tests for further processing
 789         final int[] types = new int[DTM.NTYPES + names.size()];
 790         for (int i = 0; i < types.length; i++) {
 791             types[i] = i;
 792         }
 793 
 794         // Initialize isAttribute[] and isNamespace[] arrays
 795         final boolean[] isAttribute = new boolean[types.length];
 796         final boolean[] isNamespace = new boolean[types.length];
 797         for (int i = 0; i < names.size(); i++) {
 798             final String name = names.get(i);
 799             isAttribute[i + DTM.NTYPES] = isAttributeName(name);
 800             isNamespace[i + DTM.NTYPES] = isNamespaceName(name);
 801         }
 802 
 803         // Compile all templates - regardless of pattern type
 804         compileTemplates(classGen, methodGen, ihLoop);
 805 
 806         // Handle template with explicit "*" pattern
 807         final TestSeq elemTest = _testSeq[DTM.ELEMENT_NODE];
 808         InstructionHandle ihElem = ihRecurse;
 809         if (elemTest != null)
 810             ihElem = elemTest.compile(classGen, methodGen, ihRecurse);
 811 
 812         // Handle template with explicit "@*" pattern
 813         final TestSeq attrTest = _testSeq[DTM.ATTRIBUTE_NODE];
 814         InstructionHandle ihAttr = ihText;
 815         if (attrTest != null)
 816             ihAttr = attrTest.compile(classGen, methodGen, ihAttr);
 817 
 818         // Do tests for id() and key() patterns first
 819         InstructionList ilKey = null;
 820         if (_idxTestSeq != null) {
 821             loop.setTarget(_idxTestSeq.compile(classGen, methodGen, body.getStart()));
 822             ilKey = _idxTestSeq.getInstructionList();
 823         }
 824         else {
 825             loop.setTarget(body.getStart());
 826         }
 827 
 828         // If there is a match on node() we need to replace ihElem
 829         // and ihText if the priority of node() is higher
 830         if (_childNodeTestSeq != null) {
 831             // Compare priorities of node() and "*"
 832             double nodePrio = _childNodeTestSeq.getPriority();
 833             int    nodePos  = _childNodeTestSeq.getPosition();
 834             double elemPrio = (0 - Double.MAX_VALUE);
 835             int    elemPos  = Integer.MIN_VALUE;
 836 
 837             if (elemTest != null) {
 838                 elemPrio = elemTest.getPriority();
 839                 elemPos  = elemTest.getPosition();
 840             }
 841             if (elemPrio == Double.NaN || elemPrio < nodePrio ||
 842                 (elemPrio == nodePrio && elemPos < nodePos))
 843             {
 844                 ihElem = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
 845             }
 846 
 847             // Compare priorities of node() and text()
 848             final TestSeq textTest = _testSeq[DTM.TEXT_NODE];
 849             double textPrio = (0 - Double.MAX_VALUE);
 850             int    textPos  = Integer.MIN_VALUE;
 851 
 852             if (textTest != null) {
 853                 textPrio = textTest.getPriority();
 854                 textPos  = textTest.getPosition();
 855             }
 856             if (textPrio == Double.NaN || textPrio < nodePrio ||
 857                 (textPrio == nodePrio && textPos < nodePos))
 858             {
 859                 ihText = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
 860                 _testSeq[DTM.TEXT_NODE] = _childNodeTestSeq;
 861             }
 862         }
 863 
 864         // Handle templates with "ns:*" pattern
 865         InstructionHandle elemNamespaceHandle = ihElem;
 866         InstructionList nsElem = compileNamespaces(classGen, methodGen,
 867                                                    isNamespace, isAttribute,
 868                                                    false, ihElem);
 869         if (nsElem != null) elemNamespaceHandle = nsElem.getStart();
 870 
 871         // Handle templates with "ns:@*" pattern
 872         InstructionHandle attrNamespaceHandle = ihAttr;
 873         InstructionList nsAttr = compileNamespaces(classGen, methodGen,
 874                                                    isNamespace, isAttribute,
 875                                                    true, ihAttr);
 876         if (nsAttr != null) attrNamespaceHandle = nsAttr.getStart();
 877 
 878         // Handle templates with "ns:elem" or "ns:@attr" pattern
 879         final InstructionHandle[] targets = new InstructionHandle[types.length];
 880         for (int i = DTM.NTYPES; i < targets.length; i++) {
 881             final TestSeq testSeq = _testSeq[i];
 882             // Jump straight to namespace tests ?
 883             if (isNamespace[i]) {
 884                 if (isAttribute[i])
 885                     targets[i] = attrNamespaceHandle;
 886                 else
 887                     targets[i] = elemNamespaceHandle;
 888             }
 889             // Test first, then jump to namespace tests
 890             else if (testSeq != null) {
 891                 if (isAttribute[i])
 892                     targets[i] = testSeq.compile(classGen, methodGen,
 893                                                  attrNamespaceHandle);
 894                 else
 895                     targets[i] = testSeq.compile(classGen, methodGen,
 896                                                  elemNamespaceHandle);
 897             }
 898             else {
 899                 targets[i] = ihLoop;
 900             }
 901         }
 902 
 903 
 904         // Handle pattern with match on root node - default: traverse children
 905         targets[DTM.ROOT_NODE] = _rootPattern != null
 906             ? getTemplateInstructionHandle(_rootPattern.getTemplate())
 907             : ihRecurse;
 908 
 909         // Handle pattern with match on root node - default: traverse children
 910         targets[DTM.DOCUMENT_NODE] = _rootPattern != null
 911             ? getTemplateInstructionHandle(_rootPattern.getTemplate())
 912             : ihRecurse;
 913 
 914         // Handle any pattern with match on text nodes - default: output text
 915         targets[DTM.TEXT_NODE] = _testSeq[DTM.TEXT_NODE] != null
 916             ? _testSeq[DTM.TEXT_NODE].compile(classGen, methodGen, ihText)
 917             : ihText;
 918 
 919         // This DOM-type is not in use - default: process next node
 920         targets[DTM.NAMESPACE_NODE] = ihLoop;
 921 
 922         // Match unknown element in DOM - default: check for namespace match
 923         targets[DTM.ELEMENT_NODE] = elemNamespaceHandle;
 924 
 925         // Match unknown attribute in DOM - default: check for namespace match
 926         targets[DTM.ATTRIBUTE_NODE] = attrNamespaceHandle;
 927 
 928         // Match on processing instruction - default: process next node
 929         InstructionHandle ihPI = ihLoop;
 930         if (_childNodeTestSeq != null) ihPI = ihElem;
 931         if (_testSeq[DTM.PROCESSING_INSTRUCTION_NODE] != null)
 932             targets[DTM.PROCESSING_INSTRUCTION_NODE] =
 933                 _testSeq[DTM.PROCESSING_INSTRUCTION_NODE].
 934                 compile(classGen, methodGen, ihPI);
 935         else
 936             targets[DTM.PROCESSING_INSTRUCTION_NODE] = ihPI;
 937 
 938         // Match on comments - default: process next node
 939         InstructionHandle ihComment = ihLoop;
 940         if (_childNodeTestSeq != null) ihComment = ihElem;
 941         targets[DTM.COMMENT_NODE] = _testSeq[DTM.COMMENT_NODE] != null
 942             ? _testSeq[DTM.COMMENT_NODE].compile(classGen, methodGen, ihComment)
 943             : ihComment;
 944 
 945             // This DOM-type is not in use - default: process next node
 946         targets[DTM.CDATA_SECTION_NODE] = ihLoop;
 947 
 948         // This DOM-type is not in use - default: process next node
 949         targets[DTM.DOCUMENT_FRAGMENT_NODE] = ihLoop;
 950 
 951         // This DOM-type is not in use - default: process next node
 952         targets[DTM.DOCUMENT_TYPE_NODE] = ihLoop;
 953 
 954         // This DOM-type is not in use - default: process next node
 955         targets[DTM.ENTITY_NODE] = ihLoop;
 956 
 957         // This DOM-type is not in use - default: process next node
 958         targets[DTM.ENTITY_REFERENCE_NODE] = ihLoop;
 959 
 960         // This DOM-type is not in use - default: process next node
 961         targets[DTM.NOTATION_NODE] = ihLoop;
 962 
 963 
 964         // Now compile test sequences for various match patterns:
 965         for (int i = DTM.NTYPES; i < targets.length; i++) {
 966             final TestSeq testSeq = _testSeq[i];
 967             // Jump straight to namespace tests ?
 968             if ((testSeq == null) || (isNamespace[i])) {
 969                 if (isAttribute[i])
 970                     targets[i] = attrNamespaceHandle;
 971                 else
 972                     targets[i] = elemNamespaceHandle;
 973             }
 974             // Match on node type
 975             else {
 976                 if (isAttribute[i])
 977                     targets[i] = testSeq.compile(classGen, methodGen,
 978                                                  attrNamespaceHandle);
 979                 else
 980                     targets[i] = testSeq.compile(classGen, methodGen,
 981                                                  elemNamespaceHandle);
 982             }
 983         }
 984 
 985         if (ilKey != null) body.insert(ilKey);
 986 
 987         // Append first code in applyTemplates() - get type of current node
 988         final int getType = cpg.addInterfaceMethodref(DOM_INTF,
 989                                                       "getExpandedTypeID",
 990                                                       "(I)I");
 991         body.append(methodGen.loadDOM());
 992         body.append(new ILOAD(_currentIndex));
 993         body.append(new INVOKEINTERFACE(getType, 2));
 994 
 995         // Append switch() statement - main dispatch loop in applyTemplates()
 996         InstructionHandle disp = body.append(new SWITCH(types, targets, ihLoop));
 997 
 998         // Append all the "case:" statements
 999         appendTestSequences(body);
1000         // Append the actual template code
1001         appendTemplateCode(body);
1002 
1003         // Append NS:* node tests (if any)
1004         if (nsElem != null) body.append(nsElem);
1005         // Append NS:@* node tests (if any)
1006         if (nsAttr != null) body.append(nsAttr);
1007 
1008         // Append default action for element and root nodes
1009         body.append(ilRecurse);
1010         // Append default action for text and attribute nodes
1011         body.append(ilText);
1012 
1013         // putting together constituent instruction lists
1014         mainIL.append(body);
1015         // fall through to ilLoop
1016         mainIL.append(ilLoop);
1017 
1018         peepHoleOptimization(methodGen);
1019         classGen.addMethod(methodGen);
1020 
1021         // Compile method(s) for <xsl:apply-imports/> for this mode
1022         if (_importLevels != null) {
1023             for (Map.Entry<Integer, Integer> entry : _importLevels.entrySet()) {
1024                 compileApplyImports(classGen, entry.getValue(), entry.getKey());
1025             }
1026         }
1027     }
1028 
1029     private void compileTemplateCalls(ClassGenerator classGen,
1030                                       MethodGenerator methodGen,
1031                                       InstructionHandle next, int min, int max){
1032         _neededTemplates.keySet().stream().forEach((template) -> {
1033             final int prec = template.getImportPrecedence();
1034             if ((prec >= min) && (prec < max)) {
1035                 if (template.hasContents()) {
1036                     InstructionList til = template.compile(classGen, methodGen);
1037                     til.append(new GOTO_W(next));
1038                     _templateILs.put(template, til);
1039                     _templateIHs.put(template, til.getStart());
1040                 }
1041                 else {
1042                     // empty template
1043                     _templateIHs.put(template, next);
1044                 }
1045             }
1046         });
1047     }
1048 
1049     @SuppressWarnings({"rawtypes", "unchecked"})
1050     public void compileApplyImports(ClassGenerator classGen, int min, int max) {
1051         final XSLTC xsltc = classGen.getParser().getXSLTC();
1052         final ConstantPoolGen cpg = classGen.getConstantPool();
1053         final List<String> names = xsltc.getNamesIndex();
1054 
1055         // Clear some datastructures
1056         _namedTemplates = new HashMap<>();
1057         _neededTemplates = new HashMap<>();
1058         _templateIHs = new HashMap<>();
1059         _templateILs = new HashMap<>();
1060         _patternGroups = (List<LocationPathPattern>[])new ArrayList[32];
1061         _rootPattern = null;
1062 
1063         // IMPORTANT: Save orignal & complete set of templates!!!!
1064         List<Template> oldTemplates = _templates;
1065 
1066         // Gather templates that are within the scope of this import
1067         _templates = new ArrayList<>();
1068         for (Template template : oldTemplates) {
1069             final int prec = template.getImportPrecedence();
1070             if ((prec >= min) && (prec < max)) addTemplate(template);
1071         }
1072 
1073         // Process all patterns from those templates
1074         processPatterns(_keys);
1075 
1076         // Create the applyTemplates() method
1077         final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
1078             new com.sun.org.apache.bcel.internal.generic.Type[4];
1079         argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
1080         argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
1081         argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
1082         argTypes[3] = com.sun.org.apache.bcel.internal.generic.Type.INT;
1083 
1084         final String[] argNames = new String[4];
1085         argNames[0] = DOCUMENT_PNAME;
1086         argNames[1] = ITERATOR_PNAME;
1087         argNames[2] = TRANSLET_OUTPUT_PNAME;
1088         argNames[3] = NODE_PNAME;
1089 
1090         final InstructionList mainIL = new InstructionList();
1091         final MethodGenerator methodGen =
1092             new MethodGenerator(ACC_PUBLIC | ACC_FINAL,
1093                                 com.sun.org.apache.bcel.internal.generic.Type.VOID,
1094                                 argTypes, argNames, functionName()+'_'+max,
1095                                 getClassName(), mainIL,
1096                                 classGen.getConstantPool());
1097         methodGen.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");
1098 
1099         // Create the local variable to hold the current node
1100         final LocalVariableGen current;
1101         current = methodGen.addLocalVariable2("current",
1102                                               com.sun.org.apache.bcel.internal.generic.Type.INT,
1103                                               null);
1104         _currentIndex = current.getIndex();
1105 
1106         mainIL.append(new ILOAD(methodGen.getLocalIndex(NODE_PNAME)));
1107         current.setStart(mainIL.append(new ISTORE(_currentIndex)));
1108 
1109         // Create the "body" instruction list that will eventually hold the
1110         // code for the entire method (other ILs will be appended).
1111         final InstructionList body = new InstructionList();
1112         body.append(NOP);
1113 
1114         // Create an instruction list that contains the default next-node
1115         // iteration
1116         final InstructionList ilLoop = new InstructionList();
1117         ilLoop.append(RETURN);
1118         final InstructionHandle ihLoop = ilLoop.getStart();
1119 
1120         // Compile default handling of elements (traverse children)
1121         InstructionList ilRecurse =
1122             compileDefaultRecursion(classGen, methodGen, ihLoop);
1123         InstructionHandle ihRecurse = ilRecurse.getStart();
1124 
1125         // Compile default handling of text/attribute nodes (output text)
1126         InstructionList ilText =
1127             compileDefaultText(classGen, methodGen, ihLoop);
1128         InstructionHandle ihText = ilText.getStart();
1129 
1130         // Distinguish attribute/element/namespace tests for further processing
1131         final int[] types = new int[DTM.NTYPES + names.size()];
1132         for (int i = 0; i < types.length; i++) {
1133             types[i] = i;
1134         }
1135 
1136         final boolean[] isAttribute = new boolean[types.length];
1137         final boolean[] isNamespace = new boolean[types.length];
1138         for (int i = 0; i < names.size(); i++) {
1139             final String name = names.get(i);
1140             isAttribute[i+DTM.NTYPES] = isAttributeName(name);
1141             isNamespace[i+DTM.NTYPES] = isNamespaceName(name);
1142         }
1143 
1144         // Compile all templates - regardless of pattern type
1145         compileTemplateCalls(classGen, methodGen, ihLoop, min, max);
1146 
1147         // Handle template with explicit "*" pattern
1148         final TestSeq elemTest = _testSeq[DTM.ELEMENT_NODE];
1149         InstructionHandle ihElem = ihRecurse;
1150         if (elemTest != null) {
1151             ihElem = elemTest.compile(classGen, methodGen, ihLoop);
1152         }
1153 
1154         // Handle template with explicit "@*" pattern
1155         final TestSeq attrTest = _testSeq[DTM.ATTRIBUTE_NODE];
1156         InstructionHandle ihAttr = ihLoop;
1157         if (attrTest != null) {
1158             ihAttr = attrTest.compile(classGen, methodGen, ihAttr);
1159         }
1160 
1161         // Do tests for id() and key() patterns first
1162         InstructionList ilKey = null;
1163         if (_idxTestSeq != null) {
1164             ilKey = _idxTestSeq.getInstructionList();
1165         }
1166 
1167         // If there is a match on node() we need to replace ihElem
1168         // and ihText if the priority of node() is higher
1169         if (_childNodeTestSeq != null) {
1170             // Compare priorities of node() and "*"
1171             double nodePrio = _childNodeTestSeq.getPriority();
1172             int    nodePos  = _childNodeTestSeq.getPosition();
1173             double elemPrio = (0 - Double.MAX_VALUE);
1174             int    elemPos  = Integer.MIN_VALUE;
1175 
1176             if (elemTest != null) {
1177                 elemPrio = elemTest.getPriority();
1178                 elemPos  = elemTest.getPosition();
1179             }
1180 
1181             if (elemPrio == Double.NaN || elemPrio < nodePrio ||
1182                 (elemPrio == nodePrio && elemPos < nodePos))
1183             {
1184                 ihElem = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
1185             }
1186 
1187             // Compare priorities of node() and text()
1188             final TestSeq textTest = _testSeq[DTM.TEXT_NODE];
1189             double textPrio = (0 - Double.MAX_VALUE);
1190             int    textPos  = Integer.MIN_VALUE;
1191 
1192             if (textTest != null) {
1193                 textPrio = textTest.getPriority();
1194                 textPos  = textTest.getPosition();
1195             }
1196 
1197             if (textPrio == Double.NaN || textPrio < nodePrio ||
1198                 (textPrio == nodePrio && textPos < nodePos))
1199             {
1200                 ihText = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
1201                 _testSeq[DTM.TEXT_NODE] = _childNodeTestSeq;
1202             }
1203         }
1204 
1205         // Handle templates with "ns:*" pattern
1206         InstructionHandle elemNamespaceHandle = ihElem;
1207         InstructionList nsElem = compileNamespaces(classGen, methodGen,
1208                                                    isNamespace, isAttribute,
1209                                                    false, ihElem);
1210         if (nsElem != null) elemNamespaceHandle = nsElem.getStart();
1211 
1212         // Handle templates with "ns:@*" pattern
1213         InstructionList nsAttr = compileNamespaces(classGen, methodGen,
1214                                                    isNamespace, isAttribute,
1215                                                    true, ihAttr);
1216         InstructionHandle attrNamespaceHandle = ihAttr;
1217         if (nsAttr != null) attrNamespaceHandle = nsAttr.getStart();
1218 
1219         // Handle templates with "ns:elem" or "ns:@attr" pattern
1220         final InstructionHandle[] targets = new InstructionHandle[types.length];
1221         for (int i = DTM.NTYPES; i < targets.length; i++) {
1222             final TestSeq testSeq = _testSeq[i];
1223             // Jump straight to namespace tests ?
1224             if (isNamespace[i]) {
1225                 if (isAttribute[i])
1226                     targets[i] = attrNamespaceHandle;
1227                 else
1228                     targets[i] = elemNamespaceHandle;
1229             }
1230             // Test first, then jump to namespace tests
1231             else if (testSeq != null) {
1232                 if (isAttribute[i])
1233                     targets[i] = testSeq.compile(classGen, methodGen,
1234                                                  attrNamespaceHandle);
1235                 else
1236                     targets[i] = testSeq.compile(classGen, methodGen,
1237                                                  elemNamespaceHandle);
1238             }
1239             else {
1240                 targets[i] = ihLoop;
1241             }
1242         }
1243 
1244         // Handle pattern with match on root node - default: traverse children
1245         targets[DTM.ROOT_NODE] = _rootPattern != null
1246             ? getTemplateInstructionHandle(_rootPattern.getTemplate())
1247             : ihRecurse;
1248         // Handle pattern with match on root node - default: traverse children
1249         targets[DTM.DOCUMENT_NODE] = _rootPattern != null
1250             ? getTemplateInstructionHandle(_rootPattern.getTemplate())
1251             : ihRecurse;    // %HZ%:  Was ihLoop in XSLTC_DTM branch
1252 
1253         // Handle any pattern with match on text nodes - default: loop
1254         targets[DTM.TEXT_NODE] = _testSeq[DTM.TEXT_NODE] != null
1255             ? _testSeq[DTM.TEXT_NODE].compile(classGen, methodGen, ihText)
1256             : ihText;
1257 
1258         // This DOM-type is not in use - default: process next node
1259         targets[DTM.NAMESPACE_NODE] = ihLoop;
1260 
1261         // Match unknown element in DOM - default: check for namespace match
1262         targets[DTM.ELEMENT_NODE] = elemNamespaceHandle;
1263 
1264         // Match unknown attribute in DOM - default: check for namespace match
1265         targets[DTM.ATTRIBUTE_NODE] = attrNamespaceHandle;
1266 
1267         // Match on processing instruction - default: loop
1268         InstructionHandle ihPI = ihLoop;
1269         if (_childNodeTestSeq != null) ihPI = ihElem;
1270         if (_testSeq[DTM.PROCESSING_INSTRUCTION_NODE] != null) {
1271             targets[DTM.PROCESSING_INSTRUCTION_NODE] =
1272                 _testSeq[DTM.PROCESSING_INSTRUCTION_NODE].
1273                 compile(classGen, methodGen, ihPI);
1274         }
1275         else {
1276             targets[DTM.PROCESSING_INSTRUCTION_NODE] = ihPI;
1277         }
1278 
1279         // Match on comments - default: process next node
1280         InstructionHandle ihComment = ihLoop;
1281         if (_childNodeTestSeq != null) ihComment = ihElem;
1282         targets[DTM.COMMENT_NODE] = _testSeq[DTM.COMMENT_NODE] != null
1283             ? _testSeq[DTM.COMMENT_NODE].compile(classGen, methodGen, ihComment)
1284             : ihComment;
1285 
1286                 // This DOM-type is not in use - default: process next node
1287         targets[DTM.CDATA_SECTION_NODE] = ihLoop;
1288 
1289         // This DOM-type is not in use - default: process next node
1290         targets[DTM.DOCUMENT_FRAGMENT_NODE] = ihLoop;
1291 
1292         // This DOM-type is not in use - default: process next node
1293         targets[DTM.DOCUMENT_TYPE_NODE] = ihLoop;
1294 
1295         // This DOM-type is not in use - default: process next node
1296         targets[DTM.ENTITY_NODE] = ihLoop;
1297 
1298         // This DOM-type is not in use - default: process next node
1299         targets[DTM.ENTITY_REFERENCE_NODE] = ihLoop;
1300 
1301         // This DOM-type is not in use - default: process next node
1302         targets[DTM.NOTATION_NODE] = ihLoop;
1303 
1304 
1305 
1306         // Now compile test sequences for various match patterns:
1307         for (int i = DTM.NTYPES; i < targets.length; i++) {
1308             final TestSeq testSeq = _testSeq[i];
1309             // Jump straight to namespace tests ?
1310             if ((testSeq == null) || (isNamespace[i])) {
1311                 if (isAttribute[i])
1312                     targets[i] = attrNamespaceHandle;
1313                 else
1314                     targets[i] = elemNamespaceHandle;
1315             }
1316             // Match on node type
1317             else {
1318                 if (isAttribute[i])
1319                     targets[i] = testSeq.compile(classGen, methodGen,
1320                                                  attrNamespaceHandle);
1321                 else
1322                     targets[i] = testSeq.compile(classGen, methodGen,
1323                                                  elemNamespaceHandle);
1324             }
1325         }
1326 
1327         if (ilKey != null) body.insert(ilKey);
1328 
1329         // Append first code in applyTemplates() - get type of current node
1330         final int getType = cpg.addInterfaceMethodref(DOM_INTF,
1331                                                       "getExpandedTypeID",
1332                                                       "(I)I");
1333         body.append(methodGen.loadDOM());
1334         body.append(new ILOAD(_currentIndex));
1335         body.append(new INVOKEINTERFACE(getType, 2));
1336 
1337         // Append switch() statement - main dispatch loop in applyTemplates()
1338         InstructionHandle disp = body.append(new SWITCH(types,targets,ihLoop));
1339 
1340         // Append all the "case:" statements
1341         appendTestSequences(body);
1342         // Append the actual template code
1343         appendTemplateCode(body);
1344 
1345         // Append NS:* node tests (if any)
1346         if (nsElem != null) body.append(nsElem);
1347         // Append NS:@* node tests (if any)
1348         if (nsAttr != null) body.append(nsAttr);
1349 
1350         // Append default action for element and root nodes
1351         body.append(ilRecurse);
1352         // Append default action for text and attribute nodes
1353         body.append(ilText);
1354 
1355         // putting together constituent instruction lists
1356         mainIL.append(body);
1357 
1358         // Mark the end of the live range for the "current" variable
1359         current.setEnd(body.getEnd());
1360 
1361         // fall through to ilLoop
1362         mainIL.append(ilLoop);
1363 
1364         peepHoleOptimization(methodGen);
1365         classGen.addMethod(methodGen);
1366 
1367         // Restore original (complete) set of templates for this transformation
1368         _templates = oldTemplates;
1369     }
1370 
1371     /**
1372       * Peephole optimization.
1373       */
1374     private void peepHoleOptimization(MethodGenerator methodGen) {
1375         InstructionList il = methodGen.getInstructionList();
1376         InstructionFinder find = new InstructionFinder(il);
1377         InstructionHandle ih;
1378         String pattern;
1379 
1380         // LoadInstruction, POP => (removed)
1381         // pattern = "LoadInstruction POP";
1382         // changed to lower case - changing to all lower case although only the instruction with capital I
1383         // is creating a problem in the Turkish locale
1384         pattern = "loadinstruction pop";
1385 
1386         for (Iterator<InstructionHandle[]> iter = find.search(pattern); iter.hasNext();) {
1387             InstructionHandle[] match = iter.next();
1388             try {
1389                 if (!match[0].hasTargeters() && !match[1].hasTargeters()) {
1390                     il.delete(match[0], match[1]);
1391                 }
1392             }
1393             catch (TargetLostException e) {
1394                 // TODO: move target down into the list
1395             }
1396         }
1397 
1398         // ILOAD_N, ILOAD_N, SWAP, ISTORE_N => ILOAD_N
1399         // pattern = "ILOAD ILOAD SWAP ISTORE";
1400         // changed to lower case - changing to all lower case although only the instruction with capital I
1401         // is creating a problem in the Turkish locale
1402         pattern = "iload iload swap istore";
1403         for (Iterator<InstructionHandle[]> iter = find.search(pattern); iter.hasNext();) {
1404             InstructionHandle[] match = iter.next();
1405             try {
1406                 com.sun.org.apache.bcel.internal.generic.ILOAD iload1 =
1407                     (com.sun.org.apache.bcel.internal.generic.ILOAD) match[0].getInstruction();
1408                 com.sun.org.apache.bcel.internal.generic.ILOAD iload2 =
1409                     (com.sun.org.apache.bcel.internal.generic.ILOAD) match[1].getInstruction();
1410                 com.sun.org.apache.bcel.internal.generic.ISTORE istore =
1411                     (com.sun.org.apache.bcel.internal.generic.ISTORE) match[3].getInstruction();
1412 
1413                 if (!match[1].hasTargeters() &&
1414                     !match[2].hasTargeters() &&
1415                     !match[3].hasTargeters() &&
1416                     iload1.getIndex() == iload2.getIndex() &&
1417                     iload2.getIndex() == istore.getIndex())
1418                 {
1419                     il.delete(match[1], match[3]);
1420                 }
1421             }
1422             catch (TargetLostException e) {
1423                 // TODO: move target down into the list
1424             }
1425         }
1426 
1427         // LoadInstruction_N, LoadInstruction_M, SWAP => LoadInstruction_M, LoadInstruction_N
1428         // pattern = "LoadInstruction LoadInstruction SWAP";
1429         // changed to lower case - changing to all lower case although only the instruction with capital I
1430         // is creating a problem in the Turkish locale
1431         pattern = "loadinstruction loadinstruction swap";
1432         for (Iterator<InstructionHandle[]> iter = find.search(pattern); iter.hasNext();) {
1433             InstructionHandle[] match = iter.next();
1434             try {
1435                 if (!match[0].hasTargeters() &&
1436                     !match[1].hasTargeters() &&
1437                     !match[2].hasTargeters())
1438                 {
1439                     Instruction load_m = match[1].getInstruction();
1440                     il.insert(match[0], load_m);
1441                     il.delete(match[1], match[2]);
1442                 }
1443             }
1444             catch (TargetLostException e) {
1445                 // TODO: move target down into the list
1446             }
1447         }
1448 
1449         // ALOAD_N ALOAD_N => ALOAD_N DUP
1450         // pattern = "ALOAD ALOAD";
1451         // changed to lower case - changing to all lower case although only the instruction with capital I
1452         // is creating a problem in the Turkish locale
1453         pattern = "aload aload";
1454         for (Iterator<InstructionHandle[]> iter = find.search(pattern); iter.hasNext();) {
1455             InstructionHandle[] match = iter.next();
1456             try {
1457                 if (!match[1].hasTargeters()) {
1458                     com.sun.org.apache.bcel.internal.generic.ALOAD aload1 =
1459                         (com.sun.org.apache.bcel.internal.generic.ALOAD) match[0].getInstruction();
1460                     com.sun.org.apache.bcel.internal.generic.ALOAD aload2 =
1461                         (com.sun.org.apache.bcel.internal.generic.ALOAD) match[1].getInstruction();
1462 
1463                     if (aload1.getIndex() == aload2.getIndex()) {
1464                         il.insert(match[1], new DUP());
1465                         il.delete(match[1]);
1466                     }
1467                 }
1468             }
1469             catch (TargetLostException e) {
1470                 // TODO: move target down into the list
1471             }
1472         }
1473     }
1474 
1475     public InstructionHandle getTemplateInstructionHandle(Template template) {
1476         return _templateIHs.get(template);
1477     }
1478 
1479     /**
1480      * Auxiliary method to determine if a qname is an attribute.
1481      */
1482     private static boolean isAttributeName(String qname) {
1483         final int col = qname.lastIndexOf(':') + 1;
1484         return (qname.charAt(col) == '@');
1485     }
1486 
1487     /**
1488      * Auxiliary method to determine if a qname is a namespace
1489      * qualified "*".
1490      */
1491     private static boolean isNamespaceName(String qname) {
1492         final int col = qname.lastIndexOf(':');
1493         return (col > -1 && qname.charAt(qname.length()-1) == '*');
1494     }
1495 }