Skip to content

Appears to be a scope resolution bug #1386

@z0ffy

Description

@z0ffy

Expected Behavior

The obfuscated output should preserve the original binding semantics.

A semantically correct result would be equivalent to:

import { i as _imp } from './runtime.js';

function factory() {
  return 'factory-result';
}

var P = class _cls {
  static instance;

  static getInstance() {
    return _cls.instance ||= new _cls(), _cls.instance;
  }
};

var x = _imp(factory(), 1);

export { P, x };

or:

import { i as _imp } from './runtime.js';

function factory() {
  return 'factory-result';
}

var _P = class {
  static instance;

  static getInstance() {
    return _P.instance ||= new _P(), _P.instance;
  }
};

var x = _imp(factory(), 1);

export { _P as P, x };

Current Behavior

The obfuscated output rewrites the class self-reference to the imported binding.

It becomes effectively:

import { i as _0x48e620 } from './runtime.js';

function factory() {
    return 'factory-result';
}

var P = class e {
    static ['instance'];
    static ['getInstance']() {
        return _0x48e620['instance'] ||= new _0x48e620(), _0x48e620['instance'];
    }
};

var x = _0x48e620(factory(), 0x1);

export { P, x };

This is incorrect because _0x48e620 is the imported binding, not the class.

If the imported binding is a function or arrow function, runtime execution fails with:

TypeError: function is not a constructor

Steps to Reproduce

  1. Open https://obfuscator.io/legacy-playground
  2. Paste runtime.js and the main module code below into the editor
  3. Use the obfuscator options listed below
  4. Run obfuscation
  5. Inspect the generated getInstance() method
  6. Notice that the class self-reference is rewritten to the imported binding

JavaScript Obfuscator Edition

Your Environment

  • javascript-obfuscator: 4.2.2
    • Also reproduced after testing newer versions, including 5.3.0
  • Node.js: 20.x
  • OS: macOS

Stack trace

Minimal working example that will help to reproduce issue

// runtime.js
export const i = (value, flag) => ({ value, flag });
import { i as e } from './runtime.js';

function factory() {
  return 'factory-result';
}

var P = class e {
  static instance;

  static getInstance() {
    return e.instance ||= new e(), e.instance;
  }
};

var x = e(factory(), 1);

export { P, x };

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions