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 }