///|
fn NameAlphabet::short_name(alphabet : NameAlphabet, ordinal : Int) -> String {
let builder = StringBuilder()
let first_radix = alphabet.first.length()
let rest_radix = alphabet.rest.length()
builder.write_char(alphabet.first.get_char(ordinal % first_radix).unwrap())
for remaining = ordinal / first_radix
remaining > 0
remaining = (remaining - 1) / rest_radix {
builder.write_char(
alphabet.rest.get_char((remaining - 1) % rest_radix).unwrap(),
)
}
builder.to_string()
}
///|
let keywords : @hashset.HashSet[String] = @hashset.HashSet([
"break", "case", "catch", "class", "const", "continue", "debugger", "default",
"delete", "do", "else", "enum", "export", "extends", "false", "finally", "for",
"function", "if", "import", "in", "instanceof", "new", "null", "return", "super",
"switch", "this", "throw", "true", "try", "typeof", "var", "void", "while", "with",
"yield", "let", "static", "await", "async", "get", "set", "of", "as", "from", "arguments",
"eval", "undefined", "NaN", "Infinity", "implements", "interface", "package", "private",
"protected", "public",
])
///|
fn Program::symbol_name(program : Program, symbol : Int) -> String {
program.names[program.symbols[symbol].name]
}
///|
fn Program::mangle(program : Program, rename_top_level : Bool) -> Unit {
guard !program.has_dynamic_scope else { return }
let reserved = program.reserve_symbol_names(rename_top_level)
program.alphabet = program.name_alphabet()
let declarations = program.renameable_declarations()
let captured = program.captured_symbols()
program.assign_symbol_names(reserved, declarations, captured)
}
///|
fn Program::reserve_symbol_names(
self : Program,
rename_top_level : Bool,
) -> @hashset.HashSet[String] {
let reserved = keywords.copy()
for reference in self.references {
guard reference.symbol < 0 else { continue }
reserved.add(self.names[reference.name])
}
for index in 0.. Array[Array[Int]] {
let declarations : Array[Array[Int]] = Array::makei(self.scopes.length(), _ => {
[]
})
for index in 0.. Array[@hashset.HashSet[Int]] {
let captured : Array[@hashset.HashSet[Int]] = Array::makei(
self.scopes.length(),
_ => @hashset.HashSet([]),
)
for reference in self.references {
guard reference.symbol >= 0 else { continue }
self.reserve_capture(captured, reference.scope, reference.symbol)
}
// Parameters copied into a function-body environment and linked catch/var
// declarations also constrain names even when the binding is never read.
for scope in 0.. Unit {
let candidates = self.generated_names
let blocked : Array[Int] = []
let generator = NameGenerator::new(self.alphabet, reserved)
// Scope allocation is preorder; ancestors receive names before descendants.
for scope in 0.. 0 else { continue }
targets.sort_by((left, right) => {
let frequency = self.symbols[right].use_count -
self.symbols[left].use_count
guard frequency == 0 else { return frequency }
left - right
})
for symbol in captured[scope] {
let slot = self.symbols[symbol].name_slot
guard slot >= 0 else { continue }
blocked[slot] = scope
}
let mut slot = 0
for index in targets {
while slot < blocked.length() && blocked[slot] == scope {
slot += 1
}
while slot >= candidates.length() {
candidates.push(generator.next_name())
blocked.push(-1)
}
self.symbols[index] = { ..self.symbols[index], name_slot: slot, }
slot += 1
}
}
}
///|
/// A binding's name must remain visible on the path from a use or declaration
/// occurrence to its owning scope. Disjoint subtrees may reuse that name.
fn Program::reserve_capture(
program : Program,
captured : Array[@hashset.HashSet[Int]],
scope : Int,
symbol : Int,
) -> Unit {
let binding = program.symbols[symbol]
guard (binding.flags & symbol_preserved) == 0 else { return }
for current = scope {
guard current >= 0 && current != binding.scope else { break }
guard captured[current].add_and_check(symbol) else { break }
continue program.scopes[current].parent
}
}