1 /*
   2  * Copyright (c) 2015, 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.generic.ConstantPoolGen;
  25 import com.sun.org.apache.bcel.internal.generic.IFEQ;
  26 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  27 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  28 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
  29 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  30 import com.sun.org.apache.bcel.internal.generic.InstructionConst;
  31 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  32 import com.sun.org.apache.bcel.internal.generic.InvokeInstruction;
  33 import com.sun.org.apache.bcel.internal.generic.LDC;
  34 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  35 import com.sun.org.apache.bcel.internal.generic.NEW;
  36 import com.sun.org.apache.bcel.internal.generic.PUSH;
  37 import com.sun.org.apache.xalan.internal.utils.ObjectFactory;
  38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType;
  39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
  42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
  44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MultiHashtable;
  45 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ObjectType;
  46 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
  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 java.lang.reflect.Constructor;
  50 import java.lang.reflect.Method;
  51 import java.lang.reflect.Modifier;
  52 import java.util.ArrayList;
  53 import java.util.Collections;
  54 import java.util.HashMap;
  55 import java.util.List;
  56 import java.util.Map;
  57 import java.util.Objects;
  58 import jdk.xml.internal.JdkXmlFeatures;
  59 
  60 /**
  61  * @author Jacek Ambroziak
  62  * @author Santiago Pericas-Geertsen
  63  * @author Morten Jorgensen
  64  * @author Erwin Bolwidt <ejb@klomp.org>
  65  * @author Todd Miller
  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 = (Type) 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 = (Type)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 = (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 }