// Licensing scope: see ../../LICENSE and this directory's LICENSE and NOTICE.
// Potentially incorporated Binaryen expression retains Apache-2.0 terms.
// Original Starshine contributions use the scoped MIT grant in ../../LICENSE.
// Modified for Starshine through 2026: implementation and validation use
// Starshine's representations and APIs; this is not an unchanged upstream file.
// Conservative notice retention does not establish copying or independence.
///|
pub fn string_gathering_summary() -> String {
"Hoist wasm string constants into shared immutable string globals and rewrite uses to global.get."
}
///|
priv struct SgStringSet {
strings : Array[Bytes]
mut indices : Map[Bytes, Int32]
}
///|
priv struct SgRewritePlan {
strings : Array[Bytes]
string_indices : Map[Bytes, Int32]
string_global_abs : Array[Int32]
old_defined_to_abs : Array[Int32]
old_preserve_type : Array[Bool]
entry_is_fresh : Array[Bool]
entry_value : Array[Int32]
}
///|
fn SgStringSet::new() -> SgStringSet {
{ strings: [], indices: Map([]), }
}
///|
fn sg_string_index(set : SgStringSet, bytes : Bytes) -> Int32? {
set.indices.get(bytes)
}
///|
fn sg_intern_string(set : SgStringSet, bytes : Bytes) -> Int32 {
match sg_string_index(set, bytes) {
Some(idx) => idx
None => {
let idx = set.strings.length()
set.strings.push(bytes)
set.indices[bytes] = idx
idx
}
}
}
///|
fn sg_collect_instr(instr : @lib.Instruction, set : SgStringSet) -> Unit {
match instr {
@lib.StringConst(bytes) => ignore(sg_intern_string(set, bytes))
@lib.Block(_, body) | @lib.Loop(_, body) => sg_collect_expr(body, set)
@lib.If(_, then_body, else_body) => {
sg_collect_instrs(then_body, set)
match else_body {
Some(body) => sg_collect_instrs(body, set)
None => ()
}
}
@lib.TryTable(_, _, body) => sg_collect_expr(body, set)
_ => ()
}
}
///|
fn sg_collect_instrs(
instrs : Array[@lib.Instruction],
set : SgStringSet,
) -> Unit {
for instr in instrs {
sg_collect_instr(instr, set)
}
}
///|
fn sg_collect_expr(expr : @lib.Expr, set : SgStringSet) -> Unit {
let @lib.Expr(instrs) = expr
sg_collect_instrs(instrs, set)
}
///|
fn sg_collect_code_sec(code_sec : @lib.CodeSec?, set : SgStringSet) -> Unit {
match code_sec {
Some(@lib.CodeSec(funcs)) =>
for func in funcs {
let @lib.Func(_, body) = func
sg_collect_expr(body, set)
}
None => ()
}
}
///|
fn sg_collect_global_sec(
global_sec : @lib.GlobalSec?,
set : SgStringSet,
) -> Unit {
match global_sec {
Some(@lib.GlobalSec(globals)) =>
for global in globals {
let @lib.Global(_, init) = global
sg_collect_expr(init, set)
}
None => ()
}
}
///|
fn sg_collect_elem_mode(mode : @lib.ElemMode, set : SgStringSet) -> Unit {
match mode {
@lib.Active(_, expr) => sg_collect_expr(expr, set)
_ => ()
}
}
///|
fn sg_collect_elem_kind(kind : @lib.ElemKind, set : SgStringSet) -> Unit {
match kind {
@lib.FuncExprsElemKind(exprs) | @lib.TypedExprsElemKind(_, exprs) =>
for expr in exprs {
sg_collect_expr(expr, set)
}
_ => ()
}
}
///|
fn sg_collect_elem_sec(elem_sec : @lib.ElemSec?, set : SgStringSet) -> Unit {
match elem_sec {
Some(@lib.ElemSec(elems)) =>
for elem in elems {
let @lib.Elem(mode, kind) = elem
sg_collect_elem_mode(mode, set)
sg_collect_elem_kind(kind, set)
}
None => ()
}
}
///|
fn sg_collect_data_sec(data_sec : @lib.DataSec?, set : SgStringSet) -> Unit {
match data_sec {
Some(@lib.DataSec(datas)) =>
for data in datas {
let @lib.Data(mode, _) = data
match mode {
@lib.Active(_, expr) => sg_collect_expr(expr, set)
_ => ()
}
}
None => ()
}
}
///|
fn sg_collect_strings(mod_ : @lib.Module) -> SgStringSet {
let set = SgStringSet::new()
// Binaryen's StringGathering visits function bodies before module-level
// constant expressions, then inserts gathered globals before existing defined
// globals. Keep that order for normalized-WAT parity.
sg_collect_code_sec(mod_.code_sec, set)
sg_collect_global_sec(mod_.global_sec, set)
sg_collect_elem_sec(mod_.elem_sec, set)
sg_collect_data_sec(mod_.data_sec, set)
set
}
///|
fn sg_instrs_have_string_const(instrs : Array[@lib.Instruction]) -> Bool {
for instr in instrs {
match instr {
@lib.StringConst(_) => return true
@lib.Block(_, body) | @lib.Loop(_, body) | @lib.TryTable(_, _, body) =>
if sg_expr_has_string_const(body) {
return true
}
@lib.If(_, then_body, else_body) => {
if sg_instrs_have_string_const(then_body) {
return true
}
match else_body {
Some(body) => if sg_instrs_have_string_const(body) { return true }
None => ()
}
}
_ => ()
}
}
false
}
///|
fn sg_expr_has_string_const(expr : @lib.Expr) -> Bool {
let @lib.Expr(instrs) = expr
sg_instrs_have_string_const(instrs)
}
///|
fn sg_module_has_string_const(mod_ : @lib.Module) -> Bool {
match mod_.code_sec {
Some(@lib.CodeSec(funcs)) =>
for func in funcs {
let @lib.Func(_, body) = func
if sg_expr_has_string_const(body) {
return true
}
}
None => ()
}
match mod_.global_sec {
Some(@lib.GlobalSec(globals)) =>
for global in globals {
let @lib.Global(_, init) = global
if sg_expr_has_string_const(init) {
return true
}
}
None => ()
}
match mod_.elem_sec {
Some(@lib.ElemSec(elems)) =>
for elem in elems {
let @lib.Elem(mode, kind) = elem
match mode {
@lib.Active(_, offset) =>
if sg_expr_has_string_const(offset) {
return true
}
_ => ()
}
match kind {
@lib.FuncExprsElemKind(exprs) | @lib.TypedExprsElemKind(_, exprs) =>
for expr in exprs {
if sg_expr_has_string_const(expr) {
return true
}
}
_ => ()
}
}
None => ()
}
match mod_.data_sec {
Some(@lib.DataSec(datas)) =>
for data in datas {
let @lib.Data(mode, _) = data
match mode {
@lib.Active(_, offset) =>
if sg_expr_has_string_const(offset) {
return true
}
_ => ()
}
}
None => ()
}
false
}
///|
fn sg_imported_global_count(mod_ : @lib.Module) -> Int32 {
let mut count = 0
match mod_.import_sec {
Some(@lib.ImportSec(imports)) =>
for import_ in imports {
match import_ {
@lib.Import(_, _, @lib.GlobalExternType(_)) => count += 1
_ => ()
}
}
None => ()
}
count
}
///|
fn sg_global_idx_raw(idx : @lib.GlobalIdx) -> Int32 {
let @lib.GlobalIdx(raw) = idx
raw.reinterpret_as_int()
}
///|
fn sg_global_idx(raw : Int32) -> @lib.GlobalIdx {
@lib.GlobalIdx::new(raw.reinterpret_as_uint())
}
///|
fn sg_remap_existing_global_idx(
idx : @lib.GlobalIdx,
imported_globals : Int32,
plan : SgRewritePlan,
) -> @lib.GlobalIdx {
let raw = sg_global_idx_raw(idx)
if raw < imported_globals {
return idx
}
let defined_idx = raw - imported_globals
if defined_idx < 0 || defined_idx >= plan.old_defined_to_abs.length() {
return idx
}
let next = plan.old_defined_to_abs[defined_idx]
if next < 0 {
idx
} else {
sg_global_idx(next)
}
}
///|
fn sg_string_global_idx(plan : SgRewritePlan, bytes : Bytes) -> @lib.GlobalIdx {
let idx = match plan.string_indices.get(bytes) {
Some(idx) => idx
None => abort("string-gathering rewrite saw uncollected string constant")
}
let raw = plan.string_global_abs[idx]
if raw < 0 {
abort("string-gathering rewrite saw unplanned string constant")
}
sg_global_idx(raw)
}
///|
fn sg_direct_string_init(global : @lib.Global) -> Bytes? {
match global {
@lib.Global(
@lib.GlobalType(@lib.RefTypeValType(ref_type), false),
@lib.Expr([@lib.Instruction::StringConst(bytes)])
) if ref_type.get_heap_type() ==
@lib.HeapType::abs(@lib.AbsHeapType::string_()) => Some(bytes)
_ => None
}
}
///|
fn sg_defined_globals(mod_ : @lib.Module) -> Array[@lib.Global] {
match mod_.global_sec {
Some(@lib.GlobalSec(globals)) => globals
None => []
}
}
///|
fn sg_bytes_less_than(lhs : Bytes, rhs : Bytes) -> Bool {
let min_len = if lhs.length() < rhs.length() {
lhs.length()
} else {
rhs.length()
}
for i = 0; i < min_len; i = i + 1 {
if lhs[i] < rhs[i] {
return true
}
if lhs[i] > rhs[i] {
return false
}
}
lhs.length() < rhs.length()
}
///|
fn sg_sort_strings(strings : Array[Bytes]) -> Unit {
strings.sort_by(fn(lhs, rhs) {
if sg_bytes_less_than(lhs, rhs) {
-1
} else if sg_bytes_less_than(rhs, lhs) {
1
} else {
0
}
})
}
///|
fn sg_reindex_strings(set : SgStringSet) -> Unit {
let indices : Map[Bytes, Int32] = Map([])
for idx = 0; idx < set.strings.length(); idx = idx + 1 {
indices[set.strings[idx]] = idx
}
set.indices = indices
}
///|
fn sg_make_rewrite_plan(
mod_ : @lib.Module,
set : SgStringSet,
imported_globals : Int32,
) -> SgRewritePlan {
sg_sort_strings(set.strings)
sg_reindex_strings(set)
let globals = sg_defined_globals(mod_)
let exported_globals : Map[UInt32, Unit] = Map([])
match mod_.export_sec {
Some(@lib.ExportSec(exports)) =>
for export_ in exports {
match export_ {
@lib.Export(_, @lib.GlobalExternIdx(@lib.GlobalIdx(idx))) =>
exported_globals[idx] = ()
_ => ()
}
}
None => ()
}
let old_preserve_type = Array::make(globals.length(), false)
for old_idx = 0; old_idx < globals.length(); old_idx = old_idx + 1 {
let nullable = match globals[old_idx] {
@lib.Global(@lib.GlobalType(@lib.RefTypeValType(ref_type), _), _) =>
ref_type.is_nullable()
_ => false
}
old_preserve_type[old_idx] = nullable &&
exported_globals.contains(
(imported_globals + old_idx).reinterpret_as_uint(),
)
}
let canonical_old = Array::make(set.strings.length(), -1)
for old_idx = 0; old_idx < globals.length(); old_idx = old_idx + 1 {
match sg_direct_string_init(globals[old_idx]) {
Some(bytes) =>
match sg_string_index(set, bytes) {
Some(string_idx) if canonical_old[string_idx] < 0 &&
!old_preserve_type[old_idx] => canonical_old[string_idx] = old_idx
_ => ()
}
None => ()
}
}
let string_global_abs = Array::make(set.strings.length(), -1)
let old_defined_to_abs = Array::make(globals.length(), -1)
let entry_is_fresh : Array[Bool] = []
let entry_value : Array[Int32] = []
fn push_old(old_idx : Int32) -> Unit {
old_defined_to_abs[old_idx] = imported_globals + entry_value.length()
entry_is_fresh.push(false)
entry_value.push(old_idx)
}
fn push_fresh(string_idx : Int32) -> Unit {
string_global_abs[string_idx] = imported_globals + entry_value.length()
entry_is_fresh.push(true)
entry_value.push(string_idx)
}
for old_idx = 0; old_idx < globals.length(); old_idx = old_idx + 1 {
match sg_direct_string_init(globals[old_idx]) {
Some(bytes) =>
match sg_string_index(set, bytes) {
Some(string_idx) if canonical_old[string_idx] == old_idx => {
string_global_abs[string_idx] = imported_globals +
entry_value.length()
push_old(old_idx)
}
_ => ()
}
None => ()
}
}
for string_idx = 0
string_idx < set.strings.length()
string_idx = string_idx + 1 {
if canonical_old[string_idx] < 0 {
push_fresh(string_idx)
}
}
for old_idx = 0; old_idx < globals.length(); old_idx = old_idx + 1 {
if old_defined_to_abs[old_idx] < 0 {
push_old(old_idx)
}
}
SgRewritePlan::{
strings: set.strings,
string_indices: set.indices,
string_global_abs,
old_defined_to_abs,
old_preserve_type,
entry_is_fresh,
entry_value,
}
}
///|
fn sg_rewrite_instr(
instr : @lib.Instruction,
plan : SgRewritePlan,
imported_globals : Int32,
) -> @lib.Instruction {
match instr {
@lib.StringConst(bytes) =>
@lib.Instruction::global_get(sg_string_global_idx(plan, bytes))
@lib.GlobalGet(idx) =>
@lib.Instruction::global_get(
sg_remap_existing_global_idx(idx, imported_globals, plan),
)
@lib.GlobalSet(idx) =>
@lib.Instruction::global_set(
sg_remap_existing_global_idx(idx, imported_globals, plan),
)
@lib.Block(block_type, body) =>
@lib.Instruction::block(
block_type,
sg_rewrite_expr(body, plan, imported_globals),
)
@lib.Loop(block_type, body) =>
@lib.Instruction::loop_(
block_type,
sg_rewrite_expr(body, plan, imported_globals),
)
@lib.If(block_type, then_body, else_body) => {
let next_then = sg_rewrite_instrs(then_body, plan, imported_globals)
let next_else = match else_body {
Some(body) => Some(sg_rewrite_instrs(body, plan, imported_globals))
None => None
}
@lib.Instruction::if_(block_type, next_then, next_else)
}
@lib.TryTable(block_type, catches, body) =>
@lib.Instruction::try_table(
block_type,
catches,
sg_rewrite_expr(body, plan, imported_globals),
)
_ => instr
}
}
///|
fn sg_rewrite_instrs(
instrs : Array[@lib.Instruction],
plan : SgRewritePlan,
imported_globals : Int32,
) -> Array[@lib.Instruction] {
if !sg_instrs_need_rewrite(instrs, plan, imported_globals) {
return instrs
}
sg_rewrite_instrs_unchecked(instrs, plan, imported_globals)
}
///|
fn sg_rewrite_instrs_unchecked(
instrs : Array[@lib.Instruction],
plan : SgRewritePlan,
imported_globals : Int32,
) -> Array[@lib.Instruction] {
let out : Array[@lib.Instruction] = []
for instr in instrs {
out.push(sg_rewrite_instr(instr, plan, imported_globals))
}
out
}
///|
fn sg_instrs_need_rewrite(
instrs : Array[@lib.Instruction],
plan : SgRewritePlan,
imported_globals : Int32,
) -> Bool {
for instr in instrs {
match instr {
@lib.StringConst(_) => return true
@lib.GlobalGet(idx) | @lib.GlobalSet(idx) =>
if sg_remap_existing_global_idx(idx, imported_globals, plan) != idx {
return true
}
@lib.Block(_, body) | @lib.Loop(_, body) =>
if sg_expr_needs_rewrite(body, plan, imported_globals) {
return true
}
@lib.If(_, then_body, else_body) => {
if sg_instrs_need_rewrite(then_body, plan, imported_globals) {
return true
}
match else_body {
Some(body) =>
if sg_instrs_need_rewrite(body, plan, imported_globals) {
return true
}
None => ()
}
}
@lib.TryTable(_, _, body) =>
if sg_expr_needs_rewrite(body, plan, imported_globals) {
return true
}
_ => ()
}
}
false
}
///|
fn sg_expr_needs_rewrite(
expr : @lib.Expr,
plan : SgRewritePlan,
imported_globals : Int32,
) -> Bool {
sg_instrs_need_rewrite(expr.0, plan, imported_globals)
}
///|
fn sg_rewrite_expr(
expr : @lib.Expr,
plan : SgRewritePlan,
imported_globals : Int32,
) -> @lib.Expr {
if !sg_expr_needs_rewrite(expr, plan, imported_globals) {
return expr
}
let @lib.Expr(instrs) = expr
@lib.Expr::new(sg_rewrite_instrs_unchecked(instrs, plan, imported_globals))
}
///|
fn sg_rewrite_global_sec(
global_sec : @lib.GlobalSec?,
plan : SgRewritePlan,
imported_globals : Int32,
) -> @lib.GlobalSec {
let globals = match global_sec {
Some(@lib.GlobalSec(globals)) => globals
None => []
}
let out : Array[@lib.Global] = []
let string_global_type = @lib.GlobalType::new(
@lib.ValType::ref_type(
@lib.RefType::new(false, @lib.HeapType::abs(@lib.AbsHeapType::string_())),
),
false,
)
for entry_idx = 0
entry_idx < plan.entry_value.length()
entry_idx = entry_idx + 1 {
let value = plan.entry_value[entry_idx]
if plan.entry_is_fresh[entry_idx] {
out.push(
@lib.Global::new(
string_global_type,
@lib.Expr::new([@lib.Instruction::string_const(plan.strings[value])]),
),
)
} else {
let @lib.Global(global_type, init) = globals[value]
let rewritten_init = match sg_direct_string_init(globals[value]) {
Some(bytes) =>
match plan.string_indices.get(bytes) {
Some(string_idx) =>
if plan.string_global_abs[string_idx] ==
imported_globals + entry_idx {
init
} else {
sg_rewrite_expr(init, plan, imported_globals)
}
_ => sg_rewrite_expr(init, plan, imported_globals)
}
None => sg_rewrite_expr(init, plan, imported_globals)
}
// A direct immutable string literal is non-null, including when its
// original declaration used a nullable shorthand. Reused globals must
// expose that proof to every rewritten literal consumer.
let rewritten_type = if sg_direct_string_init(globals[value]) is Some(_) {
if plan.old_preserve_type[value] {
global_type
} else {
string_global_type
}
} else {
global_type
}
out.push(@lib.Global::new(rewritten_type, rewritten_init))
}
}
@lib.GlobalSec::new(out)
}
///|
fn sg_rewrite_export_sec(
export_sec : @lib.ExportSec?,
imported_globals : Int32,
plan : SgRewritePlan,
) -> @lib.ExportSec? {
match export_sec {
Some(@lib.ExportSec(exports)) => {
let out : Array[@lib.Export] = []
for export_ in exports {
match export_ {
@lib.Export(name, @lib.GlobalExternIdx(idx)) =>
out.push(
@lib.Export::new(
name,
@lib.ExternIdx::global(
sg_remap_existing_global_idx(idx, imported_globals, plan),
),
),
)
_ => out.push(export_)
}
}
Some(@lib.ExportSec::new(out))
}
None => None
}
}
///|
fn sg_rewrite_elem_mode(
mode : @lib.ElemMode,
plan : SgRewritePlan,
imported_globals : Int32,
) -> @lib.ElemMode {
match mode {
@lib.Active(table_idx, expr) =>
@lib.ElemMode::active(
table_idx,
sg_rewrite_expr(expr, plan, imported_globals),
)
_ => mode
}
}
///|
fn sg_rewrite_elem_kind(
kind : @lib.ElemKind,
plan : SgRewritePlan,
imported_globals : Int32,
) -> @lib.ElemKind {
match kind {
@lib.FuncExprsElemKind(exprs) => {
let out : Array[@lib.Expr] = []
for expr in exprs {
out.push(sg_rewrite_expr(expr, plan, imported_globals))
}
@lib.ElemKind::func_exprs(out)
}
@lib.TypedExprsElemKind(ref_type, exprs) => {
let out : Array[@lib.Expr] = []
for expr in exprs {
out.push(sg_rewrite_expr(expr, plan, imported_globals))
}
@lib.ElemKind::typed_exprs(ref_type, out)
}
_ => kind
}
}
///|
fn sg_rewrite_elem_sec(
elem_sec : @lib.ElemSec?,
plan : SgRewritePlan,
imported_globals : Int32,
) -> @lib.ElemSec? {
match elem_sec {
Some(@lib.ElemSec(elems)) => {
let out : Array[@lib.Elem] = []
for elem in elems {
let @lib.Elem(mode, kind) = elem
out.push(
@lib.Elem::new(
sg_rewrite_elem_mode(mode, plan, imported_globals),
sg_rewrite_elem_kind(kind, plan, imported_globals),
),
)
}
Some(@lib.ElemSec::new(out))
}
None => None
}
}
///|
fn sg_rewrite_data_sec(
data_sec : @lib.DataSec?,
plan : SgRewritePlan,
imported_globals : Int32,
) -> @lib.DataSec? {
match data_sec {
Some(@lib.DataSec(datas)) => {
let out : Array[@lib.Data] = []
for data in datas {
let @lib.Data(mode, bytes) = data
let next_mode = match mode {
@lib.Active(mem_idx, expr) =>
@lib.DataMode::active(
mem_idx,
sg_rewrite_expr(expr, plan, imported_globals),
)
_ => mode
}
out.push(@lib.Data::new(next_mode, bytes))
}
Some(@lib.DataSec::new(out))
}
None => None
}
}
///|
fn sg_rewrite_global_name_map(
names : @lib.NameMap?,
imported_globals : Int32,
plan : SgRewritePlan,
) -> @lib.NameMap? {
match names {
Some(@lib.NameMap(entries)) => {
let out : Array[@lib.NameAssoc] = []
for entry in entries {
let @lib.NameAssoc(idx, name) = entry
let raw = idx.reinterpret_as_int()
if raw >= imported_globals {
let defined_idx = raw - imported_globals
if defined_idx >= 0 && defined_idx < plan.old_defined_to_abs.length() {
out.push(
@lib.NameAssoc::new(
plan.old_defined_to_abs[defined_idx].reinterpret_as_uint(),
name,
),
)
}
} else {
out.push(entry)
}
}
out.sort_by(fn(left, right) {
let @lib.NameAssoc(left_idx, _) = left
let @lib.NameAssoc(right_idx, _) = right
left_idx.compare(right_idx)
})
Some(@lib.NameMap::new(out))
}
None => None
}
}
///|
fn sg_rewrite_name_sec(
name_sec : @lib.NameSec?,
imported_globals : Int32,
plan : SgRewritePlan,
) -> @lib.NameSec? {
match name_sec {
Some(sec) =>
Some(
@lib.NameSec::new(
module_name=sec.module_name,
func_names=sec.func_names,
local_names=sec.local_names,
label_names=sec.label_names,
type_names=sec.type_names,
table_names=sec.table_names,
memory_names=sec.memory_names,
global_names=sg_rewrite_global_name_map(
sec.global_names,
imported_globals,
plan,
),
elem_names=sec.elem_names,
data_names=sec.data_names,
field_names=sec.field_names,
tag_names=sec.tag_names,
),
)
None => None
}
}
///|
fn sg_rewrite_code_sec(
code_sec : @lib.CodeSec?,
plan : SgRewritePlan,
imported_globals : Int32,
) -> @lib.CodeSec? {
match code_sec {
Some(@lib.CodeSec(funcs)) => {
let mut out : Array[@lib.Func]? = None
for index, func in funcs {
let @lib.Func(locals, body) = func
let next_body = sg_rewrite_expr(body, plan, imported_globals)
if next_body != body {
if out is None {
out = Some(funcs.copy())
}
out.unwrap()[index] = @lib.Func::new(locals, next_body)
}
}
match out {
Some(rewritten) => Some(@lib.CodeSec::new(rewritten))
None => code_sec
}
}
None => None
}
}
///|
pub fn string_gathering_run_module_pass(mod_ : @lib.Module) -> @lib.Module {
if pass_module_has_legacy_try(mod_) ||
pass_module_has_offset_sensitive_metadata(mod_) {
return mod_
}
let set = sg_collect_strings(mod_)
if set.strings.is_empty() {
return mod_
}
let imported_globals = sg_imported_global_count(mod_)
let plan = sg_make_rewrite_plan(mod_, set, imported_globals)
@lib.Module::new(
custom_secs=mod_.custom_secs,
type_sec=mod_.type_sec,
import_sec=mod_.import_sec,
func_annotation_sec=mod_.func_annotation_sec,
func_sec=mod_.func_sec,
table_sec=mod_.table_sec,
mem_sec=mod_.mem_sec,
tag_sec=mod_.tag_sec,
stringrefs_sec=mod_.stringrefs_sec,
global_sec=Some(
sg_rewrite_global_sec(mod_.global_sec, plan, imported_globals),
),
export_sec=sg_rewrite_export_sec(mod_.export_sec, imported_globals, plan),
start_sec=mod_.start_sec,
elem_sec=sg_rewrite_elem_sec(mod_.elem_sec, plan, imported_globals),
data_cnt_sec=mod_.data_cnt_sec,
code_sec=sg_rewrite_code_sec(mod_.code_sec, plan, imported_globals),
data_sec=sg_rewrite_data_sec(mod_.data_sec, plan, imported_globals),
name_sec=sg_rewrite_name_sec(mod_.name_sec, imported_globals, plan),
raw_name_sec_payload=None,
)
}