mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-04 20:17:42 +00:00
1138 lines
30 KiB
ObjectPascal
Executable File
1138 lines
30 KiB
ObjectPascal
Executable File
//
|
|
//
|
|
// The Nimrod Compiler
|
|
// (c) Copyright 2009 Andreas Rumpf
|
|
//
|
|
// See the file "copying.txt", included in this
|
|
// distribution, for details about the copyright.
|
|
//
|
|
unit rodread;
|
|
|
|
// This module is responsible for loading of rod files.
|
|
(*
|
|
Reading and writing binary files are really hard to debug. Therefore we use
|
|
a special text format. ROD-files only describe the interface of a module.
|
|
Thus they are smaller than the source files most of the time. Even if they
|
|
are bigger, they are more efficient to process because symbols are only
|
|
loaded on demand.
|
|
It consists of:
|
|
|
|
- a header:
|
|
NIM:$fileversion\n
|
|
- the module's id (even if the module changed, its ID will not!):
|
|
ID:Ax3\n
|
|
- CRC value of this module:
|
|
CRC:CRC-val\n
|
|
- a section containing the compiler options and defines this
|
|
module has been compiled with:
|
|
OPTIONS:options\n
|
|
DEFINES:defines\n
|
|
- FILES(
|
|
myfile.inc
|
|
lib/mymodA
|
|
)
|
|
- a include file dependency section:
|
|
INCLUDES(
|
|
<fileidx> <CRC of myfile.inc>\n # fileidx is the LINE in the file section!
|
|
)
|
|
- a module dependency section:
|
|
DEPS: <fileidx> <fileidx>\n
|
|
- an interface section:
|
|
INTERF(
|
|
identifier1 id\n # id is the symbol's id
|
|
identifier2 id\n
|
|
)
|
|
- a compiler proc section:
|
|
COMPILERPROCS(
|
|
identifier1 id\n # id is the symbol's id
|
|
)
|
|
- an index consisting of (ID, linenumber)-pairs:
|
|
INDEX(
|
|
id-diff idx-diff\n
|
|
id-diff idx-diff\n
|
|
)
|
|
- an import index consisting of (ID, moduleID)-pairs:
|
|
IMPORTS(
|
|
id-diff moduleID-diff\n
|
|
id-diff moduleID-diff\n
|
|
)
|
|
- a list of all exported type converters because they are needed for correct
|
|
semantic checking:
|
|
CONVERTERS:id id\n # position of the symbol in the DATA section
|
|
- an AST section that contains the module's AST:
|
|
INIT(
|
|
idx\n # position of the node in the DATA section
|
|
idx\n
|
|
)
|
|
- a data section, where each type, symbol or AST is stored.
|
|
DATA(
|
|
type
|
|
(node)
|
|
sym
|
|
)
|
|
|
|
We now also do index compression, because an index always needs to be read.
|
|
*)
|
|
|
|
interface
|
|
|
|
{$include 'config.inc'}
|
|
|
|
uses
|
|
sysutils, nsystem, nos, options, strutils, nversion, ast, astalgo, msgs,
|
|
platform, condsyms, ropes, idents, crc;
|
|
|
|
type
|
|
TReasonForRecompile = (
|
|
rrEmpty, // used by moddeps module
|
|
rrNone, // no need to recompile
|
|
rrRodDoesNotExist, // rod file does not exist
|
|
rrRodInvalid, // rod file is invalid
|
|
rrCrcChange, // file has been edited since last recompilation
|
|
rrDefines, // defines have changed
|
|
rrOptions, // options have changed
|
|
rrInclDeps, // an include has changed
|
|
rrModDeps // a module this module depends on has been changed
|
|
);
|
|
const
|
|
reasonToFrmt: array [TReasonForRecompile] of string = (
|
|
'',
|
|
'no need to recompile: $1',
|
|
'symbol file for $1 does not exist',
|
|
'symbol file for $1 has the wrong version',
|
|
'file edited since last compilation: $1',
|
|
'list of conditional symbols changed for: $1',
|
|
'list of options changed for: $1',
|
|
'an include file edited: $1',
|
|
'a module $1 depends on has changed'
|
|
);
|
|
|
|
type
|
|
TIndex = record // an index with compression
|
|
lastIdxKey, lastIdxVal: int;
|
|
tab: TIITable;
|
|
r: PRope; // writers use this
|
|
offset: int; // readers use this
|
|
end;
|
|
TRodReader = object(NObject)
|
|
pos: int; // position; used for parsing
|
|
s: string; // the whole file in memory
|
|
options: TOptions;
|
|
reason: TReasonForRecompile;
|
|
modDeps: TStringSeq;
|
|
files: TStringSeq;
|
|
dataIdx: int; // offset of start of data section
|
|
convertersIdx: int; // offset of start of converters section
|
|
initIdx, interfIdx, compilerProcsIdx, cgenIdx: int;
|
|
filename: string;
|
|
index, imports: TIndex;
|
|
readerIndex: int;
|
|
line: int; // only used for debugging, but is always in the code
|
|
moduleID: int;
|
|
syms: TIdTable; // already processed symbols
|
|
end;
|
|
PRodReader = ^TRodReader;
|
|
|
|
const
|
|
FileVersion = '1012'; // modify this if the rod-format changes!
|
|
|
|
var
|
|
rodCompilerprocs: TStrTable; // global because this is needed by magicsys
|
|
|
|
|
|
function handleSymbolFile(module: PSym; const filename: string): PRodReader;
|
|
function GetCRC(const filename: string): TCrc32;
|
|
|
|
function loadInitSection(r: PRodReader): PNode;
|
|
|
|
procedure loadStub(s: PSym);
|
|
|
|
function encodeInt(x: BiggestInt): PRope;
|
|
function encode(const s: string): PRope;
|
|
|
|
implementation
|
|
|
|
var
|
|
gTypeTable: TIdTable;
|
|
|
|
function rrGetSym(r: PRodReader; id: int; const info: TLineInfo): PSym; forward;
|
|
// `info` is only used for debugging purposes
|
|
|
|
function rrGetType(r: PRodReader; id: int; const info: TLineInfo): PType; forward;
|
|
|
|
function decode(r: PRodReader): string; forward;
|
|
function decodeInt(r: PRodReader): int; forward;
|
|
function decodeBInt(r: PRodReader): biggestInt; forward;
|
|
|
|
function encode(const s: string): PRope;
|
|
var
|
|
i: int;
|
|
res: string;
|
|
begin
|
|
res := '';
|
|
for i := strStart to length(s)+strStart-1 do begin
|
|
case s[i] of
|
|
'a'..'z', 'A'..'Z', '0'..'9', '_':
|
|
addChar(res, s[i]);
|
|
else
|
|
res := res +{&} '\' +{&} toHex(ord(s[i]), 2)
|
|
end
|
|
end;
|
|
result := toRope(res);
|
|
end;
|
|
|
|
procedure encodeIntAux(var str: string; x: BiggestInt);
|
|
const
|
|
chars: string =
|
|
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
var
|
|
v, rem: biggestInt;
|
|
d: char;
|
|
idx: int;
|
|
begin
|
|
v := x;
|
|
rem := v mod 190;
|
|
if (rem < 0) then begin
|
|
str := str + '-';
|
|
v := -(v div 190);
|
|
rem := -rem;
|
|
end
|
|
else
|
|
v := v div 190;
|
|
idx := int(rem);
|
|
if idx < 62 then d := chars[idx+strStart]
|
|
else d := chr(idx - 62 + 128);
|
|
if (v <> 0) then encodeIntAux(str, v);
|
|
addChar(str, d);
|
|
end;
|
|
|
|
function encodeInt(x: BiggestInt): PRope;
|
|
var
|
|
res: string;
|
|
begin
|
|
res := '';
|
|
encodeIntAux(res, x);
|
|
result := toRope(res);
|
|
end;
|
|
|
|
|
|
procedure decodeLineInfo(r: PRodReader; var info: TLineInfo);
|
|
begin
|
|
if r.s[r.pos] = '?' then begin
|
|
inc(r.pos);
|
|
if r.s[r.pos] = ',' then
|
|
info.col := int16(-1)
|
|
else
|
|
info.col := int16(decodeInt(r));
|
|
if r.s[r.pos] = ',' then begin
|
|
inc(r.pos);
|
|
if r.s[r.pos] = ',' then info.line := int16(-1)
|
|
else info.line := int16(decodeInt(r));
|
|
if r.s[r.pos] = ',' then begin
|
|
inc(r.pos);
|
|
info := newLineInfo(r.files[decodeInt(r)], info.line, info.col);
|
|
end
|
|
end
|
|
end
|
|
end;
|
|
|
|
function decodeNode(r: PRodReader; const fInfo: TLineInfo): PNode;
|
|
var
|
|
id: int;
|
|
fl: string;
|
|
begin
|
|
result := nil;
|
|
if r.s[r.pos] = '(' then begin
|
|
inc(r.pos);
|
|
if r.s[r.pos] = ')' then begin
|
|
inc(r.pos); exit; // nil node
|
|
end;
|
|
result := newNodeI(TNodeKind(decodeInt(r)), fInfo);
|
|
decodeLineInfo(r, result.info);
|
|
if r.s[r.pos] = '$' then begin
|
|
inc(r.pos);
|
|
result.flags := {@cast}TNodeFlags(int32(decodeInt(r)));
|
|
end;
|
|
if r.s[r.pos] = '^' then begin
|
|
inc(r.pos);
|
|
id := decodeInt(r);
|
|
result.typ := rrGetType(r, id, result.info);
|
|
end;
|
|
case result.kind of
|
|
nkCharLit..nkInt64Lit: begin
|
|
if r.s[r.pos] = '!' then begin
|
|
inc(r.pos);
|
|
result.intVal := decodeBInt(r);
|
|
end
|
|
end;
|
|
nkFloatLit..nkFloat64Lit: begin
|
|
if r.s[r.pos] = '!' then begin
|
|
inc(r.pos);
|
|
fl := decode(r);
|
|
result.floatVal := parseFloat(fl);
|
|
end
|
|
end;
|
|
nkStrLit..nkTripleStrLit: begin
|
|
if r.s[r.pos] = '!' then begin
|
|
inc(r.pos);
|
|
result.strVal := decode(r);
|
|
end
|
|
else
|
|
result.strVal := ''; // BUGFIX
|
|
end;
|
|
nkIdent: begin
|
|
if r.s[r.pos] = '!' then begin
|
|
inc(r.pos);
|
|
fl := decode(r);
|
|
result.ident := getIdent(fl);
|
|
end
|
|
else
|
|
internalError(result.info, 'decodeNode: nkIdent');
|
|
end;
|
|
nkSym: begin
|
|
if r.s[r.pos] = '!' then begin
|
|
inc(r.pos);
|
|
id := decodeInt(r);
|
|
result.sym := rrGetSym(r, id, result.info);
|
|
end
|
|
else
|
|
internalError(result.info, 'decodeNode: nkSym');
|
|
end;
|
|
else begin
|
|
while r.s[r.pos] <> ')' do
|
|
addSon(result, decodeNode(r, result.info));
|
|
end
|
|
end;
|
|
if r.s[r.pos] = ')' then inc(r.pos)
|
|
else internalError(result.info, 'decodeNode');
|
|
end
|
|
else InternalError(result.info, 'decodeNode ' + r.s[r.pos])
|
|
end;
|
|
|
|
procedure decodeLoc(r: PRodReader; var loc: TLoc; const info: TLineInfo);
|
|
begin
|
|
if r.s[r.pos] = '<' then begin
|
|
inc(r.pos);
|
|
if r.s[r.pos] in ['0'..'9', 'a'..'z', 'A'..'Z'] then
|
|
loc.k := TLocKind(decodeInt(r))
|
|
else
|
|
loc.k := low(loc.k);
|
|
if r.s[r.pos] = '*' then begin
|
|
inc(r.pos);
|
|
loc.s := TStorageLoc(decodeInt(r));
|
|
end
|
|
else
|
|
loc.s := low(loc.s);
|
|
if r.s[r.pos] = '$' then begin
|
|
inc(r.pos);
|
|
loc.flags := {@cast}TLocFlags(int32(decodeInt(r)));
|
|
end
|
|
else
|
|
loc.flags := {@set}[];
|
|
if r.s[r.pos] = '^' then begin
|
|
inc(r.pos);
|
|
loc.t := rrGetType(r, decodeInt(r), info);
|
|
end
|
|
else
|
|
loc.t := nil;
|
|
if r.s[r.pos] = '!' then begin
|
|
inc(r.pos);
|
|
loc.r := toRope(decode(r));
|
|
end
|
|
else
|
|
loc.r := nil;
|
|
if r.s[r.pos] = '?' then begin
|
|
inc(r.pos);
|
|
loc.a := decodeInt(r);
|
|
end
|
|
else
|
|
loc.a := 0;
|
|
if r.s[r.pos] = '>' then inc(r.pos)
|
|
else InternalError(info, 'decodeLoc ' + r.s[r.pos]);
|
|
end
|
|
end;
|
|
|
|
function decodeType(r: PRodReader; const info: TLineInfo): PType;
|
|
var
|
|
d: int;
|
|
begin
|
|
result := nil;
|
|
if r.s[r.pos] = '[' then begin
|
|
inc(r.pos);
|
|
if r.s[r.pos] = ']' then begin
|
|
inc(r.pos); exit; // nil type
|
|
end;
|
|
end;
|
|
new(result);
|
|
{@ignore}
|
|
FillChar(result^, sizeof(result^), 0);
|
|
{@emit}
|
|
result.kind := TTypeKind(decodeInt(r));
|
|
if r.s[r.pos] = '+' then begin
|
|
inc(r.pos);
|
|
result.id := decodeInt(r);
|
|
setId(result.id);
|
|
if debugIds then registerID(result);
|
|
end
|
|
else
|
|
InternalError(info, 'decodeType: no id');
|
|
IdTablePut(gTypeTable, result, result); // here this also
|
|
// avoids endless recursion for recursive type
|
|
if r.s[r.pos] = '(' then
|
|
result.n := decodeNode(r, UnknownLineInfo());
|
|
if r.s[r.pos] = '$' then begin
|
|
inc(r.pos);
|
|
result.flags := {@cast}TTypeFlags(int32(decodeInt(r)));
|
|
end;
|
|
if r.s[r.pos] = '?' then begin
|
|
inc(r.pos);
|
|
result.callConv := TCallingConvention(decodeInt(r));
|
|
end;
|
|
if r.s[r.pos] = '*' then begin
|
|
inc(r.pos);
|
|
result.owner := rrGetSym(r, decodeInt(r), info);
|
|
end;
|
|
if r.s[r.pos] = '&' then begin
|
|
inc(r.pos);
|
|
result.sym := rrGetSym(r, decodeInt(r), info);
|
|
end;
|
|
if r.s[r.pos] = '/' then begin
|
|
inc(r.pos);
|
|
result.size := decodeInt(r);
|
|
end
|
|
else result.size := -1;
|
|
if r.s[r.pos] = '=' then begin
|
|
inc(r.pos);
|
|
result.align := decodeInt(r);
|
|
end
|
|
else result.align := 2;
|
|
if r.s[r.pos] = '@' then begin
|
|
inc(r.pos);
|
|
result.containerID := decodeInt(r);
|
|
end;
|
|
decodeLoc(r, result.loc, info);
|
|
while r.s[r.pos] = '^' do begin
|
|
inc(r.pos);
|
|
if r.s[r.pos] = '(' then begin
|
|
inc(r.pos);
|
|
if r.s[r.pos] = ')' then inc(r.pos)
|
|
else InternalError(info, 'decodeType ^(' + r.s[r.pos]);
|
|
addSon(result, nil);
|
|
end
|
|
else begin
|
|
d := decodeInt(r);
|
|
addSon(result, rrGetType(r, d, info));
|
|
end;
|
|
end
|
|
end;
|
|
|
|
function decodeLib(r: PRodReader): PLib;
|
|
begin
|
|
result := nil;
|
|
if r.s[r.pos] = '|' then begin
|
|
new(result);
|
|
{@ignore}
|
|
fillChar(result^, sizeof(result^), 0);
|
|
{@emit}
|
|
inc(r.pos);
|
|
result.kind := TLibKind(decodeInt(r));
|
|
if r.s[r.pos] <> '|' then InternalError('decodeLib: 1');
|
|
inc(r.pos);
|
|
result.name := toRope(decode(r));
|
|
if r.s[r.pos] <> '|' then InternalError('decodeLib: 2');
|
|
inc(r.pos);
|
|
result.path := decode(r);
|
|
end
|
|
end;
|
|
|
|
function decodeSym(r: PRodReader; const info: TLineInfo): PSym;
|
|
var
|
|
k: TSymKind;
|
|
id: int;
|
|
ident: PIdent;
|
|
begin
|
|
result := nil;
|
|
if r.s[r.pos] = '{' then begin
|
|
inc(r.pos);
|
|
if r.s[r.pos] = '}' then begin
|
|
inc(r.pos); exit; // nil sym
|
|
end
|
|
end;
|
|
k := TSymKind(decodeInt(r));
|
|
if r.s[r.pos] = '+' then begin
|
|
inc(r.pos);
|
|
id := decodeInt(r);
|
|
setId(id);
|
|
end
|
|
else
|
|
InternalError(info, 'decodeSym: no id');
|
|
if r.s[r.pos] = '&' then begin
|
|
inc(r.pos);
|
|
ident := getIdent(decode(r));
|
|
end
|
|
else
|
|
InternalError(info, 'decodeSym: no ident');
|
|
result := PSym(IdTableGet(r.syms, id));
|
|
if result = nil then begin
|
|
new(result);
|
|
{@ignore}
|
|
FillChar(result^, sizeof(result^), 0);
|
|
{@emit}
|
|
result.id := id;
|
|
IdTablePut(r.syms, result, result);
|
|
if debugIds then registerID(result);
|
|
end
|
|
else if (result.id <> id) then
|
|
InternalError(info, 'decodeSym: wrong id');
|
|
result.kind := k;
|
|
result.name := ident;
|
|
// read the rest of the symbol description:
|
|
if r.s[r.pos] = '^' then begin
|
|
inc(r.pos);
|
|
result.typ := rrGetType(r, decodeInt(r), info);
|
|
end;
|
|
decodeLineInfo(r, result.info);
|
|
if r.s[r.pos] = '*' then begin
|
|
inc(r.pos);
|
|
result.owner := rrGetSym(r, decodeInt(r), result.info);
|
|
end;
|
|
if r.s[r.pos] = '$' then begin
|
|
inc(r.pos);
|
|
result.flags := {@cast}TSymFlags(int32(decodeInt(r)));
|
|
end;
|
|
if r.s[r.pos] = '@' then begin
|
|
inc(r.pos);
|
|
result.magic := TMagic(decodeInt(r));
|
|
end;
|
|
if r.s[r.pos] = '(' then
|
|
result.ast := decodeNode(r, result.info);
|
|
if r.s[r.pos] = '!' then begin
|
|
inc(r.pos);
|
|
result.options := {@cast}TOptions(int32(decodeInt(r)));
|
|
end
|
|
else
|
|
result.options := r.options;
|
|
if r.s[r.pos] = '%' then begin
|
|
inc(r.pos);
|
|
result.position := decodeInt(r);
|
|
end
|
|
else
|
|
result.position := 0; // BUGFIX: this may have been misused as reader index!
|
|
if r.s[r.pos] = '`' then begin
|
|
inc(r.pos);
|
|
result.offset := decodeInt(r);
|
|
end
|
|
else
|
|
result.offset := -1;
|
|
decodeLoc(r, result.loc, result.info);
|
|
result.annex := decodeLib(r);
|
|
end;
|
|
|
|
function decodeInt(r: PRodReader): int; // base 190 numbers
|
|
var
|
|
i: int;
|
|
sign: int;
|
|
begin
|
|
i := r.pos;
|
|
sign := -1;
|
|
assert(r.s[i] in ['a'..'z', 'A'..'Z', '0'..'9', '-', #128..#255]);
|
|
if r.s[i] = '-' then begin
|
|
inc(i);
|
|
sign := 1
|
|
end;
|
|
result := 0;
|
|
while true do begin
|
|
case r.s[i] of
|
|
'0'..'9': result := result * 190 - (ord(r.s[i]) - ord('0'));
|
|
'a'..'z': result := result * 190 - (ord(r.s[i]) - ord('a') + 10);
|
|
'A'..'Z': result := result * 190 - (ord(r.s[i]) - ord('A') + 36);
|
|
#128..#255: result := result * 190 - (ord(r.s[i]) - 128 + 62);
|
|
else break;
|
|
end;
|
|
inc(i)
|
|
end;
|
|
result := result * sign;
|
|
r.pos := i
|
|
end;
|
|
|
|
function decodeBInt(r: PRodReader): biggestInt;
|
|
var
|
|
i: int;
|
|
sign: biggestInt;
|
|
begin
|
|
i := r.pos;
|
|
sign := -1;
|
|
assert(r.s[i] in ['a'..'z', 'A'..'Z', '0'..'9', '-', #128..#255]);
|
|
if r.s[i] = '-' then begin
|
|
inc(i);
|
|
sign := 1
|
|
end;
|
|
result := 0;
|
|
while true do begin
|
|
case r.s[i] of
|
|
'0'..'9': result := result * 190 - (ord(r.s[i]) - ord('0'));
|
|
'a'..'z': result := result * 190 - (ord(r.s[i]) - ord('a') + 10);
|
|
'A'..'Z': result := result * 190 - (ord(r.s[i]) - ord('A') + 36);
|
|
#128..#255: result := result * 190 - (ord(r.s[i]) - 128 + 62);
|
|
else break;
|
|
end;
|
|
inc(i)
|
|
end;
|
|
result := result * sign;
|
|
r.pos := i
|
|
end;
|
|
|
|
procedure hexChar(c: char; var xi: int);
|
|
begin
|
|
case c of
|
|
'0'..'9': xi := (xi shl 4) or (ord(c) - ord('0'));
|
|
'a'..'f': xi := (xi shl 4) or (ord(c) - ord('a') + 10);
|
|
'A'..'F': xi := (xi shl 4) or (ord(c) - ord('A') + 10);
|
|
else begin end
|
|
end
|
|
end;
|
|
|
|
function decode(r: PRodReader): string;
|
|
var
|
|
i, xi: int;
|
|
begin
|
|
i := r.pos;
|
|
result := '';
|
|
while true do begin
|
|
case r.s[i] of
|
|
'\': begin
|
|
inc(i, 3); xi := 0;
|
|
hexChar(r.s[i-2], xi);
|
|
hexChar(r.s[i-1], xi);
|
|
addChar(result, chr(xi));
|
|
end;
|
|
'a'..'z', 'A'..'Z', '0'..'9', '_': begin
|
|
addChar(result, r.s[i]);
|
|
inc(i);
|
|
end
|
|
else break
|
|
end
|
|
end;
|
|
r.pos := i;
|
|
end;
|
|
|
|
procedure skipSection(r: PRodReader);
|
|
var
|
|
c: int;
|
|
begin
|
|
if r.s[r.pos] = ':' then begin
|
|
while r.s[r.pos] > #10 do inc(r.pos);
|
|
end
|
|
else if r.s[r.pos] = '(' then begin
|
|
c := 0; // count () pairs
|
|
inc(r.pos);
|
|
while true do begin
|
|
case r.s[r.pos] of
|
|
#10: inc(r.line);
|
|
'(': inc(c);
|
|
')': begin
|
|
if c = 0 then begin inc(r.pos); break end
|
|
else if c > 0 then dec(c);
|
|
end;
|
|
#0: break; // end of file
|
|
else begin end;
|
|
end;
|
|
inc(r.pos);
|
|
end
|
|
end
|
|
else
|
|
InternalError('skipSection ' + toString(r.line));
|
|
end;
|
|
|
|
function rdWord(r: PRodReader): string;
|
|
begin
|
|
result := '';
|
|
while r.s[r.pos] in ['A'..'Z', '_', 'a'..'z', '0'..'9'] do begin
|
|
addChar(result, r.s[r.pos]);
|
|
inc(r.pos);
|
|
end;
|
|
end;
|
|
|
|
function newStub(r: PRodReader; const name: string; id: int): PSym;
|
|
begin
|
|
new(result);
|
|
{@ignore}
|
|
fillChar(result^, sizeof(result^), 0);
|
|
{@emit}
|
|
result.kind := skStub;
|
|
result.id := id;
|
|
result.name := getIdent(name);
|
|
result.position := r.readerIndex;
|
|
setID(id);
|
|
//MessageOut(result.name.s);
|
|
if debugIds then registerID(result);
|
|
end;
|
|
|
|
procedure processInterf(r: PRodReader; module: PSym);
|
|
var
|
|
s: PSym;
|
|
w: string;
|
|
key: int;
|
|
begin
|
|
if r.interfIdx = 0 then InternalError('processInterf');
|
|
r.pos := r.interfIdx;
|
|
while (r.s[r.pos] > #10) and (r.s[r.pos] <> ')') do begin
|
|
w := decode(r);
|
|
inc(r.pos);
|
|
key := decodeInt(r);
|
|
inc(r.pos); // #10
|
|
s := newStub(r, w, key);
|
|
s.owner := module;
|
|
StrTableAdd(module.tab, s);
|
|
IdTablePut(r.syms, s, s);
|
|
end;
|
|
end;
|
|
|
|
procedure processCompilerProcs(r: PRodReader; module: PSym);
|
|
var
|
|
s: PSym;
|
|
w: string;
|
|
key: int;
|
|
begin
|
|
if r.compilerProcsIdx = 0 then InternalError('processCompilerProcs');
|
|
r.pos := r.compilerProcsIdx;
|
|
while (r.s[r.pos] > #10) and (r.s[r.pos] <> ')') do begin
|
|
w := decode(r);
|
|
inc(r.pos);
|
|
key := decodeInt(r);
|
|
inc(r.pos); // #10
|
|
s := PSym(IdTableGet(r.syms, key));
|
|
if s = nil then begin
|
|
s := newStub(r, w, key);
|
|
s.owner := module;
|
|
IdTablePut(r.syms, s, s);
|
|
end;
|
|
StrTableAdd(rodCompilerProcs, s);
|
|
end;
|
|
end;
|
|
|
|
procedure processIndex(r: PRodReader; var idx: TIndex);
|
|
var
|
|
key, val, tmp: int;
|
|
begin
|
|
inc(r.pos, 2); // skip "(\10"
|
|
inc(r.line);
|
|
while (r.s[r.pos] > #10) and (r.s[r.pos] <> ')') do begin
|
|
tmp := decodeInt(r);
|
|
if r.s[r.pos] = ' ' then begin
|
|
inc(r.pos);
|
|
key := idx.lastIdxKey + tmp;
|
|
val := decodeInt(r) + idx.lastIdxVal;
|
|
end
|
|
else begin
|
|
key := idx.lastIdxKey + 1;
|
|
val := tmp + idx.lastIdxVal;
|
|
end;
|
|
IITablePut(idx.tab, key, val);
|
|
idx.lastIdxKey := key;
|
|
idx.lastIdxVal := val;
|
|
setID(key); // ensure that this id will not be used
|
|
if r.s[r.pos] = #10 then begin inc(r.pos); inc(r.line) end;
|
|
end;
|
|
if r.s[r.pos] = ')' then inc(r.pos);
|
|
end;
|
|
|
|
procedure processRodFile(r: PRodReader; crc: TCrc32);
|
|
var
|
|
section, w: string;
|
|
d, L, inclCrc: int;
|
|
begin
|
|
while r.s[r.pos] <> #0 do begin
|
|
section := rdWord(r);
|
|
if r.reason <> rrNone then break; // no need to process this file further
|
|
if section = 'CRC' then begin
|
|
inc(r.pos); // skip ':'
|
|
if int(crc) <> decodeInt(r) then
|
|
r.reason := rrCrcChange
|
|
end
|
|
else if section = 'ID' then begin
|
|
inc(r.pos); // skip ':'
|
|
r.moduleID := decodeInt(r);
|
|
setID(r.moduleID);
|
|
end
|
|
else if section = 'OPTIONS' then begin
|
|
inc(r.pos); // skip ':'
|
|
r.options := {@cast}TOptions(int32(decodeInt(r)));
|
|
if options.gOptions <> r.options then r.reason := rrOptions
|
|
end
|
|
else if section = 'DEFINES' then begin
|
|
inc(r.pos); // skip ':'
|
|
d := 0;
|
|
while r.s[r.pos] > #10 do begin
|
|
w := decode(r);
|
|
inc(d);
|
|
if not condsyms.isDefined(getIdent(w)) then begin
|
|
r.reason := rrDefines;
|
|
//MessageOut('not defined, but should: ' + w);
|
|
end;
|
|
if r.s[r.pos] = ' ' then inc(r.pos);
|
|
end;
|
|
if (d <> countDefinedSymbols()) then
|
|
r.reason := rrDefines
|
|
end
|
|
else if section = 'FILES' then begin
|
|
inc(r.pos, 2); // skip "(\10"
|
|
inc(r.line);
|
|
L := 0;
|
|
while (r.s[r.pos] > #10) and (r.s[r.pos] <> ')') do begin
|
|
setLength(r.files, L+1);
|
|
r.files[L] := decode(r);
|
|
inc(r.pos); // skip #10
|
|
inc(r.line);
|
|
inc(L);
|
|
end;
|
|
if r.s[r.pos] = ')' then inc(r.pos);
|
|
end
|
|
else if section = 'INCLUDES' then begin
|
|
inc(r.pos, 2); // skip "(\10"
|
|
inc(r.line);
|
|
while (r.s[r.pos] > #10) and (r.s[r.pos] <> ')') do begin
|
|
w := r.files[decodeInt(r)];
|
|
inc(r.pos); // skip ' '
|
|
inclCrc := decodeInt(r);
|
|
if r.reason = rrNone then begin
|
|
if not ExistsFile(w) or (inclCrc <> int(crcFromFile(w))) then
|
|
r.reason := rrInclDeps
|
|
end;
|
|
if r.s[r.pos] = #10 then begin inc(r.pos); inc(r.line) end;
|
|
end;
|
|
if r.s[r.pos] = ')' then inc(r.pos);
|
|
end
|
|
else if section = 'DEPS' then begin
|
|
inc(r.pos); // skip ':'
|
|
L := 0;
|
|
while (r.s[r.pos] > #10) do begin
|
|
setLength(r.modDeps, L+1);
|
|
r.modDeps[L] := r.files[decodeInt(r)];
|
|
inc(L);
|
|
if r.s[r.pos] = ' ' then inc(r.pos);
|
|
end;
|
|
end
|
|
else if section = 'INTERF' then begin
|
|
r.interfIdx := r.pos+2;
|
|
skipSection(r);
|
|
end
|
|
else if section = 'COMPILERPROCS' then begin
|
|
r.compilerProcsIdx := r.pos+2;
|
|
skipSection(r);
|
|
end
|
|
else if section = 'INDEX' then begin
|
|
processIndex(r, r.index);
|
|
end
|
|
else if section = 'IMPORTS' then begin
|
|
processIndex(r, r.imports);
|
|
end
|
|
else if section = 'CONVERTERS' then begin
|
|
r.convertersIdx := r.pos+1;
|
|
skipSection(r);
|
|
end
|
|
else if section = 'DATA' then begin
|
|
r.dataIdx := r.pos+2; // "(\10"
|
|
// We do not read the DATA section here! We read the needed objects on
|
|
// demand.
|
|
skipSection(r);
|
|
end
|
|
else if section = 'INIT' then begin
|
|
r.initIdx := r.pos+2; // "(\10"
|
|
skipSection(r);
|
|
end
|
|
else if section = 'CGEN' then begin
|
|
r.cgenIdx := r.pos+2;
|
|
skipSection(r);
|
|
end
|
|
else begin
|
|
MessageOut('skipping section: ' + toString(r.pos));
|
|
skipSection(r);
|
|
end;
|
|
if r.s[r.pos] = #10 then begin inc(r.pos); inc(r.line) end;
|
|
end
|
|
end;
|
|
|
|
function newRodReader(const modfilename: string; crc: TCrc32;
|
|
readerIndex: int): PRodReader;
|
|
var
|
|
version: string;
|
|
r: PRodReader;
|
|
begin
|
|
new(result);
|
|
{@ignore}
|
|
fillChar(result^, sizeof(result^), 0);
|
|
{@emit result.files := @[];}
|
|
{@emit result.modDeps := @[];}
|
|
r := result;
|
|
r.reason := rrNone;
|
|
r.pos := strStart;
|
|
r.line := 1;
|
|
r.readerIndex := readerIndex;
|
|
r.filename := modfilename;
|
|
InitIdTable(r.syms);
|
|
r.s := readFile(modfilename) {@ignore} + #0 {@emit};
|
|
if startsWith(r.s, 'NIM:') then begin
|
|
initIITable(r.index.tab);
|
|
initIITable(r.imports.tab);
|
|
// looks like a ROD file
|
|
inc(r.pos, 4);
|
|
version := '';
|
|
while not (r.s[r.pos] in [#0,#10]) do begin
|
|
addChar(version, r.s[r.pos]);
|
|
inc(r.pos);
|
|
end;
|
|
if r.s[r.pos] = #10 then inc(r.pos);
|
|
if version = FileVersion then begin
|
|
// since ROD files are only for caching, no backwarts compability is
|
|
// needed
|
|
processRodFile(r, crc);
|
|
end
|
|
else
|
|
result := nil
|
|
end
|
|
else
|
|
result := nil;
|
|
end;
|
|
|
|
function rrGetType(r: PRodReader; id: int; const info: TLineInfo): PType;
|
|
var
|
|
oldPos, d: int;
|
|
begin
|
|
result := PType(IdTableGet(gTypeTable, id));
|
|
if result = nil then begin
|
|
// load the type:
|
|
oldPos := r.pos;
|
|
d := IITableGet(r.index.tab, id);
|
|
if d = invalidKey then InternalError(info, 'rrGetType');
|
|
r.pos := d + r.dataIdx;
|
|
result := decodeType(r, info);
|
|
r.pos := oldPos;
|
|
end;
|
|
end;
|
|
|
|
type
|
|
TFileModuleRec = record
|
|
filename: string;
|
|
reason: TReasonForRecompile;
|
|
rd: PRodReader;
|
|
crc: TCrc32;
|
|
end;
|
|
TFileModuleMap = array of TFileModuleRec;
|
|
var
|
|
gMods: TFileModuleMap = {@ignore} nil {@emit @[]}; // all compiled modules
|
|
|
|
function decodeSymSafePos(rd: PRodReader; offset: int;
|
|
const info: TLineInfo): PSym;
|
|
var
|
|
oldPos: int;
|
|
begin
|
|
if rd.dataIdx = 0 then InternalError(info, 'dataIdx == 0');
|
|
oldPos := rd.pos;
|
|
rd.pos := offset + rd.dataIdx;
|
|
result := decodeSym(rd, info);
|
|
rd.pos := oldPos;
|
|
end;
|
|
|
|
function rrGetSym(r: PRodReader; id: int; const info: TLineInfo): PSym;
|
|
var
|
|
d, i, moduleID: int;
|
|
rd: PRodReader;
|
|
begin
|
|
result := PSym(IdTableGet(r.syms, id));
|
|
if result = nil then begin
|
|
// load the symbol:
|
|
d := IITableGet(r.index.tab, id);
|
|
if d = invalidKey then begin
|
|
moduleID := IiTableGet(r.imports.tab, id);
|
|
if moduleID < 0 then
|
|
InternalError(info,
|
|
'missing from both indexes: +' + ropeToStr(encodeInt(id)));
|
|
// find the reader with the correct moduleID:
|
|
for i := 0 to high(gMods) do begin
|
|
rd := gMods[i].rd;
|
|
if (rd <> nil) then begin
|
|
if (rd.moduleID = moduleID) then begin
|
|
d := IITableGet(rd.index.tab, id);
|
|
if d <> invalidKey then begin
|
|
result := decodeSymSafePos(rd, d, info);
|
|
break
|
|
end
|
|
else
|
|
InternalError(info,
|
|
'rrGetSym: no reader found: +' + ropeToStr(encodeInt(id)));
|
|
end
|
|
else begin
|
|
//if IiTableGet(rd.index.tab, id) <> invalidKey then
|
|
// XXX expensive check!
|
|
//InternalError(info,
|
|
//'id found in other module: +' + ropeToStr(encodeInt(id)))
|
|
end
|
|
end
|
|
end;
|
|
end
|
|
else begin
|
|
// own symbol:
|
|
result := decodeSymSafePos(r, d, info);
|
|
end;
|
|
end;
|
|
if (result <> nil) and (result.kind = skStub) then loadStub(result);
|
|
end;
|
|
|
|
function loadInitSection(r: PRodReader): PNode;
|
|
var
|
|
d, oldPos, p: int;
|
|
begin
|
|
if (r.initIdx = 0) or (r.dataIdx = 0) then InternalError('loadInitSection');
|
|
oldPos := r.pos;
|
|
r.pos := r.initIdx;
|
|
result := newNode(nkStmtList);
|
|
while (r.s[r.pos] > #10) and (r.s[r.pos] <> ')') do begin
|
|
d := decodeInt(r);
|
|
inc(r.pos); // #10
|
|
p := r.pos;
|
|
r.pos := d + r.dataIdx;
|
|
addSon(result, decodeNode(r, UnknownLineInfo()));
|
|
r.pos := p;
|
|
end;
|
|
r.pos := oldPos;
|
|
end;
|
|
|
|
procedure loadConverters(r: PRodReader);
|
|
var
|
|
d: int;
|
|
begin
|
|
// We have to ensure that no exported converter is a stub anymore.
|
|
if (r.convertersIdx = 0) or (r.dataIdx = 0) then
|
|
InternalError('importConverters');
|
|
r.pos := r.convertersIdx;
|
|
while (r.s[r.pos] > #10) do begin
|
|
d := decodeInt(r);
|
|
{@discard} rrGetSym(r, d, UnknownLineInfo());
|
|
if r.s[r.pos] = ' ' then inc(r.pos)
|
|
end;
|
|
end;
|
|
|
|
function getModuleIdx(const filename: string): int;
|
|
var
|
|
i: int;
|
|
begin
|
|
for i := 0 to high(gMods) do
|
|
if sameFile(gMods[i].filename, filename) then begin
|
|
result := i; exit
|
|
end;
|
|
// not found, reserve space:
|
|
result := length(gMods);
|
|
setLength(gMods, result+1);
|
|
end;
|
|
|
|
function checkDep(const filename: string): TReasonForRecompile;
|
|
var
|
|
crc: TCrc32;
|
|
r: PRodReader;
|
|
rodfile: string;
|
|
idx, i: int;
|
|
res: TReasonForRecompile;
|
|
begin
|
|
idx := getModuleIdx(filename);
|
|
if gMods[idx].reason <> rrEmpty then begin
|
|
// reason has already been computed for this module:
|
|
result := gMods[idx].reason; exit
|
|
end;
|
|
crc := crcFromFile(filename);
|
|
gMods[idx].reason := rrNone; // we need to set it here to avoid cycles
|
|
gMods[idx].filename := filename;
|
|
gMods[idx].crc := crc;
|
|
result := rrNone;
|
|
r := nil;
|
|
rodfile := toGeneratedFile(filename, RodExt);
|
|
if ExistsFile(rodfile) then begin
|
|
r := newRodReader(rodfile, crc, idx);
|
|
if r = nil then
|
|
result := rrRodInvalid
|
|
else begin
|
|
result := r.reason;
|
|
if result = rrNone then begin
|
|
// check modules it depends on
|
|
// NOTE: we need to process the entire module graph so that no ID will
|
|
// be used twice! However, compilation speed does not suffer much from
|
|
// this, since results are cached.
|
|
res := checkDep(JoinPath(options.libpath, addFileExt('system', nimExt)));
|
|
if res <> rrNone then result := rrModDeps;
|
|
for i := 0 to high(r.modDeps) do begin
|
|
res := checkDep(r.modDeps[i]);
|
|
if res <> rrNone then begin
|
|
result := rrModDeps;
|
|
//break // BUGFIX: cannot break here!
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
result := rrRodDoesNotExist;
|
|
if (result <> rrNone) and (gVerbosity > 0) then
|
|
MessageOut(format(reasonToFrmt[result], [filename]));
|
|
if (result <> rrNone) or (optForceFullMake in gGlobalOptions) then begin
|
|
// recompilation is necessary:
|
|
r := nil;
|
|
end;
|
|
gMods[idx].rd := r;
|
|
gMods[idx].reason := result; // now we know better
|
|
end;
|
|
|
|
function handleSymbolFile(module: PSym; const filename: string): PRodReader;
|
|
var
|
|
idx: int;
|
|
begin
|
|
if not (optSymbolFiles in gGlobalOptions) then begin
|
|
module.id := getID();
|
|
result := nil;
|
|
exit
|
|
end;
|
|
{@discard} checkDep(filename);
|
|
idx := getModuleIdx(filename);
|
|
if gMods[idx].reason = rrEmpty then InternalError('handleSymbolFile');
|
|
result := gMods[idx].rd;
|
|
if result <> nil then begin
|
|
module.id := result.moduleID;
|
|
IdTablePut(result.syms, module, module);
|
|
processInterf(result, module);
|
|
processCompilerProcs(result, module);
|
|
loadConverters(result);
|
|
end
|
|
else
|
|
module.id := getID();
|
|
end;
|
|
|
|
function GetCRC(const filename: string): TCrc32;
|
|
var
|
|
idx: int;
|
|
begin
|
|
idx := getModuleIdx(filename);
|
|
result := gMods[idx].crc;
|
|
end;
|
|
|
|
procedure loadStub(s: PSym);
|
|
var
|
|
rd: PRodReader;
|
|
d, theId: int;
|
|
rs: PSym;
|
|
begin
|
|
if s.kind <> skStub then InternalError('loadStub');
|
|
//MessageOut('loading stub: ' + s.name.s);
|
|
rd := gMods[s.position].rd;
|
|
theId := s.id; // used for later check
|
|
d := IITableGet(rd.index.tab, s.id);
|
|
if d = invalidKey then InternalError('loadStub: invalid key');
|
|
rs := decodeSymSafePos(rd, d, UnknownLineInfo());
|
|
if rs <> s then InternalError(rs.info, 'loadStub: wrong symbol')
|
|
else if rs.id <> theId then InternalError(rs.info, 'loadStub: wrong ID');
|
|
//MessageOut('loaded stub: ' + s.name.s);
|
|
end;
|
|
|
|
initialization
|
|
InitIdTable(gTypeTable);
|
|
InitStrTable(rodCompilerProcs);
|
|
end.
|