1/* 2 * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package sun.tools.tree; 27 28import sun.tools.java.*; 29import sun.tools.asm.Assembler; 30import java.io.PrintStream; 31import java.util.Hashtable; 32 33/** 34 * WARNING: The contents of this source file are not part of any 35 * supported API. Code that depends on them does so at its own risk: 36 * they are subject to change or removal without notice. 37 */ 38public 39class MethodExpression extends NaryExpression { 40 Identifier id; 41 ClassDefinition clazz; // The class in which the called method is defined 42 MemberDefinition field; 43 Expression implementation; 44 45 private boolean isSuper; // Set if qualified by 'super' or '<class>.super'. 46 47 /** 48 * constructor 49 */ 50 public MethodExpression(long where, Expression right, Identifier id, Expression args[]) { 51 super(METHOD, where, Type.tError, right, args); 52 this.id = id; 53 } 54 public MethodExpression(long where, Expression right, MemberDefinition field, Expression args[]) { 55 super(METHOD, where, field.getType().getReturnType(), right, args); 56 this.id = field.getName(); 57 this.field = field; 58 this.clazz = field.getClassDefinition(); 59 } 60 61 // This is a hack used only within certain access methods generated by 62 // 'SourceClass.getAccessMember'. It allows an 'invokespecial' instruction 63 // to be forced even though 'super' does not appear within the call. 64 // Such access methods are needed for access to protected methods when using 65 // the qualified '<class>.super.<method>(...)' notation. 66 public MethodExpression(long where, Expression right, 67 MemberDefinition field, Expression args[], boolean forceSuper) { 68 this(where, right, field, args); 69 this.isSuper = forceSuper; 70 } 71 72 public Expression getImplementation() { 73 if (implementation != null) 74 return implementation; 75 return this; 76 } 77 78 /** 79 * Check expression type 80 */ 81 public Vset checkValue(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp) { 82 ClassDeclaration c = null; 83 boolean isArray = false; 84 boolean staticRef = false; 85 86 // Access method to use if required. 87 MemberDefinition implMethod = null; 88 89 ClassDefinition ctxClass = ctx.field.getClassDefinition(); 90 91 // When calling a constructor, we may need to add an 92 // additional argument to transmit the outer instance link. 93 Expression args[] = this.args; 94 if (id.equals(idInit)){ 95 ClassDefinition conCls = ctxClass; 96 try { 97 Expression conOuter = null; 98 if (right instanceof SuperExpression) { 99 // outer.super(...) 100 conCls = conCls.getSuperClass().getClassDefinition(env); 101 conOuter = ((SuperExpression)right).outerArg; 102 } else if (right instanceof ThisExpression) { 103 // outer.this(...) 104 conOuter = ((ThisExpression)right).outerArg; 105 } 106 args = NewInstanceExpression. 107 insertOuterLink(env, ctx, where, conCls, conOuter, args); 108 } catch (ClassNotFound ee) { 109 // the same error is handled elsewhere 110 } 111 } 112 113 Type argTypes[] = new Type[args.length]; 114 115 // The effective accessing class, for access checking. 116 // This is normally the immediately enclosing class. 117 ClassDefinition sourceClass = ctxClass; 118 119 try { 120 if (right == null) { 121 staticRef = ctx.field.isStatic(); 122 // Find the first outer scope that mentions the method. 123 ClassDefinition cdef = ctxClass; 124 MemberDefinition m = null; 125 for (; cdef != null; cdef = cdef.getOuterClass()) { 126 m = cdef.findAnyMethod(env, id); 127 if (m != null) { 128 break; 129 } 130 } 131 if (m == null) { 132 // this is the scope for error diagnosis 133 c = ctx.field.getClassDeclaration(); 134 } else { 135 // found the innermost scope in which m occurs 136 c = cdef.getClassDeclaration(); 137 138 // Maybe an inherited method hides an apparent method. 139 // Keep looking at enclosing scopes to find out. 140 if (m.getClassDefinition() != cdef) { 141 ClassDefinition cdef2 = cdef; 142 while ((cdef2 = cdef2.getOuterClass()) != null) { 143 MemberDefinition m2 = cdef2.findAnyMethod(env, id); 144 if (m2 != null && m2.getClassDefinition() == cdef2) { 145 env.error(where, "inherited.hides.method", 146 id, cdef.getClassDeclaration(), 147 cdef2.getClassDeclaration()); 148 break; 149 } 150 } 151 } 152 } 153 } else { 154 if (id.equals(idInit)) { 155 int thisN = ctx.getThisNumber(); 156 if (!ctx.field.isConstructor()) { 157 env.error(where, "invalid.constr.invoke"); 158 return vset.addVar(thisN); 159 } 160 // As a consequence of the DA/DU rules in the JLS (draft of 161 // forthcoming 2e), all variables are both definitely assigned 162 // and definitely unassigned in unreachable code. Normally, this 163 // correctly suppresses DA/DU-related errors in such code. 164 // The use of the DA status of the 'this' variable for the extra 165 // check below on correct constructor usage, however, does not quite 166 // fit into this DA/DU scheme. The current representation of 167 // Vsets for unreachable dead-ends, does not allow 'clearVar' 168 // to work, as the DA/DU bits (all on) are implicitly represented 169 // by the fact that the Vset is a dead-end. The DA/DU status 170 // of the 'this' variable is supposed to be temporarily 171 // cleared at the beginning of a constructor and during the 172 // checking of constructor arguments (see below in this method). 173 // Since 'clearVar' has no effect on dead-ends, we may 174 // find the 'this' variable in an erroneously definitely-assigned state. 175 // As a workaround, we suppress the following error message when 176 // the Vset is a dead-end, i.e., when we are in unreachable code. 177 // Unfortunately, the special-case treatment of reachability for 178 // if-then and if-then-else allows unreachable code in some circumstances, 179 // thus it is possible that no error message will be emitted at all. 180 // While this behavior is strictly incorrect (thus we call this a 181 // workaround), the problematic code is indeed unreachable and will 182 // not be executed. In fact, it will be entirely omitted from the 183 // translated program, and can cause no harm at runtime. A correct 184 // solution would require modifying the representation of the DA/DU 185 // analysis to use finite Vsets only, restricting the universe 186 // of variables about which assertions are made (even in unreachable 187 // code) to variables that are actually in scope. Alternatively, the 188 // Vset extension and the dead-end marker (currently a reserved value 189 // of the extension) could be represented orthogonally. In either case, 190 // 'clearVar' could then be made to work on (non-canonical) dead ends. 191 // See file 'Vset.java'. 192 if (!vset.isReallyDeadEnd() && vset.testVar(thisN)) { 193 env.error(where, "constr.invoke.not.first"); 194 return vset; 195 } 196 vset = vset.addVar(thisN); 197 if (right instanceof SuperExpression) { 198 // supers require this specific kind of checking 199 vset = right.checkAmbigName(env, ctx, vset, exp, this); 200 } else { 201 vset = right.checkValue(env, ctx, vset, exp); 202 } 203 } else { 204 vset = right.checkAmbigName(env, ctx, vset, exp, this); 205 if (right.type == Type.tPackage) { 206 FieldExpression.reportFailedPackagePrefix(env, right); 207 return vset; 208 } 209 if (right instanceof TypeExpression) { 210 staticRef = true; 211 } 212 } 213 if (right.type.isType(TC_CLASS)) { 214 c = env.getClassDeclaration(right.type); 215 } else if (right.type.isType(TC_ARRAY)) { 216 isArray = true; 217 c = env.getClassDeclaration(Type.tObject); 218 } else { 219 if (!right.type.isType(TC_ERROR)) { 220 env.error(where, "invalid.method.invoke", right.type); 221 } 222 return vset; 223 } 224 225 // Normally, the effective accessing class is the innermost 226 // class surrounding the current method call, but, for calls 227 // of the form '<class>.super.<method>(...)', it is <class>. 228 // This allows access to protected members of a superclass 229 // from within a class nested within one of its subclasses. 230 // Otherwise, for example, the call below to 'matchMethod' 231 // may fail due to the rules for visibility of inaccessible 232 // members. For consistency, we treat qualified 'this' in 233 // the same manner, as error diagnostics will be affected. 234 // QUERY: Are there subtle unexplored language issues here? 235 if (right instanceof FieldExpression) { 236 Identifier id = ((FieldExpression)right).id; 237 if (id == idThis) { 238 sourceClass = ((FieldExpression)right).clazz; 239 } else if (id == idSuper) { 240 isSuper = true; 241 sourceClass = ((FieldExpression)right).clazz; 242 } 243 } else if (right instanceof SuperExpression) { 244 isSuper = true; 245 } 246 247 // Fix for 4158650. When we extend a protected inner 248 // class in a different package, we may not have access 249 // to the type of our superclass. Allow the call to 250 // the superclass constructor from within our constructor 251 // Note that this check does not apply to constructor 252 // calls in new instance expressions -- those are part 253 // of NewInstanceExpression#check(). 254 if (id != idInit) { 255 // Required by JLS 6.6.1. Fixes 4143715. 256 // (See also 4094658.) 257 if (!FieldExpression.isTypeAccessible(where, env, 258 right.type, 259 sourceClass)) { 260 ClassDeclaration cdecl = 261 sourceClass.getClassDeclaration(); 262 if (staticRef) { 263 env.error(where, "no.type.access", 264 id, right.type.toString(), cdecl); 265 } else { 266 env.error(where, "cant.access.member.type", 267 id, right.type.toString(), cdecl); 268 } 269 } 270 } 271 } 272 273 // Compose a list of argument types 274 boolean hasErrors = false; 275 276 // "this" is not defined during argument checking 277 if (id.equals(idInit)) { 278 vset = vset.clearVar(ctx.getThisNumber()); 279 } 280 281 for (int i = 0 ; i < args.length ; i++) { 282 vset = args[i].checkValue(env, ctx, vset, exp); 283 argTypes[i] = args[i].type; 284 hasErrors = hasErrors || argTypes[i].isType(TC_ERROR); 285 } 286 287 // "this" is defined after the constructor invocation 288 if (id.equals(idInit)) { 289 vset = vset.addVar(ctx.getThisNumber()); 290 } 291 292 // Check if there are any type errors in the arguments 293 if (hasErrors) { 294 return vset; 295 } 296 297 // Get the method field, given the argument types 298 clazz = c.getClassDefinition(env); 299 300 if (field == null) { 301 302 field = clazz.matchMethod(env, sourceClass, id, argTypes); 303 304 if (field == null) { 305 if (id.equals(idInit)) { 306 if (diagnoseMismatch(env, args, argTypes)) 307 return vset; 308 String sig = clazz.getName().getName().toString(); 309 sig = Type.tMethod(Type.tError, argTypes).typeString(sig, false, false); 310 env.error(where, "unmatched.constr", sig, c); 311 return vset; 312 } 313 String sig = id.toString(); 314 sig = Type.tMethod(Type.tError, argTypes).typeString(sig, false, false); 315 if (clazz.findAnyMethod(env, id) == null) { 316 if (ctx.getField(env, id) != null) { 317 env.error(where, "invalid.method", id, c); 318 } else { 319 env.error(where, "undef.meth", sig, c); 320 } 321 } else if (diagnoseMismatch(env, args, argTypes)) { 322 } else { 323 env.error(where, "unmatched.meth", sig, c); 324 } 325 return vset; 326 } 327 328 } 329 330 type = field.getType().getReturnType(); 331 332 // Make sure that static references are allowed 333 if (staticRef && !field.isStatic()) { 334 env.error(where, "no.static.meth.access", 335 field, field.getClassDeclaration()); 336 return vset; 337 } 338 339 if (field.isProtected() 340 && !(right == null) 341 && !(right instanceof SuperExpression 342 // Extension of JLS 6.6.2 for qualified 'super'. 343 || (right instanceof FieldExpression && 344 ((FieldExpression)right).id == idSuper)) 345 && !sourceClass.protectedAccess(env, field, right.type)) { 346 env.error(where, "invalid.protected.method.use", 347 field.getName(), field.getClassDeclaration(), 348 right.type); 349 return vset; 350 } 351 352 // In <class>.super.<method>(), we cannot simply evaluate 353 // <class>.super to an object reference (as we would for 354 // <class>.super.<field>) and then perform an 'invokespecial'. 355 // An 'invokespecial' must be performed from within (a subclass of) 356 // the class in which the target method is located. 357 if (right instanceof FieldExpression && 358 ((FieldExpression)right).id == idSuper) { 359 if (!field.isPrivate()) { 360 // The private case is handled below. 361 // Use an access method unless the effective accessing class 362 // (the class qualifying the 'super') is the same as the 363 // immediately enclosing class, i.e., the qualification was 364 // unnecessary. 365 if (sourceClass != ctxClass) { 366 implMethod = sourceClass.getAccessMember(env, ctx, field, true); 367 } 368 } 369 } 370 371 // Access method for private field if not in the same class. 372 if (implMethod == null && field.isPrivate()) { 373 ClassDefinition cdef = field.getClassDefinition(); 374 if (cdef != ctxClass) { 375 implMethod = cdef.getAccessMember(env, ctx, field, false); 376 } 377 } 378 379 // Make sure that we are not invoking an abstract method 380 if (field.isAbstract() && (right != null) && (right.op == SUPER)) { 381 env.error(where, "invoke.abstract", field, field.getClassDeclaration()); 382 return vset; 383 } 384 385 if (field.reportDeprecated(env)) { 386 if (field.isConstructor()) { 387 env.error(where, "warn.constr.is.deprecated", field); 388 } else { 389 env.error(where, "warn.meth.is.deprecated", 390 field, field.getClassDefinition()); 391 } 392 } 393 394 // Check for recursive constructor 395 if (field.isConstructor() && ctx.field.equals(field)) { 396 env.error(where, "recursive.constr", field); 397 } 398 399 // When a package-private class defines public or protected 400 // members, those members may sometimes be accessed from 401 // outside of the package in public subclasses. In these 402 // cases, we need to massage the method call to refer to 403 // to an accessible subclass rather than the package-private 404 // parent class. Part of fix for 4135692. 405 406 // Find out if the class which contains this method 407 // call has access to the class which declares the 408 // public or protected method referent. 409 // We don't perform this translation on constructor calls. 410 if (sourceClass == ctxClass) { 411 ClassDefinition declarer = field.getClassDefinition(); 412 if (!field.isConstructor() && 413 declarer.isPackagePrivate() && 414 !declarer.getName().getQualifier() 415 .equals(sourceClass.getName().getQualifier())) { 416 417 //System.out.println("The access of member " + 418 // field + " declared in class " + 419 // declarer + 420 // " is not allowed by the VM from class " + 421 // accessor + 422 // ". Replacing with an access of class " + 423 // clazz); 424 425 // We cannot make this access at the VM level. 426 // Construct a member which will stand for this 427 // method in clazz and set `field' to refer to it. 428 field = 429 MemberDefinition.makeProxyMember(field, clazz, env); 430 } 431 } 432 433 sourceClass.addDependency(field.getClassDeclaration()); 434 if (sourceClass != ctxClass) { 435 ctxClass.addDependency(field.getClassDeclaration()); 436 } 437 438 } catch (ClassNotFound ee) { 439 env.error(where, "class.not.found", ee.name, ctx.field); 440 return vset; 441 442 } catch (AmbiguousMember ee) { 443 env.error(where, "ambig.field", id, ee.field1, ee.field2); 444 return vset; 445 } 446 447 // Make sure it is qualified 448 if ((right == null) && !field.isStatic()) { 449 right = ctx.findOuterLink(env, where, field); 450 vset = right.checkValue(env, ctx, vset, exp); 451 } 452 453 // Cast arguments 454 argTypes = field.getType().getArgumentTypes(); 455 for (int i = 0 ; i < args.length ; i++) { 456 args[i] = convert(env, ctx, argTypes[i], args[i]); 457 } 458 459 if (field.isConstructor()) { 460 MemberDefinition m = field; 461 if (implMethod != null) { 462 m = implMethod; 463 } 464 int nargs = args.length; 465 Expression[] newargs = args; 466 if (nargs > this.args.length) { 467 // Argument was added above. 468 // Maintain the model for hidden outer args in outer.super(...): 469 Expression rightI; 470 if (right instanceof SuperExpression) { 471 rightI = new SuperExpression(right.where, ctx); 472 ((SuperExpression)right).outerArg = args[0]; 473 } else if (right instanceof ThisExpression) { 474 rightI = new ThisExpression(right.where, ctx); 475 } else { 476 throw new CompilerError("this.init"); 477 } 478 if (implMethod != null) { 479 // Need dummy argument for access method. 480 // Dummy argument follows outer instance link. 481 // Leave 'this.args' equal to 'newargs' but 482 // without the outer instance link. 483 newargs = new Expression[nargs+1]; 484 this.args = new Expression[nargs]; 485 newargs[0] = args[0]; // outer instance 486 this.args[0] = newargs[1] = new NullExpression(where); // dummy argument 487 for (int i = 1 ; i < nargs ; i++) { 488 this.args[i] = newargs[i+1] = args[i]; 489 } 490 } else { 491 // Strip outer instance link from 'this.args'. 492 // ASSERT(this.arg.length == nargs-1); 493 for (int i = 1 ; i < nargs ; i++) { 494 this.args[i-1] = args[i]; 495 } 496 } 497 implementation = new MethodExpression(where, rightI, m, newargs); 498 implementation.type = type; // Is this needed? 499 } else { 500 // No argument was added. 501 if (implMethod != null) { 502 // Need dummy argument for access method. 503 // Dummy argument is first, as there is no outer instance link. 504 newargs = new Expression[nargs+1]; 505 newargs[0] = new NullExpression(where); 506 for (int i = 0 ; i < nargs ; i++) { 507 newargs[i+1] = args[i]; 508 } 509 } 510 implementation = new MethodExpression(where, right, m, newargs); 511 } 512 } else { 513 // Have ordinary method. 514 // Argument should have been added only for a constructor. 515 if (args.length > this.args.length) { 516 throw new CompilerError("method arg"); 517 } 518 if (implMethod != null) { 519 //System.out.println("Calling " + field + " via " + implMethod); 520 Expression oldargs[] = this.args; 521 if (field.isStatic()) { 522 Expression call = new MethodExpression(where, null, implMethod, oldargs); 523 implementation = new CommaExpression(where, right, call); 524 } else { 525 // Access method needs an explicit 'this' pointer. 526 int nargs = oldargs.length; 527 Expression newargs[] = new Expression[nargs+1]; 528 newargs[0] = right; 529 for (int i = 0; i < nargs; i++) { 530 newargs[i+1] = oldargs[i]; 531 } 532 implementation = new MethodExpression(where, null, implMethod, newargs); 533 } 534 } 535 } 536 537 // Follow super() by variable initializations 538 if (ctx.field.isConstructor() && 539 field.isConstructor() && (right != null) && (right.op == SUPER)) { 540 Expression e = makeVarInits(env, ctx); 541 if (e != null) { 542 if (implementation == null) 543 implementation = (Expression)this.clone(); 544 implementation = new CommaExpression(where, implementation, e); 545 } 546 } 547 548 // Throw the declared exceptions. 549 ClassDeclaration exceptions[] = field.getExceptions(env); 550 if (isArray && (field.getName() == idClone) && 551 (field.getType().getArgumentTypes().length == 0)) { 552 /* Arrays pretend that they have "public Object clone()" that doesn't 553 * throw anything, according to the language spec. 554 */ 555 exceptions = new ClassDeclaration[0]; 556 /* See if there's a bogus catch for it, to issue a warning. */ 557 for (Context p = ctx; p != null; p = p.prev) { 558 if (p.node != null && p.node.op == TRY) { 559 ((TryStatement) p.node).arrayCloneWhere = where; 560 } 561 } 562 } 563 for (int i = 0 ; i < exceptions.length ; i++) { 564 if (exp.get(exceptions[i]) == null) { 565 exp.put(exceptions[i], this); 566 } 567 } 568 569 // Mark all blank finals as definitely assigned following 'this(...)'. 570 // Correctness follows inductively from the requirement that all blank finals 571 // be definitely assigned at the completion of every constructor. 572 if (ctx.field.isConstructor() && 573 field.isConstructor() && (right != null) && (right.op == THIS)) { 574 ClassDefinition cls = field.getClassDefinition(); 575 for (MemberDefinition f = cls.getFirstMember() ; f != null ; f = f.getNextMember()) { 576 if (f.isVariable() && f.isBlankFinal() && !f.isStatic()) { 577 // Static variables should also be considered defined as well, but this 578 // is handled in 'SourceClass.checkMembers', and we should not interfere. 579 vset = vset.addVar(ctx.getFieldNumber(f)); 580 } 581 } 582 } 583 584 return vset; 585 } 586 587 /** 588 * Check void expression 589 */ 590 public Vset check(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp) { 591 return checkValue(env, ctx, vset, exp); 592 } 593 594 /** 595 * We're about to report a "unmatched method" error. 596 * Try to issue a better diagnostic by comparing the actual argument types 597 * with the method (or methods) available. 598 * In particular, if there is an argument which fails to match <em>any</em> 599 * method, we report a type mismatch error against that particular argument. 600 * The diagnostic will report a target type taken from one of the methods. 601 * <p> 602 * Return false if we couldn't think of anything smart to say. 603 */ 604 boolean diagnoseMismatch(Environment env, Expression args[], 605 Type argTypes[]) throws ClassNotFound { 606 Type margType[] = new Type[1]; 607 boolean saidSomething = false; 608 int start = 0; 609 while (start < argTypes.length) { 610 int code = clazz.diagnoseMismatch(env, id, argTypes, start, margType); 611 String opName = (id.equals(idInit)) ? "constructor" : opNames[op]; 612 if (code == -2) { 613 env.error(where, "wrong.number.args", opName); 614 saidSomething = true; 615 } 616 if (code < 0) break; 617 int i = code >> 2; 618 boolean castOK = (code & 2) != 0; 619 boolean ambig = (code & 1) != 0; 620 Type targetType = margType[0]; 621 622 // At least one argument is offensive to all overloadings. 623 // targetType is one of the argument types it does not match. 624 String ttype = ""+targetType; 625 626 // The message might be slightly misleading, if there are other 627 // argument types that also would match. Hint at this: 628 //if (ambig) ttype = "{"+ttype+";...}"; 629 630 if (castOK) 631 env.error(args[i].where, "explicit.cast.needed", opName, argTypes[i], ttype); 632 else 633 env.error(args[i].where, "incompatible.type", opName, argTypes[i], ttype); 634 saidSomething = true; 635 start = i+1; // look for other bad arguments, too 636 } 637 return saidSomething; 638 } 639 640 /** 641 * Inline 642 */ 643 static final int MAXINLINECOST = Statement.MAXINLINECOST; 644 645 private 646 Expression inlineMethod(Environment env, Context ctx, Statement s, boolean valNeeded) { 647 if (env.dump()) { 648 System.out.println("INLINE METHOD " + field + " in " + ctx.field); 649 } 650 LocalMember v[] = LocalMember.copyArguments(ctx, field); 651 Statement body[] = new Statement[v.length + 2]; 652 653 int n = 0; 654 if (field.isStatic()) { 655 body[0] = new ExpressionStatement(where, right); 656 } else { 657 if ((right != null) && (right.op == SUPER)) { 658 right = new ThisExpression(right.where, ctx); 659 } 660 body[0] = new VarDeclarationStatement(where, v[n++], right); 661 } 662 for (int i = 0 ; i < args.length ; i++) { 663 body[i + 1] = new VarDeclarationStatement(where, v[n++], args[i]); 664 } 665 //System.out.print("BEFORE:"); s.print(System.out); System.out.println(); 666 // Note: If !valNeeded, then all returns in the body of the method 667 // change to void returns. 668 body[body.length - 1] = (s != null) ? s.copyInline(ctx, valNeeded) : null; 669 //System.out.print("COPY:"); body[body.length - 1].print(System.out); System.out.println(); 670 LocalMember.doneWithArguments(ctx, v); 671 672 // Make sure the type matches what the return statements are returning. 673 Type type = valNeeded ? this.type : Type.tVoid; 674 Expression e = new InlineMethodExpression(where, type, field, new CompoundStatement(where, body)); 675 return valNeeded ? e.inlineValue(env, ctx) : e.inline(env, ctx); 676 } 677 678 public Expression inline(Environment env, Context ctx) { 679 if (implementation != null) 680 return implementation.inline(env, ctx); 681 try { 682 if (right != null) { 683 right = field.isStatic() ? right.inline(env, ctx) : right.inlineValue(env, ctx); 684 } 685 for (int i = 0 ; i < args.length ; i++) { 686 args[i] = args[i].inlineValue(env, ctx); 687 } 688 689 // ctxClass is the current class trying to inline this method 690 ClassDefinition ctxClass = ctx.field.getClassDefinition(); 691 692 Expression e = this; 693 if (env.opt() && field.isInlineable(env, clazz.isFinal()) && 694 695 // Don't inline if a qualified non-static method: the call 696 // itself might throw NullPointerException as a side effect 697 ((right == null) || (right.op==THIS) || field.isStatic()) && 698 699 // We only allow the inlining if the current class can access 700 // the field, the field's class, and right's declared type. 701 ctxClass.permitInlinedAccess(env, 702 field.getClassDeclaration()) && 703 ctxClass.permitInlinedAccess(env, field) && 704 (right==null || ctxClass.permitInlinedAccess(env, 705 env.getClassDeclaration(right.type))) && 706 707 ((id == null) || !id.equals(idInit)) && 708 (!ctx.field.isInitializer()) && ctx.field.isMethod() && 709 (ctx.getInlineMemberContext(field) == null)) { 710 Statement s = (Statement)field.getValue(env); 711 if ((s == null) || 712 (s.costInline(MAXINLINECOST, env, ctx) < MAXINLINECOST)) { 713 e = inlineMethod(env, ctx, s, false); 714 } 715 } 716 return e; 717 718 } catch (ClassNotFound e) { 719 throw new CompilerError(e); 720 } 721 } 722 723 public Expression inlineValue(Environment env, Context ctx) { 724 if (implementation != null) 725 return implementation.inlineValue(env, ctx); 726 try { 727 if (right != null) { 728 right = field.isStatic() ? right.inline(env, ctx) : right.inlineValue(env, ctx); 729 } 730 if (field.getName().equals(idInit)) { 731 ClassDefinition refc = field.getClassDefinition(); 732 UplevelReference r = refc.getReferencesFrozen(); 733 if (r != null) { 734 r.willCodeArguments(env, ctx); 735 } 736 } 737 for (int i = 0 ; i < args.length ; i++) { 738 args[i] = args[i].inlineValue(env, ctx); 739 } 740 741 // ctxClass is the current class trying to inline this method 742 ClassDefinition ctxClass = ctx.field.getClassDefinition(); 743 744 if (env.opt() && field.isInlineable(env, clazz.isFinal()) && 745 746 // Don't inline if a qualified non-static method: the call 747 // itself might throw NullPointerException as a side effect 748 ((right == null) || (right.op==THIS) || field.isStatic()) && 749 750 // We only allow the inlining if the current class can access 751 // the field, the field's class, and right's declared type. 752 ctxClass.permitInlinedAccess(env, 753 field.getClassDeclaration()) && 754 ctxClass.permitInlinedAccess(env, field) && 755 (right==null || ctxClass.permitInlinedAccess(env, 756 env.getClassDeclaration(right.type))) && 757 758 (!ctx.field.isInitializer()) && ctx.field.isMethod() && 759 (ctx.getInlineMemberContext(field) == null)) { 760 Statement s = (Statement)field.getValue(env); 761 if ((s == null) || 762 (s.costInline(MAXINLINECOST, env, ctx) < MAXINLINECOST)) { 763 return inlineMethod(env, ctx, s, true); 764 } 765 } 766 return this; 767 } catch (ClassNotFound e) { 768 throw new CompilerError(e); 769 } 770 } 771 772 public Expression copyInline(Context ctx) { 773 if (implementation != null) 774 return implementation.copyInline(ctx); 775 return super.copyInline(ctx); 776 } 777 778 public int costInline(int thresh, Environment env, Context ctx) { 779 if (implementation != null) 780 return implementation.costInline(thresh, env, ctx); 781 782 // for now, don't allow calls to super() to be inlined. We may fix 783 // this later 784 if ((right != null) && (right.op == SUPER)) { 785 return thresh; 786 } 787 return super.costInline(thresh, env, ctx); 788 } 789 790 /* 791 * Grab all instance initializer code from the class definition, 792 * and return as one bolus. Note that we are assuming the 793 * the relevant fields have already been checked. 794 * (See the pre-pass in SourceClass.checkMembers which ensures this.) 795 */ 796 private Expression makeVarInits(Environment env, Context ctx) { 797 // insert instance initializers 798 ClassDefinition clazz = ctx.field.getClassDefinition(); 799 Expression e = null; 800 for (MemberDefinition f = clazz.getFirstMember() ; f != null ; f = f.getNextMember()) { 801 if ((f.isVariable() || f.isInitializer()) && !f.isStatic()) { 802 try { 803 f.check(env); 804 } catch (ClassNotFound ee) { 805 env.error(f.getWhere(), "class.not.found", ee.name, 806 f.getClassDefinition()); 807 } 808 Expression val = null; 809 if (f.isUplevelValue()) { 810 if (f != clazz.findOuterMember()) { 811 // it's too early to accumulate these 812 continue; 813 } 814 IdentifierExpression arg = 815 new IdentifierExpression(where, f.getName()); 816 if (!arg.bind(env, ctx)) { 817 throw new CompilerError("bind "+arg.id); 818 } 819 val = arg; 820 } else if (f.isInitializer()) { 821 Statement s = (Statement)f.getValue(); 822 val = new InlineMethodExpression(where, Type.tVoid, f, s); 823 } else { 824 val = (Expression)f.getValue(); 825 } 826 // append all initializers to "e": 827 // This section used to check for variables which were 828 // initialized to their default values and elide such 829 // initialization. This is specifically disallowed by 830 // JLS 12.5 numeral 4, which requires a textual ordering 831 // on the execution of initializers. 832 if ((val != null)) { // && !val.equals(0)) { 833 long p = f.getWhere(); 834 val = val.copyInline(ctx); 835 Expression init = val; 836 if (f.isVariable()) { 837 Expression v = new ThisExpression(p, ctx); 838 v = new FieldExpression(p, v, f); 839 init = new AssignExpression(p, v, val); 840 } 841 e = (e == null) ? init : new CommaExpression(p, e, init); 842 } 843 } 844 } 845 return e; 846 } 847 848 /** 849 * Code 850 */ 851 public void codeValue(Environment env, Context ctx, Assembler asm) { 852 if (implementation != null) 853 throw new CompilerError("codeValue"); 854 int i = 0; // argument index 855 if (field.isStatic()) { 856 if (right != null) { 857 right.code(env, ctx, asm); 858 } 859 } else if (right == null) { 860 asm.add(where, opc_aload, 0); 861 } else if (right.op == SUPER) { 862 // 'super.<method>(...)', 'super(...)', or '<expr>.super(...)' 863 /***** 864 isSuper = true; 865 *****/ 866 right.codeValue(env, ctx, asm); 867 if (idInit.equals(id)) { 868 // 'super(...)' or '<expr>.super(...)' only 869 ClassDefinition refc = field.getClassDefinition(); 870 UplevelReference r = refc.getReferencesFrozen(); 871 if (r != null) { 872 // When calling a constructor for a class with 873 // embedded uplevel references, add extra arguments. 874 if (r.isClientOuterField()) { 875 // the extra arguments are inserted after this one 876 args[i++].codeValue(env, ctx, asm); 877 } 878 r.codeArguments(env, ctx, asm, where, field); 879 } 880 } 881 } else { 882 right.codeValue(env, ctx, asm); 883 /***** 884 if (right.op == FIELD && 885 ((FieldExpression)right).id == idSuper) { 886 // '<class>.super.<method>(...)' 887 isSuper = true; 888 } 889 *****/ 890 } 891 892 for ( ; i < args.length ; i++) { 893 args[i].codeValue(env, ctx, asm); 894 } 895 896 if (field.isStatic()) { 897 asm.add(where, opc_invokestatic, field); 898 } else if (field.isConstructor() || field.isPrivate() || isSuper) { 899 asm.add(where, opc_invokespecial, field); 900 } else if (field.getClassDefinition().isInterface()) { 901 asm.add(where, opc_invokeinterface, field); 902 } else { 903 asm.add(where, opc_invokevirtual, field); 904 } 905 906 if (right != null && right.op == SUPER && idInit.equals(id)) { 907 // 'super(...)' or '<expr>.super(...)' 908 ClassDefinition refc = ctx.field.getClassDefinition(); 909 UplevelReference r = refc.getReferencesFrozen(); 910 if (r != null) { 911 // After calling a superclass constructor in a class with 912 // embedded uplevel references, initialize uplevel fields. 913 r.codeInitialization(env, ctx, asm, where, field); 914 } 915 } 916 } 917 918 /** 919 * Check if the first thing is a constructor invocation 920 */ 921 public Expression firstConstructor() { 922 return id.equals(idInit) ? this : null; 923 } 924 925 /** 926 * Print 927 */ 928 public void print(PrintStream out) { 929 out.print("(" + opNames[op]); 930 if (right != null) { 931 out.print(" "); 932 right.print(out); 933 } 934 out.print(" " + ((id == null) ? idInit : id)); 935 for (int i = 0 ; i < args.length ; i++) { 936 out.print(" "); 937 if (args[i] != null) { 938 args[i].print(out); 939 } else { 940 out.print("<null>"); 941 } 942 } 943 out.print(")"); 944 if (implementation != null) { 945 out.print("/IMPL="); 946 implementation.print(out); 947 } 948 } 949} 950