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