Milán Major

netscape-revival

1 branch
Code

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

/*
 * @(#)SourceClass.java	1.54 95/12/07 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.tree.CompoundStatement;
import sun.tools.asm.Assembler;
import sun.tools.asm.ConstantPool;
import java.util.Vector;
import java.util.Enumeration;
import java.io.IOException;
import java.io.OutputStream;
import java.io.DataOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;

/**
 * This class represents an Java class as it is read from
 * an Java source file.
 */

public final
class SourceClass extends ClassDefinition {
    /**
     * The classes and packages imported by this class
     */
    Imports imports;

    /**
     * The default constructor
     */
    SourceField defConstructor;

    /**
     * The constant pool
     */
    ConstantPool tab = new ConstantPool();

    /**
     * Constructor
     */
    public SourceClass(Environment env, int where, ClassDeclaration declaration, String documentation,
		       int modifiers, ClassDeclaration superClass, ClassDeclaration interfaces[], 
		       Imports imports) {
	super(env.getSource(), where, declaration, modifiers, superClass, interfaces);
	this.imports = imports;
	this.documentation = documentation;
    }

    /**
     * Return imports
     */
    public final Imports getImports() {
	return imports;
    }

    /**
     * Add a dependency
     */
    public void addDependency(ClassDeclaration c) {
	if (tab != null) {
	    tab.put(c);
	}
    }

    /**
     * Check method override
     */
    void checkOverride(Environment env, 
		       ClassDeclaration clazz, 
		       FieldDefinition field) throws ClassNotFound {
        ClassDefinition c = clazz.getClassDefinition(env);
	for (FieldDefinition f = c.getFirstMatch(field.getName()); f != null;
	              f = f.getNextMatch()) {
	    if (f.isPrivate() || f.isVariable() || 
		      !field.getType().equalArguments(f.getType())) {
		continue;
	    }
	    if (f.isFinal()) {
		env.error(field.getWhere(), "final.meth.override", 
			  field, f.getClassDeclaration());
		return;
	    }
	    Type t1 = field.getType().getReturnType();
	    Type t2 = f.getType().getReturnType();
	    if (!t1.equals(t2)) {
		// We used to have 
		//  && (!t1.isType(TC_CLASS)) 
		//  && (!t2.isType(TC_CLASS)) 
		//  && (!env.getClassDefinition(t2)
		//         .implementedBy(env, env.getClassDeclaration(t1)))
		env.error(field.getWhere(), "redef.return.type", field, f);
		return;
	    }
	    if (field.isStatic() != f.isStatic()) {
		String errName = 
		    f.isStatic() ? "override.static.meth" 
		                 : "override.instance.method.static";
		env.error(field.getWhere(), errName, 
			  f, f.getClassDeclaration());
		return;
	    }
	    if (field.isConstructor()) {
		continue;
	    }
	    if ((!field.isPublic()) && (!f.isPrivate())) {
		if (f.isPublic()) {
		    env.error(field.getWhere(), "override.public", 
			      f, f.getClassDeclaration());
		    return;
		}
		if (f.isProtected() && !field.isProtected()) {
		    env.error(field.getWhere(), 
			      "override.protected", f, f.getClassDeclaration());
		    return;
		}
		if (field.isPrivate()) {
		    env.error(field.getWhere(), 
			      "override.private", f, f.getClassDeclaration());
		    return;
		}
	    }
	    ClassDeclaration e1[] = field.getExceptions(env);
	    ClassDeclaration e2[] = f.getExceptions(env);
	    for (int i = 0 ; i < e1.length ; i++) {
		ClassDefinition c1 = e1[i].getClassDefinition(env);
		boolean ok = false;
		for (int j = 0 ; j < e2.length ; j++) {
		    if (c1.subClassOf(env, e2[j])) {
			ok = true;
			break;
		    }
		}
		if (!ok) {
		    env.error(field.getWhere(), "invalid.throws", c1, f, f.getClassDeclaration());
		    return;
		}
	    }
	}

	// check the super class
    if (c.getSuperClass() != null) {
	    checkOverride(env, c.getSuperClass(), field);
	}

	// check the interfaces
	ClassDeclaration intf[] = c.getInterfaces();
	for (int i = 0 ; i < intf.length ; i++) {
	    checkOverride(env, intf[i], field);
	}
    }

    /**
     * Add a field (check it first)
     */
    public void addField(Environment env, FieldDefinition f) {
	if (f.isMethod()) {
	    if (f.isConstructor()) {
		if (f.getClassDefinition().isInterface()) {
		    env.error(f.getWhere(), "intf.constructor");
		    return;
		}
		if (f.isNative() || f.isAbstract() || f.isStatic() || f.isSynchronized() || f.isFinal()) {
		    env.error(f.getWhere(), "constr.modifier", f);
		    f.subModifiers(M_NATIVE | M_ABSTRACT | M_STATIC | M_SYNCHRONIZED | M_FINAL);
		}
	    } else if (f.isInitializer()) {
		if (f.getClassDefinition().isInterface()) {
		    env.error(f.getWhere(), "intf.initializer");
		    return;
		}
	    }
	    if (f.getClassDefinition().isInterface() && 
		(f.isStatic() || f.isSynchronized() || f.isNative() 
		 || f.isFinal() || f.isPrivate() || f.isProtected())) {
		env.error(f.getWhere(), "intf.modifier.method", f);
		f.subModifiers(M_STATIC |  M_SYNCHRONIZED | M_NATIVE |
			       M_FINAL | M_PRIVATE);
	    }
	    if (f.isTransient()) {
		env.error(f.getWhere(), "transient.meth", f);
		f.subModifiers(M_TRANSIENT);
	    }
	    if (f.isVolatile()) {
		env.error(f.getWhere(), "volatile.meth", f);
		f.subModifiers(M_VOLATILE);
	    }
	    if (f.isStatic() && f.isAbstract()) {
		env.error(f.getWhere(), "static.modifier", f);
		f.subModifiers(M_STATIC);
	    }
	    if (f.isAbstract() || f.isNative()) {
		if (f.getValue() != null) {
		    env.error(f.getWhere(), "invalid.meth.body", f);
		    f.setValue(null);
		}
	    } else {
		if (f.getValue() == null) {
		    if (f.isConstructor()) {
			env.error(f.getWhere(), "no.constructor.body", f);
		    } else { 
			env.error(f.getWhere(), "no.meth.body", f);
		    }
		    f.addModifiers(M_ABSTRACT);
		}
	    }
            Vector arguments = f.getArguments();
	    if (arguments != null) { 
		// arguments can be null if this is an implicit abstract method
		int argumentLength = arguments.size();
		for (int i = 0; i < argumentLength; i++) {
		    FieldDefinition arg = 
			(FieldDefinition)arguments.elementAt(i);
		    if (arg.getType().isType(TC_VOID)) {
			env.error(arg.getWhere(), "void.argument", arg);
		    }
		}
	    }
	} else {
	    if (f.getType().isType(TC_VOID)) {
		env.error(f.getWhere(), "void.inst.var", f.getName());
		// REMIND: set type to error
		return;
	    }
	    if (f.isSynchronized() || f.isAbstract() || f.isNative()) {
		env.error(f.getWhere(), "var.modifier", f);
		f.subModifiers(M_SYNCHRONIZED | M_ABSTRACT | M_NATIVE);
	    }
	    if (f.isTransient() && (f.isFinal() || f.isStatic())) {
		env.error(f.getWhere(), "transient.modifier", f);
		f.subModifiers(M_FINAL | M_STATIC);
	    }
	    if (f.isFinal() && (f.getValue() == null)) {
		env.error(f.getWhere(), "initializer.needed", f);
		f.subModifiers(M_FINAL);
	    }
	    if (f.getClassDefinition().isInterface() &&
		  (f.isPrivate() || f.isProtected())) {
		env.error(f.getWhere(), "intf.modifier.field", f);
		f.subModifiers(M_PRIVATE);
	    }
	}
	if (!f.isInitializer()) {
	    for (FieldDefinition f2 = getFirstMatch(f.getName()) ; f2 != null ; f2 = f2.getNextMatch()) {
		if (f.isMethod()) {
		    if (f.getType().equals(f2.getType())) {
			env.error(f.getWhere(), "meth.multidef", f);
			return;
		    }
		    if (f.getType().equalArguments(f2.getType())) {
			env.error(f.getWhere(), "meth.redef.rettype", f, f2);
			return;
		    }
		} else {
		    env.error(f.getWhere(), "var.multidef", f, f2);
		    return;
		}
	    }
	}

	addField(f);
    }

    private void 
    addAbstractMethodsFromInterfaces(Environment env, ClassDefinition c) 
	     throws ClassNotFound { 
	FieldDefinition f;
	if (c.isInterface()) {
	    // "this" implements c.  Look at each of the methods in c and
	    // and ensure that this method implements them.
	    for (f = c.getFirstField(); f != null ; f = f.getNextField()) {
		if (f.isMethod() && f.isAbstract() && 
		        (findMethod(env, f.getName(), f.getType()) == null)) {
		    // No, we don't implement it.  Implicitly create the
		    // abstract method for this guy.
		    SourceField fieldCopy = new SourceField(f, this, env);
		    // System.out.println("Adding " + fieldCopy + " to class " + this);
		    addField(env, fieldCopy);
		}
	    }
	}

	// we don't need to look at the superclass.  It must already have
	// declared the appropriate methods abstract within itself.
	// ClassDeclaration sup = def.getSuperClass();
	// if (sup != null) 
	//   addAbstractMethodsFromInterfaces(env, sup.getClassDefinition(env));
	
	ClassDeclaration interfaces[] = c.getInterfaces();
	for (int i = 0 ; i < interfaces.length ; i++) {
	    ClassDefinition intDef = interfaces[i].getClassDefinition(env);
	    addAbstractMethodsFromInterfaces(env, intDef);
	}
    }


    /**
     * Check this class.
     */
    public void check(Environment env) throws ClassNotFound {
	Identifier nm = getClassDeclaration().getName();
	if (env.verbose()) {
	    env.output("[checking class " + nm + "]");
	}

	basicCheck(env);

	// Make sure it was defined in the right file
	if (isPublic()) {
	    String fname = getName().getName() + ".java";
	    String src = ((ClassFile)getSource()).getName();
	    if (env.warnings() && !src.equals(fname)) {
		env.error(getWhere(), "public.class.file", this, fname);
	    }
	}

	if (isInterface()) {
	    if (isFinal()) {
		env.error(getWhere(), "final.intf", nm);
	    }
	} else {
	    // Check super class
	    if (getSuperClass() != null) {
		try {
		    ClassDefinition def = getSuperClass().getClassDefinition(env);
		    if (!canAccess(env, getSuperClass())) {
			env.error(getWhere(), "cant.access.class", getSuperClass());
			superClass = null;
		    } else if (def.isFinal()) {
			env.error(getWhere(), "super.is.final", getSuperClass());
			superClass = null;
		    } else if (def.isInterface()) {
			env.error(getWhere(), "super.is.intf", getSuperClass());
			superClass = null;
		    } else if (superClassOf(env, getSuperClass())) {
			env.error(getWhere(), "cyclic.super");
			superClass = null;
		    }
		} catch (ClassNotFound e) {
		    env.error(getWhere(), "super.not.found", e.name, this);
		    superClass = null;
		}
	    }
	}

	// Check interfaces
	for (int i = 0 ; i < interfaces.length ; i++) {
	    ClassDeclaration intf = interfaces[i];
	    try {
		if (!canAccess(env, intf)) {
		    env.error(getWhere(), "cant.access.class", intf);
		} else if (!intf.getClassDefinition(env).isInterface()) {
		    env.error(getWhere(), "not.intf", intf);
		} else if (isInterface() && implementedBy(env, intf)) {
		    env.error(getWhere(), "cyclic.intf", intf);
		}
	    } catch (ClassNotFound ee) {
		env.error(getWhere(), "intf.not.found", ee.name, this);
	    }
	}

	// bail out if there were any errors
	if (getError()) {
	    return;
	}

	try {
	    // Check override
	    for (FieldDefinition f = getFirstField() ; f != null ; f = f.getNextField()) {
		if (f.isVariable()) {
		    continue;
		}
		
		// check the super class
		if (getSuperClass() != null) {
		    checkOverride(env, getSuperClass(), f);
		}

		// check the interfaces
		for (int i = 0 ; i < interfaces.length ; i++) {
		    checkOverride(env, interfaces[i], f);
		}
	    }
	} catch (ClassNotFound ee) {
	    env.error(getWhere(), "class.not.found", ee.name, this);
	}

	// bail out if there were any errors
	if (getError()) {
	    return;
	}

	if (isFinal() && isAbstract()) {
	    env.error(where, "final.abstract", this.getName().getName());
	}

	// Check if abstract
	if ((!isInterface()) && (!isAbstract()) && isAbstract(env)) {
	    modifiers |= M_ABSTRACT;
	    String errName = 
		  isFinal() ? "abstract.class.not.final" : "abstract.class";
	    Enumeration e = getAbstractFields(env);
	    while (e.hasMoreElements()) { 
		FieldDefinition f = (FieldDefinition)e.nextElement();
		env.error(where, errName, this, 
			  f, f.getDefiningClassDeclaration());
	    }
	}

	// Check field definitions
	for (FieldDefinition f = getFirstField() ; f != null ; f = f.getNextField()) {
	    try {
		f.check(env);
	    } catch (ClassNotFound ee) {
		env.error(f.getWhere(), "class.not.found", ee.name, this);
	    }
	}

	// bail out if there were any errors
	if (getError()) {
	    return;
	}
    }

    /**
     * Check this class has its superclass and its interfaces.  Also
     * force it to have an <init> method (if it doesn't already have one)
     * and to have all the abstract methods of its parents.
     */
    private boolean basicChecking = false;
    private boolean basicCheckDone = false;
    protected void basicCheck(Environment env) throws ClassNotFound {
	if (basicChecking || basicCheckDone) 
	    return;
	basicChecking = true;

	env = new Environment(env, this);

	// check the existence of the superclass
	if (getSuperClass() != null) {
	    try {
		getSuperClass().getClassDefinition(env);
	    } catch (ClassNotFound e) {
		env.error(getWhere(), "super.not.found", e.name, this);
		superClass = null;
		setError(true);
	    }
	}
	
	// Check the existence of all the interfaces
	for (int i = 0 ; i < interfaces.length ; i++) {
	    try { 
		interfaces[i].getClassDefinition(env);
	    } catch (ClassNotFound e) {	
		env.error(getWhere(), "intf.not.found", e.name, this);
		ClassDeclaration newInterfaces[] = 
		    new ClassDeclaration[interfaces.length - 1];
		System.arraycopy(interfaces, 0, newInterfaces, 0, i);
		System.arraycopy(interfaces, i + 1, newInterfaces, i, 
				 newInterfaces.length - i);
		interfaces = newInterfaces;
		setError(true);
	    }
	}

	if (!isInterface()) {
	    // add implicit <init> method, if necessary
	    if (getFirstMatch(idInit) == null) {
		Node code = new CompoundStatement(getWhere(), new Statement[0]);
		Type t = Type.tMethod(Type.tVoid);
	        SourceField f = 
		    new SourceField(getWhere(), this, null, 
				    isPublic() ? M_PUBLIC : 0,
				    t, idInit, 
				    null, new ClassDeclaration[0], code);
		addField(f);
		
	    }
	    // add implicit abstract methods, if necessary
	    if (!getError()) 
		addAbstractMethodsFromInterfaces(env, this);
	}
	basicChecking = false;
	basicCheckDone = true;
    }




    /**
     * A list of active ongoing compilations. This list
     * is used to stop two compilations from saving the
     * same class.
     */
    private static Vector active = new Vector();

    /**
     * Compile this class
     */
    public void compile(Environment env, OutputStream out) throws InterruptedException, IOException {
	synchronized (active) {
	    while (active.contains(getName())) {
		active.wait();
	    }
	    active.addElement(getName());
	}

	try {
	    compileClass(env, out);
	} catch (ClassNotFound e) {
	    throw new CompilerError(e);
	} finally {
	    synchronized (active) {
		active.removeElement(getName());
		active.notifyAll();
	    }
	}
    }

    private void compileClass(Environment env, OutputStream out) throws IOException, ClassNotFound {
	env = new Environment(env, this);
	check(env);

	// bail out if there were any errors
	if (getError()) {
	    return;
	}

	Vector variables = new Vector();
	Vector methods = new Vector();
	CompilerField init = new CompilerField(new FieldDefinition(getWhere(), this, M_STATIC, Type.tMethod(Type.tVoid), idClassInit, new ClassDeclaration[0], null), new Assembler());
	Context ctx = new Context(init.field);

	// System.out.println("compile class " + getName());

	// Generate code for all fields
	for (FieldDefinition field = getFirstField() ; field != null ; field = field.getNextField()) {
	    //System.out.println("compile field " + field.getName());

	    try {
		if (field.isMethod()) {
		    if (field.isInitializer()) {
			((SourceField)field).code(env, init.asm);
		    } else {
			CompilerField f = new CompilerField(field, new Assembler());
			((SourceField)field).code(env, f.asm);
			methods.addElement(f);
		    }
		} else {
		    CompilerField f = new CompilerField(field, null);
		    variables.addElement(f);
		    if (field.isStatic()) {
			field.codeInit(env, ctx, init.asm);
		    }
		}
	    } catch (CompilerError ee) {
		ee.printStackTrace();
		env.error(field, 0, "generic", field.getClassDeclaration() + ":" + field + "@" + ee.toString(), null, null);
	    }
	}
	if (!init.asm.empty()) {
	    init.asm.add(getWhere(), opc_return);
	    methods.addElement(init);
	}

	// bail out if there were any errors
	if (getError()) {
	    return;
	}

	// Insert constants
	tab.put("Code");
	tab.put("ConstantValue");
	tab.put("LocalVariables");
	tab.put("SourceFile");
	tab.put("Exceptions");
	if (!env.optimize()) {
	    tab.put("LineNumberTable");
	}
	if (env.debug()) {
	    tab.put("LocalVariableTable");
	}

	String sourceFile = ((ClassFile)getSource()).getName();
	tab.put(sourceFile);
	tab.put(getClassDeclaration());
	if (getSuperClass() != null) {
	    tab.put(getSuperClass());
	}
	for (int i = 0 ; i < interfaces.length ; i++) {
	    tab.put(interfaces[i]);
	}

	// Optimize Code and Collect method constants
	for (Enumeration e = methods.elements() ; e.hasMoreElements() ; ) {
	    CompilerField f = (CompilerField)e.nextElement();
	    try {
		f.asm.optimize(env);
		f.asm.collect(env, f.field, tab);
		tab.put(f.name);
		tab.put(f.sig);
		ClassDeclaration exp[] = f.field.getExceptions(env);
		for (int i = 0 ; i < exp.length ; i++) {
		    tab.put(exp[i]);
		}
	    } catch (Exception ee) {
		ee.printStackTrace();
		env.error(f.field, -1, "generic", f.field.getName() + "@" + ee.toString(), null, null);
		f.asm.listing(System.out);
	    }
	}

	// Collect field constants
	for (Enumeration e = variables.elements() ; e.hasMoreElements() ; ) {
	    CompilerField f = (CompilerField)e.nextElement();
	    tab.put(f.name);
	    tab.put(f.sig);

	    Object val = f.field.getInitialValue();
	    if (val != null) {
		tab.put((val instanceof String) ? new StringExpression(f.field.getWhere(), (String)val) : val);
	    }
	}

	// Write header
	DataOutputStream data = new DataOutputStream(out);
	data.writeInt(JAVA_MAGIC);
	data.writeShort(JAVA_MINOR_VERSION);
	data.writeShort(JAVA_VERSION);
	tab.write(env, data);

	// Write class information
	data.writeShort(getModifiers() & MM_CLASS);
	data.writeShort(tab.index(getClassDeclaration()));
	data.writeShort((getSuperClass() != null) ? tab.index(getSuperClass()) : 0);
	data.writeShort(interfaces.length);
	for (int i = 0 ; i < interfaces.length ; i++) {
	    data.writeShort(tab.index(interfaces[i]));
	}

	// write variables
	ByteArrayOutputStream buf = new ByteArrayOutputStream(256);
	ByteArrayOutputStream attbuf = new ByteArrayOutputStream(256);
	DataOutputStream databuf = new DataOutputStream(buf);

	data.writeShort(variables.size());
	for (Enumeration e = variables.elements() ; e.hasMoreElements() ; ) {
	    CompilerField f = (CompilerField)e.nextElement();
	    Object val = f.field.getInitialValue();

	    data.writeShort(f.field.getModifiers() & MM_FIELD);
	    data.writeShort(tab.index(f.name));
	    data.writeShort(tab.index(f.sig));

	    if (val != null) {
		data.writeShort(1);
		data.writeShort(tab.index("ConstantValue"));
		data.writeInt(2);
		data.writeShort(tab.index((val instanceof String) ? new StringExpression(f.field.getWhere(), (String)val) : val));
	    } else {
		data.writeShort(0);
	    }
	}

	// write methods

	data.writeShort(methods.size());
	for (Enumeration e = methods.elements() ; e.hasMoreElements() ; ) {
	    CompilerField f = (CompilerField)e.nextElement();
	    data.writeShort(f.field.getModifiers() & MM_METHOD);
	    data.writeShort(tab.index(f.name));
	    data.writeShort(tab.index(f.sig));
	    ClassDeclaration exp[] = f.field.getExceptions(env);

	    if (!f.asm.empty()) {
		data.writeShort((exp.length > 0) ? 2 : 1);
		f.asm.write(env, databuf, f.field, tab);
		int natts = 0;
		if (!env.optimize()) {
		    natts++;
		}
		if (env.debug()) {
		    natts++;
		}
		databuf.writeShort(natts);

		if (!env.optimize()) {
		    f.asm.writeLineNumberTable(env, new DataOutputStream(attbuf), tab);
		    databuf.writeShort(tab.index("LineNumberTable"));
		    databuf.writeInt(attbuf.size());
		    attbuf.writeTo(buf);
		    attbuf.reset();
		}

		if (env.debug()) {
		    f.asm.writeLocalVariableTable(env, f.field, new DataOutputStream(attbuf), tab);
		    databuf.writeShort(tab.index("LocalVariableTable"));
		    databuf.writeInt(attbuf.size());
		    attbuf.writeTo(buf);
		    attbuf.reset();
		}

		data.writeShort(tab.index("Code"));
		data.writeInt(buf.size());
		buf.writeTo(data);
		buf.reset();
	    } else {
		data.writeShort((exp.length > 0) ? 1 : 0);
	    }

	    if (exp.length > 0) {
		data.writeShort(tab.index("Exceptions"));
		data.writeInt(2 + exp.length * 2);
		data.writeShort(exp.length);
		for (int i = 0 ; i < exp.length ; i++) {
		    data.writeShort(tab.index(exp[i]));
		}
	    }
	}

	// class attributes
	data.writeShort(1);
	data.writeShort(tab.index("SourceFile"));
	data.writeInt(2);
	data.writeShort(tab.index(sourceFile));

	// Cleanup
	data.flush();
	tab = null;
    }
}