Skip to content

dead_branch_remover changes this when calling methods on objects #2466

@mischnic

Description

@mischnic

Describe the bug

(Related to #2465)

dead_branch_remover changes the behaviour of calls like (0, obj.foo)()

Input code

const X = {
    run() {
        console.log(this === globalThis);
    },
};

X.run();
(0, X.run)();

Prints false, true

Config

Details
use swc_common::{
    chain, comments::SingleThreadedComments, sync::Lrc, FileName, Globals, Mark, SourceMap,
};
use swc_ecma_ast::Module;
use swc_ecma_codegen::{text_writer::JsWriter, Config, Emitter};
use swc_ecma_parser::{lexer::Lexer, EsConfig, PResult, Parser, StringInput, Syntax};
use swc_ecma_transforms::{
    compat::reserved_words::reserved_words, fixer, helpers, hygiene,
    optimization::simplify::dead_branch_remover, resolver::resolver_with_mark,
};
use swc_ecma_visit::FoldWith;

fn main() {
    let cm = Lrc::<SourceMap>::default();
    let src = r#"
const X = {
    run() {
        console.log(this === globalThis);
    },
};

X.run();
(0, X.run)();
"#;
    let (module, comments) = parse(src, "test.js", &cm).unwrap();

    swc_common::GLOBALS.set(&Globals::new(), || {
        helpers::HELPERS.set(&helpers::Helpers::default(), || {
            let global_mark = Mark::fresh(Mark::root());
            let module = module.fold_with(&mut resolver_with_mark(global_mark));

            let transform = &mut dead_branch_remover();
            let module = module.fold_with(transform);

            let module = module.fold_with(&mut chain!(
                reserved_words(),
                hygiene(),
                fixer(Some(&comments))
            ));

            let code = emit(&module, &comments, cm);
            println!("{}", code);
        });
    });
}

fn parse(
    code: &str,
    filename: &str,
    cm: &Lrc<SourceMap>,
) -> PResult<(Module, SingleThreadedComments)> {
    let source_file = cm.new_source_file(FileName::Real(filename.into()), code.into());
    let comments = SingleThreadedComments::default();

    let lexer = Lexer::new(
        Syntax::Es(EsConfig {
            jsx: true,
            ..Default::default()
        }),
        // Syntax::Typescript(TsConfig {
        //     tsx: true,
        //     ..Default::default()
        // }),
        Default::default(),
        StringInput::from(&*source_file),
        Some(&comments),
    );
    let mut parser = Parser::new_from(lexer);
    match parser.parse_module() {
        Err(err) => Err(err),
        Ok(module) => Ok((module, comments)),
    }
}

fn emit(module: &Module, comments: &SingleThreadedComments, cm: Lrc<SourceMap>) -> String {
    let mut buf = vec![];
    {
        let writer = Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None));
        let config = Config { minify: false };
        let mut emitter = Emitter {
            cfg: config,
            comments: Some(&comments),
            cm,
            wr: writer,
        };
        emitter.emit_module(&module).unwrap();
    }

    String::from_utf8(buf).unwrap()
}

Current output

const X = {
    run() {
        console.log(this === globalThis);
    },
};

X.run();
X.run();

Prints false, false

Expected behavior
Output should be the same as the input

Version
98a18e3

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions