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