2019-11-27 09:18:17 -05:00
|
|
|
// Proof-of-concept TAME linker
|
|
|
|
//
|
2020-03-06 11:05:18 -05:00
|
|
|
// Copyright (C) 2014-2020 Ryan Specialty Group, LLC.
|
|
|
|
//
|
|
|
|
// This file is part of TAME.
|
2019-11-27 09:18:17 -05:00
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 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 General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
//! **This is a poorly-written proof of concept; do not use!** It has been
|
|
|
|
//! banished to its own file to try to make that more clear.
|
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
use crate::global;
|
2020-03-07 10:49:41 -05:00
|
|
|
use crate::ir::asg::{
|
|
|
|
Asg, AsgError, DefaultAsg, IdentKind, Object, ObjectRef, Source,
|
|
|
|
};
|
2020-02-13 15:43:25 -05:00
|
|
|
use crate::obj::xmle::writer::{Sections, XmleWriter};
|
2020-01-12 22:59:16 -05:00
|
|
|
use crate::obj::xmlo::reader::{XmloError, XmloEvent, XmloReader};
|
2020-01-14 16:26:36 -05:00
|
|
|
use crate::sym::{DefaultInterner, Interner, Symbol};
|
2020-01-15 10:27:35 -05:00
|
|
|
use fxhash::{FxHashMap, FxHashSet};
|
2020-01-12 22:59:16 -05:00
|
|
|
use petgraph::visit::DfsPostOrder;
|
|
|
|
use std::convert::TryInto;
|
2019-11-27 09:18:17 -05:00
|
|
|
use std::error::Error;
|
|
|
|
use std::fs;
|
2020-02-13 15:43:25 -05:00
|
|
|
use std::io::BufReader;
|
2019-11-27 09:18:17 -05:00
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
type LinkerAsg<'i> = DefaultAsg<'i, global::ProgIdentSize>;
|
|
|
|
type LinkerObjectRef = ObjectRef<global::ProgIdentSize>;
|
2019-11-27 09:18:17 -05:00
|
|
|
|
2020-03-06 12:44:22 -05:00
|
|
|
type LoadResult<'i> =
|
|
|
|
Result<Option<(Option<&'i Symbol<'i>>, Option<String>)>, Box<dyn Error>>;
|
|
|
|
|
2020-03-04 15:31:20 -05:00
|
|
|
pub fn main(package_path: &str, output: &str) -> Result<(), Box<dyn Error>> {
|
2020-01-15 10:27:35 -05:00
|
|
|
let mut pkgs_seen: FxHashSet<String> = Default::default();
|
|
|
|
let mut fragments: FxHashMap<&str, String> = Default::default();
|
2020-01-12 22:59:16 -05:00
|
|
|
let mut depgraph = LinkerAsg::with_capacity(65536, 65536);
|
|
|
|
let mut roots = Vec::new();
|
2020-01-09 10:56:24 -05:00
|
|
|
let interner = DefaultInterner::new();
|
2019-11-27 09:18:17 -05:00
|
|
|
|
2020-03-06 11:29:04 -05:00
|
|
|
let abs_path = fs::canonicalize(package_path)?;
|
2019-11-27 09:18:17 -05:00
|
|
|
|
|
|
|
println!("WARNING: This is proof-of-concept; do not use!");
|
|
|
|
|
2020-01-14 16:26:36 -05:00
|
|
|
let (name, relroot) = load_xmlo(
|
2019-11-27 09:18:17 -05:00
|
|
|
&abs_path.to_str().unwrap().to_string(),
|
|
|
|
&mut pkgs_seen,
|
|
|
|
&mut fragments,
|
2019-12-01 01:17:37 -05:00
|
|
|
&mut depgraph,
|
2020-01-09 10:56:24 -05:00
|
|
|
&interner,
|
2020-01-12 22:59:16 -05:00
|
|
|
&mut roots,
|
2020-01-14 16:26:36 -05:00
|
|
|
)?
|
|
|
|
.expect("missing root package information");
|
2019-11-27 09:18:17 -05:00
|
|
|
|
2019-12-01 01:17:37 -05:00
|
|
|
// println!(
|
|
|
|
// "Graph {:?}",
|
|
|
|
// depgraph
|
|
|
|
// .graph
|
|
|
|
// .raw_nodes()
|
|
|
|
// .iter()
|
|
|
|
// .map(|node| &node.weight)
|
|
|
|
// .collect::<Vec<_>>()
|
|
|
|
// );
|
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
roots.extend(
|
|
|
|
vec!["___yield", "___worksheet"]
|
|
|
|
.iter()
|
|
|
|
.map(|name| interner.intern(name))
|
|
|
|
.filter_map(|sym| depgraph.lookup(sym)),
|
|
|
|
);
|
|
|
|
|
2020-03-07 10:49:41 -05:00
|
|
|
let mut sorted = sort_deps(&depgraph, &roots)?;
|
2019-11-27 09:18:17 -05:00
|
|
|
|
2020-01-13 16:41:06 -05:00
|
|
|
//println!("Sorted ({}): {:?}", sorted.len(), sorted);
|
|
|
|
|
2020-01-14 16:26:36 -05:00
|
|
|
output_xmle(
|
|
|
|
&depgraph,
|
|
|
|
&interner,
|
2020-02-13 15:43:25 -05:00
|
|
|
&mut sorted,
|
2020-01-14 16:26:36 -05:00
|
|
|
name.expect("missing root package name"),
|
|
|
|
relroot.expect("missing root package relroot"),
|
2020-03-04 15:31:20 -05:00
|
|
|
output,
|
2020-01-14 16:26:36 -05:00
|
|
|
)?;
|
2019-11-27 09:18:17 -05:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-01-09 10:56:24 -05:00
|
|
|
fn load_xmlo<'a, 'i, I: Interner<'i>>(
|
2019-11-27 09:18:17 -05:00
|
|
|
path_str: &'a str,
|
2020-01-15 10:27:35 -05:00
|
|
|
pkgs_seen: &mut FxHashSet<String>,
|
|
|
|
fragments: &mut FxHashMap<&'i str, String>,
|
2020-01-12 22:59:16 -05:00
|
|
|
depgraph: &mut LinkerAsg<'i>,
|
2020-01-09 10:56:24 -05:00
|
|
|
interner: &'i I,
|
2020-01-12 22:59:16 -05:00
|
|
|
roots: &mut Vec<LinkerObjectRef>,
|
2020-03-06 12:44:22 -05:00
|
|
|
) -> LoadResult<'i> {
|
2019-11-27 09:18:17 -05:00
|
|
|
let path = fs::canonicalize(path_str)?;
|
|
|
|
let path_str = path.to_str().unwrap();
|
|
|
|
|
2020-01-14 16:26:36 -05:00
|
|
|
let first = pkgs_seen.len() == 0;
|
|
|
|
|
2019-11-27 09:18:17 -05:00
|
|
|
if !pkgs_seen.insert(path_str.to_string()) {
|
2020-01-14 16:26:36 -05:00
|
|
|
return Ok(None);
|
2019-11-27 09:18:17 -05:00
|
|
|
}
|
|
|
|
|
2020-01-09 10:56:24 -05:00
|
|
|
//println!("processing {}", path_str);
|
|
|
|
|
2020-01-15 10:27:35 -05:00
|
|
|
let mut found: FxHashSet<&str> = Default::default();
|
2020-01-09 10:56:24 -05:00
|
|
|
|
|
|
|
let file = fs::File::open(&path)?;
|
|
|
|
let reader = BufReader::new(file);
|
|
|
|
let mut xmlo = XmloReader::new(reader, interner);
|
2020-01-12 22:59:16 -05:00
|
|
|
let mut elig = None;
|
2020-01-09 10:56:24 -05:00
|
|
|
|
2020-01-14 16:26:36 -05:00
|
|
|
let mut name: Option<&'i Symbol<'i>> = None;
|
|
|
|
let mut relroot: Option<String> = None;
|
|
|
|
|
2020-01-09 10:56:24 -05:00
|
|
|
loop {
|
2020-01-12 22:59:16 -05:00
|
|
|
match xmlo.read_event() {
|
|
|
|
Ok(XmloEvent::Package(attrs)) => {
|
2020-01-14 16:26:36 -05:00
|
|
|
if first {
|
|
|
|
name = attrs.name;
|
|
|
|
relroot = attrs.relroot;
|
|
|
|
}
|
2020-01-12 22:59:16 -05:00
|
|
|
elig = attrs.elig;
|
|
|
|
}
|
2020-01-11 23:29:53 -05:00
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
Ok(XmloEvent::SymDeps(sym, deps)) => {
|
2020-01-09 10:56:24 -05:00
|
|
|
// TODO: API needs to expose whether a symbol is already
|
|
|
|
// known so that we can warn on them
|
|
|
|
|
2020-01-13 16:41:06 -05:00
|
|
|
// Maps should not pull in symbols since we may end up
|
|
|
|
// mapping to params that are never actually used
|
|
|
|
if !sym.starts_with(":map:") {
|
|
|
|
for dep_sym in deps {
|
2020-01-14 16:26:36 -05:00
|
|
|
depgraph.add_dep_lookup(sym, dep_sym);
|
2020-01-13 16:41:06 -05:00
|
|
|
}
|
2020-01-02 23:29:49 -05:00
|
|
|
}
|
2020-01-09 10:56:24 -05:00
|
|
|
}
|
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
Ok(XmloEvent::SymDecl(sym, attrs)) => {
|
2020-01-09 10:56:24 -05:00
|
|
|
if let Some(sym_src) = attrs.src {
|
|
|
|
found.insert(sym_src);
|
2020-01-14 16:26:36 -05:00
|
|
|
} else if attrs.extern_ {
|
|
|
|
// TODO: externs (they're implicitly handled, without
|
|
|
|
// checks, by Missing)
|
|
|
|
// depgraph.declare_extern(sym, kind);
|
|
|
|
} else {
|
|
|
|
let owned = attrs.src.is_none();
|
|
|
|
|
|
|
|
let kind = (&attrs).try_into().map_err(|err| {
|
|
|
|
format!("sym `{}` attrs error: {}", sym, err)
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut src: Source = attrs.into();
|
|
|
|
|
|
|
|
// Existing convention is to omit @src of local package
|
|
|
|
// (in this case, the program being linked)
|
|
|
|
if first {
|
|
|
|
src.pkg_name = None;
|
|
|
|
}
|
2020-01-13 15:15:38 -05:00
|
|
|
|
2020-01-14 16:26:36 -05:00
|
|
|
match kind {
|
|
|
|
Ok(kindval) => {
|
|
|
|
// TODO: inefficient
|
|
|
|
let link_root = owned
|
|
|
|
&& (kindval == IdentKind::Meta
|
|
|
|
|| kindval == IdentKind::Map
|
|
|
|
|| kindval == IdentKind::RetMap);
|
2020-01-12 22:59:16 -05:00
|
|
|
|
2020-01-14 16:26:36 -05:00
|
|
|
let node = depgraph.declare(sym, kindval, src)?;
|
2020-01-12 22:59:16 -05:00
|
|
|
|
2020-01-14 16:26:36 -05:00
|
|
|
if link_root {
|
|
|
|
roots.push(node);
|
|
|
|
}
|
2020-01-12 22:59:16 -05:00
|
|
|
}
|
2020-03-06 13:39:44 -05:00
|
|
|
Err(e) => return Err(e.into()),
|
2020-01-14 16:26:36 -05:00
|
|
|
};
|
|
|
|
}
|
2019-11-27 09:18:17 -05:00
|
|
|
}
|
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
Ok(XmloEvent::Fragment(sym, text)) => {
|
2020-03-07 10:28:39 -05:00
|
|
|
match depgraph.lookup(sym) {
|
|
|
|
Some(frag) => match depgraph.set_fragment(frag, text) {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => return Err(e.into()),
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
return Err(XmloError::MissingFragment(String::from(
|
|
|
|
"missing fragment",
|
|
|
|
))
|
|
|
|
.into());
|
|
|
|
}
|
2020-01-12 22:59:16 -05:00
|
|
|
};
|
2020-01-09 10:56:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// We don't need to read any further than the end of the
|
|
|
|
// header (symtable, sym-deps, fragments)
|
2020-01-12 22:59:16 -05:00
|
|
|
Ok(XmloEvent::Eoh) => break,
|
|
|
|
|
2020-03-06 14:08:55 -05:00
|
|
|
Err(e) => return Err(e.into()),
|
2020-01-09 10:56:24 -05:00
|
|
|
}
|
2019-11-27 09:18:17 -05:00
|
|
|
}
|
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
if let Some(elig_sym) = elig {
|
|
|
|
roots.push(depgraph.lookup(elig_sym).expect(
|
|
|
|
"internal error: package elig references nonexistant symbol",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2019-11-27 09:18:17 -05:00
|
|
|
let mut dir = path.clone();
|
|
|
|
dir.pop();
|
|
|
|
|
|
|
|
for relpath in found.iter() {
|
|
|
|
let mut path_buf = dir.clone();
|
|
|
|
path_buf.push(relpath);
|
|
|
|
path_buf.set_extension("xmlo");
|
|
|
|
|
2020-01-14 16:26:36 -05:00
|
|
|
// println!("Trying {:?}", path_buf);
|
2020-03-06 12:32:42 -05:00
|
|
|
let path_abs = path_buf.canonicalize()?;
|
2019-11-27 09:18:17 -05:00
|
|
|
let path = path_abs.to_str().unwrap();
|
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
load_xmlo(path, pkgs_seen, fragments, depgraph, interner, roots)?;
|
2019-11-27 09:18:17 -05:00
|
|
|
}
|
|
|
|
|
2020-01-14 16:26:36 -05:00
|
|
|
if first {
|
|
|
|
Ok(Some((name, relroot)))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
2019-11-27 09:18:17 -05:00
|
|
|
}
|
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
fn sort_deps<'a, 'i>(
|
|
|
|
depgraph: &'a LinkerAsg<'i>,
|
|
|
|
roots: &Vec<LinkerObjectRef>,
|
2020-03-07 10:49:41 -05:00
|
|
|
) -> Result<Sections<'a, 'i>, Box<dyn Error>> {
|
2019-11-27 09:18:17 -05:00
|
|
|
// @type=meta, @preproc:elig-class-yields
|
|
|
|
// @type={ret}map{,:head,:tail}
|
|
|
|
|
2020-02-13 15:43:25 -05:00
|
|
|
let mut deps: Sections = Sections::new();
|
2020-01-13 16:41:06 -05:00
|
|
|
|
2019-12-01 01:17:37 -05:00
|
|
|
// This is technically a topological sort, but functions have
|
|
|
|
// cycles. Once we have more symbol metadata, we can filter them out
|
|
|
|
// and actually invoke toposort.
|
|
|
|
let mut dfs = DfsPostOrder::empty(&depgraph);
|
2019-11-27 09:18:17 -05:00
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
//println!("discovered roots: {:?}", roots);
|
|
|
|
|
2019-12-01 01:17:37 -05:00
|
|
|
// TODO: we'll be processing various roots separately
|
|
|
|
for index in roots {
|
2020-01-12 22:59:16 -05:00
|
|
|
dfs.stack.push((*index).into());
|
2019-12-01 01:17:37 -05:00
|
|
|
}
|
2019-11-27 09:18:17 -05:00
|
|
|
|
2020-01-12 22:59:16 -05:00
|
|
|
// TODO: can we encapsulate NodeIndex?
|
2019-12-01 01:17:37 -05:00
|
|
|
while let Some(index) = dfs.next(&depgraph) {
|
2020-03-07 10:49:41 -05:00
|
|
|
let ident = depgraph.get(index).expect("missing node");
|
2020-01-13 16:41:06 -05:00
|
|
|
|
|
|
|
match ident {
|
|
|
|
Object::Ident(_, kind, _)
|
|
|
|
| Object::IdentFragment(_, kind, _, _) => match kind {
|
2020-02-13 15:43:25 -05:00
|
|
|
IdentKind::Meta => deps.meta.push_body(ident),
|
|
|
|
IdentKind::Worksheet => deps.worksheet.push_body(ident),
|
|
|
|
IdentKind::Param(_, _) => deps.params.push_body(ident),
|
|
|
|
IdentKind::Type(_) => deps.types.push_body(ident),
|
|
|
|
IdentKind::Func(_, _) => deps.funcs.push_body(ident),
|
2020-01-13 16:41:06 -05:00
|
|
|
IdentKind::MapHead | IdentKind::Map | IdentKind::MapTail => {
|
2020-02-13 15:43:25 -05:00
|
|
|
deps.map.push_body(ident)
|
2020-01-13 16:41:06 -05:00
|
|
|
}
|
|
|
|
IdentKind::RetMapHead
|
|
|
|
| IdentKind::RetMap
|
2020-02-13 15:43:25 -05:00
|
|
|
| IdentKind::RetMapTail => deps.retmap.push_body(ident),
|
|
|
|
_ => deps.rater.push_body(ident),
|
2020-01-13 16:41:06 -05:00
|
|
|
},
|
2020-03-07 10:49:41 -05:00
|
|
|
_ => {
|
|
|
|
return Err(
|
|
|
|
AsgError::UnexpectedNode(format!("{:?}", ident)).into()
|
|
|
|
)
|
|
|
|
}
|
2020-01-13 16:41:06 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 10:49:41 -05:00
|
|
|
Ok(deps)
|
2020-01-13 16:41:06 -05:00
|
|
|
}
|
|
|
|
|
2020-02-13 15:43:25 -05:00
|
|
|
fn get_interner_value<'a, 'i, I: Interner<'i>>(
|
|
|
|
depgraph: &'a LinkerAsg<'i>,
|
|
|
|
interner: &'i I,
|
|
|
|
name: &str,
|
2020-03-07 11:18:56 -05:00
|
|
|
) -> Result<&'a Object<'i>, Box<dyn Error>> {
|
|
|
|
match depgraph.lookup(interner.intern(name)) {
|
|
|
|
Some(frag) => match depgraph.get(frag) {
|
|
|
|
Some(result) => Ok(result),
|
|
|
|
None => Err(XmloError::MissingFragment(String::from(name)).into()),
|
|
|
|
},
|
|
|
|
None => Err(XmloError::MissingFragment(String::from(name)).into()),
|
|
|
|
}
|
2020-02-13 15:43:25 -05:00
|
|
|
}
|
|
|
|
|
2020-01-13 16:41:06 -05:00
|
|
|
fn output_xmle<'a, 'i, I: Interner<'i>>(
|
|
|
|
depgraph: &'a LinkerAsg<'i>,
|
|
|
|
interner: &'i I,
|
2020-02-13 15:43:25 -05:00
|
|
|
sorted: &mut Sections<'a, 'i>,
|
2020-01-14 16:26:36 -05:00
|
|
|
name: &'i Symbol<'i>,
|
|
|
|
relroot: String,
|
2020-03-04 15:31:20 -05:00
|
|
|
output: &str,
|
2020-01-13 16:41:06 -05:00
|
|
|
) -> Result<(), Box<dyn Error>> {
|
2020-02-13 15:43:25 -05:00
|
|
|
if !sorted.map.is_empty() {
|
|
|
|
sorted.map.push_head(get_interner_value(
|
|
|
|
depgraph,
|
|
|
|
interner,
|
|
|
|
&String::from(":map:___head"),
|
2020-03-07 11:18:56 -05:00
|
|
|
)?);
|
2020-02-13 15:43:25 -05:00
|
|
|
sorted.map.push_tail(get_interner_value(
|
|
|
|
depgraph,
|
|
|
|
interner,
|
|
|
|
&String::from(":map:___tail"),
|
2020-03-07 11:18:56 -05:00
|
|
|
)?);
|
2020-01-13 16:41:06 -05:00
|
|
|
}
|
|
|
|
|
2020-02-13 15:43:25 -05:00
|
|
|
if !sorted.retmap.is_empty() {
|
|
|
|
sorted.retmap.push_head(get_interner_value(
|
|
|
|
depgraph,
|
|
|
|
interner,
|
|
|
|
&String::from(":retmap:___head"),
|
2020-03-07 11:18:56 -05:00
|
|
|
)?);
|
2020-02-13 15:43:25 -05:00
|
|
|
sorted.retmap.push_tail(get_interner_value(
|
|
|
|
depgraph,
|
|
|
|
interner,
|
|
|
|
&String::from(":retmap:___tail"),
|
2020-03-07 11:18:56 -05:00
|
|
|
)?);
|
2020-01-13 16:41:06 -05:00
|
|
|
}
|
|
|
|
|
2020-03-04 15:31:20 -05:00
|
|
|
let file = fs::File::create(output)?;
|
|
|
|
let mut xmle_writer = XmleWriter::new(file);
|
2020-03-07 11:18:56 -05:00
|
|
|
xmle_writer.write(&sorted, name, &relroot)?;
|
2020-01-13 16:41:06 -05:00
|
|
|
|
|
|
|
Ok(())
|
2019-11-27 09:18:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2019-12-06 15:03:29 -05:00
|
|
|
mod test {
|
2019-11-27 09:18:17 -05:00
|
|
|
#[test]
|
|
|
|
fn placeholder() {}
|
|
|
|
}
|