| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package Exporter |
| |
| import Globals "globals" |
| import Object "object" |
| import Type "type" |
| import Universe "universe" |
| |
| |
| export Exporter // really only want to export Export() |
| type Exporter struct { |
| comp *Globals.Compilation; |
| debug bool; |
| buf [4*1024] byte; |
| pos int; |
| pkg_ref int; |
| type_ref int; |
| }; |
| |
| |
| func (E *Exporter) WriteType(typ *Globals.Type); |
| func (E *Exporter) WriteObject(obj *Globals.Object); |
| func (E *Exporter) WritePackage(pkg *Globals.Package); |
| |
| |
| func (E *Exporter) WriteByte(x byte) { |
| E.buf[E.pos] = x; |
| E.pos++; |
| /* |
| if E.debug { |
| print " ", x; |
| } |
| */ |
| } |
| |
| |
| func (E *Exporter) WriteInt(x int) { |
| /* |
| if E.debug { |
| print " #", x; |
| } |
| */ |
| for x < -64 || x >= 64 { |
| E.WriteByte(byte(x & 127)); |
| x = int(uint(x >> 7)); // arithmetic shift |
| } |
| // -64 <= x && x < 64 |
| E.WriteByte(byte(x + 192)); |
| } |
| |
| |
| func (E *Exporter) WriteString(s string) { |
| if E.debug { |
| print ` "`, s, `"`; |
| } |
| n := len(s); |
| E.WriteInt(n); |
| for i := 0; i < n; i++ { |
| E.WriteByte(s[i]); |
| } |
| } |
| |
| |
| func (E *Exporter) WriteObjTag(tag int) { |
| if tag < 0 { |
| panic "tag < 0"; |
| } |
| if E.debug { |
| print "\nObj: ", tag; // obj kind |
| } |
| E.WriteInt(tag); |
| } |
| |
| |
| func (E *Exporter) WriteTypeTag(tag int) { |
| if E.debug { |
| if tag > 0 { |
| print "\nTyp ", E.type_ref, ": ", tag; // type form |
| } else { |
| print " [Typ ", -tag, "]"; // type ref |
| } |
| } |
| E.WriteInt(tag); |
| } |
| |
| |
| func (E *Exporter) WritePackageTag(tag int) { |
| if E.debug { |
| if tag > 0 { |
| print "\nPkg ", E.pkg_ref, ": ", tag; // package no |
| } else { |
| print " [Pkg ", -tag, "]"; // package ref |
| } |
| } |
| E.WriteInt(tag); |
| } |
| |
| |
| func (E *Exporter) WriteTypeField(fld *Globals.Object) { |
| if fld.kind != Object.VAR { |
| panic "fld.kind != Object.VAR"; |
| } |
| E.WriteType(fld.typ); |
| } |
| |
| |
| func (E *Exporter) WriteScope(scope *Globals.Scope) { |
| if E.debug { |
| print " {"; |
| } |
| |
| // determine number of objects to export |
| n := 0; |
| for p := scope.entries.first; p != nil; p = p.next { |
| if p.obj.mark { |
| n++; |
| } |
| } |
| |
| // export the objects, if any |
| if n > 0 { |
| for p := scope.entries.first; p != nil; p = p.next { |
| if p.obj.mark { |
| E.WriteObject(p.obj); |
| } |
| } |
| } |
| |
| if E.debug { |
| print " }"; |
| } |
| } |
| |
| |
| func (E *Exporter) WriteObject(obj *Globals.Object) { |
| if obj == nil || !obj.mark { |
| panic "obj == nil || !obj.mark"; |
| } |
| |
| if obj.kind == Object.TYPE && obj.typ.obj == obj { |
| // primary type object - handled entirely by WriteType() |
| E.WriteObjTag(Object.PTYPE); |
| E.WriteType(obj.typ); |
| |
| } else { |
| E.WriteObjTag(obj.kind); |
| E.WriteString(obj.ident); |
| E.WriteType(obj.typ); |
| E.WritePackage(E.comp.pkgs[obj.pnolev]); |
| |
| switch obj.kind { |
| case Object.BAD: fallthrough; |
| case Object.PACKAGE: fallthrough; |
| case Object.PTYPE: |
| panic "UNREACHABLE"; |
| case Object.CONST: |
| E.WriteInt(0); // should be the correct value |
| break; |
| case Object.TYPE: |
| // nothing to do |
| case Object.VAR: |
| E.WriteInt(0); // should be the correct address/offset |
| case Object.FUNC: |
| E.WriteInt(0); // should be the correct address/offset |
| default: |
| panic "UNREACHABLE"; |
| } |
| } |
| } |
| |
| |
| func (E *Exporter) WriteType(typ *Globals.Type) { |
| if typ == nil { |
| panic "typ == nil"; |
| } |
| |
| if typ.ref >= 0 { |
| E.WriteTypeTag(-typ.ref); // type already exported |
| return; |
| } |
| |
| if typ.form <= 0 { |
| panic "typ.form <= 0"; |
| } |
| E.WriteTypeTag(typ.form); |
| typ.ref = E.type_ref; |
| E.type_ref++; |
| |
| if typ.obj != nil { |
| if typ.obj.typ != typ { |
| panic "typ.obj.type() != typ"; // primary type |
| } |
| E.WriteString(typ.obj.ident); |
| E.WritePackage(E.comp.pkgs[typ.obj.pnolev]); |
| } else { |
| E.WriteString(""); |
| } |
| |
| switch typ.form { |
| case Type.UNDEF: fallthrough; |
| case Type.BAD: fallthrough; |
| case Type.NIL: fallthrough; |
| case Type.BOOL: fallthrough; |
| case Type.UINT: fallthrough; |
| case Type.INT: fallthrough; |
| case Type.FLOAT: fallthrough; |
| case Type.STRING: fallthrough; |
| case Type.ANY: |
| panic "UNREACHABLE"; |
| |
| case Type.ARRAY: |
| E.WriteInt(typ.len_); |
| E.WriteTypeField(typ.elt); |
| |
| case Type.MAP: |
| E.WriteTypeField(typ.key); |
| E.WriteTypeField(typ.elt); |
| |
| case Type.CHANNEL: |
| E.WriteInt(typ.flags); |
| E.WriteTypeField(typ.elt); |
| |
| case Type.FUNCTION: |
| E.WriteInt(typ.flags); |
| fallthrough; |
| case Type.STRUCT: fallthrough; |
| case Type.INTERFACE: |
| E.WriteScope(typ.scope); |
| |
| case Type.POINTER: fallthrough; |
| case Type.REFERENCE: |
| E.WriteTypeField(typ.elt); |
| |
| default: |
| panic "UNREACHABLE"; |
| } |
| } |
| |
| |
| func (E *Exporter) WritePackage(pkg *Globals.Package) { |
| if pkg.ref >= 0 { |
| E.WritePackageTag(-pkg.ref); // package already exported |
| return; |
| } |
| |
| if Object.PACKAGE <= 0 { |
| panic "Object.PACKAGE <= 0"; |
| } |
| E.WritePackageTag(Object.PACKAGE); |
| pkg.ref = E.pkg_ref; |
| E.pkg_ref++; |
| |
| E.WriteString(pkg.obj.ident); |
| E.WriteString(pkg.file_name); |
| E.WriteString(pkg.key); |
| } |
| |
| |
| func (E *Exporter) Export(comp* Globals.Compilation, file_name string) { |
| if E.debug { |
| print "exporting to ", file_name; |
| } |
| |
| E.comp = comp; |
| E.debug = true; |
| E.pos = 0; |
| E.pkg_ref = 0; |
| E.type_ref = 0; |
| |
| // Predeclared types are "pre-exported". |
| // TODO run the loop below only in debug mode |
| { i := 0; |
| for p := Universe.types.first; p != nil; p = p.next { |
| if p.typ.ref != i { |
| panic "incorrect ref for predeclared type"; |
| } |
| i++; |
| } |
| } |
| E.type_ref = Universe.types.len_; |
| |
| pkg := comp.pkgs[0]; |
| E.WritePackage(pkg); |
| for p := pkg.scope.entries.first; p != nil; p = p.next { |
| if p.obj.mark { |
| E.WriteObject(p.obj); |
| } |
| } |
| E.WriteObjTag(0); |
| |
| if E.debug { |
| print "\n(", E.pos, " bytes)\n"; |
| } |
| |
| data := string(E.buf)[0 : E.pos]; |
| ok := sys.writefile(file_name, data); |
| |
| if !ok { |
| panic "export failed"; |
| } |
| } |