1 /*
2  * Copyright (c) 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.ASTORE;
26 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
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.D2I;
30 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
31 import com.sun.org.apache.bcel.internal.generic.GOTO;
32 import com.sun.org.apache.bcel.internal.generic.IFNONNULL;
33 import com.sun.org.apache.bcel.internal.generic.ILOAD;
34 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
35 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
36 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
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.PUSH;
41 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MatchGenerator;
44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeCounterGenerator;
46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.RealType;
47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
48 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
49 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
50 import java.util.ArrayList;
51 import java.util.List;
52 
53 /**
54  * @author Jacek Ambroziak
55  * @author Santiago Pericas-Geertsen
56  * @LastModified: Nov 2017
57  */
58 final class Number extends Instruction implements Closure {
59     private static final int LEVEL_SINGLE   = 0;
60     private static final int LEVEL_MULTIPLE = 1;
61     private static final int LEVEL_ANY      = 2;
62 
63     static final private String[] ClassNames = {
64         "com.sun.org.apache.xalan.internal.xsltc.dom.SingleNodeCounter",          // LEVEL_SINGLE
65         "com.sun.org.apache.xalan.internal.xsltc.dom.MultipleNodeCounter", // LEVEL_MULTIPLE
66         "com.sun.org.apache.xalan.internal.xsltc.dom.AnyNodeCounter"      // LEVEL_ANY
67     };
68 
69     static final private String[] FieldNames = {
70         "___single_node_counter",                  // LEVEL_SINGLE
71         "___multiple_node_counter",                // LEVEL_MULTIPLE
72         "___any_node_counter"                      // LEVEL_ANY
73     };
74 
75     private Pattern _from = null;
76     private Pattern _count = null;
77     private Expression _value = null;
78 
79     private AttributeValueTemplate _lang = null;
80     private AttributeValueTemplate _format = null;
81     private AttributeValueTemplate _letterValue = null;
82     private AttributeValueTemplate _groupingSeparator = null;
83     private AttributeValueTemplate _groupingSize = null;
84 
85     private int _level = LEVEL_SINGLE;
86     private boolean _formatNeeded = false;
87 
88     private String _className = null;
89     private List<VariableRefBase> _closureVars = null;
90 
91      // -- Begin Closure interface --------------------
92 
93     /**
94      * Returns true if this closure is compiled in an inner class (i.e.
95      * if this is a real closure).
96      */
inInnerClass()97     public boolean inInnerClass() {
98         return (_className != null);
99     }
100 
101     /**
102      * Returns a reference to its parent closure or null if outermost.
103      */
getParentClosure()104     public Closure getParentClosure() {
105         return null;
106     }
107 
108     /**
109      * Returns the name of the auxiliary class or null if this predicate
110      * is compiled inside the Translet.
111      */
getInnerClassName()112     public String getInnerClassName() {
113         return _className;
114     }
115 
116     /**
117      * Add new variable to the closure.
118      */
addVariable(VariableRefBase variableRef)119     public void addVariable(VariableRefBase variableRef) {
120         if (_closureVars == null) {
121             _closureVars = new ArrayList<>();
122         }
123 
124         // Only one reference per variable
125         if (!_closureVars.contains(variableRef)) {
126             _closureVars.add(variableRef);
127         }
128     }
129 
130     // -- End Closure interface ----------------------
131 
parseContents(Parser parser)132    public void parseContents(Parser parser) {
133         final int count = _attributes.getLength();
134 
135         for (int i = 0; i < count; i++) {
136             final String name = _attributes.getQName(i);
137             final String value = _attributes.getValue(i);
138 
139             if (name.equals("value")) {
140                 _value = parser.parseExpression(this, name, null);
141             }
142             else if (name.equals("count")) {
143                 _count = parser.parsePattern(this, name, null);
144             }
145             else if (name.equals("from")) {
146                 _from = parser.parsePattern(this, name, null);
147             }
148             else if (name.equals("level")) {
149                 if (value.equals("single")) {
150                     _level = LEVEL_SINGLE;
151                 }
152                 else if (value.equals("multiple")) {
153                     _level = LEVEL_MULTIPLE;
154                 }
155                 else if (value.equals("any")) {
156                     _level = LEVEL_ANY;
157                 }
158             }
159             else if (name.equals("format")) {
160                 _format = new AttributeValueTemplate(value, parser, this);
161                 _formatNeeded = true;
162             }
163             else if (name.equals("lang")) {
164                 _lang = new AttributeValueTemplate(value, parser, this);
165                 _formatNeeded = true;
166             }
167             else if (name.equals("letter-value")) {
168                 _letterValue = new AttributeValueTemplate(value, parser, this);
169                 _formatNeeded = true;
170             }
171             else if (name.equals("grouping-separator")) {
172                 _groupingSeparator = new AttributeValueTemplate(value, parser, this);
173                 _formatNeeded = true;
174             }
175             else if (name.equals("grouping-size")) {
176                 _groupingSize = new AttributeValueTemplate(value, parser, this);
177                 _formatNeeded = true;
178             }
179         }
180     }
181 
typeCheck(SymbolTable stable)182     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
183         if (_value != null) {
184             Type tvalue = _value.typeCheck(stable);
185             if (tvalue instanceof RealType == false) {
186                 _value = new CastExpr(_value, Type.Real);
187             }
188         }
189         if (_count != null) {
190             _count.typeCheck(stable);
191         }
192         if (_from != null) {
193             _from.typeCheck(stable);
194         }
195         if (_format != null) {
196             _format.typeCheck(stable);
197         }
198         if (_lang != null) {
199             _lang.typeCheck(stable);
200         }
201         if (_letterValue != null) {
202             _letterValue.typeCheck(stable);
203         }
204         if (_groupingSeparator != null) {
205             _groupingSeparator.typeCheck(stable);
206         }
207         if (_groupingSize != null) {
208             _groupingSize.typeCheck(stable);
209         }
210         return Type.Void;
211     }
212 
213     /**
214      * True if the has specified a value for this instance of number.
215      */
hasValue()216     public boolean hasValue() {
217         return _value != null;
218     }
219 
220     /**
221      * Returns <tt>true</tt> if this instance of number has neither
222      * a from nor a count pattern.
223      */
isDefault()224     public boolean isDefault() {
225         return _from == null && _count == null;
226     }
227 
compileDefault(ClassGenerator classGen, MethodGenerator methodGen)228     private void compileDefault(ClassGenerator classGen,
229                                 MethodGenerator methodGen) {
230         int index;
231         ConstantPoolGen cpg = classGen.getConstantPool();
232         InstructionList il = methodGen.getInstructionList();
233 
234         int[] fieldIndexes = getXSLTC().getNumberFieldIndexes();
235 
236         if (fieldIndexes[_level] == -1) {
237             Field defaultNode = new Field(ACC_PRIVATE,
238                                           cpg.addUtf8(FieldNames[_level]),
239                                           cpg.addUtf8(NODE_COUNTER_SIG),
240                                           null,
241                                           cpg.getConstantPool());
242 
243             // Add a new private field to this class
244             classGen.addField(defaultNode);
245 
246             // Get a reference to the newly added field
247             fieldIndexes[_level] = cpg.addFieldref(classGen.getClassName(),
248                                                    FieldNames[_level],
249                                                    NODE_COUNTER_SIG);
250         }
251 
252         // Check if field is initialized (runtime)
253         il.append(classGen.loadTranslet());
254         il.append(new GETFIELD(fieldIndexes[_level]));
255         final BranchHandle ifBlock1 = il.append(new IFNONNULL(null));
256 
257         // Create an instance of DefaultNodeCounter
258         index = cpg.addMethodref(ClassNames[_level],
259                                  "getDefaultNodeCounter",
260                                  "(" + TRANSLET_INTF_SIG
261                                  + DOM_INTF_SIG
262                                  + NODE_ITERATOR_SIG
263                                  + ")" + NODE_COUNTER_SIG);
264         il.append(classGen.loadTranslet());
265         il.append(methodGen.loadDOM());
266         il.append(methodGen.loadIterator());
267         il.append(new INVOKESTATIC(index));
268         il.append(DUP);
269 
270         // Store the node counter in the field
271         il.append(classGen.loadTranslet());
272         il.append(SWAP);
273         il.append(new PUTFIELD(fieldIndexes[_level]));
274         final BranchHandle ifBlock2 = il.append(new GOTO(null));
275 
276         // Backpatch conditionals
277         ifBlock1.setTarget(il.append(classGen.loadTranslet()));
278         il.append(new GETFIELD(fieldIndexes[_level]));
279 
280         ifBlock2.setTarget(il.append(NOP));
281     }
282 
283     /**
284      * Compiles a constructor for the class <tt>_className</tt> that
285      * inherits from {Any,Single,Multiple}NodeCounter. This constructor
286      * simply calls the same constructor in the super class.
287      */
compileConstructor(ClassGenerator classGen)288     private void compileConstructor(ClassGenerator classGen) {
289         MethodGenerator cons;
290         final InstructionList il = new InstructionList();
291         final ConstantPoolGen cpg = classGen.getConstantPool();
292 
293         cons = new MethodGenerator(ACC_PUBLIC,
294                                    com.sun.org.apache.bcel.internal.generic.Type.VOID,
295                                    new com.sun.org.apache.bcel.internal.generic.Type[] {
296                                        Util.getJCRefType(TRANSLET_INTF_SIG),
297                                        Util.getJCRefType(DOM_INTF_SIG),
298                                        Util.getJCRefType(NODE_ITERATOR_SIG),
299                                        com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN
300                                    },
301                                    new String[] {
302                                        "dom",
303                                        "translet",
304                                        "iterator",
305                                        "hasFrom"
306                                    },
307                                    "<init>", _className, il, cpg);
308 
309         il.append(ALOAD_0);         // this
310         il.append(ALOAD_1);         // translet
311         il.append(ALOAD_2);         // DOM
312         il.append(new ALOAD(3));    // iterator
313         il.append(new ILOAD(4));    // hasFrom
314 
315         int index = cpg.addMethodref(ClassNames[_level],
316                                      "<init>",
317                                      "(" + TRANSLET_INTF_SIG
318                                      + DOM_INTF_SIG
319                                      + NODE_ITERATOR_SIG
320                                      + "Z)V");
321         il.append(new INVOKESPECIAL(index));
322         il.append(RETURN);
323 
324         classGen.addMethod(cons);
325     }
326 
327     /**
328      * This method compiles code that is common to matchesFrom() and
329      * matchesCount() in the auxillary class.
330      */
compileLocals(NodeCounterGenerator nodeCounterGen, MatchGenerator matchGen, InstructionList il)331     private void compileLocals(NodeCounterGenerator nodeCounterGen,
332                                MatchGenerator matchGen,
333                                InstructionList il)
334     {
335         int field;
336         LocalVariableGen local;
337         ConstantPoolGen cpg = nodeCounterGen.getConstantPool();
338 
339         // Get NodeCounter._iterator and store locally
340         local = matchGen.addLocalVariable("iterator",
341                                           Util.getJCRefType(NODE_ITERATOR_SIG),
342                                           null, null);
343         field = cpg.addFieldref(NODE_COUNTER, "_iterator",
344                                 ITERATOR_FIELD_SIG);
345         il.append(ALOAD_0); // 'this' pointer on stack
346         il.append(new GETFIELD(field));
347         local.setStart(il.append(new ASTORE(local.getIndex())));
348         matchGen.setIteratorIndex(local.getIndex());
349 
350         // Get NodeCounter._translet and store locally
351         local = matchGen.addLocalVariable("translet",
352                                   Util.getJCRefType(TRANSLET_SIG),
353                                   null, null);
354         field = cpg.addFieldref(NODE_COUNTER, "_translet",
355                                 "Lcom/sun/org/apache/xalan/internal/xsltc/Translet;");
356         il.append(ALOAD_0); // 'this' pointer on stack
357         il.append(new GETFIELD(field));
358         il.append(new CHECKCAST(cpg.addClass(TRANSLET_CLASS)));
359         local.setStart(il.append(new ASTORE(local.getIndex())));
360         nodeCounterGen.setTransletIndex(local.getIndex());
361 
362         // Get NodeCounter._document and store locally
363         local = matchGen.addLocalVariable("document",
364                                           Util.getJCRefType(DOM_INTF_SIG),
365                                           null, null);
366         field = cpg.addFieldref(_className, "_document", DOM_INTF_SIG);
367         il.append(ALOAD_0); // 'this' pointer on stack
368         il.append(new GETFIELD(field));
369         // Make sure we have the correct DOM type on the stack!!!
370         local.setStart(il.append(new ASTORE(local.getIndex())));
371         matchGen.setDomIndex(local.getIndex());
372     }
373 
compilePatterns(ClassGenerator classGen, MethodGenerator methodGen)374     private void compilePatterns(ClassGenerator classGen,
375                                  MethodGenerator methodGen)
376     {
377         int current;
378         int field;
379         LocalVariableGen local;
380         MatchGenerator matchGen;
381         NodeCounterGenerator nodeCounterGen;
382 
383         _className = getXSLTC().getHelperClassName();
384         nodeCounterGen = new NodeCounterGenerator(_className,
385                                                   ClassNames[_level],
386                                                   toString(),
387                                                   ACC_PUBLIC | ACC_SUPER,
388                                                   null,
389                                                   classGen.getStylesheet());
390         InstructionList il = null;
391         ConstantPoolGen cpg = nodeCounterGen.getConstantPool();
392 
393         // Add a new instance variable for each var in closure
394         final int closureLen = (_closureVars == null) ? 0 :
395             _closureVars.size();
396 
397         for (int i = 0; i < closureLen; i++) {
398             VariableBase var = (_closureVars.get(i)).getVariable();
399 
400             nodeCounterGen.addField(new Field(ACC_PUBLIC,
401                                         cpg.addUtf8(var.getEscapedName()),
402                                         cpg.addUtf8(var.getType().toSignature()),
403                                         null, cpg.getConstantPool()));
404         }
405 
406         // Add a single constructor to the class
407         compileConstructor(nodeCounterGen);
408 
409         /*
410          * Compile method matchesFrom()
411          */
412         if (_from != null) {
413             il = new InstructionList();
414             matchGen =
415                 new MatchGenerator(ACC_PUBLIC | ACC_FINAL,
416                                    com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
417                                    new com.sun.org.apache.bcel.internal.generic.Type[] {
418                                        com.sun.org.apache.bcel.internal.generic.Type.INT,
419                                    },
420                                    new String[] {
421                                        "node",
422                                    },
423                                    "matchesFrom", _className, il, cpg);
424 
425             compileLocals(nodeCounterGen,matchGen,il);
426 
427             // Translate Pattern
428             il.append(matchGen.loadContextNode());
429             _from.translate(nodeCounterGen, matchGen);
430             _from.synthesize(nodeCounterGen, matchGen);
431             il.append(IRETURN);
432 
433             nodeCounterGen.addMethod(matchGen);
434         }
435 
436         /*
437          * Compile method matchesCount()
438          */
439         if (_count != null) {
440             il = new InstructionList();
441             matchGen = new MatchGenerator(ACC_PUBLIC | ACC_FINAL,
442                                           com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
443                                           new com.sun.org.apache.bcel.internal.generic.Type[] {
444                                               com.sun.org.apache.bcel.internal.generic.Type.INT,
445                                           },
446                                           new String[] {
447                                               "node",
448                                           },
449                                           "matchesCount", _className, il, cpg);
450 
451             compileLocals(nodeCounterGen,matchGen,il);
452 
453             // Translate Pattern
454             il.append(matchGen.loadContextNode());
455             _count.translate(nodeCounterGen, matchGen);
456             _count.synthesize(nodeCounterGen, matchGen);
457 
458             il.append(IRETURN);
459 
460             nodeCounterGen.addMethod(matchGen);
461         }
462 
463         getXSLTC().dumpClass(nodeCounterGen.getJavaClass());
464 
465         // Push an instance of the newly created class
466         cpg = classGen.getConstantPool();
467         il = methodGen.getInstructionList();
468 
469         final int index = cpg.addMethodref(_className, "<init>",
470                                            "(" + TRANSLET_INTF_SIG
471                                            + DOM_INTF_SIG
472                                            + NODE_ITERATOR_SIG
473                                            + "Z)V");
474         il.append(new NEW(cpg.addClass(_className)));
475         il.append(DUP);
476         il.append(classGen.loadTranslet());
477         il.append(methodGen.loadDOM());
478         il.append(methodGen.loadIterator());
479         il.append(_from != null ? ICONST_1 : ICONST_0);
480         il.append(new INVOKESPECIAL(index));
481 
482         // Initialize closure variables
483         for (int i = 0; i < closureLen; i++) {
484             final VariableRefBase varRef = _closureVars.get(i);
485             final VariableBase var = varRef.getVariable();
486             final Type varType = var.getType();
487 
488             // Store variable in new closure
489             il.append(DUP);
490             il.append(var.loadInstruction());
491             il.append(new PUTFIELD(
492                     cpg.addFieldref(_className, var.getEscapedName(),
493                         varType.toSignature())));
494         }
495     }
496 
translate(ClassGenerator classGen, MethodGenerator methodGen)497     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
498         int index;
499         final ConstantPoolGen cpg = classGen.getConstantPool();
500         final InstructionList il = methodGen.getInstructionList();
501 
502         // Push "this" for the call to characters()
503         il.append(classGen.loadTranslet());
504 
505         if (hasValue()) {
506             compileDefault(classGen, methodGen);
507             _value.translate(classGen, methodGen);
508 
509             // Using java.lang.Math.floor(number + 0.5) to return a double value
510             il.append(new PUSH(cpg, 0.5));
511             il.append(DADD);
512             index = cpg.addMethodref(MATH_CLASS, "floor", "(D)D");
513             il.append(new INVOKESTATIC(index));
514 
515             // Call setValue on the node counter
516             index = cpg.addMethodref(NODE_COUNTER,
517                                      "setValue",
518                                      "(D)" + NODE_COUNTER_SIG);
519             il.append(new INVOKEVIRTUAL(index));
520         }
521         else if (isDefault()) {
522             compileDefault(classGen, methodGen);
523         }
524         else {
525             compilePatterns(classGen, methodGen);
526         }
527 
528         // Call setStartNode()
529         if (!hasValue()) {
530             il.append(methodGen.loadContextNode());
531             index = cpg.addMethodref(NODE_COUNTER,
532                                      SET_START_NODE,
533                                      "(I)" + NODE_COUNTER_SIG);
534             il.append(new INVOKEVIRTUAL(index));
535         }
536 
537         // Call getCounter() with or without args
538         if (_formatNeeded) {
539             if (_format != null) {
540                 _format.translate(classGen, methodGen);
541             }
542             else {
543                 il.append(new PUSH(cpg, "1"));
544             }
545 
546             if (_lang != null) {
547                 _lang.translate(classGen, methodGen);
548             }
549             else {
550                 il.append(new PUSH(cpg, "en"));         // TODO ??
551             }
552 
553             if (_letterValue != null) {
554                 _letterValue.translate(classGen, methodGen);
555             }
556             else {
557                 il.append(new PUSH(cpg, Constants.EMPTYSTRING));
558             }
559 
560             if (_groupingSeparator != null) {
561                 _groupingSeparator.translate(classGen, methodGen);
562             }
563             else {
564                 il.append(new PUSH(cpg, Constants.EMPTYSTRING));
565             }
566 
567             if (_groupingSize != null) {
568                 _groupingSize.translate(classGen, methodGen);
569             }
570             else {
571                 il.append(new PUSH(cpg, "0"));
572             }
573 
574             index = cpg.addMethodref(NODE_COUNTER, "getCounter",
575                                      "(" + STRING_SIG + STRING_SIG
576                                      + STRING_SIG + STRING_SIG
577                                      + STRING_SIG + ")" + STRING_SIG);
578             il.append(new INVOKEVIRTUAL(index));
579         }
580         else {
581             index = cpg.addMethodref(NODE_COUNTER, "setDefaultFormatting",
582                                      "()" + NODE_COUNTER_SIG);
583             il.append(new INVOKEVIRTUAL(index));
584 
585             index = cpg.addMethodref(NODE_COUNTER, "getCounter",
586                                      "()" + STRING_SIG);
587             il.append(new INVOKEVIRTUAL(index));
588         }
589 
590         // Output the resulting string to the handler
591         il.append(methodGen.loadHandler());
592         index = cpg.addMethodref(TRANSLET_CLASS,
593                                  CHARACTERSW,
594                                  CHARACTERSW_SIG);
595         il.append(new INVOKEVIRTUAL(index));
596     }
597 }
598