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