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