ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/wm/rt.b/
implement WmRt;
include "sys.m";
sys: Sys;
sprint: import sys;
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "draw.m";
include "tk.m";
tk: Tk;
Toplevel: import tk;
include "tkclient.m";
tkclient: Tkclient;
include "dialog.m";
dialog: Dialog;
include "selectfile.m";
selectfile: Selectfile;
include "dis.m";
dis: Dis;
Inst, Type, Data, Link, Mod: import dis;
XMAGIC: import Dis;
MUSTCOMPILE, DONTCOMPILE: import Dis;
AMP, AFP, AIMM, AXXX, AIND, AMASK: import Dis;
ARM, AXNON, AXIMM, AXINF, AXINM: import Dis;
DEFB, DEFW, DEFS, DEFF, DEFA, DIND, DAPOP, DEFL: import Dis;
WmRt: module
{
init: fn(ctxt: ref Draw->Context, argv: list of string);
};
gctxt: ref Draw->Context;
t: ref Toplevel;
disfile: string;
TK: con 1;
m: ref Mod;
rt := 0;
ss := -1;
rt_cfg := array[] of {
"frame .m",
"menubutton .m.open -text File -menu .file",
"menubutton .m.prop -text Properties -menu .prop",
"menubutton .m.view -text View -menu .view",
"label .m.l",
"pack .m.open .m.view .m.prop -side left",
"pack .m.l -side right",
"frame .b",
"text .b.t -width 12c -height 7c -yscrollcommand {.b.s set} -bg white",
"scrollbar .b.s -command {.b.t yview}",
"pack .b.s -fill y -side left",
"pack .b.t -fill both -expand 1",
"pack .m -anchor w -fill x",
"pack .b -fill both -expand 1",
"pack propagate . 0",
"update",
"menu .prop",
".prop add checkbutton -text {Must compile} -command {send cmd must}",
".prop add checkbutton -text {Don't compile} -command {send cmd dont}",
".prop add separator",
".prop add command -text {Set stack extent} -command {send cmd stack}",
".prop add command -text {Sign module} -command {send cmd sign}",
"menu .view",
".view add command -text {Header} -command {send cmd hdr}",
".view add command -text {Code segment} -command {send cmd code}",
".view add command -text {Data segment} -command {send cmd data}",
".view add command -text {Type descriptors} -command {send cmd type}",
".view add command -text {Link descriptors} -command {send cmd link}",
".view add command -text {Import descriptors} -command {send cmd imports}",
".view add command -text {Exception handlers} -command {send cmd handlers}",
"menu .file",
".file add command -text {Open module} -command {send cmd open}",
".file add separator",
".file add command -text {Write .dis module} -command {send cmd save}",
".file add command -text {Write .s file} -command {send cmd list}",
};
init(ctxt: ref Draw->Context, nil: list of string)
{
sys = load Sys Sys->PATH;
if (ctxt == nil) {
sys->fprint(sys->fildes(2), "rt: no window context\n");
raise "fail:bad context";
}
tk = load Tk Tk->PATH;
tkclient = load Tkclient Tkclient->PATH;
dialog = load Dialog Dialog->PATH;
selectfile = load Selectfile Selectfile->PATH;
sys->pctl(Sys->NEWPGRP, nil);
tkclient->init();
dialog->init();
selectfile->init();
gctxt = ctxt;
menubut: chan of string;
(t, menubut) = tkclient->toplevel(ctxt, "", "Dis Module Manager", Tkclient->Appl);
cmd := chan of string;
tk->namechan(t, cmd, "cmd");
tkcmds(t, rt_cfg);
tkclient->onscreen(t, nil);
tkclient->startinput(t, "kbd"::"ptr"::nil);
dis = load Dis Dis->PATH;
if(dis == nil) {
dialog->prompt(ctxt, t.image, "error -fg red", "Load Module",
"wmrt requires Dis",
0, "Exit"::nil);
return;
}
dis->init();
for(;;) alt {
s := <-t.ctxt.kbd =>
tk->keyboard(t, s);
s := <-t.ctxt.ptr =>
tk->pointer(t, *s);
s := <-t.ctxt.ctl or
s = <-t.wreq =>
tkclient->wmctl(t, s);
menu := <-menubut =>
if(menu == "exit")
return;
tkclient->wmctl(t, menu);
s := <-cmd =>
case s {
"open" =>
openfile(ctxt);
"save" =>
writedis();
"list" =>
writeasm();
"hdr" =>
hdr();
"code" =>
das(TK);
"data" =>
dat(TK);
"type" =>
desc(TK);
"link" =>
link(TK);
"imports" =>
imports(TK);
"handlers" =>
handlers(TK);
"must" =>
rt ^= MUSTCOMPILE;
"dont" =>
rt ^= DONTCOMPILE;
"stack" =>
spawn stack(ctxt);
"sign" =>
dialog->prompt(ctxt, t.image, "error -fg red", "Signed Modules",
"not implemented",
0, "Continue"::nil);
}
}
}
stack_cfg := array[] of {
"scale .s -length 200 -to 32768 -resolution 128 -orient horizontal",
"frame .f",
"pack .s .f -pady 5 -fill x -expand 1",
};
stack(ctxt: ref Draw->Context)
{
# (s, sbut) := tkclient->toplevel(ctxt, tkclient->geom(t), "Dis Stack", 0);
(s, sbut) := tkclient->toplevel(ctxt, "", "Dis Stack", 0);
cmd := chan of string;
tk->namechan(s, cmd, "cmd");
tkcmds(s, stack_cfg);
tk->cmd(s, ".s set " + string ss);
tk->cmd(s, "update");
tkclient->onscreen(s, nil);
tkclient->startinput(s, "kbd"::"ptr"::nil);
for(;;) alt {
c := <-s.ctxt.kbd =>
tk->keyboard(s, c);
c := <-s.ctxt.ptr =>
tk->pointer(s, *c);
c := <-s.ctxt.ctl or
c = <-s.wreq =>
tkclient->wmctl(s, c);
wmctl := <-sbut =>
if(wmctl == "exit") {
ss = int tk->cmd(s, ".s get");
return;
}
tkclient->wmctl(s, wmctl);
}
}
openfile(ctxt: ref Draw->Context)
{
pattern := list of {
"*.dis (Dis VM module)",
"* (All files)"
};
for(;;) {
disfile = selectfile->filename(ctxt, t.image, "Dis file", pattern, nil);
if(disfile == "")
break;
s: string;
(m, s) = dis->loadobj(disfile);
if(s == nil) {
ss = m.ssize;
rt = m.rt;
tk->cmd(t, ".m.l configure -text {"+m.name+"}");
das(TK);
return;
}
r := dialog->prompt(ctxt, t.image, "error -fg red", "Open Dis File",
s,
0, "Retry" :: "Abort" :: nil);
if(r == 1)
return;
}
}
writedis()
{
if(m == nil || m.magic == 0) {
dialog->prompt(gctxt, t.image, "error -fg red", "Write .dis",
"no module loaded",
0, "Continue"::nil);
return;
}
if(rt < 0)
rt = m.rt;
if(ss < 0)
ss = m.ssize;
if(rt == m.rt && ss == m.ssize)
return;
while((fd := sys->open(disfile, Sys->OREAD)) == nil){
if(dialog->prompt(gctxt, t.image, "error -fg red", "Open Dis File", "open failed: "+sprint("%r"),
0, "Retry" :: "Abort" :: nil))
return;
}
if(len discona(rt) == len discona(m.rt) && len discona(ss) == len discona(m.ssize)){
sys->seek(fd, big 4, Sys->SEEKSTART); # skip magic
discon(fd, rt);
discon(fd, ss);
m.rt = rt;
m.ssize = ss;
return;
}
# rt and ss representations changed in length: read the file in,
# make a copy and update rt and ss when copying
(ok, d) := sys->fstat(fd);
if(ok < 0){
ioerror("Reading Dis file "+disfile, "can't find file length: "+sprint("%r"));
return;
}
length := int d.length;
disbuf := array[length] of byte;
if(sys->read(fd, disbuf, length) != length){
ioerror("Reading Dis file "+disfile, "read error: "+sprint("%r"));
return;
}
outbuf := array[length+2*4] of byte; # could avoid this buffer if required, by writing portions of disbuf
(magic, i) := operand(disbuf, 0);
o := putoperand(outbuf, magic);
if(magic == Dis->SMAGIC){
ns: int;
(ns, i) = operand(disbuf, i);
o += putoperand(outbuf[o:], ns);
sign := disbuf[i:i+ns];
i += ns;
outbuf[o:] = sign;
o += ns;
}
(nil, i) = operand(disbuf, i);
(nil, i) = operand(disbuf, i);
if(i < 0){
ioerror("Reading Dis file "+disfile, "Dis header too short");
return;
}
o += putoperand(outbuf[o:], rt);
o += putoperand(outbuf[o:], ss);
outbuf[o:] = disbuf[i:];
o += len disbuf - i;
fd = sys->create(disfile, Sys->OWRITE, 8r666);
if(fd == nil){
ioerror("Rewriting "+disfile, sys->sprint("can't create %s: %r",disfile));
return;
}
if(sys->write(fd, outbuf, o) != o)
ioerror("Rewriting "+disfile, "write error: "+sprint("%r"));
m.rt = rt;
m.ssize = ss;
}
ioerror(title: string, err: string)
{
dialog->prompt(gctxt, t.image, "error -fg red", title, err, 0, "Dismiss" :: nil);
}
putoperand(out: array of byte, v: int): int
{
a := discona(v);
out[0:] = a;
return len a;
}
discona(val: int): array of byte
{
if(val >= -64 && val <= 63)
return array[] of { byte(val & ~16r80) };
else if(val >= -8192 && val <= 8191)
return array[] of { byte((val>>8) & ~16rC0 | 16r80), byte val };
else
return array[] of { byte(val>>24 | 16rC0), byte(val>>16), byte(val>>8), byte val };
}
discon(fd: ref Sys->FD, val: int)
{
a := discona(val);
sys->write(fd, a, len a);
}
operand(disobj: array of byte, o: int): (int, int)
{
if(o >= len disobj)
return (-1, -1);
b := int disobj[o++];
case b & 16rC0 {
16r00 =>
return (b, o);
16r40 =>
return (b | ~16r7F, o);
16r80 =>
if(o >= len disobj)
return (-1, -1);
if(b & 16r20)
b |= ~16r3F;
else
b &= 16r3F;
b = (b<<8) | int disobj[o++];
return (b, o);
16rC0 =>
if(o+2 >= len disobj)
return (-1, -1);
if(b & 16r20)
b |= ~16r3F;
else
b &= 16r3F;
b = b<<24 |
(int disobj[o]<<16) |
(int disobj[o+1]<<8)|
int disobj[o+2];
o += 3;
return (b, o);
}
return (0, -1); # can't happen
}
fasm: ref Iobuf;
writeasm()
{
if(m == nil || m.magic == 0) {
dialog->prompt(gctxt, t.image, "error -fg red", "Write .s",
"no module loaded",
0, "Continue"::nil);
return;
}
bufio = load Bufio Bufio->PATH;
if(bufio == nil) {
dialog->prompt(gctxt, t.image, "error -fg red", "Write .s",
"Bufio load failed: "+sprint("%r"),
0, "Exit"::nil);
return;
}
for(;;) {
asmfile: string;
if(len disfile > 4 && disfile[len disfile-4:] == ".dis")
asmfile = disfile[0:len disfile-3] + "s";
else
asmfile = disfile + ".s";
fasm = bufio->create(asmfile, Sys->OWRITE|Sys->OTRUNC, 8r666);
if(fasm != nil)
break;
r := dialog->prompt(gctxt, t.image, "error -fg red", "Create .s file",
"open failed: "+sprint("%r"),
0, "Retry" :: "Abort" :: nil);
if(r == 0)
continue;
else
return;
}
das(!TK);
fasm.puts("\tentry\t" + string m.entry + "," + string m.entryt + "\n");
desc(!TK);
dat(!TK);
fasm.puts("\tmodule\t" + m.name + "\n");
link(!TK);
imports(!TK);
handlers(!TK);
fasm.close();
}
link(flag: int)
{
if(m == nil || m.magic == 0) {
dialog->prompt(gctxt, t.image, "error -fg red", "Link Descriptors",
"no module loaded",
0, "Continue"::nil);
return;
}
if(flag == TK)
tk->cmd(t, ".b.t delete 1.0 end");
for(i := 0; i < m.lsize; i++) {
l := m.links[i];
s := sprint(" link %d,%d, 0x%ux, \"%s\"\n",
l.desc, l.pc, l.sig, l.name);
if(flag == TK)
tk->cmd(t, ".b.t insert end '"+s);
else
fasm.puts(s);
}
if(flag == TK)
tk->cmd(t, ".b.t see 1.0; update");
}
imports(flag: int)
{
if(m == nil || m.magic == 0) {
dialog->prompt(gctxt, t.image, "error -fg red", "Import Descriptors",
"no module loaded",
0, "Continue"::nil);
return;
}
if(flag == TK)
tk->cmd(t, ".b.t delete 1.0 end");
mi := m.imports;
for(i := 0; i < len mi; i++) {
a := mi[i];
for(j := 0; j < len a; j++) {
ai := a[j];
s := sprint(" import 0x%ux, \"%s\"\n", ai.sig, ai.name);
if(flag == TK)
tk->cmd(t, ".b.t insert end '"+s);
else
fasm.puts(s);
}
}
if(flag == TK)
tk->cmd(t, ".b.t see 1.0; update");
}
handlers(flag: int)
{
if(m == nil || m.magic == 0) {
dialog->prompt(gctxt, t.image, "error -fg red", "Exception Handlers",
"no module loaded",
0, "Continue"::nil);
return;
}
if(flag == TK)
tk->cmd(t, ".b.t delete 1.0 end");
hs := m.handlers;
for(i := 0; i < len hs; i++) {
h := hs[i];
tt := -1;
for(j := 0; j < len m.types; j++) {
if(h.t == m.types[j]) {
tt = j;
break;
}
}
s := sprint(" %d-%d, o=%d, e=%d t=%d\n", h.pc1, h.pc2, h.eoff, h.ne, tt);
if(flag == TK)
tk->cmd(t, ".b.t insert end '"+s);
else
fasm.puts(s);
et := h.etab;
for(j = 0; j < len et; j++) {
e := et[j];
if(e.s == nil)
s = sprint(" %d *\n", e.pc);
else
s = sprint(" %d \"%s\"\n", e.pc, e.s);
if(flag == TK)
tk->cmd(t, ".b.t insert end '"+s);
else
fasm.puts(s);
}
}
if(flag == TK)
tk->cmd(t, ".b.t see 1.0; update");
}
desc(flag: int)
{
if(m == nil || m.magic == 0) {
dialog->prompt(gctxt, t.image, "error -fg red", "Type Descriptors",
"no module loaded",
0, "Continue"::nil);
return;
}
if(flag == TK)
tk->cmd(t, ".b.t delete 1.0 end");
for(i := 0; i < m.tsize; i++) {
h := m.types[i];
s := sprint(" desc $%d, %d, \"", i, h.size);
for(j := 0; j < h.np; j++)
s += sprint("%.2ux", int h.map[j]);
s += "\"\n";
if(flag == TK)
tk->cmd(t, ".b.t insert end '"+s);
else
fasm.puts(s);
}
if(flag == TK)
tk->cmd(t, ".b.t see 1.0; update");
}
hdr()
{
if(m == nil || m.magic == 0) {
dialog->prompt(gctxt, t.image, "error -fg red", "Header",
"no module loaded",
0, "Continue"::nil);
return;
}
tk->cmd(t, ".b.t delete 1.0 end");
s := sprint("%.8ux Version %d Dis VM\n", m.magic, m.magic - XMAGIC + 1);
s += sprint("%.8ux Runtime flags %s\n", m.rt, rtflag(m.rt));
s += sprint("%8d bytes per stack extent\n\n", m.ssize);
s += sprint("%8d instructions\n", m.isize);
s += sprint("%8d data size\n", m.dsize);
s += sprint("%8d heap type descriptors\n", m.tsize);
s += sprint("%8d link directives\n", m.lsize);
s += sprint("%8d entry pc\n", m.entry);
s += sprint("%8d entry type descriptor\n\n", m.entryt);
if(m.sign == nil)
s += "Module is Insecure\n";
tk->cmd(t, ".b.t insert end '"+s);
tk->cmd(t, ".b.t see 1.0; update");
}
rtflag(flag: int): string
{
if(flag == 0)
return "";
s := "[";
if(flag & MUSTCOMPILE)
s += "MustCompile";
if(flag & DONTCOMPILE) {
if(flag & MUSTCOMPILE)
s += "|";
s += "DontCompile";
}
s[len s] = ']';
return s;
}
das(flag: int)
{
if(m == nil || m.magic == 0) {
dialog->prompt(gctxt, t.image, "error -fg red", "Assembly",
"no module loaded",
0, "Continue"::nil);
return;
}
if(flag == TK)
tk->cmd(t, ".b.t delete 1.0 end");
for(i := 0; i < m.isize; i++) {
prefix := "";
if(flag == TK)
prefix = sprint(".b.t insert end '%4d ", i);
else {
if(i % 10 == 0)
fasm.puts("#" + string i + "\n");
prefix = sprint("\t");
}
s := prefix + dis->inst2s(m.inst[i]) + "\n";
if(flag == TK)
tk->cmd(t, s);
else
fasm.puts(s);
}
if(flag == TK)
tk->cmd(t, ".b.t see 1.0; update");
}
dat(flag: int)
{
if(m == nil || m.magic == 0) {
dialog->prompt(gctxt, t.image, "error -fg red", "Module Data",
"no module loaded",
0, "Continue"::nil);
return;
}
s := sprint(" var @mp, %d\n", m.types[0].size);
if(flag == TK) {
tk->cmd(t, ".b.t delete 1.0 end");
tk->cmd(t, ".b.t insert end '"+s);
} else
fasm.puts(s);
s = "";
for(d := m.data; d != nil; d = tl d) {
pick dat := hd d {
Bytes =>
s = sprint("\tbyte @mp+%d", dat.off);
for(n := 0; n < dat.n; n++)
s += sprint(",%d", int dat.bytes[n]);
Words =>
s = sprint("\tword @mp+%d", dat.off);
for(n := 0; n < dat.n; n++)
s += sprint(",%d", dat.words[n]);
String =>
s = sprint("\tstring @mp+%d, \"%s\"", dat.off, mapstr(dat.str));
Reals =>
s = sprint("\treal @mp+%d", dat.off);
for(n := 0; n < dat.n; n++)
s += sprint(", %g", dat.reals[n]);
break;
Array =>
s = sprint("\tarray @mp+%d,$%d,%d", dat.off, dat.typex, dat.length);
Aindex =>
s = sprint("\tindir @mp+%d,%d", dat.off, dat.index);
Arestore =>
s = "\tapop";
break;
Bigs =>
s = sprint("\tlong @mp+%d", dat.off);
for(n := 0; n < dat.n; n++)
s += sprint(", %bd", dat.bigs[n]);
}
if(flag == TK)
tk->cmd(t, ".b.t insert end '"+s+"\n");
else
fasm.puts(s+"\n");
}
if(flag == TK)
tk->cmd(t, ".b.t see 1.0; update");
}
mapstr(s: string): string
{
for(i := 0; i < len s; i++) {
if(s[i] == '\n')
s = s[0:i] + "\\n" + s[i+1:];
}
return s;
}
tkcmds(t: ref Toplevel, cfg: array of string)
{
for(i := 0; i < len cfg; i++)
tk->cmd(t, cfg[i]);
}