logo

final class

compiler::FPod

sys::Obj
  compiler::FPod : compiler::CPod, compiler::FConst
//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   26 Dec 05  Brian Frank  Creation
//   19 Aug 06  Brian Frank  Ported from Java to Fan
//

**
** FPod is the read/write fcode representation of sys::Pod.  It's main job in
** life is to manage all the pod-wide constant tables for names, literals,
** type/slot references and type/slot definitions.
**
final class FPod : CPod, FConst
{

//////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////

  new make(FPodNamespace ns, Str podName, Zip zip)
  {
    this.ns         = ns
    this.name       = podName
    this.zip        = zip
    this.names      = FTable.makeStrs(this)
    this.typeRefs   = FTable.makeTypeRefs(this)
    this.fieldRefs  = FTable.makeFieldRefs(this)
    this.methodRefs = FTable.makeMethodRefs(this)
    this.ints       = FTable.makeInts(this)
    this.floats     = FTable.makeFloats(this)
    this.decimals   = FTable.makeDecimals(this)
    this.strs       = FTable.makeStrs(this)
    this.durations  = FTable.makeDurations(this)
    this.uris       = FTable.makeStrs(this)
  }

//////////////////////////////////////////////////////////////////////////
// CPod
//////////////////////////////////////////////////////////////////////////

  override CType resolveType(Str name, Bool checked)
  {
    t := ftypesByName[name]
    if (t != null) return t
    if (checked) throw UnknownTypeErr.make("${this.name}::$name")
    return null
  }

  override CType[] types()
  {
    return ftypes
  }

  CType toType(Int index)
  {
    if (index == 0xffff) return null
    r := typeRef(index)

    Str sig
    if (r.isGenericInstance)
      sig = r.sig
    else
      sig = n(r.podName) + "::" + n(r.typeName)
    return ns.resolveType(sig)
  }

  CType[] resolveTypes(Int[] indexes)
  {
    ctypes := CType[,]
    ctypes.capacity = indexes.size
    indexes.map(ctypes) |Int index->Obj| { return toType(index) }
    return ctypes
  }

//////////////////////////////////////////////////////////////////////////
// Convenience
//////////////////////////////////////////////////////////////////////////

  Str n(Int index)                { return (Str)names[index] }
  FTypeRef typeRef(Int index)     { return (FTypeRef)typeRefs[index] }
  FFieldRef fieldRef(Int index)   { return (FFieldRef)fieldRefs[index] }
  FMethodRef methodRef(Int index) { return (FMethodRef)methodRefs[index] }
  Int integer(Int index)          { return (Int)ints[index] }
  Float float(Int index)          { return (Float)floats[index] }
  Decimal decimal(Int index)      { return (Decimal)decimals[index] }
  Str str(Int index)              { return (Str)strs[index] }
  Duration duration(Int index)    { return (Duration)durations[index] }
  Str uri(Int index)              { return (Str)uris[index] }

  Str typeRefStr(Int index) { return typeRef(index).format(this) }
  Str fieldRefStr(Int index) { return fieldRef(index).format(this) }
  Str methodRefStr(Int index) { return methodRef(index).format(this) }

//////////////////////////////////////////////////////////////////////////
// Compile Utils
//////////////////////////////////////////////////////////////////////////

  Int addName(Str val)
  {
    return names.add(val)
  }

  Int addTypeRef(CType t)
  {
    p   := addName(t.pod.name)
    n   := addName(t.name)
    sig := t.isParameterized ? t.signature : ""
    return typeRefs.add(FTypeRef.make(p, n, sig))
  }

  Int addFieldRef(CField field)
  {
    p := addTypeRef(field.parent)
    n := addName(field.name)
    t := addTypeRef(field.fieldType)
    return fieldRefs.add(FFieldRef.make(p, n, t))
  }

  Int addMethodRef(CMethod method, Int argCount := null)
  {
    // if this is a generic instantiation, we want to call
    // against the original generic method using it's raw
    // types, since that is how the system library will
    // implement the type
    if (method.isParameterized) method = method.generic

    p := addTypeRef(method.parent)
    n := addName(method.name)
    r := addTypeRef(method.inheritedReturnType.raw)  // CLR can't deal with covariance
    params := (Int[])method.params.map(Int[,]) |CParam x->Obj| { return addTypeRef(x.paramType.raw) }
    if (argCount != null && argCount < params.size)
      params = params[0...argCount]
    return methodRefs.add(FMethodRef.make(p, n, r, params))
  }

  Void dump()
  {
    p := FPrinter.make(this)
    p.showCode = true
    p.ftypes
  }

//////////////////////////////////////////////////////////////////////////
// Read
//////////////////////////////////////////////////////////////////////////

  **
  ** Read the just the pod and type meta-data, but
  ** not each type's full definition
  **
  Void read()
  {
    echo("     FPod.reading [$zip.file]...")

    // read tables
    names.read(in(`/names.def`))
    typeRefs.read(in(`/typeRefs.def`))
    fieldRefs.read(in(`/fieldRefs.def`))
    methodRefs.read(in(`/methodRefs.def`))
    ints.read(in(`/ints.def`))
    floats.read(in(`/floats.def`))
    decimals.read(in(`/decimals.def`))
    strs.read(in(`/strs.def`))
    durations.read(in(`/durations.def`))
    uris.read(in(`/uris.def`))

    // read pod meta-data
    in := in(`/pod.def`)
    readPodMeta(in)
    in.close

    // read type meta-data
    in = this.in(`/types.def`)
    ftypes = FType[,]
    ftypesByName = Str:FType[:]
    in.readU2.times |,|
    {
      ftype := FType.make(this).readMeta(in)
      ftypes.add(ftype)
      ftypesByName[ftype.name] = ftype
      ns.types[ftype.qname] = ftype
    }
    in.close
  }

  **
  ** Read the entire pod into memory (including full type specifications)
  **
  Void readFully()
  {
    ftypes.each |FType t| { t.read }
  }

//////////////////////////////////////////////////////////////////////////
// Write
//////////////////////////////////////////////////////////////////////////

  **
  ** Write the tables and type files out to zip storage
  **
  Void write(Zip zip := this.zip)
  {
    this.zip = zip

    // write non-empty tables
    if (!names.isEmpty)      names.write(out(`/names.def`))
    if (!typeRefs.isEmpty)   typeRefs.write(out(`/typeRefs.def`))
    if (!fieldRefs.isEmpty)  fieldRefs.write(out(`/fieldRefs.def`))
    if (!methodRefs.isEmpty) methodRefs.write(out(`/methodRefs.def`))
    if (!ints.isEmpty)       ints.write(out(`/ints.def`))
    if (!floats.isEmpty)     floats.write(out(`/floats.def`))
    if (!decimals.isEmpty)   decimals.write(out(`/decimals.def`))
    if (!strs.isEmpty)       strs.write(out(`/strs.def`))
    if (!durations.isEmpty)  durations.write(out(`/durations.def`))
    if (!uris.isEmpty)       uris.write(out(`/uris.def`))

    // write pod meta-data
    out := out(`/pod.def`)
    writePodMeta(out)
    out.close

    // write type meta-data
    out = this.out(`/types.def`)
    out.writeI2(ftypes.size)
    ftypes.each |FType t| { t.writeMeta(out) }
    out.close

    // write type full fcode
    ftypes.each |FType t| { t.write }
  }

//////////////////////////////////////////////////////////////////////////
// Pod Meta
//////////////////////////////////////////////////////////////////////////

  Void readPodMeta(InStream in)
  {
    if (in.readU4 != FCodeMagic)
      throw IOErr.make("Invalid fcode magic number")
    if (in.readU4 != FCodeVersion)
      throw IOErr.make("Unsupported fcode version")

    name = in.readUtf
    version = Version.fromStr(in.readUtf)
    depends = Depend[,]
    in.readU1.times |,| { depends.add(Depend.fromStr(in.readUtf)) }
    fattrs = FAttr[,]
    in.readU2.times |,| { fattrs.add(FAttr.make.read(in)) }
  }

  Void writePodMeta(OutStream out)
  {
    out.writeI4(FConst.FCodeMagic)
    out.writeI4(FConst.FCodeVersion)
    out.writeUtf(name)
    out.writeUtf(version.toStr)
    out.write(depends.size)
    depends.each |Depend d| { out.writeUtf(d.toStr) }
    out.writeI2(fattrs.size)
    fattrs.each |FAttr a| { a.write(out) }
  }

//////////////////////////////////////////////////////////////////////////
// Zip
//////////////////////////////////////////////////////////////////////////

  **
  ** Get input stream to read the specified file from zip storage.
  **
  InStream in(Uri uri)
  {
    file := zip.contents[uri]
    if (file == null) return null
    return file.in
  }

  **
  ** Get output stream to write the specified file to zip storage.
  **
  OutStream out(Uri uri) { return zip.writeNext(uri) }

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  override CNamespace ns    // compiler's namespace
  override Str name         // pod's unique name
  override Version version  // pod version
  Depend[] depends          // pod dependencies
  FAttr[] fattrs            // pod attributes
  Zip zip                   // zipped storage
  FType[] ftypes            // pod's declared types
  FTable names              // identifier names: foo
  FTable typeRefs           // types refs:   [pod,type,sig]
  FTable fieldRefs          // fields refs:  [parent,name,type]
  FTable methodRefs         // methods refs: [parent,name,ret,params]
  FTable ints               // Int literals
  FTable floats             // Float literals
  FTable decimals           // Decimal literals
  FTable strs               // Str literals
  FTable durations          // Duration literals
  FTable uris               // Uri literals
  Str:FType ftypesByName    // if loaded

}