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