1 /* 2 * Copyright (c) 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.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 = _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 }