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