Fix an assert while deleting table elements

LLVM's mergefunc pass may mean that the same descriptor function is used
for different closure invocation sites even when the closure itself is
different. This typically only happens with LTO but in theory could
happen at any time!

The assert was tripping when we tried to delete the same function table
entry twice, so instead of a `Vec<usize>` of entries to delete this
commit switches to a `HashSet<usize>` which should do the deduplication
for us and enusre that we delete each descriptor only once.

Closes #1264
This commit is contained in:
Alex Crichton
2019-02-19 08:17:14 -08:00
parent e498d99b2d
commit 9bab9d4af1
3 changed files with 7 additions and 11 deletions

View File

@ -13,7 +13,7 @@ use crate::descriptor::Descriptor;
use crate::js::js2rust::Js2Rust;
use crate::js::Context;
use failure::Error;
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashSet};
use std::mem;
use walrus::ir::{Expr, ExprId};
use walrus::{FunctionId, LocalFunction};
@ -21,12 +21,6 @@ use walrus::{FunctionId, LocalFunction};
pub fn rewrite(input: &mut Context) -> Result<(), Error> {
let info = ClosureDescriptors::new(input);
// Sanity check to make sure things look ok and skip everything below if
// there's not calls to `Closure::new`.
assert_eq!(
info.element_removal_list.len(),
info.func_to_descriptor.len(),
);
if info.element_removal_list.len() == 0 {
return Ok(());
}
@ -41,7 +35,7 @@ struct ClosureDescriptors {
/// A list of elements to remove from the function table. The first element
/// of the pair is the index of the entry in the element section, and the
/// second element of the pair is the index within that entry to remove.
element_removal_list: Vec<usize>,
element_removal_list: HashSet<usize>,
/// A map from local functions which contain calls to
/// `__wbindgen_describe_closure` to the information about the closure
@ -150,6 +144,7 @@ impl ClosureDescriptors {
walrus::TableKind::Function(f) => f,
};
for idx in self.element_removal_list.iter().cloned() {
log::trace!("delete element {}", idx);
assert!(table.elements[idx].is_some());
table.elements[idx] = None;
}