1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  22 
  23 import java.util.ArrayList;
  24 import java.util.Vector;
  25 
  26 import com.sun.org.apache.bcel.internal.classfile.Field;
  27 import com.sun.org.apache.bcel.internal.generic.ALOAD;
  28 import com.sun.org.apache.bcel.internal.generic.ANEWARRAY;
  29 import com.sun.org.apache.bcel.internal.generic.ASTORE;
  30 import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
  31 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  32 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
  33 import com.sun.org.apache.bcel.internal.generic.ILOAD;
  34 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  35 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  36 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  37 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  38 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  39 import com.sun.org.apache.bcel.internal.generic.NEW;
  40 import com.sun.org.apache.bcel.internal.generic.NOP;
  41 import com.sun.org.apache.bcel.internal.generic.PUSH;
  42 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
  43 import com.sun.org.apache.bcel.internal.generic.TABLESWITCH;
  44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.CompareGenerator;
  46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
  48 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  49 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSortRecordFactGenerator;
  50 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSortRecordGenerator;
  51 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
  52 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  53 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  54 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  55 import com.sun.org.apache.xml.internal.dtm.Axis;
  56 
  57 
  58 /**
  59  * @author Jacek Ambroziak
  60  * @author Santiago Pericas-Geertsen
  61  * @author Morten Jorgensen
  62  */
  63 final class Sort extends Instruction implements Closure {
  64 
  65     private Expression     _select;
  66     private AttributeValue _order;
  67     private AttributeValue _caseOrder;
  68     private AttributeValue _dataType;
  69     private AttributeValue _lang; // bug! see 26869, see XALANJ-2546
  70 
  71     private String _className = null;
  72     private ArrayList<VariableRefBase> _closureVars = null;
  73     private boolean _needsSortRecordFactory = false;
  74 
  75     // -- Begin Closure interface --------------------
  76 
  77     /**
  78      * Returns true if this closure is compiled in an inner class (i.e.
  79      * if this is a real closure).
  80      */
  81     public boolean inInnerClass() {
  82         return (_className != null);
  83     }
  84 
  85     /**
  86      * Returns a reference to its parent closure or null if outermost.
  87      */
  88     public Closure getParentClosure() {
  89         return null;
  90     }
  91 
  92     /**
  93      * Returns the name of the auxiliary class or null if this predicate
  94      * is compiled inside the Translet.
  95      */
  96     public String getInnerClassName() {
  97         return _className;
  98     }
  99 
 100     /**
 101      * Add new variable to the closure.
 102      */
 103     public void addVariable(VariableRefBase variableRef) {
 104         if (_closureVars == null) {
 105             _closureVars = new ArrayList<>();
 106         }
 107 
 108         // Only one reference per variable
 109         if (!_closureVars.contains(variableRef)) {
 110             _closureVars.add(variableRef);
 111             _needsSortRecordFactory = true;
 112         }
 113     }
 114 
 115     // -- End Closure interface ----------------------
 116 
 117     private void setInnerClassName(String className) {
 118         _className = className;
 119     }
 120 
 121     /**
 122      * Parse the attributes of the xsl:sort element
 123      */
 124     public void parseContents(Parser parser) {
 125 
 126         final SyntaxTreeNode parent = getParent();
 127         if (!(parent instanceof ApplyTemplates) &&
 128             !(parent instanceof ForEach)) {
 129             reportError(this, parser, ErrorMsg.STRAY_SORT_ERR, null);
 130             return;
 131         }
 132 
 133         // Parse the select expression (node string value if no expression)
 134         _select = parser.parseExpression(this, "select", "string(.)");
 135 
 136         // Get the sort order; default is 'ascending'
 137         String val = getAttribute("order");
 138         if (val.length() == 0) val = "ascending";
 139         _order = AttributeValue.create(this, val, parser);
 140 
 141         // Get the sort data type; default is text
 142         val = getAttribute("data-type");
 143         if (val.length() == 0) {
 144             try {
 145                 final Type type = _select.typeCheck(parser.getSymbolTable());
 146                 if (type instanceof IntType)
 147                     val = "number";
 148                 else
 149                     val = "text";
 150             }
 151             catch (TypeCheckError e) {
 152                 val = "text";
 153             }
 154         }
 155         _dataType = AttributeValue.create(this, val, parser);
 156 
 157         val =  getAttribute("lang");
 158         _lang = AttributeValue.create(this, val, parser);
 159         // Get the case order; default is language dependant
 160         val = getAttribute("case-order");
 161         _caseOrder = AttributeValue.create(this, val, parser);
 162     }
 163 
 164     /**
 165      * Run type checks on the attributes; expression must return a string
 166      * which we will use as a sort key
 167      */
 168     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
 169         final Type tselect = _select.typeCheck(stable);
 170 
 171         // If the sort data-type is not set we use the natural data-type
 172         // of the data we will sort
 173         if (!(tselect instanceof StringType)) {
 174             _select = new CastExpr(_select, Type.String);
 175         }
 176 
 177         _order.typeCheck(stable);
 178         _caseOrder.typeCheck(stable);
 179         _dataType.typeCheck(stable);
 180         _lang.typeCheck(stable);
 181         return Type.Void;
 182     }
 183 
 184     /**
 185      * These two methods are needed in the static methods that compile the
 186      * overloaded NodeSortRecord.compareType() and NodeSortRecord.sortOrder()
 187      */
 188     public void translateSortType(ClassGenerator classGen,
 189                                   MethodGenerator methodGen) {
 190         _dataType.translate(classGen, methodGen);
 191     }
 192 
 193     public void translateSortOrder(ClassGenerator classGen,
 194                                    MethodGenerator methodGen) {
 195         _order.translate(classGen, methodGen);
 196     }
 197 
 198     public void translateCaseOrder(ClassGenerator classGen,
 199                    MethodGenerator methodGen) {
 200         _caseOrder.translate(classGen, methodGen);
 201     }
 202 
 203     public void translateLang(ClassGenerator classGen,
 204                    MethodGenerator methodGen) {
 205         _lang.translate(classGen, methodGen);
 206     }
 207 
 208     /**
 209      * This method compiles code for the select expression for this
 210      * xsl:sort element. The method is called from the static code-generating
 211      * methods in this class.
 212      */
 213     public void translateSelect(ClassGenerator classGen,
 214                                 MethodGenerator methodGen) {
 215         _select.translate(classGen,methodGen);
 216     }
 217 
 218     /**
 219      * This method should not produce any code
 220      */
 221     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
 222         // empty
 223     }
 224 
 225     /**
 226      * Compiles code that instantiates a SortingIterator object.
 227      * This object's constructor needs referencdes to the current iterator
 228      * and a node sort record producing objects as its parameters.
 229      */
 230     public static void translateSortIterator(ClassGenerator classGen,
 231                                       MethodGenerator methodGen,
 232                                       Expression nodeSet,
 233                                       Vector<Sort> sortObjects)
 234     {
 235         final ConstantPoolGen cpg = classGen.getConstantPool();
 236         final InstructionList il = methodGen.getInstructionList();
 237 
 238         // SortingIterator.SortingIterator(NodeIterator,NodeSortRecordFactory);
 239         final int init = cpg.addMethodref(SORT_ITERATOR, "<init>",
 240                                           "("
 241                                           + NODE_ITERATOR_SIG
 242                                           + NODE_SORT_FACTORY_SIG
 243                                           + ")V");
 244 
 245         // Backwards branches are prohibited if an uninitialized object is
 246         // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed.
 247         // We don't know whether this code might contain backwards branches
 248         // so we mustn't create the new object until after we've created
 249         // the suspect arguments to its constructor.  Instead we calculate
 250         // the values of the arguments to the constructor first, store them
 251         // in temporary variables, create the object and reload the
 252         // arguments from the temporaries to avoid the problem.
 253 
 254         LocalVariableGen nodesTemp =
 255             methodGen.addLocalVariable("sort_tmp1",
 256                                        Util.getJCRefType(NODE_ITERATOR_SIG),
 257                                        null, null);
 258 
 259         LocalVariableGen sortRecordFactoryTemp =
 260             methodGen.addLocalVariable("sort_tmp2",
 261                                       Util.getJCRefType(NODE_SORT_FACTORY_SIG),
 262                                       null, null);
 263 
 264         // Get the current node iterator
 265         if (nodeSet == null) {  // apply-templates default
 266             final int children = cpg.addInterfaceMethodref(DOM_INTF,
 267                                                            "getAxisIterator",
 268                                                            "(I)"+
 269                                                            NODE_ITERATOR_SIG);
 270             il.append(methodGen.loadDOM());
 271             il.append(new PUSH(cpg, Axis.CHILD));
 272             il.append(new INVOKEINTERFACE(children, 2));
 273         }
 274         else {
 275             nodeSet.translate(classGen, methodGen);
 276         }
 277 
 278         nodesTemp.setStart(il.append(new ASTORE(nodesTemp.getIndex())));
 279 
 280         // Compile the code for the NodeSortRecord producing class and pass
 281         // that as the last argument to the SortingIterator constructor.
 282         compileSortRecordFactory(sortObjects, classGen, methodGen);
 283         sortRecordFactoryTemp.setStart(
 284                 il.append(new ASTORE(sortRecordFactoryTemp.getIndex())));
 285 
 286         il.append(new NEW(cpg.addClass(SORT_ITERATOR)));
 287         il.append(DUP);
 288         nodesTemp.setEnd(il.append(new ALOAD(nodesTemp.getIndex())));
 289         sortRecordFactoryTemp.setEnd(
 290                 il.append(new ALOAD(sortRecordFactoryTemp.getIndex())));
 291         il.append(new INVOKESPECIAL(init));
 292     }
 293 
 294 
 295     /**
 296      * Compiles code that instantiates a NodeSortRecordFactory object which
 297      * will produce NodeSortRecord objects of a specific type.
 298      */
 299     public static void compileSortRecordFactory(Vector<Sort> sortObjects,
 300         ClassGenerator classGen, MethodGenerator methodGen)
 301     {
 302         String sortRecordClass =
 303             compileSortRecord(sortObjects, classGen, methodGen);
 304 
 305         boolean needsSortRecordFactory = false;
 306         final int nsorts = sortObjects.size();
 307         for (int i = 0; i < nsorts; i++) {
 308             final Sort sort = sortObjects.elementAt(i);
 309             needsSortRecordFactory |= sort._needsSortRecordFactory;
 310         }
 311 
 312         String sortRecordFactoryClass = NODE_SORT_FACTORY;
 313         if (needsSortRecordFactory) {
 314             sortRecordFactoryClass =
 315                 compileSortRecordFactory(sortObjects, classGen, methodGen,
 316                     sortRecordClass);
 317         }
 318 
 319         final ConstantPoolGen cpg = classGen.getConstantPool();
 320         final InstructionList il = methodGen.getInstructionList();
 321 
 322         // Backwards branches are prohibited if an uninitialized object is
 323         // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed.
 324         // We don't know whether this code might contain backwards branches
 325         // so we mustn't create the new object until after we've created
 326         // the suspect arguments to its constructor.  Instead we calculate
 327         // the values of the arguments to the constructor first, store them
 328         // in temporary variables, create the object and reload the
 329         // arguments from the temporaries to avoid the problem.
 330 
 331         // Compile code that initializes the static _sortOrder
 332         LocalVariableGen sortOrderTemp
 333                  = methodGen.addLocalVariable("sort_order_tmp",
 334                                       Util.getJCRefType("[" + STRING_SIG),
 335                                       null, null);
 336         il.append(new PUSH(cpg, nsorts));
 337         il.append(new ANEWARRAY(cpg.addClass(STRING)));
 338         for (int level = 0; level < nsorts; level++) {
 339             final Sort sort = (Sort)sortObjects.elementAt(level);
 340             il.append(DUP);
 341             il.append(new PUSH(cpg, level));
 342             sort.translateSortOrder(classGen, methodGen);
 343             il.append(AASTORE);
 344         }
 345         sortOrderTemp.setStart(il.append(new ASTORE(sortOrderTemp.getIndex())));
 346 
 347         LocalVariableGen sortTypeTemp
 348                  = methodGen.addLocalVariable("sort_type_tmp",
 349                                       Util.getJCRefType("[" + STRING_SIG),
 350                                       null, null);
 351         il.append(new PUSH(cpg, nsorts));
 352         il.append(new ANEWARRAY(cpg.addClass(STRING)));
 353         for (int level = 0; level < nsorts; level++) {
 354             final Sort sort = (Sort)sortObjects.elementAt(level);
 355             il.append(DUP);
 356             il.append(new PUSH(cpg, level));
 357             sort.translateSortType(classGen, methodGen);
 358             il.append(AASTORE);
 359         }
 360         sortTypeTemp.setStart(il.append(new ASTORE(sortTypeTemp.getIndex())));
 361 
 362         LocalVariableGen sortLangTemp
 363                  = methodGen.addLocalVariable("sort_lang_tmp",
 364                                       Util.getJCRefType("[" + STRING_SIG),
 365                                       null, null);
 366         il.append(new PUSH(cpg, nsorts));
 367         il.append(new ANEWARRAY(cpg.addClass(STRING)));
 368         for (int level = 0; level < nsorts; level++) {
 369               final Sort sort = (Sort)sortObjects.elementAt(level);
 370               il.append(DUP);
 371               il.append(new PUSH(cpg, level));
 372               sort.translateLang(classGen, methodGen);
 373               il.append(AASTORE);
 374         }
 375         sortLangTemp.setStart(il.append(new ASTORE(sortLangTemp.getIndex())));
 376 
 377         LocalVariableGen sortCaseOrderTemp
 378                  = methodGen.addLocalVariable("sort_case_order_tmp",
 379                                       Util.getJCRefType("[" + STRING_SIG),
 380                                       null, null);
 381         il.append(new PUSH(cpg, nsorts));
 382         il.append(new ANEWARRAY(cpg.addClass(STRING)));
 383         for (int level = 0; level < nsorts; level++) {
 384             final Sort sort = (Sort)sortObjects.elementAt(level);
 385             il.append(DUP);
 386             il.append(new PUSH(cpg, level));
 387             sort.translateCaseOrder(classGen, methodGen);
 388             il.append(AASTORE);
 389         }
 390         sortCaseOrderTemp.setStart(
 391                 il.append(new ASTORE(sortCaseOrderTemp.getIndex())));
 392 
 393         il.append(new NEW(cpg.addClass(sortRecordFactoryClass)));
 394         il.append(DUP);
 395         il.append(methodGen.loadDOM());
 396         il.append(new PUSH(cpg, sortRecordClass));
 397         il.append(classGen.loadTranslet());
 398 
 399         sortOrderTemp.setEnd(il.append(new ALOAD(sortOrderTemp.getIndex())));
 400         sortTypeTemp.setEnd(il.append(new ALOAD(sortTypeTemp.getIndex())));
 401         sortLangTemp.setEnd(il.append(new ALOAD(sortLangTemp.getIndex())));
 402         sortCaseOrderTemp.setEnd(
 403                 il.append(new ALOAD(sortCaseOrderTemp.getIndex())));
 404 
 405         il.append(new INVOKESPECIAL(
 406             cpg.addMethodref(sortRecordFactoryClass, "<init>",
 407                 "(" + DOM_INTF_SIG
 408                     + STRING_SIG
 409                     + TRANSLET_INTF_SIG
 410                     + "[" + STRING_SIG
 411                     + "[" + STRING_SIG
 412                     + "[" + STRING_SIG
 413                     + "[" + STRING_SIG + ")V")));
 414 
 415         // Initialize closure variables in sortRecordFactory
 416         final ArrayList<VariableRefBase> dups = new ArrayList<>();
 417 
 418         for (int j = 0; j < nsorts; j++) {
 419             final Sort sort = (Sort) sortObjects.get(j);
 420             final int length = (sort._closureVars == null) ? 0 :
 421                 sort._closureVars.size();
 422 
 423             for (int i = 0; i < length; i++) {
 424                 VariableRefBase varRef = sort._closureVars.get(i);
 425 
 426                 // Discard duplicate variable references
 427                 if (dups.contains(varRef)) continue;
 428 
 429                 final VariableBase var = varRef.getVariable();
 430 
 431                 // Store variable in new closure
 432                 il.append(DUP);
 433                 il.append(var.loadInstruction());
 434                 il.append(new PUTFIELD(
 435                         cpg.addFieldref(sortRecordFactoryClass, var.getEscapedName(),
 436                             var.getType().toSignature())));
 437                 dups.add(varRef);
 438             }
 439         }
 440     }
 441 
 442     public static String compileSortRecordFactory(Vector<Sort> sortObjects,
 443         ClassGenerator classGen, MethodGenerator methodGen,
 444         String sortRecordClass)
 445     {
 446         final XSLTC xsltc = (sortObjects.firstElement()).getXSLTC();
 447         final String className = xsltc.getHelperClassName();
 448 
 449         final NodeSortRecordFactGenerator sortRecordFactory =
 450             new NodeSortRecordFactGenerator(className,
 451                                         NODE_SORT_FACTORY,
 452                                         className + ".java",
 453                                         ACC_PUBLIC | ACC_SUPER | ACC_FINAL,
 454                                         new String[] {},
 455                                         classGen.getStylesheet());
 456 
 457         ConstantPoolGen cpg = sortRecordFactory.getConstantPool();
 458 
 459         // Add a new instance variable for each var in closure
 460         final int nsorts = sortObjects.size();
 461         final ArrayList<VariableRefBase> dups = new ArrayList<>();
 462 
 463         for (int j = 0; j < nsorts; j++) {
 464             final Sort sort = sortObjects.get(j);
 465             final int length = (sort._closureVars == null) ? 0 :
 466                 sort._closureVars.size();
 467 
 468             for (int i = 0; i < length; i++) {
 469                 final VariableRefBase varRef = sort._closureVars.get(i);
 470 
 471                 // Discard duplicate variable references
 472                 if (dups.contains(varRef)) continue;
 473 
 474                 final VariableBase var = varRef.getVariable();
 475                 sortRecordFactory.addField(new Field(ACC_PUBLIC,
 476                                            cpg.addUtf8(var.getEscapedName()),
 477                                            cpg.addUtf8(var.getType().toSignature()),
 478                                            null, cpg.getConstantPool()));
 479                 dups.add(varRef);
 480             }
 481         }
 482 
 483         // Define a constructor for this class
 484         final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
 485             new com.sun.org.apache.bcel.internal.generic.Type[7];
 486         argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
 487         argTypes[1] = Util.getJCRefType(STRING_SIG);
 488         argTypes[2] = Util.getJCRefType(TRANSLET_INTF_SIG);
 489         argTypes[3] = Util.getJCRefType("[" + STRING_SIG);
 490         argTypes[4] = Util.getJCRefType("[" + STRING_SIG);
 491   argTypes[5] = Util.getJCRefType("[" + STRING_SIG);
 492   argTypes[6] = Util.getJCRefType("[" + STRING_SIG);
 493 
 494         final String[] argNames = new String[7];
 495         argNames[0] = DOCUMENT_PNAME;
 496         argNames[1] = "className";
 497         argNames[2] = TRANSLET_PNAME;
 498         argNames[3] = "order";
 499         argNames[4] = "type";
 500   argNames[5] = "lang";
 501   argNames[6] = "case_order";
 502 
 503 
 504         InstructionList il = new InstructionList();
 505         final MethodGenerator constructor =
 506             new MethodGenerator(ACC_PUBLIC,
 507                                 com.sun.org.apache.bcel.internal.generic.Type.VOID,
 508                                 argTypes, argNames, "<init>",
 509                                 className, il, cpg);
 510 
 511         // Push all parameters onto the stack and called super.<init>()
 512         il.append(ALOAD_0);
 513         il.append(ALOAD_1);
 514         il.append(ALOAD_2);
 515         il.append(new ALOAD(3));
 516         il.append(new ALOAD(4));
 517         il.append(new ALOAD(5));
 518   il.append(new ALOAD(6));
 519   il.append(new ALOAD(7));
 520         il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_FACTORY,
 521             "<init>",
 522             "(" + DOM_INTF_SIG
 523                 + STRING_SIG
 524                 + TRANSLET_INTF_SIG
 525                 + "[" + STRING_SIG
 526     + "[" + STRING_SIG
 527     + "[" + STRING_SIG
 528                 + "[" + STRING_SIG + ")V")));
 529         il.append(RETURN);
 530 
 531         // Override the definition of makeNodeSortRecord()
 532         il = new InstructionList();
 533         final MethodGenerator makeNodeSortRecord =
 534             new MethodGenerator(ACC_PUBLIC,
 535                 Util.getJCRefType(NODE_SORT_RECORD_SIG),
 536                 new com.sun.org.apache.bcel.internal.generic.Type[] {
 537                     com.sun.org.apache.bcel.internal.generic.Type.INT,
 538                     com.sun.org.apache.bcel.internal.generic.Type.INT },
 539                 new String[] { "node", "last" }, "makeNodeSortRecord",
 540                 className, il, cpg);
 541 
 542         il.append(ALOAD_0);
 543         il.append(ILOAD_1);
 544         il.append(ILOAD_2);
 545         il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_FACTORY,
 546             "makeNodeSortRecord", "(II)" + NODE_SORT_RECORD_SIG)));
 547         il.append(DUP);
 548         il.append(new CHECKCAST(cpg.addClass(sortRecordClass)));
 549 
 550         // Initialize closure in record class
 551         final int ndups = dups.size();
 552         for (int i = 0; i < ndups; i++) {
 553             final VariableRefBase varRef = (VariableRefBase) dups.get(i);
 554             final VariableBase var = varRef.getVariable();
 555             final Type varType = var.getType();
 556 
 557             il.append(DUP);
 558 
 559             // Get field from factory class
 560             il.append(ALOAD_0);
 561             il.append(new GETFIELD(
 562                 cpg.addFieldref(className,
 563                     var.getEscapedName(), varType.toSignature())));
 564 
 565             // Put field in record class
 566             il.append(new PUTFIELD(
 567                 cpg.addFieldref(sortRecordClass,
 568                     var.getEscapedName(), varType.toSignature())));
 569         }
 570         il.append(POP);
 571         il.append(ARETURN);
 572 
 573         constructor.setMaxLocals();
 574         constructor.setMaxStack();
 575         sortRecordFactory.addMethod(constructor);
 576         makeNodeSortRecord.setMaxLocals();
 577         makeNodeSortRecord.setMaxStack();
 578         sortRecordFactory.addMethod(makeNodeSortRecord);
 579         xsltc.dumpClass(sortRecordFactory.getJavaClass());
 580 
 581         return className;
 582     }
 583 
 584     /**
 585      * Create a new auxillary class extending NodeSortRecord.
 586      */
 587     private static String compileSortRecord(Vector<Sort> sortObjects,
 588                                             ClassGenerator classGen,
 589                                             MethodGenerator methodGen) {
 590         final XSLTC  xsltc = sortObjects.firstElement().getXSLTC();
 591         final String className = xsltc.getHelperClassName();
 592 
 593         // This generates a new class for handling this specific sort
 594         final NodeSortRecordGenerator sortRecord =
 595             new NodeSortRecordGenerator(className,
 596                                         NODE_SORT_RECORD,
 597                                         "sort$0.java",
 598                                         ACC_PUBLIC | ACC_SUPER | ACC_FINAL,
 599                                         new String[] {},
 600                                         classGen.getStylesheet());
 601 
 602         final ConstantPoolGen cpg = sortRecord.getConstantPool();
 603 
 604         // Add a new instance variable for each var in closure
 605         final int nsorts = sortObjects.size();
 606         final ArrayList<VariableRefBase> dups = new ArrayList<>();
 607 
 608         for (int j = 0; j < nsorts; j++) {
 609             final Sort sort = sortObjects.get(j);
 610 
 611             // Set the name of the inner class in this sort object
 612             sort.setInnerClassName(className);
 613 
 614             final int length = (sort._closureVars == null) ? 0 :
 615                 sort._closureVars.size();
 616             for (int i = 0; i < length; i++) {
 617                 final VariableRefBase varRef = (VariableRefBase) sort._closureVars.get(i);
 618 
 619                 // Discard duplicate variable references
 620                 if (dups.contains(varRef)) continue;
 621 
 622                 final VariableBase var = varRef.getVariable();
 623                 sortRecord.addField(new Field(ACC_PUBLIC,
 624                                     cpg.addUtf8(var.getEscapedName()),
 625                                     cpg.addUtf8(var.getType().toSignature()),
 626                                     null, cpg.getConstantPool()));
 627                 dups.add(varRef);
 628             }
 629         }
 630 
 631         MethodGenerator init = compileInit(sortRecord, cpg, className);
 632         MethodGenerator extract = compileExtract(sortObjects, sortRecord,
 633                                         cpg, className);
 634         sortRecord.addMethod(init);
 635         sortRecord.addMethod(extract);
 636 
 637         xsltc.dumpClass(sortRecord.getJavaClass());
 638         return className;
 639     }
 640 
 641     /**
 642      * Create a constructor for the new class. Updates the reference to the
 643      * collator in the super calls only when the stylesheet specifies a new
 644      * language in xsl:sort.
 645      */
 646     private static MethodGenerator compileInit(NodeSortRecordGenerator sortRecord,
 647                                            ConstantPoolGen cpg,
 648                                            String className)
 649     {
 650         final InstructionList il = new InstructionList();
 651         final MethodGenerator init =
 652             new MethodGenerator(ACC_PUBLIC,
 653                                 com.sun.org.apache.bcel.internal.generic.Type.VOID,
 654                                 null, null, "<init>", className,
 655                                 il, cpg);
 656 
 657         // Call the constructor in the NodeSortRecord superclass
 658         il.append(ALOAD_0);
 659         il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_RECORD,
 660                                                      "<init>", "()V")));
 661 
 662 
 663 
 664         il.append(RETURN);
 665 
 666         return init;
 667     }
 668 
 669 
 670     /**
 671      * Compiles a method that overloads NodeSortRecord.extractValueFromDOM()
 672      */
 673     private static MethodGenerator compileExtract(Vector<Sort> sortObjects,
 674                                          NodeSortRecordGenerator sortRecord,
 675                                          ConstantPoolGen cpg,
 676                                          String className) {
 677         final InstructionList il = new InstructionList();
 678 
 679         // String NodeSortRecord.extractValueFromDOM(dom,node,level);
 680         final CompareGenerator extractMethod =
 681             new CompareGenerator(ACC_PUBLIC | ACC_FINAL,
 682                                  com.sun.org.apache.bcel.internal.generic.Type.STRING,
 683                                  new com.sun.org.apache.bcel.internal.generic.Type[] {
 684                                      Util.getJCRefType(DOM_INTF_SIG),
 685                                      com.sun.org.apache.bcel.internal.generic.Type.INT,
 686                                      com.sun.org.apache.bcel.internal.generic.Type.INT,
 687                                      Util.getJCRefType(TRANSLET_SIG),
 688                                      com.sun.org.apache.bcel.internal.generic.Type.INT
 689                                  },
 690                                  new String[] { "dom",
 691                                                 "current",
 692                                                 "level",
 693                                                 "translet",
 694                                                 "last"
 695                                  },
 696                                  "extractValueFromDOM", className, il, cpg);
 697 
 698         // Values needed for the switch statement
 699         final int levels = sortObjects.size();
 700         final int match[] = new int[levels];
 701         final InstructionHandle target[] = new InstructionHandle[levels];
 702         InstructionHandle tblswitch = null;
 703 
 704         // Compile switch statement only if the key has multiple levels
 705         if (levels > 1) {
 706             // Put the parameter to the swtich statement on the stack
 707             il.append(new ILOAD(extractMethod.getLocalIndex("level")));
 708             // Append the switch statement here later on
 709             tblswitch = il.append(new NOP());
 710         }
 711 
 712         // Append all the cases for the switch statment
 713         for (int level = 0; level < levels; level++) {
 714             match[level] = level;
 715             final Sort sort = sortObjects.elementAt(level);
 716             target[level] = il.append(NOP);
 717             sort.translateSelect(sortRecord, extractMethod);
 718             il.append(ARETURN);
 719         }
 720 
 721         // Compile def. target for switch statement if key has multiple levels
 722         if (levels > 1) {
 723             // Append the default target - it will _NEVER_ be reached
 724             InstructionHandle defaultTarget =
 725                 il.append(new PUSH(cpg, EMPTYSTRING));
 726             il.insert(tblswitch,new TABLESWITCH(match, target, defaultTarget));
 727             il.append(ARETURN);
 728         }
 729 
 730         return extractMethod;
 731     }
 732 }