
/*
 * Copyright (c) 1998 David Stes.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Library General Public License as published 
 * by the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: assoc.m,v 1.17 1998/05/26 13:13:58 stes Exp $
 */

#include "config.h"
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <Object.h>
#include <ocstring.h>
#include "symbol.h"
#include <set.h>
#include <ordcltn.h>
#include <sequence.h>
#include <dictnary.h>
#include "node.h"
#include "expr.h"
#include "trlunit.h"
#include "type.h"
#include "options.h"
#include "datadef.h"
#include "classdef.h"
#include "msgxpr.h"

id trlunit;

@implementation TranslationUnit

+ new
{
  [Symbol commonsymbols];
  [Type commontypes];
  [Expr commonexprs];
  return trlunit = [super new];
}

- (int)msgcount
{
  return msgcount++;		/* count for tmp variables */
}

- gettmpvar
{
  return [String sprintf:"objcT%i", [trlunit msgcount]];
}

- (int)icachecount
{
  return icachecount++;		/* count for tmp variables */
}

- (int)blockcount
{
  return blockcount++;		/* count for tmp variables */
}

- (int)heapvarcount
{
  return heapvarcount++;	/* count for tmp variables */
}

- returnlabel
{
  return [String sprintf:"_cleanup%i", retlabelcount++];
}

- (BOOL)usingselfassign
{
  return usingselfassign;
}

- usingselfassign:(BOOL)x
{
  usingselfassign = x;
  return self;
}

- (BOOL)usingblocks
{
  return usingblocks;
}

- usingblocks:(BOOL)x
{
  usingblocks = x;
  return self;
}

- inlinecacheprologue
{
  /* inline cache data type emitted here, must match one from objcrt.m */
  if (o_cplus) {
    gs("struct objcrt_inlineCache {id cls;id (*imp)(...);};\n");
  } else {
    gs("struct objcrt_inlineCache {id cls;id (*imp)();};\n");
  }

  /* messages to nil with inlinecache */
  /* emit o_bind so that the definition matches the one in objcrt */
  if (o_cplus) {
    gextc();
    gf("id %s _nilHandler(...);\n", o_bind);
  } else {
    gf("id %s _nilHandler(id,char*);\n", o_bind);
  }

  return self;
}

- setmodversion:(char*)v
{
  modversion = v;
  return self;
}

static char *
mystrrchr(const char *s, int c)
{				/* sunos4.1.1 ? */
  char *t = (char *) s;

  while (*t) {
    t++;
  }

  while (t != s) {
    if (*t == c)
      return t;
    else
      t--;
  }

  return (*t == c) ? t : NULL;
}

- setmodname:(char *)filename
{
  id s;
  char *p;
  char *cp;

  p = mystrrchr(filename, o_pathsep[0]);
  s = [String str:(p) ? p + 1 : (char *) filename];
  p = [s strCopy];
  if ((cp = mystrrchr(p, '.')) != NULL)
    *cp = '\0';			/* strip extension */
  for (cp = p; *cp != '\0'; cp++) {
    if (!isalnum(*cp))
      *cp = '_';
  }

  /* module name */
  modname = p;

  /* name bind function */
  s = [String sprintf:"_OBJCBIND_%s", p];
  bindfunname = [s strCopy];

  /* name moddesc (because of bizar c++ problem, this used to be static) */
#ifndef MODDESCSTATIC
  s = [String sprintf:"%s_modDesc", p];
  moddescname = [s strCopy];
#else
  moddescname = "_modDesc";
#endif

  return self;
}

- (char *)moddescname
{
  assert(moddescname != NULL);
  return moddescname;
}

- checkbindprologue
{
  gs("\nextern char *objcrt_bindError(char *);\n");
  return self;
}

- prologue
{
  assert(modname != NULL);

  if (o_comments)
    gs("/* objc prologue */\n");

  if (o_otb) {
    gs("struct _PRIVATE {\n");
    gs("  struct OTB *isa;\n");
    gs("};\n");
    gs("struct OTB {\n");
    gs("  struct _PRIVATE *ptr;\n");
    g_otbvars();
    gs("};\n");
    gs("typedef struct OTB *id;\n");
  } else {
    if (!o_refcnt) {
      gs("struct _PRIVATE { struct _PRIVATE *isa; };\n");
    } else {
      gs("struct _PRIVATE { struct _PRIVATE *isa;unsigned int _refcnt; };\n");
    }
    gs("typedef struct _PRIVATE *id;\n");
  }

  [[ClassDef new] genshartype];

  if (!o_fwd) {
    if (o_cplus) {
      gextc();			/* in C++ IMP is defined as id *(...) */
      gf("id %s (* _imp(id,char*))(...);\n", o_bind);
      gextc();
      gf("id %s (* _impSuper(id,char*))(...);\n", o_bind);
    } else {
      gf("extern id %s (* _imp(id,char*))();\n", o_bind);
      gf("extern id %s (* _impSuper(id,char*))();\n", o_bind);
    }
  }
  if (o_inlinecache)
    [self inlinecacheprologue];

  gf("extern struct modDescriptor %s *%s(void);\n", o_bind, bindfunname);

  if (o_refbind) {
    /* workaround 'mwcc' dead code optimizer, force reference to bindfun */
    gf("static char **selTransTbl = (char **)%s;\n", bindfunname);
  } else {
    /* 'lcc' chokes on the above initializer, so also need this case */
    gs("static char **selTransTbl;\n");
  }

  if (o_fwd) {
    if (o_cplus) {
      gs("static id (**fwdTransTbl)(...);\n");
    } else {
      gs("static id (**fwdTransTbl)();\n");
    }
  }
  /* struct used in sharedType & defined by Stepstone objcc */
  if (o_cplus) {
    gs("struct _SLT {char *_cmd;id (*_imp)(...);};\n");
  } else {
    gs("struct _SLT {char *_cmd;id (*_imp)();};\n");
  }

  /* type for Objective C modules */
  gs("struct modDescriptor {\n");
  /* keep the following compat. with Stepstone objcc */
  gs("  char *modName;\n");
  gs("  char *modVersion;\n");
  gs("  long modStatus;\n");
  gs("  char *modMinSel;\n");
  gs("  char *modMaxSel;\n");
  gs("  id *modClsLst;\n");
  gs("  short modSelRef;\n");
  gs("  char **modSelTbl;\n");
  gs("  struct methodDescriptor *modMapTbl;\n");
  /* POC extensions if any */
  gs("};\n");

  gf("extern struct modDescriptor %s;\n", moddescname);
  if (o_checkbind)
    [self checkbindprologue];
  if (o_comments)
    gs("/* end of objc prologue */\n");
  return self;
}

- allclsimpls
{
  if (clsimpls)
    return clsimpls;
  if (clsimpl)
    return [[OrdCltn new] add:clsimpl];
  return nil;
}

- addclsimpl:c
{
  /* for Stepstone compatibility we have to support the oneperfile case */
  if (clsimpls) {
    assert(clsimpl == nil && [clsimpls size] >= 2);
    [clsimpls add:c];
  } else {
    if (clsimpl) {
      if (o_oneperfile) {
	fatal("only one implementation per file allowed");
      } else {
	clsimpls = [OrdCltn new];
	[clsimpls add:clsimpl];
	[clsimpls add:c];
	clsimpl = nil;
      }
    } else {
      clsimpl = c;
    }
  }
  return self;
}

- (int)seloffset:selname
{
  int n;
  id val;

  if (!selcltn) {
    selcltn = [OrdCltn new];
    seldic = [Dictionary new];
  }
  n = [selcltn size];
  if ((val = [seldic atKey:selname])) {
    return [val asInt];
  } else {
    [selcltn add:selname];
    [seldic atKey:selname put:[String sprintf:"%i", n]];
  }
  return n;
}

- (int)fwdoffset:msg
{
  int n;
  id val;

  if (!fwdcltn) {
    fwdcltn = [OrdCltn new];
    msgdic = [Dictionary new];
  }
  n = [fwdcltn size];
  /* try to find another selector with same argument types */
  /* see -hash and -isEqual: */
  /* it's okay if this fails, it will just generate more code */
  if ((val = [msgdic atKey:msg])) {
    if (o_debuginfo) {
      id x = [msg selector];
      id y = [[fwdcltn at:[val asInt]] selector];

      fprintf(stderr, "using '%s' dispatch fun for '%s'\n", [y str], [x str]);
    }
    return [val asInt];
  } else {
    [fwdcltn add:msg];
    [msgdic atKey:msg put:[String sprintf:"%i", n]];
  }
  return n;
}

- genmodclslst
{
  int i, n;

  assert(clsimpls && !o_oneperfile);
  gs("static id _modClsLst[] ={\n");
  for (i = 0, n = [clsimpls size]; i < n; i++) {
    STR s = [[clsimpls at:i] classname];

    /* &_Foo is the value of "id Foo := (id)&_Foo" */
    gf("(id)&_%s,\n", s);
  }
  /* must be NULL terminated */
  gs("(id)0};\n");
  return self;
}

- genseltranstbl
{
  int i, n = (selcltn) ? [selcltn size] : 0;

  gs("static char *_selTransTbl[] ={\n");
  for (i = 0; i < n; i++) {
    STR s = [[selcltn at:i] str];

    gf("\"%s\",\n", s);
  }
  /* always at least one entry (SGI cc chokes on empty decls) */
  gs("0\n};\n");
  return self;
}

- genfwdstubs
{
  int i, n;

  /* this can generate a lot of output
   * a good test in -fwdoffset: is important to reduce the size of gen. code
   */
  n = (fwdcltn) ? [fwdcltn size] : 0;
  for (i = 0; i < n; i++) {
    id msg = [fwdcltn at:i];

    [msg genargstruct];
    [msg gendispfun];
    [msg genfwdstub];
  }
  return self;
}

- genfwdtranstbl
{
  int i, n;

  n = (fwdcltn) ? [fwdcltn size] : 0;

  if (o_cplus) {
    gs("static id (*(_fwdTransTbl[]))(...) ={\n");
  } else {
    gs("static id (*(_fwdTransTbl[]))() ={\n");
  }

  for (i = 0; i < n; i++) {
    char *s = [[fwdcltn at:i] fwdname];

    if (o_cplus) {
      gf("(id(*)(...))%s,\n", s);
    } else {
      gf("(id(*)())%s,\n", s);
    }
  }

  /* always at least one entry (SGI cc chokes on empty decls)    */
  if (o_cplus) {
    gs("(id(*)(...))0\n};\n");
  } else {
    gs("(id(*)())0\n};\n");
  }

  return self;
}

/* the modClsLst field is either a pointer to a class (one per file case) */
/* or it is a pointer to a list with >= 2 class pointers (more than one) */
/* our runtime must be compatible with both cases */

#define MOD_MORETHANONE 0x4L	/* must match objcrt.m value !! */

- genmoddesc
{
  int selsize = (selcltn) ? [selcltn size] : 0;
  long modstatus = (clsimpls) ? MOD_MORETHANONE : 0;

#ifdef MODDESCSTATIC
  gs("static");
#endif
  gf("struct modDescriptor %s = {\n", moddescname);
  gf("  \"%s\",\n", modname);
  gf("  \"%s\",\n", modversion);
  gf("  %iL,\n", modstatus);
/* min/max are probably used by objcc at runtime for checking pointer range */
  gs("  0,\n");			/* modMinSel */
  gs("  0,\n");			/* modMaxSel */
  if (clsimpl) {
    /* this is the "one class per file" case */
    char *cname = [clsimpl classname];

    gf("  &%s,\n", cname);
  } else {
    if (clsimpls) {
      /* this is the more than one case */
      assert([clsimpls size] >= 2);
      gs("  _modClsLst,\n");	/* modClsLst */
    } else {
      gs("  0,\n");		/* NULL modClsLst */
    }
  }
  if (selsize) {
    gf("  %i,\n", selsize);	/* modSelRef */
    gs("  _selTransTbl,\n");	/* modSelTbl */
  } else {
    gs("  0,\n");		/* modSelRef */
    gs("  0,\n");		/* modSelTbl */
  }
  /* we don't support static references _mapTbl */
  /* this is just here for Stepstone compatibility */
  gs("  0\n};\n");		/* modMapTbl */
  return self;
}

- genglobfuncall
{
  id all = [self allclsimpls];

  if (all)
    [all elementsPerform:@selector(genglobfuncall)];
  return self;
}

- genbindfun
{
  /* for DLL's have to emit bind string */
  gf("struct modDescriptor %s*%s(void)\n{\n", o_bind, bindfunname);
  gs("  selTransTbl = _selTransTbl;\n");
  if (o_fwd) {
    gs("  fwdTransTbl = _fwdTransTbl;\n");
  }
  /* can't have global data shared across windows DLL's */
  if (!o_shareddata)
    [self genglobfuncall];
  gf("  return &%s;\n}\n", moddescname);
  return self;
}

- usesentry:name
{
  if (!usesentries)
    usesentries = [Set new];
#ifdef TRACEOCU
  if (o_debuginfo)
    fprintf(stderr, "OCU use %s\n", [name str]);
#endif
  [usesentries add:name];
  return self;
}

- definesentry:name
{
  if (!definesentries)
    definesentries = [Set new];
#ifdef TRACEOCU
  if (o_debuginfo)
    fprintf(stderr, "OCU defines %s\n", [name str]);
#endif
  [definesentries add:name];
  return self;
}

- (BOOL)definesmain
{
  return [definesentries contains:s_main];
}

- genusesentries
{
  id seq, entry;

  if (o_comments) {
    gs("/* Objective C Use (OCU) entries */\n");
  }
  seq = [usesentries eachElement];
  while ((entry = [seq next])) {
    char *s = [entry str];

    /* define an uninitialized global for the entry */
    /* goes into bss segment *if* no matching define entry */
    gf("struct useDescriptor *OCU_%s;\n", s);
  }
  return self;
}

- genusecontrol
{
  id seq, entry;

  gs("static struct useDescriptor **_useControl[] = {\n");

  seq = [usesentries eachElement];
  while ((entry = [seq next])) {
    char *s = [entry str];

    gf("  &OCU_%s,\n", s);
  }
  gs("0};\n");
  gs("static struct useDescriptor _useDesc = {\n");
  gs("  0,\n");			/* processed */
  gs("  0,\n");			/* next */
  gs("  _useControl,\n");	/* uses */
  gf("  %s", bindfunname);	/* bind */
  gs("\n};\n");

  return self;
}

- gendefinesentries
{
  id seq, entry;

  if (o_comments)
    gs("/* Objective C Use (OCU) defines */\n");

  seq = [definesentries eachElement];
  while ((entry = [seq next])) {
    char *s = [entry str];

    gf("struct useDescriptor *OCU_%s = &_useDesc;\n", s);
  }

  /* objcrt references OCU_main but our main function can be bar() */
  /* in that case, we emitted an OCU_bar but no OCU_main yet */
  if ([self definesmain] && (strcmp(o_mainfun, "main"))) {
    gf("\nstruct useDescriptor *OCU_main=&_useDesc;\n");
  }
  return self;
}

- genocu
{
  gs("struct useDescriptor {\n");
  gs("  int processed;\n");
  gs("  struct useDescriptor *next;\n");
  gs("  struct useDescriptor ***uses;\n");
  gs("  struct modDescriptor *(*bind)();\n");
  gs("};\n");

  [self genusesentries];
  [self genusecontrol];
  [self gendefinesentries];

  /* turn *on* auto-initialization code by defining the postlink
   * entry point (objcModules) as zero (non-zero -> postlink).
   */

  if ([self definesmain]) {
    gs("struct modEntry *_objcModules = 0;\n");
  }
  return self;
}

- postlinkmark
{
  /* the postLink version doesn't need the OCU entries
   * however, we want to 'mark' the .o file as being
   * compiled with -postlink, so that when linking
   * against a library that was compiled with -postlink
   * we could automatically detect this (and use postlink).
   */

  gf("int _OBJCPOSTLINK_%s = 1;\n", modname);
  if ([self definesmain]) {
    gs("struct useDescriptor *OCU_main = 0;\n");
  }
  return self;
}

- otbmark
{
  gf("int _OBJCOTB_%s = 1;\n", modname);
  return self;
}

- epilogue
{
  id e;

  o_nolinetags++;

  if (curclassdef) {
    [curclassdef warnpending];
  }
  if (o_comments) {
    gs("\n/* objc epilogue */\n");
  } else {
    gc('\n');
  }

  if ((e = [self allclsimpls])) {
    [e elementsPerform:@selector(genimpl)];
  }
  if (o_fwd) {
    [self genfwdstubs];
    [self genseltranstbl];
    [self genfwdtranstbl];
  } else {
    [self genseltranstbl];
  }

  if (clsimpls) {
    [self genmodclslst];
  }
  [self genmoddesc];
  [self genbindfun];

  if (o_postlink) {
    [self postlinkmark];
  } else {
    [self genocu];
  }

  if (o_otb) {
    [self otbmark];
  }
  gc('\n');
  gc('\n');
  o_nolinetags--;
  return self;
}

- (BOOL)istypeword:node
{
  if (builtintypes != nil && [builtintypes find:node] != nil)
    return YES;
  if (types != nil && [types includes:node])
    return YES;
  return NO;
}

- (BOOL)isbuiltinfun:node
{
  return builtinfuns != nil && [builtinfuns find:node] != nil;
}

- defbuiltinfun:node
{
  if (!builtinfuns)
    builtinfuns = [Set new];
  [builtinfuns add:node];
  return self;
}

- defbuiltintype:node
{
  if (!builtintypes)
    builtintypes = [Set new];
  [builtintypes add:node];
  return self;
}

- def:node astype:aType 
{
  if (!typedic) {
    typedic = [Dictionary new];
    types = [Set new];
  }
  if (o_debuginfo) {
    fprintf(stderr, "typedef %s as '", [node str]);
    gstderr();
    [aType gen];
    gnormal();
    fprintf(stderr, "'\n");
  }
  [types add:node];
  assert([aType isKindOf:(id) [Type class]]);
  [typedic atKey:node put:aType];
  return self;
}

- defenumtor:e
{
  if (!enumtors)
    enumtors = [Set new];
  [enumtors add:e];
  return self;
}

- lookupenumtor:sym
{
  return (enumtors) ? [enumtors find : sym]:nil;	/* works for Symbol class */
}

- defstruct:e
{
  if (!structdefs)
    structdefs = [Set new];
  [structdefs add:e];
  return self;
}

- lookupstruct:e
{
  return (structdefs) ? [structdefs find : e]:nil;
}

- lookuptype:sym
{
  return (typedic) ? [typedic atKey : sym]:nil;
}

- lookupglobal:sym
{
  return (globaldic) ? [globaldic atKey : sym]:nil;
}

- defdata:node astype:aType 
{
  if (!globaldic) {
    globaldic = [Dictionary new];
    globals = [Set new];
  }
  [globals add:node];
  assert([aType isKindOf:(id) [Type class]]);
  [globaldic atKey:node put:aType];
  return self;
}

- def:sym asclass:classdef
{
  if (!classdefs)
    classdefs = [Dictionary new];
  [classdefs atKey:sym put:classdef];
  return self;
}

- lookupclass:sym
{
  return [classdefs atKey:sym];
}

- lookupmethod:sel
{
  return [methods atKey:sel];
}

- def:sel asmethod:method
{
  if (!methods)
    methods = [Dictionary new];
  [methods atKey:sel put:method];
  return self;
}

- addgentype:s
{
  if (!gentypes)
    gentypes = [Set new];
  [gentypes add:s];
  return self;
}

- (BOOL)isgentype:s
{
  return [gentypes includes:s];
}

@end
 
