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