Repository: servo/servo
File: components/script/module_loading.rs (Rust)

Code before:
        promise: promise.clone(),
        is_loading: Cell::new(true),
        pending_modules_count: Cell::new(1),
        visited: RefCell::new(HashSet::new()),
        load_state,
    };

    // Step 4. Perform InnerModuleLoading(state, module).
    inner_module_loading(global, &Rc::new(state), module);

    // Step 5. Return pc.[[Promise]].
    promise
}

/// <https://tc39.es/ecma262/#sec-InnerModuleLoading>
fn inner_module_loading(
    global: &GlobalScope,
    state: &Rc<GraphLoadingState>,
    module: Rc<ModuleTree>,
) {
    let cx = GlobalScope::get_cx();

    // Step 1. Assert: state.[[IsLoading]] is true.
    assert!(state.is_loading.get());

    let module_borrow = module.get_record().borrow();

    let module_handle = module_borrow
        .as_ref()
        .map(|module| module.handle())
        .unwrap();

    let module_url = module.get_url();
    let visited_contains_module = state.visited.borrow().contains(&module_url);

    // Step 2. If module is a Cyclic Module Record, module.[[Status]] is new, and state.[[Visited]] does not contain module, then
    // Note: mozjs doesn't expose a way to check the ModuleStatus of a ModuleObject.
    if unsafe { IsCyclicModule(module_handle.get()) } && !visited_contains_module {
        // a. Append module to state.[[Visited]].
        state.visited.borrow_mut().insert(module_url);

        // b. Let requestedModulesCount be the number of elements in module.[[RequestedModules]].
        let requested_modules_count = unsafe { GetRequestedModulesCount(*cx, module_handle) };

        // c. Set state.[[PendingModulesCount]] to state.[[PendingModulesCount]] + requestedModulesCount.
        let pending_modules_count = state.pending_modules_count.get();
        state
            .pending_modules_count
            .set(pending_modules_count + requested_modules_count);

        // d. For each ModuleRequest Record request of module.[[RequestedModules]], do

Review comment (suggestion):
I changed `GetRequestedModulesCount()` and `GetRequestedModuleSpecifier()` functions below (to their `js::rust::wrappers` variants) so we use the lifetime handle (`js::gc::HandleObject`) more consistently, but now this borrow has to live through step 2 below, where those functions are called.

Given we don't need this borrow after step 2, would it be preferable to use something like `std::mem::drop(module_borrow)` between step 2 and 3? An alternative would be to introduce a new scope for this borrow, but the code below is already heavily indented, so I opted no to do that.

Code after:
    let state = GraphLoadingState {
        promise: promise.clone(),
        is_loading: Cell::new(true),
        pending_modules_count: Cell::new(1),
        visited: RefCell::new(HashSet::new()),
        load_state,
    };

    // Step 4. Perform InnerModuleLoading(state, module).
    inner_module_loading(global, &Rc::new(state), module);

    // Step 5. Return pc.[[Promise]].
    promise
}

/// <https://tc39.es/ecma262/#sec-InnerModuleLoading>
fn inner_module_loading(
    global: &GlobalScope,
    state: &Rc<GraphLoadingState>,
    module: Rc<ModuleTree>,
) {
    let cx = GlobalScope::get_cx();

    // Step 1. Assert: state.[[IsLoading]] is true.
    assert!(state.is_loading.get());

    let module_handle = module.get_record().map(|module| module.handle()).unwrap();

    let module_url = module.get_url();
    let visited_contains_module = state.visited.borrow().contains(&module_url);

    // Step 2. If module is a Cyclic Module Record, module.[[Status]] is new, and state.[[Visited]] does not contain module, then
    // Note: mozjs doesn't expose a way to check the ModuleStatus of a ModuleObject.
    if unsafe { IsCyclicModule(module_handle.get()) } && !visited_contains_module {
        // a. Append module to state.[[Visited]].
        state.visited.borrow_mut().insert(module_url);

        // b. Let requestedModulesCount be the number of elements in module.[[RequestedModules]].
        let requested_modules_count = unsafe { GetRequestedModulesCount(*cx, module_handle) };

        // c. Set state.[[PendingModulesCount]] to state.[[PendingModulesCount]] + requestedModulesCount.
        let pending_modules_count = state.pending_modules_count.get();
        state
            .pending_modules_count
            .set(pending_modules_count + requested_modules_count);

        // d. For each ModuleRequest Record request of module.[[RequestedModules]], do
        for index in 0..requested_modules_count {
            // i. If AllImportAttributesSupported(request.[[Attributes]]) is false, then
            // Note: Gecko will call hasFirstUnsupportedAttributeKey on each module request,
            // GetRequestedModuleSpecifier will do it for us.