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