To remove definitions which are never used: for example, functions which are never called or referred to.
§1. Experience shows that around 20 per cent of the code generated by inform7 and, especially, inter consists of functions which are never in fact needed. (A kit, of its nature, is a library of code: some will be useful, some not, in any given circumstance. Many Inform 7 works will never need to sort a table, for example.)
This stage removes everything that isn't used.
NB: At present (January 2022) it doesn't work on some test cases, so it's not included in the standard pipeline.
void EliminateRedundantMatterStage::create_pipeline_stage(void) { ParsingPipelines::new_stage(I"eliminate-redundant-matter", EliminateRedundantMatterStage::run, NO_STAGE_ARG, FALSE); } int EliminateRedundantMatterStage::run(pipeline_step *step) { inter_tree *I = step->ephemera.tree; InterTree::traverse(I, EliminateRedundantMatterStage::preserver, step, NULL, PACKAGE_IST); InterTree::traverse(I, EliminateRedundantMatterStage::destroyer, step, NULL, PACKAGE_IST); return TRUE; }
§2. We operate on a presumption of guilt: any package which we cannot prove is needed will be deleted. Main_fn is clearly needed, since it is where execution will begin. A handful of other functions, not called yet but needed in the final compilation stage (and which replace stubs which would otherwise be provided by the veneer), must also be included. Command packages contain grammar for command verbs typed at run-time: these affect an essential data structure (i.e., the parser grammar) implicitly, and so we can't detect the dependency here. So we require all command packages to be included.
void EliminateRedundantMatterStage::preserver(inter_tree *I, inter_tree_node *P, void *state) { pipeline_step *step = (pipeline_step *) state; inter_package *pack = PackageInstruction::at_this_head(P); inter_symbol *ptype = InterPackage::type(pack); if (ptype == RunningPipelines::get_symbol(step, command_ptype_RPSYM)) EliminateRedundantMatterStage::preserve(pack, step, NULL, I"it's a _command package"); else if (ptype == RunningPipelines::get_symbol(step, property_ptype_RPSYM)) { text_stream *N = InterPackage::name(pack); if (Str::eq(N, I"workflag_prop")) EliminateRedundantMatterStage::preserve(pack, step, NULL, I"it's workflag"); if (Str::eq(N, I"pluralname_prop")) EliminateRedundantMatterStage::preserve(pack, step, NULL, I"it's pluralname"); if (Str::eq(N, I"ambigpluralname_prop")) EliminateRedundantMatterStage::preserve(pack, step, NULL, I"it's ambigpluralname"); if (Str::eq(N, I"proper_prop")) EliminateRedundantMatterStage::preserve(pack, step, NULL, I"it's proper"); } else if (ptype == RunningPipelines::get_symbol(step, function_ptype_RPSYM)) { text_stream *N = InterPackage::name(pack); if (Str::eq(N, I"Main_fn")) EliminateRedundantMatterStage::preserve(pack, step, NULL, I"it's Main"); if (Str::eq(N, I"MistakeActionSub_fn")) EliminateRedundantMatterStage::preserve(pack, step, NULL, I"it's MistakeActionSub"); if (Str::eq(N, I"TestScriptSub_fn")) EliminateRedundantMatterStage::preserve(pack, step, NULL, I"it's TestScriptSub"); } }
§3. Once you need a package, what else do you need?
void EliminateRedundantMatterStage::preserve(inter_package *pack, pipeline_step *step, inter_package *witness, text_stream *reason) { if ((pack->package_flags) & USED_PACKAGE_FLAG) return; pack->package_flags |= USED_PACKAGE_FLAG; if (witness) { LOGIF(ELIMINATION, "Need $6 because of $6 (because %S)\n", pack, witness, reason); } else { LOGIF(ELIMINATION, "Need $6 (because %S)\n", pack, reason); } If you need a package, you need its parent3.1; If you need a package, you need its external dependencies3.2; If you need a function or action, you need its internal resources3.3; }
§3.1. If you need a package, you need its parent3.1 =
inter_package *parent = InterPackage::parent(pack); if (parent) EliminateRedundantMatterStage::preserve(parent, step, pack, I"it's the parent");
- This code is used in §3.
§3.2. If you need a package, you need its external dependencies3.2 =
inter_symbols_table *tab = InterPackage::scope(pack); LOOP_OVER_SYMBOLS_TABLE(symb, tab) { if (Wiring::is_wired(symb)) { inter_symbol *E = Wiring::cable_end(symb); inter_package *needed = InterSymbol::package(E); EliminateRedundantMatterStage::preserve(needed, step, pack, I"it's an external symbol"); } }
- This code is used in §3.
§3.3. If you need a function or action, you need its internal resources3.3 =
text_stream *rationale = NULL; inter_symbol *ptype = InterPackage::type(pack); if (ptype == RunningPipelines::get_symbol(step, function_ptype_RPSYM)) rationale = I"it's a _function block"; if (ptype == RunningPipelines::get_symbol(step, action_ptype_RPSYM)) rationale = I"it's an _action subpackage"; if (rationale) { inter_tree_node *D = InterPackage::head(pack); LOOP_THROUGH_INTER_CHILDREN(C, D) if (Inode::is(C, PACKAGE_IST)) { inter_package *P = PackageInstruction::at_this_head(C); EliminateRedundantMatterStage::preserve(P, step, pack, rationale); } }
- This code is used in §3.
§4. Whatever has not been preserved, is now destroyed.
void EliminateRedundantMatterStage::destroyer(inter_tree *I, inter_tree_node *P, void *state) { inter_package *pack = PackageInstruction::at_this_head(P); if ((pack) && ((pack->package_flags & USED_PACKAGE_FLAG) == 0)) { LOGIF(ELIMINATION, "Striking unused package $6 (type %S)\n", pack, InterSymbol::identifier(InterPackage::type(pack))); NodePlacement::remove(P); } }