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