// $Id: XobjectFile.java,v 1.33 2002/02/27 16:05:04 msato Exp $
// $RWC_Release: Omni-1.6 $
// $RWC_Copyright:
//  Omni Compiler Software Version 1.5-1.6
//  Copyright (C) 2002 PC Cluster Consortium
//  
//  This software is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License version
//  2.1 published by the Free Software Foundation.
//  
//  Omni Compiler Software Version 1.0-1.4
//  Copyright (C) 1999, 2000, 2001.
//   Tsukuba Research Center, Real World Computing Partnership, Japan.
//  
//  Please check the Copyright and License information in the files named
//  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
//  Software release kit.
//  
//  
//  $
package exc.object;

import java.io.*;
import java.util.*;

//
// File interface
//
public class XobjectFile extends XobjectDefEnv {
  String inFile, outFile;
  protected Xtype typeList = null;
  protected Xtype typeListTail;
  protected Hashtable typeIdTable = new Hashtable();

  protected String sourceFileName;
  protected Vector headerLines;
  protected Vector tailerLines;
  
  public int Language = C_LANG; 	// default C
  public final static int C_LANG = 0;
  public final static int F77_LANG = 1;
  public final static int CXX_LANG = 2;

  int gensym;		// gensym counter

  public boolean debugFlag;
  public static boolean gcc_huge_common_bug;
  
  // constructor
  public XobjectFile(){ }
  
  // constructor
  public XobjectFile(String ifile,String ofile){
    inFile = ifile;
    outFile = ofile;
  }

  static void fatal(String msg){
    System.err.println("Fatal XobjectFile: "+msg);
    Thread.dumpStack();
    System.exit(1);
  }

  public String getSourceFileName() { return sourceFileName; }
    
  //
  // Global Symbol table management
  // 

  public Xobject getGlobalIdentList(){
    return identList;
  }

  public Ident declGlobalIdent(String name,Xtype t){
    Ident id = findIdent(name);
    if(id != null){
      if(id.Type().equals(t)){
	switch(id.getStorageClass()){
	case StorageClass.EXTDEF:
	  id.setStorageClass(StorageClass.EXTDEF);
	case StorageClass.EXTERN:
	case StorageClass.STATIC:
	  return id;
	}
      }
      fatal("declGlobalIdent: id is already defined,"+id);
      return null;
    } 
    if(t.isArray())
      id = new Ident(name,StorageClass.EXTDEF,t,
		     Xcons.Symbol(Xcode.ARRAY_ADDR,
				  Xtype.Pointer(t),name));
    else if(t.isFunction())
      id = new Ident(name,StorageClass.EXTDEF,t,
		     Xcons.Symbol(Xcode.FUNC_ADDR,
				  Xtype.Pointer(t),name));
    else 
      id = new Ident(name,StorageClass.EXTDEF,t,
		     Xcons.Symbol(Xcode.VAR_ADDR,
				  Xtype.Pointer(t),name));
    identList.add(id);
    return id;
  }

  public Ident declStaticIdent(String name,Xtype t){
    Ident id = findIdent(name);
    if(id != null){
      if(id.Type().equals(t)){
	switch(id.getStorageClass()){
	case StorageClass.EXTERN:
	case StorageClass.EXTDEF:
	  id.setStorageClass(StorageClass.STATIC);
	case StorageClass.STATIC:
	  return id;
	}
      }
      fatal("declStaticIdent: id is already defined,"+id);
      return null;
    } 
    if(t.isArray())
      id = new Ident(name,StorageClass.STATIC,t,
		     Xcons.Symbol(Xcode.ARRAY_ADDR,
				  Xtype.Pointer(t),name));
    else if(t.isFunction())
      id = new Ident(name,StorageClass.STATIC,t,
		     Xcons.Symbol(Xcode.FUNC_ADDR,
				  Xtype.Pointer(t),name));
    else 
      id = new Ident(name,StorageClass.STATIC,t,
		     Xcons.Symbol(Xcode.VAR_ADDR,
				  Xtype.Pointer(t),name));
    identList.add(id);
    return id;
  }

  public Ident declExternIdent(String name,Xtype t){
    Ident id = findIdent(name);
    if(id != null){
      if(id.Type().equals(t)){
	switch(id.getStorageClass()){
	case StorageClass.EXTERN:
	case StorageClass.EXTDEF:
	case StorageClass.STATIC:
	  return id;
	}
      }
      fatal("declExternIdent: id is already defined,"+id);
      return null;
    } 
    if(t.isArray())
      id = new Ident(name,StorageClass.EXTERN,t,
		     Xcons.Symbol(Xcode.ARRAY_ADDR,
				  Xtype.Pointer(t),name));
    else if(t.isFunction())
      id = new Ident(name,StorageClass.EXTERN,t,
		     Xcons.Symbol(Xcode.FUNC_ADDR,
				  Xtype.Pointer(t),name));
    else 
      id = new Ident(name,StorageClass.EXTERN,t,
		     Xcons.Symbol(Xcode.VAR_ADDR,
				  Xtype.Pointer(t),name));
    identList.add(id);
    return id;
  }

  public Ident findFCommMemberIdent(Ident id){
    if(id.getStorageClass() != StorageClass.FCOMM) return null;
    Xobject addr = id.getAddr();
    if(addr.Opcode() != Xcode.FCOMM_ARRAY_ADDR &&
       addr.Opcode() != Xcode.FCOMM_VAR_ADDR) return null;
    Ident common_id = findIdent(addr.getArg(1).getArg(0).getName());
    String common_no = "_"+addr.getArg(1).getArg(1).getInt();
    for(XobjArgs a = common_id.Type().getMemberList().getArgs();
	a != null; a = a.nextArgs()){
      Ident nid = (Ident)a.getArg();
      if(nid.getName().equals(common_no)){
	for(XobjArgs aa = nid.Type().getMemberList().getArgs();
	    aa != null; aa = aa.nextArgs()){
	  Ident mid = (Ident)aa.getArg();
	  if(mid.getName().equals(id.getName())) return mid;
	}
	break;
      }
    }
    return null;
  }

  public String genSym(String leader){
    String s = leader;
    s = s+"_"+gensym++;
    s.intern();
    return s;
  }

  Xtype findType(String name){
    switch(name.charAt(0)){
    case 'P':
    case 'F':
    case 'A':
    case 'E':
    case 'S':
    case 'U':
    case 'B':
    case 'C':
      int tid = Integer.parseInt(name.substring(1),16);
      Xtype t = (Xtype)typeIdTable.get(new Integer(tid));
      if(t != null) return t;
      /* if not found create dummy type */
      t = new Xtype(Xtype.UNDEF);
      t.type_id = tid;
      return t;

    default:
      /* basic type */
      for(int i = 0; i < BasicType.N_BASIC_TYPE; i++)
	if(name.equals(BasicType.getName(i))) 
	  return BasicType.getType(i);
      fatal("bad type '"+name+"'");
      return null;
    }
  }
    
  //
  // I/O
  //
  public void Input(){
    Input(inFile);
  }

  public void Input(String file){
    Reader in; 
    try {
      if(file == null)
	in = new InputStreamReader(System.in);
      else
	in = new FileReader(file);
      Input(in);
      if(file != null) in.close();
    } catch(FileNotFoundException e){
      fatal("cannot open file '"+file+"'");
      return;
    } catch(IOException e){
      fatal("IO exception");
      return;
    }
  }

  public void Input(Reader fin){
    XobjectReader in = new XobjectReader(fin,this);

    char c;
    String s;
    int tid,size;
    byte align;
    boolean is_const,is_volatile,is_func_proto;
    Xtype ref;
    Xtype type;
    Xobject id_list,v;
    int t;

    while((c = in.getc()) == '#'){
      if(in.getc() == '#' && in.getc() == '#'){
	String info_name = in.readName();
	if(in.getc() == ':'){
	  if(info_name.equals("source"))
	    sourceFileName = in.readFileName();
	}
      }
      /* skip to new line */
      while((c = in.getc()) != '\n'){
	if(c == in.EOF) fatal("unexpected EOF");
      }
    }
    in.ungetc(c);
    
    //	System.out.println("reading Types...");

    /* input types */
    while(true){
      c = in.skipSpace();
      if(c == '%') break;	// next section
      if(c != '{') fatal("bad type section");
      s = in.readName();
      c = s.charAt(0);
      tid = Integer.parseInt(s.substring(1),16);

      //	    System.out.println("reading type "+s);

      size = in.readInt();
      align = (byte) in.readInt();
      is_const = in.readInt() == 1;
      is_volatile = in.readInt() == 1;
	    
      switch(c){
      case 'B':
	ref = in.readType();
	type = ref.copy();
	type.copyed = ref;
	type.is_const = is_const;
	type.is_volatile = is_volatile;
	break;
	
      case 'P':  /* pointer */
	ref = in.readType();
	type = new PointerType(tid,ref,size,align,
			       is_const,is_volatile);
	break;

      case 'F':	/* function */
	ref = in.readType();
	is_func_proto = (in.readInt() == 1);
	Xobject param_list = in.readObject();
	type = new FunctionType(tid,ref,param_list,size,align,is_const,
				is_volatile,is_func_proto);
	break;
      case 'A':
	ref = in.readType();
	c = in.skipSpace();
	in.ungetc(c);	// peek
	Xobject asize = null;
	int dim = -1;
	if(c == '('){ /* adjustable array */
	  asize = in.readObject();
	} else dim = in.readInt();
	type = new ArrayType(tid,ref,dim,size,align,
			     is_const,is_volatile,asize);
	break;
      case 'E':
	Xobject moe_list = in.readObject();
	type = new EnumType(tid,moe_list,size,align,
			    is_const,is_volatile);
	break;
      case 'S':
	id_list  = in.readIdentList();
	type = new StructType(tid,id_list,size,align,
			      is_const,is_volatile);
	break;
      case 'U':
	id_list  = in.readIdentList();
	type = new UnionType(tid,id_list,size,align,
			     is_const,is_volatile);
	break;
      case 'C':
	id_list  = in.readIdentList();
	type = new ClassType(tid,id_list,size,align,
			     is_const,is_volatile);
	break;
      default:
	fatal("unknown type tag '"+c+"'");
	type = null;
      }
      if(in.skipSpace() != '}') fatal("bad type desc");
	    
      // put type list 
      if(typeList == null) typeList = type;
      else typeListTail.link = type;
      typeListTail = type;
      // put typeIdTable
      typeIdTable.put(new Integer(tid),type);
    }

    //      System.out.println("fixupTypeRef...");

    // fix up cyclic reference
    fixupTypeRef();

    //      System.out.println("reading IdentList...");

    /* read global id_list */
    identList = in.readIdentList();

    c = in.skipSpace();
    if(c != '%') fatal("bad global id list section");

    /* read declaration and function */
    //	    System.out.println("reading Decl...");
    while((v = in.readObject()) != null){
      add(v);
    }
    //	System.out.println("reading done ...");
  }

  void fixupTypeRef(){
    for(Xtype t = typeList; t != null; t = t.link){
      if(t instanceof FunctionType){
	FunctionType ft = (FunctionType)t;
	if(ft.ref.type_kind == Xtype.UNDEF)
	  ft.ref = findTypeId(ft.ref.type_id);
	if(t.getFuncParam() != null){
	  for(XobjArgs a = t.getFuncParam().getArgs();
	      a != null; a = a.nextArgs()){
	    Xobject x = a.getArg();
	    if(x == null || x.type == null) continue;
	    if(x.type.type_kind == Xtype.UNDEF)
	      x.type = findTypeId(x.type.type_id);
	  }
	}
      } else if(t instanceof PointerType){
	PointerType pt = (PointerType) t;
	if(pt.ref.type_kind == Xtype.UNDEF)
	  pt.ref = findTypeId(pt.ref.type_id);
      } else if(t instanceof ArrayType){
	ArrayType at = (ArrayType)t;
	if(at.ref.type_kind == Xtype.UNDEF)
	  at.ref = findTypeId(at.ref.type_id);
	if(at.asize != null) fixupTypeRefExpr(at.asize);
      } else if(t instanceof StructType || t instanceof UnionType){
	for(XobjArgs a = t.getMemberList().getArgs();
	    a != null; a = a.nextArgs()){
	  Ident ident = (Ident)a.getArg();
	  if(ident.type.type_kind == Xtype.UNDEF){
	    ident.type = findTypeId(ident.type.type_id);
	  }
	}
      }
    }
  }

  void fixupTypeRefExpr(Xobject e){
    if(e == null) return;
    XobjectIterator i = new topdownXobjectIterator(e);
    for(i.init();!i.end(); i.next()){
      Xobject x = i.getXobject();
      if(x != null && x.type != null && x.type.type_kind == Xtype.UNDEF)
	x.type = findTypeId(x.type.type_id);
    }
  }

  Xtype findTypeId(int id){
    Xtype t = (Xtype) typeIdTable.get(new Integer(id));
    if(t != null) return t;
    fatal("findTypeId: type is not found, id="+id);
    return null;
  }

  public void Decompile(){
    Decompile(outFile);
  }
	
  public void Decompile(String file){
    Writer out;

    try {
      if(file == null)
	out = new OutputStreamWriter(System.out);
      else
	out = new BufferedWriter(new FileWriter(file),4096);
      Decompile(out);
      if(file != null) out.close();
    } catch(FileNotFoundException e){
      fatal("cannot open file '"+file+"'");
      return;
    } catch(IOException e){
      fatal("IO exception");
      return;
    }
  }

  /* make defalut decomile writer */
  protected XobjectDecompileWriter makeDecompiler(Writer fout) {
    return new XobjectDecompileWriter(fout, this);
  }

  public void Decompile(Writer fout){
    // XobjectDecompileWriter out = new XobjectDecompileWriter(fout,this);
    XobjectDecompileWriter out = makeDecompiler(fout);

    if(Language == CXX_LANG) out.CxxFlag = true;

    out.println("/* decompile from source='"+ 
		(sourceFileName == null?"<unknown>":sourceFileName) +
		"' input="+(inFile == null?"<stdin>":inFile) +"' */");

    if(headerLines != null){
      for(int i = 0; i < headerLines.size(); i++)
	out.println((String)headerLines.elementAt(i));
    }

    if(sourceFileName != null) out.println("# 1 \""+sourceFileName+"\"");
 
    out.printIdentList(getGlobalIdentList());

    for(XobjectDef d = getHead(); d != null; d = d.getNext())
      out.print(d.getDef());

    if(tailerLines != null){
      for(int i = 0; i < tailerLines.size(); i++)
	out.println((String)tailerLines.elementAt(i));
    }

    out.flush();
  }

  public void addHeaderLine(String s){
    if(headerLines == null) headerLines = new Vector();
    headerLines.addElement(s);
  }

  public void addTailerLine(String s){
    if(tailerLines == null) tailerLines = new Vector();
    tailerLines.addElement(s);
  }

  public void Output(){
    Output(outFile);
  }

  public void Output(String file){
    Writer out;

    try {
      if(file == null)
	out = new OutputStreamWriter(System.out);
      else
	out = new BufferedWriter(new FileWriter(file),4096);
      Output(out);
      if(file != null) out.close();
    } catch(FileNotFoundException e){
      fatal("cannot open file '"+file+"'");
      return;
    } catch(IOException e){
      fatal("IO exception");
      return;
    }
  }

  public void Output(Writer fout){
    XobjectPrintWriter out = new XobjectPrintWriter(fout);
        
    collectAllTypes();
	
    /* dump types */
    for(Xtype type = typeList; type != null; type = type.link){
      if(type.getKind() == Xtype.BASIC &&
	 !type.isConst() && !type.isVolatile()) continue;

      out.print("{");
      out.printType(type);
      out.print(" ");
      out.printInt(type.getSize());
      out.print(" ");
      out.printInt(type.getAlign());
      out.print(" ");
      out.printBool(type.isConst());
      out.print(" ");
      out.printBool(type.isVolatile());
      out.print(" ");

      if(type.copyed != null){
	out.printType(type.copyed);
	out.println("}");
	continue;
      }

      switch(type.getKind()){
      case Xtype.BASIC:
	out.print(BasicType.getName(type.getBasicType()));
	break;
      case Xtype.POINTER:
	out.printType(type.getRef());
	break;
      case Xtype.FUNCTION:
	out.printType(type.getRef());
	out.print(" ");
	out.printBool(type.isFuncProto());
	out.print(" ");
	out.printObjectNoIndent(type.getFuncParam());
	break;
      case Xtype.ARRAY:
	out.printType(type.getRef());
	out.print(" ");
	out.printInt(type.getArrayDim());
	break;
      case Xtype.ENUM:
	out.printObjectNoIndent(type.getMoeList());
	break;
      case Xtype.STRUCT:
      case Xtype.UNION:
      case Xtype.CLASS:
	out.printIdentList(type.getMemberList());
	break;
      default:
	fatal("Output, bad type");
      }
      out.println("}");
    }

    /* output global env */
    out.println("%");
    out.printIdentList(identList);
    out.println();

    /* output declaration and function */
    out.println("%");
    for(XobjectDef d = getHead(); d != null; d = d.getNext())
      if(d.getDef() != null) out.printObject(d.getDef());
    out.flush();
  }

  int typeCounter = 0;

  /* collect all used types */
  void collectAllTypes(){
    typeList = null;
    typeCounter = 0;
    // collect types in GlobalIdentList
    collectType(identList);
    // collect types in GLobalDeclList
    for(XobjectDef d = getHead(); d != null; d = d.getNext())
      collectType(d.getDef());

    // clean up mark for the next phase
    for(Xtype type = typeList; type != null; type = type.link)
      type.is_marked = false;
  }
  
  void markType(Xtype t){
    if(t == null || t.is_marked) return;
    if(t.getKind() == Xtype.UNDEF){
      System.out.println("markType: undefined type ID="+
			 Integer.toHexString(t.Id()));
      /* fatal("markType: undefined type is found"); */
      return;
    }
    t.is_marked = true;
    t.type_id = typeCounter++;

    // put on type list
    if(typeList == null) typeList = t;
    else typeListTail.link = t;
    typeListTail = t;
    t.link = null;
    //    System.out.println("marked "+t);

    if(t instanceof PointerType || t instanceof ArrayType) {
      markType(t.getRef());
    } else if(t instanceof FunctionType){
      markType(t.getRef());
      collectType(t.getFuncParam());
    } else if(t instanceof StructType || t instanceof UnionType){
      collectType(t.getMemberList());
    }

    if(t.copyed != null) markType(t.copyed);
  }

  void collectType(Xobject x){
    if(x == null) return;
    markType(x.Type());
    if(x instanceof XobjList){
      for(XobjArgs a = x.getArgs(); a != null; a = a.nextArgs())
	collectType(a.getArg());
    } else if(x instanceof Ident){
      collectType(((Ident)x).getAddr());
    }
  }
}


