1 /*
2  * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Licensed to the Apache Software Foundation (ASF) under one or more
6  * contributor license agreements.  See the NOTICE file distributed with
7  * this work for additional information regarding copyright ownership.
8  * The ASF licenses this file to You under the Apache License, Version 2.0
9  * (the "License"); you may not use this file except in compliance with
10  * the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package com.sun.org.apache.xalan.internal.xsltc.compiler;
22 
23 import com.sun.org.apache.bcel.internal.classfile.Field;
24 import com.sun.org.apache.bcel.internal.generic.ALOAD;
25 import com.sun.org.apache.bcel.internal.generic.ANEWARRAY;
26 import com.sun.org.apache.bcel.internal.generic.ASTORE;
27 import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
28 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
29 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
30 import com.sun.org.apache.bcel.internal.generic.ILOAD;
31 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
32 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
33 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
34 import com.sun.org.apache.bcel.internal.generic.InstructionList;
35 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
36 import com.sun.org.apache.bcel.internal.generic.NEW;
37 import com.sun.org.apache.bcel.internal.generic.NOP;
38 import com.sun.org.apache.bcel.internal.generic.PUSH;
39 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
40 import com.sun.org.apache.bcel.internal.generic.TABLESWITCH;
41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.CompareGenerator;
43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSortRecordFactGenerator;
47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSortRecordGenerator;
48 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
49 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
50 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
51 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
52 import com.sun.org.apache.xml.internal.dtm.Axis;
53 import java.util.ArrayList;
54 import java.util.List;
55 
56 
57 /**
58  * @author Jacek Ambroziak
59  * @author Santiago Pericas-Geertsen
60  * @author Morten Jorgensen
61  * @LastModified: Nov 2017
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 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      */
inInnerClass()81     public boolean inInnerClass() {
82         return (_className != null);
83     }
84 
85     /**
86      * Returns a reference to its parent closure or null if outermost.
87      */
getParentClosure()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      */
getInnerClassName()96     public String getInnerClassName() {
97         return _className;
98     }
99 
100     /**
101      * Add new variable to the closure.
102      */
addVariable(VariableRefBase variableRef)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 
setInnerClassName(String className)117     private void setInnerClassName(String className) {
118         _className = className;
119     }
120 
121     /**
122      * Parse the attributes of the xsl:sort element
123      */
parseContents(Parser parser)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      */
typeCheck(SymbolTable stable)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      */
translateSortType(ClassGenerator classGen, MethodGenerator methodGen)188     public void translateSortType(ClassGenerator classGen,
189                                   MethodGenerator methodGen) {
190         _dataType.translate(classGen, methodGen);
191     }
192 
translateSortOrder(ClassGenerator classGen, MethodGenerator methodGen)193     public void translateSortOrder(ClassGenerator classGen,
194                                    MethodGenerator methodGen) {
195         _order.translate(classGen, methodGen);
196     }
197 
translateCaseOrder(ClassGenerator classGen, MethodGenerator methodGen)198     public void translateCaseOrder(ClassGenerator classGen,
199                    MethodGenerator methodGen) {
200         _caseOrder.translate(classGen, methodGen);
201     }
202 
translateLang(ClassGenerator classGen, MethodGenerator methodGen)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      */
translateSelect(ClassGenerator classGen, MethodGenerator methodGen)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      */
translate(ClassGenerator classGen, MethodGenerator methodGen)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      */
translateSortIterator(ClassGenerator classGen, MethodGenerator methodGen, Expression nodeSet, List<Sort> sortObjects)230     public static void translateSortIterator(ClassGenerator classGen,
231                                       MethodGenerator methodGen,
232                                       Expression nodeSet,
233                                       List<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      */
compileSortRecordFactory(List<Sort> sortObjects, ClassGenerator classGen, MethodGenerator methodGen)299     public static void compileSortRecordFactory(List<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.get(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 = sortObjects.get(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 = sortObjects.get(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 = sortObjects.get(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 = sortObjects.get(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 List<VariableRefBase> dups = new ArrayList<>();
417 
418         for (int j = 0; j < nsorts; j++) {
419             final 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 
compileSortRecordFactory(List<Sort> sortObjects, ClassGenerator classGen, MethodGenerator methodGen, String sortRecordClass)442     public static String compileSortRecordFactory(List<Sort> sortObjects,
443         ClassGenerator classGen, MethodGenerator methodGen,
444         String sortRecordClass)
445     {
446         final XSLTC xsltc = (sortObjects.get(0)).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 List<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 = 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      */
compileSortRecord(List<Sort> sortObjects, ClassGenerator classGen, MethodGenerator methodGen)587     private static String compileSortRecord(List<Sort> sortObjects,
588                                             ClassGenerator classGen,
589                                             MethodGenerator methodGen) {
590         final XSLTC  xsltc = sortObjects.get(0).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 List<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 = 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      */
compileInit(NodeSortRecordGenerator sortRecord, ConstantPoolGen cpg, String className)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      */
compileExtract(List<Sort> sortObjects, NodeSortRecordGenerator sortRecord, ConstantPoolGen cpg, String className)673     private static MethodGenerator compileExtract(List<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.get(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 }
733