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