949 lines · 41.5 KB · Java
RawDownload
/* * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.tools.tree; import sun.tools.java.*;import sun.tools.asm.Assembler;import java.io.PrintStream;import java.util.Hashtable; /** * WARNING: The contents of this source file are not part of any * supported API.  Code that depends on them does so at its own risk: * they are subject to change or removal without notice. */publicclass MethodExpression extends NaryExpression {    Identifier id;    ClassDefinition clazz;   // The class in which the called method is defined    MemberDefinition field;    Expression implementation;     private boolean isSuper;  // Set if qualified by 'super' or '<class>.super'.     /**     * constructor     */    public MethodExpression(long where, Expression right, Identifier id, Expression args[]) {        super(METHOD, where, Type.tError, right, args);        this.id = id;    }    public MethodExpression(long where, Expression right, MemberDefinition field, Expression args[]) {        super(METHOD, where, field.getType().getReturnType(), right, args);        this.id = field.getName();        this.field = field;        this.clazz = field.getClassDefinition();    }     // This is a hack used only within certain access methods generated by    // 'SourceClass.getAccessMember'.  It allows an 'invokespecial' instruction    // to be forced even though 'super' does not appear within the call.    // Such access methods are needed for access to protected methods when using    // the qualified '<class>.super.<method>(...)' notation.    public MethodExpression(long where, Expression right,                            MemberDefinition field, Expression args[], boolean forceSuper) {        this(where, right, field, args);        this.isSuper = forceSuper;    }     public Expression getImplementation() {        if (implementation != null)            return implementation;        return this;    }     /**     * Check expression type     */    public Vset checkValue(Environment env, Context ctx, Vset vset, Hashtable exp) {        ClassDeclaration c = null;        boolean isArray = false;        boolean staticRef = false;         // Access method to use if required.        MemberDefinition implMethod = null;         ClassDefinition ctxClass = ctx.field.getClassDefinition();         // When calling a constructor, we may need to add an        // additional argument to transmit the outer instance link.        Expression args[] = this.args;        if (id.equals(idInit)){            ClassDefinition conCls = ctxClass;            try {                Expression conOuter = null;                if (right instanceof SuperExpression) {                    // outer.super(...)                    conCls = conCls.getSuperClass().getClassDefinition(env);                    conOuter = ((SuperExpression)right).outerArg;                } else if (right instanceof ThisExpression) {                    // outer.this(...)                    conOuter = ((ThisExpression)right).outerArg;                }                args = NewInstanceExpression.                    insertOuterLink(env, ctx, where, conCls, conOuter, args);            } catch (ClassNotFound ee) {                // the same error is handled elsewhere            }        }         Type argTypes[] = new Type[args.length];         // The effective accessing class, for access checking.        // This is normally the immediately enclosing class.        ClassDefinition sourceClass = ctxClass;         try {            if (right == null) {                staticRef = ctx.field.isStatic();                // Find the first outer scope that mentions the method.                ClassDefinition cdef = ctxClass;                MemberDefinition m = null;                for (; cdef != null; cdef = cdef.getOuterClass()) {                    m = cdef.findAnyMethod(env, id);                    if (m != null) {                        break;                    }                }                if (m == null) {                    // this is the scope for error diagnosis                    c = ctx.field.getClassDeclaration();                } else {                    // found the innermost scope in which m occurs                    c = cdef.getClassDeclaration();                     // Maybe an inherited method hides an apparent method.                    // Keep looking at enclosing scopes to find out.                    if (m.getClassDefinition() != cdef) {                        ClassDefinition cdef2 = cdef;                        while ((cdef2 = cdef2.getOuterClass()) != null) {                            MemberDefinition m2 = cdef2.findAnyMethod(env, id);                            if (m2 != null && m2.getClassDefinition() == cdef2) {                                env.error(where, "inherited.hides.method",                                          id, cdef.getClassDeclaration(),                                          cdef2.getClassDeclaration());                                break;                            }                        }                    }                }            } else {                if (id.equals(idInit)) {                    int thisN = ctx.getThisNumber();                    if (!ctx.field.isConstructor()) {                        env.error(where, "invalid.constr.invoke");                        return vset.addVar(thisN);                    }                    // As a consequence of the DA/DU rules in the JLS (draft of                    // forthcoming 2e), all variables are both definitely assigned                    // and definitely unassigned in unreachable code.  Normally, this                    // correctly suppresses DA/DU-related errors in such code.                    // The use of the DA status of the 'this' variable for the extra                    // check below on correct constructor usage, however, does not quite                    // fit into this DA/DU scheme.  The current representation of                    // Vsets for unreachable dead-ends, does not allow 'clearVar'                    // to work, as the DA/DU bits (all on) are implicitly represented                    // by the fact that the Vset is a dead-end.  The DA/DU status                    // of the 'this' variable is supposed to be temporarily                    // cleared at the beginning of a constructor and during the                    // checking of constructor arguments (see below in this method).                    // Since 'clearVar' has no effect on dead-ends, we may                    // find the 'this' variable in an erroneously definitely-assigned state.                    // As a workaround, we suppress the following error message when                    // the Vset is a dead-end, i.e., when we are in unreachable code.                    // Unfortunately, the special-case treatment of reachability for                    // if-then and if-then-else allows unreachable code in some circumstances,                    // thus it is possible that no error message will be emitted at all.                    // While this behavior is strictly incorrect (thus we call this a                    // workaround), the problematic code is indeed unreachable and will                    // not be executed.  In fact, it will be entirely omitted from the                    // translated program, and can cause no harm at runtime.  A correct                    // solution would require modifying the representation of the DA/DU                    // analysis to use finite Vsets only, restricting the universe                    // of variables about which assertions are made (even in unreachable                    // code) to variables that are actually in scope. Alternatively, the                    // Vset extension and the dead-end marker (currently a reserved value                    // of the extension) could be represented orthogonally.  In either case,                    // 'clearVar' could then be made to work on (non-canonical) dead ends.                    // See file 'Vset.java'.                    if (!vset.isReallyDeadEnd() && vset.testVar(thisN)) {                        env.error(where, "constr.invoke.not.first");                        return vset;                    }                    vset = vset.addVar(thisN);                    if (right instanceof SuperExpression) {                        // supers require this specific kind of checking                        vset = right.checkAmbigName(env, ctx, vset, exp, this);                    } else {                        vset = right.checkValue(env, ctx, vset, exp);                    }                } else {                    vset = right.checkAmbigName(env, ctx, vset, exp, this);                    if (right.type == Type.tPackage) {                        FieldExpression.reportFailedPackagePrefix(env, right);                        return vset;                    }                    if (right instanceof TypeExpression) {                        staticRef = true;                    }                }                if (right.type.isType(TC_CLASS)) {                    c = env.getClassDeclaration(right.type);                } else if (right.type.isType(TC_ARRAY)) {                    isArray = true;                    c = env.getClassDeclaration(Type.tObject);                } else {                    if (!right.type.isType(TC_ERROR)) {                        env.error(where, "invalid.method.invoke", right.type);                    }                    return vset;                }                 // Normally, the effective accessing class is the innermost                // class surrounding the current method call, but, for calls                // of the form '<class>.super.<method>(...)', it is <class>.                // This allows access to protected members of a superclass                // from within a class nested within one of its subclasses.                // Otherwise, for example, the call below to 'matchMethod'                // may fail due to the rules for visibility of inaccessible                // members.  For consistency, we treat qualified 'this' in                // the same manner, as error diagnostics will be affected.                // QUERY: Are there subtle unexplored language issues here?                if (right instanceof FieldExpression) {                    Identifier id = ((FieldExpression)right).id;                    if (id == idThis) {                        sourceClass = ((FieldExpression)right).clazz;                    } else if (id == idSuper) {                        isSuper = true;                        sourceClass = ((FieldExpression)right).clazz;                    }                } else if (right instanceof SuperExpression) {                    isSuper = true;                }                 // Fix for 4158650.  When we extend a protected inner                // class in a different package, we may not have access                // to the type of our superclass.  Allow the call to                // the superclass constructor from within our constructor                // Note that this check does not apply to constructor                // calls in new instance expressions -- those are part                // of NewInstanceExpression#check().                if (id != idInit) {                    // Required by JLS 6.6.1.  Fixes 4143715.                    // (See also 4094658.)                    if (!FieldExpression.isTypeAccessible(where, env,                                                          right.type,                                                          sourceClass)) {                        ClassDeclaration cdecl =                            sourceClass.getClassDeclaration();                        if (staticRef) {                            env.error(where, "no.type.access",                                      id, right.type.toString(), cdecl);                        } else {                            env.error(where, "cant.access.member.type",                                      id, right.type.toString(), cdecl);                        }                    }                }            }             // Compose a list of argument types            boolean hasErrors = false;             // "this" is not defined during argument checking            if (id.equals(idInit)) {                vset = vset.clearVar(ctx.getThisNumber());            }             for (int i = 0 ; i < args.length ; i++) {                vset = args[i].checkValue(env, ctx, vset, exp);                argTypes[i] = args[i].type;                hasErrors = hasErrors || argTypes[i].isType(TC_ERROR);            }             // "this" is defined after the constructor invocation            if (id.equals(idInit)) {                vset = vset.addVar(ctx.getThisNumber());            }             // Check if there are any type errors in the arguments            if (hasErrors) {                return vset;            }             // Get the method field, given the argument types            clazz = c.getClassDefinition(env);             if (field == null) {                 field = clazz.matchMethod(env, sourceClass, id, argTypes);                 if (field == null) {                    if (id.equals(idInit)) {                        if (diagnoseMismatch(env, args, argTypes))                            return vset;                        String sig = clazz.getName().getName().toString();                        sig = Type.tMethod(Type.tError, argTypes).typeString(sig, false, false);                        env.error(where, "unmatched.constr", sig, c);                        return vset;                    }                    String sig = id.toString();                    sig = Type.tMethod(Type.tError, argTypes).typeString(sig, false, false);                    if (clazz.findAnyMethod(env, id) == null) {                        if (ctx.getField(env, id) != null) {                            env.error(where, "invalid.method", id, c);                        } else {                            env.error(where, "undef.meth", sig, c);                        }                    } else if (diagnoseMismatch(env, args, argTypes)) {                    } else {                        env.error(where, "unmatched.meth", sig, c);                    }                    return vset;                }             }             type = field.getType().getReturnType();             // Make sure that static references are allowed            if (staticRef && !field.isStatic()) {                env.error(where, "no.static.meth.access",                          field, field.getClassDeclaration());                return vset;            }             if (field.isProtected()                && !(right == null)                && !(right instanceof SuperExpression                     // Extension of JLS 6.6.2 for qualified 'super'.                     || (right instanceof FieldExpression &&                         ((FieldExpression)right).id == idSuper))                && !sourceClass.protectedAccess(env, field, right.type)) {                env.error(where, "invalid.protected.method.use",                          field.getName(), field.getClassDeclaration(),                          right.type);                return vset;            }             // In <class>.super.<method>(), we cannot simply evaluate            // <class>.super to an object reference (as we would for            // <class>.super.<field>) and then perform an 'invokespecial'.            // An 'invokespecial' must be performed from within (a subclass of)            // the class in which the target method is located.            if (right instanceof FieldExpression &&                ((FieldExpression)right).id == idSuper) {                if (!field.isPrivate()) {                    // The private case is handled below.                    // Use an access method unless the effective accessing class                    // (the class qualifying the 'super') is the same as the                    // immediately enclosing class, i.e., the qualification was                    // unnecessary.                    if (sourceClass != ctxClass) {                        implMethod = sourceClass.getAccessMember(env, ctx, field, true);                    }                }            }             // Access method for private field if not in the same class.            if (implMethod == null && field.isPrivate()) {                ClassDefinition cdef = field.getClassDefinition();                if (cdef != ctxClass) {                    implMethod = cdef.getAccessMember(env, ctx, field, false);                }            }             // Make sure that we are not invoking an abstract method            if (field.isAbstract() && (right != null) && (right.op == SUPER)) {                env.error(where, "invoke.abstract", field, field.getClassDeclaration());                return vset;            }             if (field.reportDeprecated(env)) {                if (field.isConstructor()) {                    env.error(where, "warn.constr.is.deprecated", field);                } else {                    env.error(where, "warn.meth.is.deprecated",                              field, field.getClassDefinition());                }            }             // Check for recursive constructor            if (field.isConstructor() && ctx.field.equals(field)) {                env.error(where, "recursive.constr", field);            }             // When a package-private class defines public or protected            // members, those members may sometimes be accessed from            // outside of the package in public subclasses.  In these            // cases, we need to massage the method call to refer to            // to an accessible subclass rather than the package-private            // parent class.  Part of fix for 4135692.             // Find out if the class which contains this method            // call has access to the class which declares the            // public or protected method referent.            // We don't perform this translation on constructor calls.            if (sourceClass == ctxClass) {                ClassDefinition declarer = field.getClassDefinition();                if (!field.isConstructor() &&                    declarer.isPackagePrivate() &&                    !declarer.getName().getQualifier()                    .equals(sourceClass.getName().getQualifier())) {                     //System.out.println("The access of member " +                    //             field + " declared in class " +                    //             declarer +                    //             " is not allowed by the VM from class  " +                    //             accessor +                    //             ".  Replacing with an access of class " +                    //             clazz);                     // We cannot make this access at the VM level.                    // Construct a member which will stand for this                    // method in clazz and set `field' to refer to it.                    field =                        MemberDefinition.makeProxyMember(field, clazz, env);                }            }             sourceClass.addDependency(field.getClassDeclaration());            if (sourceClass != ctxClass) {                ctxClass.addDependency(field.getClassDeclaration());            }         } catch (ClassNotFound ee) {            env.error(where, "class.not.found", ee.name, ctx.field);            return vset;         } catch (AmbiguousMember ee) {            env.error(where, "ambig.field", id, ee.field1, ee.field2);            return vset;        }         // Make sure it is qualified        if ((right == null) && !field.isStatic()) {            right = ctx.findOuterLink(env, where, field);            vset = right.checkValue(env, ctx, vset, exp);        }         // Cast arguments        argTypes = field.getType().getArgumentTypes();        for (int i = 0 ; i < args.length ; i++) {            args[i] = convert(env, ctx, argTypes[i], args[i]);        }         if (field.isConstructor()) {            MemberDefinition m = field;            if (implMethod != null) {                m = implMethod;            }            int nargs = args.length;            Expression[] newargs = args;            if (nargs > this.args.length) {                // Argument was added above.                // Maintain the model for hidden outer args in outer.super(...):                Expression rightI;                if (right instanceof SuperExpression) {                    rightI = new SuperExpression(right.where, ctx);                    ((SuperExpression)right).outerArg = args[0];                } else if (right instanceof ThisExpression) {                    rightI = new ThisExpression(right.where, ctx);                } else {                    throw new CompilerError("this.init");                }                if (implMethod != null) {                    // Need dummy argument for access method.                    // Dummy argument follows outer instance link.                    // Leave 'this.args' equal to 'newargs' but                    // without the outer instance link.                    newargs = new Expression[nargs+1];                    this.args = new Expression[nargs];                    newargs[0] = args[0]; // outer instance                    this.args[0] = newargs[1] = new NullExpression(where); // dummy argument                    for (int i = 1 ; i < nargs ; i++) {                        this.args[i] = newargs[i+1] = args[i];                    }                } else {                    // Strip outer instance link from 'this.args'.                    // ASSERT(this.arg.length == nargs-1);                    for (int i = 1 ; i < nargs ; i++) {                        this.args[i-1] = args[i];                    }                }                implementation = new MethodExpression(where, rightI, m, newargs);                implementation.type = type; // Is this needed?            } else {                // No argument was added.

Lines 1–500 of 949.