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