Milán Major

netscape-revival

1 branch
Code

sun-java/classsrc/sun/tools/javac/SourceField.java

/*
 * @(#)SourceField.java	1.38 95/11/21 Arthur van Hoff
 *
 * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

package sun.tools.javac;

import sun.tools.java.*;
import sun.tools.tree.*;
import sun.tools.asm.*;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Hashtable;
import java.io.PrintStream;

/**
 * A Source Field
 */

public final
class SourceField extends FieldDefinition implements Constants {
    /**
     * The argument names (if it is a method)
     */
    Vector args;

    // set to the FieldDefinition in the interface if we have this field because
    // it has been forced on us 
    FieldDefinition abstractSource;

    /**
     * The status of the field
     */
    int status;

    static final int PARSED	= 0;
    static final int CHECKING  	= 1;
    static final int CHECKED	= 2;
    static final int INLINING	= 3;
    static final int INLINED	= 4;
    static final int ERROR	= 5;

    public Vector getArguments() {
	return args;
    }

    /**
     * Constructor
     */
    public SourceField(int where, ClassDefinition clazz, String doc, int modifiers, Type type,
		       Identifier name, Vector argNames, ClassDeclaration exp[], Node value) {
	super(where, clazz, modifiers, type, name, exp, value);
	this.documentation = doc;

	// Create a list of arguments
	if (isMethod()) {
	    args = new Vector();

	    if (isConstructor() || !isStatic()) {
		args.addElement(new LocalField(where, clazz, 0, clazz.getType(), idThis));
	    }

	    if (argNames != null) {
		Enumeration e = argNames.elements();
		Type argTypes[] = getType().getArgumentTypes();
		for (int i = 0 ; i < argTypes.length ; i++) {
		    args.addElement(new LocalField(where, clazz, 0, argTypes[i],
						   (Identifier)e.nextElement()));
		}
	    }
	}
    }

    /**
     * Constructor.
     * Used only to generate an abstract copy of a method that a class
     * inherits from an interface 
     */
    public SourceField(FieldDefinition f, ClassDefinition c, Environment env) { 
	this(f.getWhere(), c, f.getDocumentation(), 
	     f.getModifiers() | M_ABSTRACT, f.getType(), f.getName(), null, 
	     f.getExceptions(env), null);
	this.args = f.getArguments();
	this.abstractSource = f;
    }

    /**
     * Get the class declaration in which the field is actually defined
     */
    public final ClassDeclaration getDefiningClassDeclaration() {
	if (abstractSource == null)
	    return super.getDefiningClassDeclaration();
	else
	    return abstractSource.getDefiningClassDeclaration();
    }

    /**
     * Resolve a class name
     */
    public Identifier resolve(Environment env, Identifier nm) throws ClassNotFound {
	return ((SourceClass)getClassDefinition()).imports.resolve(env, nm);
    }

    /**
     * Check this field
     */
    public void check(Environment env) throws ClassNotFound {
	if (status == PARSED) {
	    if (env.dump()) {
		System.out.println("[check field " + getClassDeclaration().getName() + "." + getName() + "]");
		if (getValue() != null) {
		    getValue().print(System.out);
		    System.out.println();
		}
	    }
	    env = new Environment(env, this);
	    env.resolve(where, getClassDefinition(), getType());

	    // make sure that all the classes that we claim to throw really
	    // are subclasses of Throwable, and are classes that we can reach
	    if (isMethod()) {
		ClassDeclaration throwable = 
		    env.getClassDeclaration(idJavaLangThrowable);
		ClassDeclaration exp[] = getExceptions(env);
		for (int i = 0 ; i < exp.length ; i++) {
		    ClassDefinition def;
		    try {
			def = exp[i].getClassDefinition(env);
		    } catch (ClassNotFound e) {
			env.error(where, "class.not.found", e.name, "throws");
			break;
		    }
		    if (!getClassDefinition().
			  canAccess(env, def.getClassDeclaration())) {
			env.error(where, "cant.access.class", def);
		    } else if (!def.subClassOf(env, throwable)) {
			env.error(where, "throws.not.throwable", def);
		    }
		}
	    }
	    
	    status = CHECKING;

	    if (isMethod() && args != null) {
		int length = args.size();
	    outer_loop:
		for (int i = 0; i < length; i++) { 
		    LocalField lf = (LocalField)(args.elementAt(i));
		    Identifier name_i = lf.getName();
		    for (int j = i + 1; j < length; j++) {
			Identifier name_j = 
			      ((LocalField)(args.elementAt(j))).getName();
			if (name_i.equals(name_j)) {
			    env.error(getWhere(), "duplicate.argument", 
				      name_i);
			    break outer_loop;
			}
		    }
		}
	    }
	    if (getValue() != null) {
		Context ctx = new Context(this);
		if (isMethod()) {
		    Statement s = (Statement)getValue();
		    long vset = 0;
		    // initialize vset, indication that each of the arguments
		    // to the function has a value
		    
		    for (Enumeration e = args.elements(); e.hasMoreElements();){
			LocalField f = (LocalField)e.nextElement();
			vset |= (long)1 << ctx.declare(env, f);
		    }

		    if (isConstructor()) {
			// undefine "this" in some constructors, until after the
			// super constructor has been called.
			vset &= ~1;
			// If the first thing in the definition isn't a call
			// to either super() or this(), then insert one.
			if ((!s.firstConstructor()) 
			    && (getClassDefinition().getSuperClass() != null)) {
			    Statement body[] = {
				new ExpressionStatement(where,
				    new MethodExpression(where, 
						    new SuperExpression(where),
							 idInit, new Expression[0])), s
			    };
			    s = new CompoundStatement(where, body);
			    setValue(s);
			}
		    }

		    //System.out.println("VSET = " + vset);
		    Hashtable thrown = new Hashtable();
		    s.checkMethod(env, ctx, vset, thrown);
		    ClassDeclaration exp[] = 
			     getExceptions(env);
		    ClassDeclaration ignore1 = 
			     env.getClassDeclaration(idJavaLangError);
		    ClassDeclaration ignore2 = 
			    env.getClassDeclaration(idJavaLangRuntimeException);

		    for (Enumeration e = thrown.keys(); e.hasMoreElements();) {	
			ClassDeclaration c = (ClassDeclaration)e.nextElement();
			ClassDefinition def = c.getClassDefinition(env);
			if (def.subClassOf(env, ignore1) 
			         || def.subClassOf(env, ignore2)) {
			    continue;
			}

			boolean ok = false;
			if (!isInitializer()) {
			    for (int i = 0 ; i < exp.length ; i++) {
				if (def.subClassOf(env, exp[i])) {
				    ok = true;
				}
			    }
			}
			if (!ok) {
			    Node n = (Node)thrown.get(c);
			    env.error(n.getWhere(), isInitializer() ? "initializer.exception" : "uncaught.exception", c.getName());
			}
		    }
		} else {
		    Hashtable thrown = new Hashtable();
		    Expression val = (Expression)getValue();
		    val.checkInitializer(env, ctx, isStatic() ? 0 : 1, 
					 getType(), thrown);
		    setValue(val.convert(env, ctx, getType(), val));
		    ClassDeclaration except = 
			 env.getClassDeclaration(idJavaLangThrowable);
		    ClassDeclaration ignore = 
			env.getClassDeclaration(idJavaLangRuntimeException);

		    for (Enumeration e = thrown.keys(); e.hasMoreElements(); ) {
			ClassDeclaration c = (ClassDeclaration)e.nextElement();
			ClassDefinition def = c.getClassDefinition(env);

			if (!def.subClassOf(env, ignore) 
			    && def.subClassOf(env, except)) { 
   			    Node n = (Node)thrown.get(c);
			    env.error(n.getWhere(), 
				      "initializer.exception", c.getName());
			}
		    }
		}
		if (env.dump()) {
		    getValue().print(System.out);
		    System.out.println();
		}
	    }
	    status = getClassDefinition().getError() ? ERROR : CHECKED;
	}
    }

    /**
     * Inline the field
     */
    void inline(Environment env) throws ClassNotFound {
	switch (status) {
	  case PARSED:
	    check(env);
	    inline(env);
	    break;

	  case CHECKED:
	    if (env.dump()) {
		System.out.println("[inline field " + getClassDeclaration().getName() + "." + getName() + "]");
	    }
	    status = INLINING;
	    env = new Environment(env, this);

	    if (isMethod()) {
		if ((!isNative()) && (!isAbstract())) {
		    Context ctx = new Context(this);
		    for (Enumeration e = args.elements() ; e.hasMoreElements() ; ) {
			ctx.declare(env, (LocalField)e.nextElement());
		    }
		    setValue(((Statement)getValue()).inline(env, ctx));
		}
	    } else {
		if (getValue() != null)  {
		    Context ctx = new Context(this);
		    setValue(((Expression)getValue()).inlineValue(env, ctx));
		}
	    }
	    if (env.dump()) {
		System.out.println("[inlined field " + getClassDeclaration().getName() + "." + getName() + "]");
		if (getValue() != null) {
		    getValue().print(System.out);
		    System.out.println();
		} else {
		    System.out.println("<empty>");
		}
	    }
	    status = INLINED;
	    break;
	}
    }

    /**
     * Get the value of the field (or null if the value can't be determined)
     */
    public Node getValue(Environment env) throws ClassNotFound {
	if (getValue() == null) {
	    return null;
	}
	switch (status) {
	  case PARSED:
	    check(env);
	    return getValue(env);

	  case CHECKED:
	    inline(env);
	    return getValue(env);

	  case INLINED:
	    return getValue();
	}
	return null;
    }

    public boolean isInlineable(Environment env, boolean fromFinal) throws ClassNotFound {
	if (super.isInlineable(env, fromFinal)) {
	    getValue(env);
	    return (status == INLINED) && !getClassDefinition().getError();
	}
	return false;
    }
	

    /**
     * Get the initial value of the field
     */
    public Object getInitialValue() {
	if (isMethod() || (getValue() == null) || (!isFinal()) || (status != INLINED)) {
	    return null;
	}
	return ((Expression)getValue()).getValue();
    }

    /**
     * Generate code
     */
    public void code(Environment env, Assembler asm) throws ClassNotFound {
	switch (status) {
	  case PARSED:
	    check(env);
	    code(env, asm);
	    return;

	  case CHECKED:
	    inline(env);
	    code(env, asm);
	    return;

	  case INLINED:
	    // Actually generate code
	    if (env.dump()) {
		System.out.println("[code field " + getClassDeclaration().getName() + "." + getName() + "]");
	    }
	    if (isMethod() && (!isNative()) && (!isAbstract())) {
		env = new Environment(env, this);
		Context ctx = new Context(this);
		Statement s = (Statement)getValue();

		for (Enumeration e = args.elements() ; e.hasMoreElements() ; ) {
		    LocalField f = (LocalField)e.nextElement();
		    ctx.declare(env, f);
		    //ctx.declare(env, (LocalField)e.nextElement());
		}

		/*
		if (isConstructor() && ((s == null) || !s.firstConstructor())) {
		    ClassDeclaration c = getClassDefinition().getSuperClass();
		    if (c != null) {
			FieldDefinition field = c.getClassDefinition(env).matchMethod(env, getClassDefinition(), idInit);
			asm.add(getWhere(), opc_aload, new Integer(0));
			asm.add(getWhere(), opc_invokenonvirtual, field);
			asm.add(getWhere(), opc_pop);
		    }

		    // Ouput initialization code
		    for (FieldDefinition f = getClassDefinition().getFirstField() ; f != null ; f = f.getNextField()) {
			if (!f.isStatic()) {
			    f.codeInit(env, ctx, asm);
			}
		    }
		}
		*/
		if (s != null) {
		    s.code(env, ctx, asm);
		}
		if (getType().getReturnType().isType(TC_VOID) && !isInitializer()) {
		    asm.add(getWhere(), opc_return);
		}
	    }
	    return;
	}
    }

    public void codeInit(Environment env, Context ctx, Assembler asm) throws ClassNotFound {
	if (isMethod()) {
	    return;
	}
	switch (status) {
	  case PARSED:
	    check(env);
	    codeInit(env, ctx, asm);
	    return;

	  case CHECKED:
	    inline(env);
	    codeInit(env, ctx, asm);
	    return;

	  case INLINED:
	    // Actually generate code
	    if (env.dump()) {
		System.out.println("[code initializer  " + getClassDeclaration().getName() + "." + getName() + "]");
	    }
	    if (getValue() != null) {
		Expression e = (Expression)getValue();
		if (isStatic()) {
		    if ((getInitialValue() == null) && !e.equalsDefault()) {
			e.codeValue(env, ctx, asm);
			asm.add(getWhere(), opc_putstatic, this);
		    }
		} else if (!e.equalsDefault()) {
		    asm.add(getWhere(), opc_aload, new Integer(0));
		    e.codeValue(env, ctx, asm);
		    asm.add(getWhere(), opc_putfield, this);
		}
	    }
	    return;
	}
    }

    /**
     * Print for debugging
     */
    public void print(PrintStream out) {
	super.print(out);
	if (getValue() != null) {
	    getValue().print(out);
	    out.println();
	}
    }
}