1 /* 2 * Copyright (c) 2015, 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.generic.ConstantPoolGen; 24 import com.sun.org.apache.bcel.internal.generic.IFEQ; 25 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE; 26 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL; 27 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC; 28 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; 29 import com.sun.org.apache.bcel.internal.generic.InstructionConst; 30 import com.sun.org.apache.bcel.internal.generic.InstructionList; 31 import com.sun.org.apache.bcel.internal.generic.InvokeInstruction; 32 import com.sun.org.apache.bcel.internal.generic.LDC; 33 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen; 34 import com.sun.org.apache.bcel.internal.generic.NEW; 35 import com.sun.org.apache.bcel.internal.generic.PUSH; 36 import com.sun.org.apache.xalan.internal.utils.ObjectFactory; 37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType; 38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; 39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; 40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType; 41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; 42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType; 43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MultiHashtable; 44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ObjectType; 45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType; 46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; 47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; 48 import java.lang.reflect.Constructor; 49 import java.lang.reflect.Method; 50 import java.lang.reflect.Modifier; 51 import java.util.ArrayList; 52 import java.util.Collections; 53 import java.util.HashMap; 54 import java.util.List; 55 import java.util.Map; 56 import java.util.Objects; 57 import jdk.xml.internal.JdkXmlFeatures; 58 59 /** 60 * @author Jacek Ambroziak 61 * @author Santiago Pericas-Geertsen 62 * @author Morten Jorgensen 63 * @author Erwin Bolwidt <ejb@klomp.org> 64 * @author Todd Miller 65 * @LastModified: Nov 2017 66 */ 67 class FunctionCall extends Expression { 68 69 // Name of this function call 70 private QName _fname; 71 // Arguments to this function call (might not be any) 72 private final List<Expression> _arguments; 73 // Empty argument list, used for certain functions 74 private final static List<Expression> EMPTY_ARG_LIST = new ArrayList<>(0); 75 76 // Valid namespaces for Java function-call extension 77 protected final static String EXT_XSLTC = 78 TRANSLET_URI; 79 80 protected final static String JAVA_EXT_XSLTC = 81 EXT_XSLTC + "/java"; 82 83 protected final static String EXT_XALAN = 84 "http://xml.apache.org/xalan"; 85 86 protected final static String JAVA_EXT_XALAN = 87 "http://xml.apache.org/xalan/java"; 88 89 protected final static String JAVA_EXT_XALAN_OLD = 90 "http://xml.apache.org/xslt/java"; 91 92 protected final static String EXSLT_COMMON = 93 "http://exslt.org/common"; 94 95 protected final static String EXSLT_MATH = 96 "http://exslt.org/math"; 97 98 protected final static String EXSLT_SETS = 99 "http://exslt.org/sets"; 100 101 protected final static String EXSLT_DATETIME = 102 "http://exslt.org/dates-and-times"; 103 104 protected final static String EXSLT_STRINGS = 105 "http://exslt.org/strings"; 106 107 protected final static String XALAN_CLASSPACKAGE_NAMESPACE = 108 "xalan://"; 109 110 // Namespace format constants 111 protected final static int NAMESPACE_FORMAT_JAVA = 0; 112 protected final static int NAMESPACE_FORMAT_CLASS = 1; 113 protected final static int NAMESPACE_FORMAT_PACKAGE = 2; 114 protected final static int NAMESPACE_FORMAT_CLASS_OR_PACKAGE = 3; 115 116 // Namespace format 117 private int _namespace_format = NAMESPACE_FORMAT_JAVA; 118 119 /** 120 * Stores reference to object for non-static Java calls 121 */ 122 Expression _thisArgument = null; 123 124 // External Java function's class/method/signature 125 private String _className; 126 private Class<?> _clazz; 127 private Method _chosenMethod; 128 private Constructor<?> _chosenConstructor; 129 private MethodType _chosenMethodType; 130 131 // Encapsulates all unsupported external function calls 132 private boolean unresolvedExternal; 133 134 // If FunctionCall is a external java constructor 135 private boolean _isExtConstructor = false; 136 137 // If the java method is static 138 private boolean _isStatic = false; 139 140 // Legal conversions between internal and Java types. 141 private static final MultiHashtable<Type, JavaType> _internal2Java = new MultiHashtable<>(); 142 143 // Legal conversions between Java and internal types. 144 private static final Map<Class<?>, Type> JAVA2INTERNAL; 145 146 // The mappings between EXSLT extension namespaces and implementation classes 147 private static final Map<String, String> EXTENSIONNAMESPACE; 148 149 // Extension functions that are implemented in BasisLibrary 150 private static final Map<String, String> EXTENSIONFUNCTION; 151 /** 152 * inner class to used in internal2Java mappings, contains 153 * the Java type and the distance between the internal type and 154 * the Java type. 155 */ 156 static class JavaType { 157 public Class<?> type; 158 public int distance; 159 160 public JavaType(Class<?> type, int distance){ 161 this.type = type; 162 this.distance = distance; 163 } 164 165 @Override 166 public int hashCode() { 167 return Objects.hashCode(this.type); 168 } 169 170 @Override 171 public boolean equals(Object query) { 172 if (query == null) { 173 return false; 174 } 175 if (query.getClass().isAssignableFrom(JavaType.class)) { 176 return ((JavaType)query).type.equals(type); 177 } else { 178 return query.equals(type); 179 } 180 } 181 } 182 183 /** 184 * Defines 2 conversion tables: 185 * 1. From internal types to Java types and 186 * 2. From Java types to internal types. 187 * These two tables are used when calling external (Java) functions. 188 */ 189 static { 190 final Class<?> nodeClass, nodeListClass; 191 try { 192 nodeClass = Class.forName("org.w3c.dom.Node"); 193 nodeListClass = Class.forName("org.w3c.dom.NodeList"); 194 } 195 catch (ClassNotFoundException e) { 196 ErrorMsg err = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR,"org.w3c.dom.Node or NodeList"); 197 throw new Error(err.toString()); 198 } 199 200 // -- Internal to Java -------------------------------------------- 201 202 // Type.Boolean -> { boolean(0), Boolean(1), Object(2) } 203 _internal2Java.put(Type.Boolean, new JavaType(Boolean.TYPE, 0)); 204 _internal2Java.put(Type.Boolean, new JavaType(Boolean.class, 1)); 205 _internal2Java.put(Type.Boolean, new JavaType(Object.class, 2)); 206 207 // Type.Real -> { double(0), Double(1), float(2), long(3), int(4), 208 // short(5), byte(6), char(7), Object(8) } 209 _internal2Java.put(Type.Real, new JavaType(Double.TYPE, 0)); 210 _internal2Java.put(Type.Real, new JavaType(Double.class, 1)); 211 _internal2Java.put(Type.Real, new JavaType(Float.TYPE, 2)); 212 _internal2Java.put(Type.Real, new JavaType(Long.TYPE, 3)); 213 _internal2Java.put(Type.Real, new JavaType(Integer.TYPE, 4)); 214 _internal2Java.put(Type.Real, new JavaType(Short.TYPE, 5)); 215 _internal2Java.put(Type.Real, new JavaType(Byte.TYPE, 6)); 216 _internal2Java.put(Type.Real, new JavaType(Character.TYPE, 7)); 217 _internal2Java.put(Type.Real, new JavaType(Object.class, 8)); 218 219 // Type.Int must be the same as Type.Real 220 _internal2Java.put(Type.Int, new JavaType(Double.TYPE, 0)); 221 _internal2Java.put(Type.Int, new JavaType(Double.class, 1)); 222 _internal2Java.put(Type.Int, new JavaType(Float.TYPE, 2)); 223 _internal2Java.put(Type.Int, new JavaType(Long.TYPE, 3)); 224 _internal2Java.put(Type.Int, new JavaType(Integer.TYPE, 4)); 225 _internal2Java.put(Type.Int, new JavaType(Short.TYPE, 5)); 226 _internal2Java.put(Type.Int, new JavaType(Byte.TYPE, 6)); 227 _internal2Java.put(Type.Int, new JavaType(Character.TYPE, 7)); 228 _internal2Java.put(Type.Int, new JavaType(Object.class, 8)); 229 230 // Type.String -> { String(0), Object(1) } 231 _internal2Java.put(Type.String, new JavaType(String.class, 0)); 232 _internal2Java.put(Type.String, new JavaType(Object.class, 1)); 233 234 // Type.NodeSet -> { NodeList(0), Node(1), Object(2), String(3) } 235 _internal2Java.put(Type.NodeSet, new JavaType(nodeListClass, 0)); 236 _internal2Java.put(Type.NodeSet, new JavaType(nodeClass, 1)); 237 _internal2Java.put(Type.NodeSet, new JavaType(Object.class, 2)); 238 _internal2Java.put(Type.NodeSet, new JavaType(String.class, 3)); 239 240 // Type.Node -> { Node(0), NodeList(1), Object(2), String(3) } 241 _internal2Java.put(Type.Node, new JavaType(nodeListClass, 0)); 242 _internal2Java.put(Type.Node, new JavaType(nodeClass, 1)); 243 _internal2Java.put(Type.Node, new JavaType(Object.class, 2)); 244 _internal2Java.put(Type.Node, new JavaType(String.class, 3)); 245 246 // Type.ResultTree -> { NodeList(0), Node(1), Object(2), String(3) } 247 _internal2Java.put(Type.ResultTree, new JavaType(nodeListClass, 0)); 248 _internal2Java.put(Type.ResultTree, new JavaType(nodeClass, 1)); 249 _internal2Java.put(Type.ResultTree, new JavaType(Object.class, 2)); 250 _internal2Java.put(Type.ResultTree, new JavaType(String.class, 3)); 251 252 _internal2Java.put(Type.Reference, new JavaType(Object.class, 0)); 253 254 _internal2Java.makeUnmodifiable(); 255 256 Map<Class<?>, Type> java2Internal = new HashMap<>(); 257 Map<String, String> extensionNamespaceTable = new HashMap<>(); 258 Map<String, String> extensionFunctionTable = new HashMap<>(); 259 260 // Possible conversions between Java and internal types 261 java2Internal.put(Boolean.TYPE, Type.Boolean); 262 java2Internal.put(Void.TYPE, Type.Void); 263 java2Internal.put(Character.TYPE, Type.Real); 264 java2Internal.put(Byte.TYPE, Type.Real); 265 java2Internal.put(Short.TYPE, Type.Real); 266 java2Internal.put(Integer.TYPE, Type.Real); 267 java2Internal.put(Long.TYPE, Type.Real); 268 java2Internal.put(Float.TYPE, Type.Real); 269 java2Internal.put(Double.TYPE, Type.Real); 270 271 java2Internal.put(String.class, Type.String); 272 273 java2Internal.put(Object.class, Type.Reference); 274 275 // Conversions from org.w3c.dom.Node/NodeList to internal NodeSet 276 java2Internal.put(nodeListClass, Type.NodeSet); 277 java2Internal.put(nodeClass, Type.NodeSet); 278 279 // Initialize the extension namespace table 280 extensionNamespaceTable.put(EXT_XALAN, "com.sun.org.apache.xalan.internal.lib.Extensions"); 281 extensionNamespaceTable.put(EXSLT_COMMON, "com.sun.org.apache.xalan.internal.lib.ExsltCommon"); 282 extensionNamespaceTable.put(EXSLT_MATH, "com.sun.org.apache.xalan.internal.lib.ExsltMath"); 283 extensionNamespaceTable.put(EXSLT_SETS, "com.sun.org.apache.xalan.internal.lib.ExsltSets"); 284 extensionNamespaceTable.put(EXSLT_DATETIME, "com.sun.org.apache.xalan.internal.lib.ExsltDatetime"); 285 extensionNamespaceTable.put(EXSLT_STRINGS, "com.sun.org.apache.xalan.internal.lib.ExsltStrings"); 286 287 // Initialize the extension function table 288 extensionFunctionTable.put(EXSLT_COMMON + ":nodeSet", "nodeset"); 289 extensionFunctionTable.put(EXSLT_COMMON + ":objectType", "objectType"); 290 extensionFunctionTable.put(EXT_XALAN + ":nodeset", "nodeset"); 291 292 JAVA2INTERNAL = Collections.unmodifiableMap(java2Internal); 293 EXTENSIONNAMESPACE = Collections.unmodifiableMap(extensionNamespaceTable); 294 EXTENSIONFUNCTION = Collections.unmodifiableMap(extensionFunctionTable); 295 296 } 297 298 public FunctionCall(QName fname, List<Expression> arguments) { 299 _fname = fname; 300 _arguments = arguments; 301 _type = null; 302 } 303 304 public FunctionCall(QName fname) { 305 this(fname, EMPTY_ARG_LIST); 306 } 307 308 public String getName() { 309 return(_fname.toString()); 310 } 311 312 @Override 313 public void setParser(Parser parser) { 314 super.setParser(parser); 315 if (_arguments != null) { 316 final int n = _arguments.size(); 317 for (int i = 0; i < n; i++) { 318 final Expression exp = _arguments.get(i); 319 exp.setParser(parser); 320 exp.setParent(this); 321 } 322 } 323 } 324 325 public String getClassNameFromUri(String uri) 326 { 327 String className = EXTENSIONNAMESPACE.get(uri); 328 329 if (className != null) 330 return className; 331 else { 332 if (uri.startsWith(JAVA_EXT_XSLTC)) { 333 int length = JAVA_EXT_XSLTC.length() + 1; 334 return (uri.length() > length) ? uri.substring(length) : EMPTYSTRING; 335 } 336 else if (uri.startsWith(JAVA_EXT_XALAN)) { 337 int length = JAVA_EXT_XALAN.length() + 1; 338 return (uri.length() > length) ? uri.substring(length) : EMPTYSTRING; 339 } 340 else if (uri.startsWith(JAVA_EXT_XALAN_OLD)) { 341 int length = JAVA_EXT_XALAN_OLD.length() + 1; 342 return (uri.length() > length) ? uri.substring(length) : EMPTYSTRING; 343 } 344 else { 345 int index = uri.lastIndexOf('/'); 346 return (index > 0) ? uri.substring(index+1) : uri; 347 } 348 } 349 } 350 351 /** 352 * Type check a function call. Since different type conversions apply, 353 * type checking is different for standard and external (Java) functions. 354 */ 355 @Override 356 public Type typeCheck(SymbolTable stable) 357 throws TypeCheckError 358 { 359 if (_type != null) return _type; 360 361 final String namespace = _fname.getNamespace(); 362 String local = _fname.getLocalPart(); 363 364 if (isExtension()) { 365 _fname = new QName(null, null, local); 366 return typeCheckStandard(stable); 367 } 368 else if (isStandard()) { 369 return typeCheckStandard(stable); 370 } 371 // Handle extension functions (they all have a namespace) 372 else { 373 try { 374 _className = getClassNameFromUri(namespace); 375 376 final int pos = local.lastIndexOf('.'); 377 if (pos > 0) { 378 _isStatic = true; 379 if (_className != null && _className.length() > 0) { 380 _namespace_format = NAMESPACE_FORMAT_PACKAGE; 381 _className = _className + "." + local.substring(0, pos); 382 } 383 else { 384 _namespace_format = NAMESPACE_FORMAT_JAVA; 385 _className = local.substring(0, pos); 386 } 387 388 _fname = new QName(namespace, null, local.substring(pos + 1)); 389 } 390 else { 391 if (_className != null && _className.length() > 0) { 392 try { 393 _clazz = ObjectFactory.findProviderClass(_className, true); 394 _namespace_format = NAMESPACE_FORMAT_CLASS; 395 } 396 catch (ClassNotFoundException e) { 397 _namespace_format = NAMESPACE_FORMAT_PACKAGE; 398 } 399 } 400 else 401 _namespace_format = NAMESPACE_FORMAT_JAVA; 402 403 if (local.indexOf('-') > 0) { 404 local = replaceDash(local); 405 } 406 407 String extFunction = EXTENSIONFUNCTION.get(namespace + ":" + local); 408 if (extFunction != null) { 409 _fname = new QName(null, null, extFunction); 410 return typeCheckStandard(stable); 411 } 412 else 413 _fname = new QName(namespace, null, local); 414 } 415 416 return typeCheckExternal(stable); 417 } 418 catch (TypeCheckError e) { 419 ErrorMsg errorMsg = e.getErrorMsg(); 420 if (errorMsg == null) { 421 final String name = _fname.getLocalPart(); 422 errorMsg = new ErrorMsg(ErrorMsg.METHOD_NOT_FOUND_ERR, name); 423 } 424 getParser().reportError(ERROR, errorMsg); 425 return _type = Type.Void; 426 } 427 } 428 } 429 430 /** 431 * Type check a call to a standard function. Insert CastExprs when needed. 432 * If as a result of the insertion of a CastExpr a type check error is 433 * thrown, then catch it and re-throw it with a new "this". 434 */ 435 public Type typeCheckStandard(SymbolTable stable) throws TypeCheckError { 436 _fname.clearNamespace(); // HACK!!! 437 438 final int n = _arguments.size(); 439 final List<Type> argsType = typeCheckArgs(stable); 440 final MethodType args = new MethodType(Type.Void, argsType); 441 final MethodType ptype = 442 lookupPrimop(stable, _fname.getLocalPart(), args); 443 444 if (ptype != null) { 445 for (int i = 0; i < n; i++) { 446 final Type argType = ptype.argsType().get(i); 447 final Expression exp = _arguments.get(i); 448 if (!argType.identicalTo(exp.getType())) { 449 try { 450 _arguments.set(i, new CastExpr(exp, argType)); 451 } 452 catch (TypeCheckError e) { 453 throw new TypeCheckError(this); // invalid conversion 454 } 455 } 456 } 457 _chosenMethodType = ptype; 458 return _type = ptype.resultType(); 459 } 460 throw new TypeCheckError(this); 461 } 462 463 464 465 public Type typeCheckConstructor(SymbolTable stable) throws TypeCheckError{ 466 final List<Constructor<?>> constructors = findConstructors(); 467 if (constructors == null) { 468 // Constructor not found in this class 469 throw new TypeCheckError(ErrorMsg.CONSTRUCTOR_NOT_FOUND, 470 _className); 471 472 } 473 474 final int nConstructors = constructors.size(); 475 final int nArgs = _arguments.size(); 476 final List<Type> argsType = typeCheckArgs(stable); 477 478 // Try all constructors 479 int bestConstrDistance = Integer.MAX_VALUE; 480 _type = null; // reset 481 for (int j, i = 0; i < nConstructors; i++) { 482 // Check if all parameters to this constructor can be converted 483 final Constructor<?> constructor = constructors.get(i); 484 final Class<?>[] paramTypes = constructor.getParameterTypes(); 485 486 Class<?> extType; 487 int currConstrDistance = 0; 488 for (j = 0; j < nArgs; j++) { 489 // Convert from internal (translet) type to external (Java) type 490 extType = paramTypes[j]; 491 final Type intType = argsType.get(j); 492 JavaType match = _internal2Java.maps(intType, new JavaType(extType, 0)); 493 if (match != null) { 494 currConstrDistance += match.distance; 495 } 496 else if (intType instanceof ObjectType) { 497 ObjectType objectType = (ObjectType)intType; 498 if (objectType.getJavaClass() == extType) 499 continue; 500 else if (extType.isAssignableFrom(objectType.getJavaClass())) 501 currConstrDistance += 1; 502 else { 503 currConstrDistance = Integer.MAX_VALUE; 504 break; 505 } 506 } 507 else { 508 // no mapping available 509 currConstrDistance = Integer.MAX_VALUE; 510 break; 511 } 512 } 513 514 if (j == nArgs && currConstrDistance < bestConstrDistance ) { 515 _chosenConstructor = constructor; 516 _isExtConstructor = true; 517 bestConstrDistance = currConstrDistance; 518 519 _type = (_clazz != null) ? Type.newObjectType(_clazz) 520 : Type.newObjectType(_className); 521 } 522 } 523 524 if (_type != null) { 525 return _type; 526 } 527 528 throw new TypeCheckError(ErrorMsg.ARGUMENT_CONVERSION_ERR, getMethodSignature(argsType)); 529 } 530 531 532 /** 533 * Type check a call to an external (Java) method. 534 * The method must be static an public, and a legal type conversion 535 * must exist for all its arguments and its return type. 536 * Every method of name <code>_fname</code> is inspected 537 * as a possible candidate. 538 */ 539 public Type typeCheckExternal(SymbolTable stable) throws TypeCheckError { 540 int nArgs = _arguments.size(); 541 final String name = _fname.getLocalPart(); 542 543 // check if function is a contructor 'new' 544 if (_fname.getLocalPart().equals("new")) { 545 return typeCheckConstructor(stable); 546 } 547 // check if we are calling an instance method 548 else { 549 boolean hasThisArgument = false; 550 551 if (nArgs == 0) 552 _isStatic = true; 553 554 if (!_isStatic) { 555 if (_namespace_format == NAMESPACE_FORMAT_JAVA 556 || _namespace_format == NAMESPACE_FORMAT_PACKAGE) 557 hasThisArgument = true; 558 559 Expression firstArg = _arguments.get(0); 560 Type firstArgType = firstArg.typeCheck(stable); 561 562 if (_namespace_format == NAMESPACE_FORMAT_CLASS 563 && firstArgType instanceof ObjectType 564 && _clazz != null 565 && _clazz.isAssignableFrom(((ObjectType)firstArgType).getJavaClass())) 566 hasThisArgument = true; 567 568 if (hasThisArgument) { 569 _thisArgument = _arguments.get(0); 570 _arguments.remove(0); nArgs--; 571 if (firstArgType instanceof ObjectType) { 572 _className = ((ObjectType) firstArgType).getJavaClassName(); 573 } 574 else 575 throw new TypeCheckError(ErrorMsg.NO_JAVA_FUNCT_THIS_REF, name); 576 } 577 } 578 else if (_className.length() == 0) { 579 /* 580 * Warn user if external function could not be resolved. 581 * Warning will _NOT_ be issued is the call is properly 582 * wrapped in an <xsl:if> or <xsl:when> element. For details 583 * see If.parserContents() and When.parserContents() 584 */ 585 final Parser parser = getParser(); 586 if (parser != null) { 587 reportWarning(this, parser, ErrorMsg.FUNCTION_RESOLVE_ERR, 588 _fname.toString()); 589 } 590 unresolvedExternal = true; 591 return _type = Type.Int; // use "Int" as "unknown" 592 } 593 } 594 595 final List<Method> methods = findMethods(); 596 597 if (methods == null) { 598 // Method not found in this class 599 throw new TypeCheckError(ErrorMsg.METHOD_NOT_FOUND_ERR, _className + "." + name); 600 } 601 602 Class<?> extType = null; 603 final int nMethods = methods.size(); 604 final List<Type> argsType = typeCheckArgs(stable); 605 606 // Try all methods to identify the best fit 607 int bestMethodDistance = Integer.MAX_VALUE; 608 _type = null; // reset internal type 609 for (int j, i = 0; i < nMethods; i++) { 610 // Check if all paramteters to this method can be converted 611 final Method method = methods.get(i); 612 final Class<?>[] paramTypes = method.getParameterTypes(); 613 614 int currMethodDistance = 0; 615 for (j = 0; j < nArgs; j++) { 616 // Convert from internal (translet) type to external (Java) type 617 extType = paramTypes[j]; 618 final Type intType = argsType.get(j); 619 JavaType match = _internal2Java.maps(intType, new JavaType(extType, 0)); 620 if (match != null) { 621 currMethodDistance += match.distance; 622 } 623 else { 624 // no mapping available 625 // 626 // Allow a Reference type to match any external (Java) type at 627 // the moment. The real type checking is performed at runtime. 628 if (intType instanceof ReferenceType) { 629 currMethodDistance += 1; 630 } 631 else if (intType instanceof ObjectType) { 632 ObjectType object = (ObjectType)intType; 633 if (extType.getName().equals(object.getJavaClassName())) 634 currMethodDistance += 0; 635 else if (extType.isAssignableFrom(object.getJavaClass())) 636 currMethodDistance += 1; 637 else { 638 currMethodDistance = Integer.MAX_VALUE; 639 break; 640 } 641 } 642 else { 643 currMethodDistance = Integer.MAX_VALUE; 644 break; 645 } 646 } 647 } 648 649 if (j == nArgs) { 650 // Check if the return type can be converted 651 extType = method.getReturnType(); 652 653 _type = JAVA2INTERNAL.get(extType); 654 if (_type == null) { 655 _type = Type.newObjectType(extType); 656 } 657 658 // Use this method if all parameters & return type match 659 if (_type != null && currMethodDistance < bestMethodDistance) { 660 _chosenMethod = method; 661 bestMethodDistance = currMethodDistance; 662 } 663 } 664 } 665 666 // It is an error if the chosen method is an instance menthod but we don't 667 // have a this argument. 668 if (_chosenMethod != null && _thisArgument == null && 669 !Modifier.isStatic(_chosenMethod.getModifiers())) { 670 throw new TypeCheckError(ErrorMsg.NO_JAVA_FUNCT_THIS_REF, getMethodSignature(argsType)); 671 } 672 673 if (_type != null) { 674 if (_type == Type.NodeSet) { 675 getXSLTC().setMultiDocument(true); 676 } 677 return _type; 678 } 679 680 throw new TypeCheckError(ErrorMsg.ARGUMENT_CONVERSION_ERR, getMethodSignature(argsType)); 681 } 682 683 /** 684 * Type check the actual arguments of this function call. 685 */ 686 public List<Type> typeCheckArgs(SymbolTable stable) throws TypeCheckError { 687 final List<Type> result = new ArrayList<>(); 688 for (Expression exp : _arguments) { 689 result.add(exp.typeCheck(stable)); 690 } 691 return result; 692 } 693 694 protected final Expression argument(int i) { 695 return _arguments.get(i); 696 } 697 698 protected final Expression argument() { 699 return argument(0); 700 } 701 702 protected final int argumentCount() { 703 return _arguments.size(); 704 } 705 706 protected final void setArgument(int i, Expression exp) { 707 _arguments.set(i, exp); 708 } 709 710 /** 711 * Compile the function call and treat as an expression 712 * Update true/false-lists. 713 */ 714 @Override 715 public void translateDesynthesized(ClassGenerator classGen, 716 MethodGenerator methodGen) 717 { 718 Type type = Type.Boolean; 719 if (_chosenMethodType != null) 720 type = _chosenMethodType.resultType(); 721 722 final InstructionList il = methodGen.getInstructionList(); 723 translate(classGen, methodGen); 724 725 if ((type instanceof BooleanType) || (type instanceof IntType)) { 726 _falseList.add(il.append(new IFEQ(null))); 727 } 728 } 729 730 731 /** 732 * Translate a function call. The compiled code will leave the function's 733 * return value on the JVM's stack. 734 */ 735 @Override 736 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 737 final int n = argumentCount(); 738 final ConstantPoolGen cpg = classGen.getConstantPool(); 739 final InstructionList il = methodGen.getInstructionList(); 740 final boolean isSecureProcessing = classGen.getParser().getXSLTC().isSecureProcessing(); 741 final boolean isExtensionFunctionEnabled = classGen.getParser().getXSLTC() 742 .getFeature(JdkXmlFeatures.XmlFeature.ENABLE_EXTENSION_FUNCTION); 743 int index; 744 745 // Translate calls to methods in the BasisLibrary 746 if (isStandard() || isExtension()) { 747 for (int i = 0; i < n; i++) { 748 final Expression exp = argument(i); 749 exp.translate(classGen, methodGen); 750 exp.startIterator(classGen, methodGen); 751 } 752 753 // append "F" to the function's name 754 final String name = _fname.toString().replace('-', '_') + "F"; 755 String args = Constants.EMPTYSTRING; 756 757 // Special precautions for some method calls 758 if (name.equals("sumF")) { 759 args = DOM_INTF_SIG; 760 il.append(methodGen.loadDOM()); 761 } 762 else if (name.equals("normalize_spaceF")) { 763 if (_chosenMethodType.toSignature(args). 764 equals("()Ljava/lang/String;")) { 765 args = "I"+DOM_INTF_SIG; 766 il.append(methodGen.loadContextNode()); 767 il.append(methodGen.loadDOM()); 768 } 769 } 770 771 // Invoke the method in the basis library 772 index = cpg.addMethodref(BASIS_LIBRARY_CLASS, name, 773 _chosenMethodType.toSignature(args)); 774 il.append(new INVOKESTATIC(index)); 775 } 776 // Add call to BasisLibrary.unresolved_externalF() to generate 777 // run-time error message for unsupported external functions 778 else if (unresolvedExternal) { 779 index = cpg.addMethodref(BASIS_LIBRARY_CLASS, 780 "unresolved_externalF", 781 "(Ljava/lang/String;)V"); 782 il.append(new PUSH(cpg, _fname.toString())); 783 il.append(new INVOKESTATIC(index)); 784 } 785 else if (_isExtConstructor) { 786 if (isSecureProcessing && !isExtensionFunctionEnabled) 787 translateUnallowedExtension(cpg, il); 788 789 final String clazz = 790 _chosenConstructor.getDeclaringClass().getName(); 791 792 // Generate call to Module.addReads: 793 // <TransletClass>.class.getModule().addReads( 794 generateAddReads(classGen, methodGen, clazz); 795 796 Class<?>[] paramTypes = _chosenConstructor.getParameterTypes(); 797 LocalVariableGen[] paramTemp = new LocalVariableGen[n]; 798 799 // Backwards branches are prohibited if an uninitialized object is 800 // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed. 801 // We don't know whether this code might contain backwards branches 802 // so we mustn't create the new object until after we've created 803 // the suspect arguments to its constructor. Instead we calculate 804 // the values of the arguments to the constructor first, store them 805 // in temporary variables, create the object and reload the 806 // arguments from the temporaries to avoid the problem. 807 808 for (int i = 0; i < n; i++) { 809 final Expression exp = argument(i); 810 Type expType = exp.getType(); 811 exp.translate(classGen, methodGen); 812 // Convert the argument to its Java type 813 exp.startIterator(classGen, methodGen); 814 expType.translateTo(classGen, methodGen, paramTypes[i]); 815 paramTemp[i] = 816 methodGen.addLocalVariable("function_call_tmp"+i, 817 expType.toJCType(), 818 null, null); 819 paramTemp[i].setStart( 820 il.append(expType.STORE(paramTemp[i].getIndex()))); 821 822 } 823 824 il.append(new NEW(cpg.addClass(_className))); 825 il.append(InstructionConst.DUP); 826 827 for (int i = 0; i < n; i++) { 828 final Expression arg = argument(i); 829 paramTemp[i].setEnd( 830 il.append(arg.getType().LOAD(paramTemp[i].getIndex()))); 831 } 832 833 final StringBuffer buffer = new StringBuffer(); 834 buffer.append('('); 835 for (int i = 0; i < paramTypes.length; i++) { 836 buffer.append(getSignature(paramTypes[i])); 837 } 838 buffer.append(')'); 839 buffer.append("V"); 840 841 index = cpg.addMethodref(clazz, 842 "<init>", 843 buffer.toString()); 844 il.append(new INVOKESPECIAL(index)); 845 846 // Convert the return type back to our internal type 847 (Type.Object).translateFrom(classGen, methodGen, 848 _chosenConstructor.getDeclaringClass()); 849 850 } 851 // Invoke function calls that are handled in separate classes 852 else { 853 if (isSecureProcessing && !isExtensionFunctionEnabled) 854 translateUnallowedExtension(cpg, il); 855 856 final String clazz = _chosenMethod.getDeclaringClass().getName(); 857 Class<?>[] paramTypes = _chosenMethod.getParameterTypes(); 858 859 860 // Generate call to Module.addReads: 861 // <TransletClass>.class.getModule().addReads( 862 // Class.forName(<clazz>).getModule()); 863 generateAddReads(classGen, methodGen, clazz); 864 865 // Push "this" if it is an instance method 866 if (_thisArgument != null) { 867 _thisArgument.translate(classGen, methodGen); 868 } 869 870 for (int i = 0; i < n; i++) { 871 final Expression exp = argument(i); 872 exp.translate(classGen, methodGen); 873 // Convert the argument to its Java type 874 exp.startIterator(classGen, methodGen); 875 exp.getType().translateTo(classGen, methodGen, paramTypes[i]); 876 } 877 878 final StringBuffer buffer = new StringBuffer(); 879 buffer.append('('); 880 for (int i = 0; i < paramTypes.length; i++) { 881 buffer.append(getSignature(paramTypes[i])); 882 } 883 buffer.append(')'); 884 buffer.append(getSignature(_chosenMethod.getReturnType())); 885 886 if (_thisArgument != null && _clazz.isInterface()) { 887 index = cpg.addInterfaceMethodref(clazz, 888 _fname.getLocalPart(), 889 buffer.toString()); 890 il.append(new INVOKEINTERFACE(index, n+1)); 891 } 892 else { 893 index = cpg.addMethodref(clazz, 894 _fname.getLocalPart(), 895 buffer.toString()); 896 il.append(_thisArgument != null ? (InvokeInstruction) new INVOKEVIRTUAL(index) : 897 (InvokeInstruction) new INVOKESTATIC(index)); 898 } 899 900 // Convert the return type back to our internal type 901 _type.translateFrom(classGen, methodGen, 902 _chosenMethod.getReturnType()); 903 } 904 } 905 906 private void generateAddReads(ClassGenerator classGen, MethodGenerator methodGen, 907 String clazz) { 908 final ConstantPoolGen cpg = classGen.getConstantPool(); 909 final InstructionList il = methodGen.getInstructionList(); 910 911 // Generate call to Module.addReads: 912 // <TransletClass>.class.getModule().addReads( 913 // Class.forName(<clazz>).getModule()); 914 // Class.forName may throw ClassNotFoundException. 915 // This is OK as it will caught higher up the stack in 916 // TransformerImpl.transform() and wrapped into a 917 // TransformerException. 918 methodGen.markChunkStart(); 919 920 int index = cpg.addMethodref(CLASS_CLASS, 921 GET_MODULE, 922 GET_MODULE_SIG); 923 int index2 = cpg.addMethodref(CLASS_CLASS, 924 FOR_NAME, 925 FOR_NAME_SIG); 926 il.append(new LDC(cpg.addString(classGen.getClassName()))); 927 il.append(new INVOKESTATIC(index2)); 928 il.append(new INVOKEVIRTUAL(index)); 929 il.append(new LDC(cpg.addString(clazz))); 930 il.append(new INVOKESTATIC(index2)); 931 il.append(new INVOKEVIRTUAL(index)); 932 index = cpg.addMethodref(MODULE_CLASS, 933 ADD_READS, 934 ADD_READS_SIG); 935 il.append(new INVOKEVIRTUAL(index)); 936 il.append(InstructionConst.POP); 937 938 methodGen.markChunkEnd(); 939 } 940 941 @Override 942 public String toString() { 943 return "funcall(" + _fname + ", " + _arguments + ')'; 944 } 945 946 public boolean isStandard() { 947 final String namespace = _fname.getNamespace(); 948 return (namespace == null) || (namespace.equals(Constants.EMPTYSTRING)); 949 } 950 951 public boolean isExtension() { 952 final String namespace = _fname.getNamespace(); 953 return (namespace != null) && (namespace.equals(EXT_XSLTC)); 954 } 955 956 /** 957 * Returns a vector with all methods named <code>_fname</code> 958 * after stripping its namespace or <code>null</code> 959 * if no such methods exist. 960 */ 961 private List<Method> findMethods() { 962 963 List<Method> result = null; 964 final String namespace = _fname.getNamespace(); 965 966 if (_className != null && _className.length() > 0) { 967 final int nArgs = _arguments.size(); 968 try { 969 if (_clazz == null) { 970 final boolean isSecureProcessing = getXSLTC().isSecureProcessing(); 971 final boolean isExtensionFunctionEnabled = getXSLTC() 972 .getFeature(JdkXmlFeatures.XmlFeature.ENABLE_EXTENSION_FUNCTION); 973 974 //Check if FSP and SM - only then process with loading 975 if (namespace != null && isSecureProcessing 976 && isExtensionFunctionEnabled 977 && (namespace.startsWith(JAVA_EXT_XALAN) 978 || namespace.startsWith(JAVA_EXT_XSLTC) 979 || namespace.startsWith(JAVA_EXT_XALAN_OLD) 980 || namespace.startsWith(XALAN_CLASSPACKAGE_NAMESPACE))) { 981 _clazz = getXSLTC().loadExternalFunction(_className); 982 } else { 983 _clazz = ObjectFactory.findProviderClass(_className, true); 984 } 985 986 if (_clazz == null) { 987 final ErrorMsg msg = 988 new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className); 989 getParser().reportError(Constants.ERROR, msg); 990 } 991 } 992 993 final String methodName = _fname.getLocalPart(); 994 final Method[] methods = _clazz.getMethods(); 995 996 for (int i = 0; i < methods.length; i++) { 997 final int mods = methods[i].getModifiers(); 998 // Is it public and same number of args ? 999 if (Modifier.isPublic(mods) 1000 && methods[i].getName().equals(methodName) 1001 && methods[i].getParameterTypes().length == nArgs) 1002 { 1003 if (result == null) { 1004 result = new ArrayList<>(); 1005 } 1006 result.add(methods[i]); 1007 } 1008 } 1009 } 1010 catch (ClassNotFoundException e) { 1011 final ErrorMsg msg = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className); 1012 getParser().reportError(Constants.ERROR, msg); 1013 } 1014 } 1015 return result; 1016 } 1017 1018 /** 1019 * Returns a vector with all constructors named <code>_fname</code> 1020 * after stripping its namespace or <code>null</code> 1021 * if no such methods exist. 1022 */ 1023 private List<Constructor<?>> findConstructors() { 1024 List<Constructor<?>> result = null; 1025 1026 final int nArgs = _arguments.size(); 1027 try { 1028 if (_clazz == null) { 1029 _clazz = ObjectFactory.findProviderClass(_className, true); 1030 1031 if (_clazz == null) { 1032 final ErrorMsg msg = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className); 1033 getParser().reportError(Constants.ERROR, msg); 1034 } 1035 } 1036 1037 final Constructor<?>[] constructors = _clazz.getConstructors(); 1038 1039 for (Constructor<?> constructor : constructors) { 1040 final int mods = constructor.getModifiers(); 1041 // Is it public, static and same number of args ? 1042 if (Modifier.isPublic(mods) && constructor.getParameterTypes().length == nArgs) { 1043 if (result == null) { 1044 result = new ArrayList<>(); 1045 } result.add(constructor); 1046 } 1047 } 1048 } 1049 catch (ClassNotFoundException e) { 1050 final ErrorMsg msg = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className); 1051 getParser().reportError(Constants.ERROR, msg); 1052 } 1053 1054 return result; 1055 } 1056 1057 1058 /** 1059 * Compute the JVM signature for the class. 1060 */ 1061 static final String getSignature(Class<?> clazz) { 1062 if (clazz.isArray()) { 1063 final StringBuffer sb = new StringBuffer(); 1064 Class<?> cl = clazz; 1065 while (cl.isArray()) { 1066 sb.append("["); 1067 cl = cl.getComponentType(); 1068 } 1069 sb.append(getSignature(cl)); 1070 return sb.toString(); 1071 } 1072 else if (clazz.isPrimitive()) { 1073 if (clazz == Integer.TYPE) { 1074 return "I"; 1075 } 1076 else if (clazz == Byte.TYPE) { 1077 return "B"; 1078 } 1079 else if (clazz == Long.TYPE) { 1080 return "J"; 1081 } 1082 else if (clazz == Float.TYPE) { 1083 return "F"; 1084 } 1085 else if (clazz == Double.TYPE) { 1086 return "D"; 1087 } 1088 else if (clazz == Short.TYPE) { 1089 return "S"; 1090 } 1091 else if (clazz == Character.TYPE) { 1092 return "C"; 1093 } 1094 else if (clazz == Boolean.TYPE) { 1095 return "Z"; 1096 } 1097 else if (clazz == Void.TYPE) { 1098 return "V"; 1099 } 1100 else { 1101 final String name = clazz.toString(); 1102 ErrorMsg err = new ErrorMsg(ErrorMsg.UNKNOWN_SIG_TYPE_ERR,name); 1103 throw new Error(err.toString()); 1104 } 1105 } 1106 else { 1107 return "L" + clazz.getName().replace('.', '/') + ';'; 1108 } 1109 } 1110 1111 /** 1112 * Compute the JVM method descriptor for the method. 1113 */ 1114 static final String getSignature(Method meth) { 1115 final StringBuffer sb = new StringBuffer(); 1116 sb.append('('); 1117 final Class<?>[] params = meth.getParameterTypes(); // avoid clone 1118 for (int j = 0; j < params.length; j++) { 1119 sb.append(getSignature(params[j])); 1120 } 1121 return sb.append(')').append(getSignature(meth.getReturnType())) 1122 .toString(); 1123 } 1124 1125 /** 1126 * Compute the JVM constructor descriptor for the constructor. 1127 */ 1128 static final String getSignature(Constructor<?> cons) { 1129 final StringBuffer sb = new StringBuffer(); 1130 sb.append('('); 1131 final Class<?>[] params = cons.getParameterTypes(); // avoid clone 1132 for (int j = 0; j < params.length; j++) { 1133 sb.append(getSignature(params[j])); 1134 } 1135 return sb.append(")V").toString(); 1136 } 1137 1138 /** 1139 * Return the signature of the current method 1140 */ 1141 private String getMethodSignature(List<Type> argsType) { 1142 final StringBuffer buf = new StringBuffer(_className); 1143 buf.append('.').append(_fname.getLocalPart()).append('('); 1144 1145 int nArgs = argsType.size(); 1146 for (int i = 0; i < nArgs; i++) { 1147 final Type intType = argsType.get(i); 1148 buf.append(intType.toString()); 1149 if (i < nArgs - 1) buf.append(", "); 1150 } 1151 1152 buf.append(')'); 1153 return buf.toString(); 1154 } 1155 1156 /** 1157 * To support EXSLT extensions, convert names with dash to allowable Java names: 1158 * e.g., convert abc-xyz to abcXyz. 1159 * Note: dashes only appear in middle of an EXSLT function or element name. 1160 */ 1161 protected static String replaceDash(String name) 1162 { 1163 char dash = '-'; 1164 final StringBuilder buff = new StringBuilder(""); 1165 for (int i = 0; i < name.length(); i++) { 1166 if (i > 0 && name.charAt(i-1) == dash) 1167 buff.append(Character.toUpperCase(name.charAt(i))); 1168 else if (name.charAt(i) != dash) 1169 buff.append(name.charAt(i)); 1170 } 1171 return buff.toString(); 1172 } 1173 1174 /** 1175 * Translate code to call the BasisLibrary.unallowed_extensionF(String) 1176 * method. 1177 */ 1178 private void translateUnallowedExtension(ConstantPoolGen cpg, 1179 InstructionList il) { 1180 int index = cpg.addMethodref(BASIS_LIBRARY_CLASS, 1181 "unallowed_extension_functionF", 1182 "(Ljava/lang/String;)V"); 1183 il.append(new PUSH(cpg, _fname.toString())); 1184 il.append(new INVOKESTATIC(index)); 1185 } 1186 }