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