1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 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.ASTORE;
  27 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  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.D2I;
  31 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
  32 import com.sun.org.apache.bcel.internal.generic.GOTO;
  33 import com.sun.org.apache.bcel.internal.generic.IFNONNULL;
  34 import com.sun.org.apache.bcel.internal.generic.ILOAD;
  35 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  36 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
  37 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  38 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  39 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  40 import com.sun.org.apache.bcel.internal.generic.NEW;
  41 import com.sun.org.apache.bcel.internal.generic.PUSH;
  42 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
  43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MatchGenerator;
  45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeCounterGenerator;
  47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.RealType;
  48 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  49 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  50 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  51 import java.util.ArrayList;
  52 import java.util.List;
  53 
  54 /**
  55  * @author Jacek Ambroziak
  56  * @author Santiago Pericas-Geertsen
  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      */
  97     public boolean inInnerClass() {
  98         return (_className != null);
  99     }
 100 
 101     /**
 102      * Returns a reference to its parent closure or null if outermost.
 103      */
 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      */
 112     public String getInnerClassName() {
 113         return _className;
 114     }
 115 
 116     /**
 117      * Add new variable to the closure.
 118      */
 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 
 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 
 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      */
 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      */
 224     public boolean isDefault() {
 225         return _from == null && _count == null;
 226     }
 227 
 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      */
 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      */
 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 
 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 = (VariableRefBase) _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 
 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 }