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