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