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