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 }