constructor(textOrConfig, astIfNoConfig) {
		let text, hasBOM, ast, parserServices, scopeManager, visitorKeys;

		// Process overloading of arguments
		if (typeof textOrConfig === "string") {
			text = textOrConfig;
			ast = astIfNoConfig;
			hasBOM = false;
		} else if (typeof textOrConfig === "object" && textOrConfig !== null) {
			text = textOrConfig.text;
			ast = textOrConfig.ast;
			hasBOM = textOrConfig.hasBOM;
			parserServices = textOrConfig.parserServices;
			scopeManager = textOrConfig.scopeManager;
			visitorKeys = textOrConfig.visitorKeys;
		}

		validate(ast);
		super(ast.tokens, ast.comments);

		/**
		 * General purpose caching for the class.
		 */
		this[caches] = new Map([
			["scopes", new WeakMap()],
			["vars", new Map()],
			["configNodes", void 0],
			["isGlobalReference", new WeakMap()],
		]);

		/**
		 * Indicates if the AST is ESTree compatible.
		 * @type {boolean}
		 */
		this.isESTree = ast.type === "Program";

		/*
		 * Backwards compatibility for BOM handling.
		 *
		 * The `hasBOM` property has been available on the `SourceCode` object
		 * for a long time and is used to indicate if the source contains a BOM.
		 * The linter strips the BOM and just passes the `hasBOM` property to the
		 * `SourceCode` constructor to make it easier for languages to not deal with
		 * the BOM.
		 *
		 * However, the text passed in to the `SourceCode` constructor might still
		 * have a BOM if the constructor is called outside of the linter, so we still
		 * need to check for the BOM in the text.
		 */
		const textHasBOM = text.charCodeAt(0) === 0xfeff;

		/**
		 * The flag to indicate that the source code has Unicode BOM.
		 * @type {boolean}
		 */
		this.hasBOM = textHasBOM || !!hasBOM;

		/**
		 * The original text source code.
		 * BOM was stripped from this text.
		 * @type {string}
		 */
		this.text = textHasBOM ? text.slice(1) : text;

		/**
		 * The parsed AST for the source code.
		 * @type {ASTNode}
		 */
		this.ast = ast;

		/**
		 * The parser services of this source code.
		 * @type {Object}
		 */
		this.parserServices = parserServices || {};

		/**
		 * The scope of this source code.
		 * @type {ScopeManager|null}
		 */
		this.scopeManager = scopeManager || null;

		/**
		 * The visitor keys to traverse AST.
		 * @type {Object}
		 */
		this.visitorKeys = visitorKeys || Traverser.DEFAULT_VISITOR_KEYS;

		// Check the source text for the presence of a shebang since it is parsed as a standard line comment.
		const shebangMatched = this.text.match(astUtils.shebangPattern);
		const hasShebang =
			shebangMatched &&
			ast.comments.length &&
			ast.comments[0].value === shebangMatched[1];

		if (hasShebang) {
			ast.comments[0].type = "Shebang";
		}

		this.tokensAndComments = sortedMerge(ast.tokens, ast.comments);

		/**
		 * The source code split into lines according to ECMA-262 specification.
		 * This is done to avoid each rule needing to do so separately.
		 * @type {string[]}
		 */
		this.lines = [];

		/**
		 * @type {number[]}
		 */
		this.lineStartIndices = [0];

		const lineEndingPattern = astUtils.createGlobalLinebreakMatcher();
		let match;

		/*
		 * Previously, this was implemented using a regex that
		 * matched a sequence of non-linebreak characters followed by a
		 * linebreak, then adding the lengths of the matches. However,
		 * this caused a catastrophic backtracking issue when the end
		 * of a file contained a large number of non-newline characters.
		 * To avoid this, the current implementation just matches newlines
		 * and uses match.index to get the correct line start indices.
		 */
		while ((match = lineEndingPattern.exec(this.text))) {
			this.lines.push(
				this.text.slice(this.lineStartIndices.at(-1), match.index),
			);
			this.lineStartIndices.push(match.index + match[0].length);
		}
		this.lines.push(this.text.slice(this.lineStartIndices.at(-1)));

		// don't allow further modification of this object
		Object.freeze(this);
		Object.freeze(this.lines);
	}
Creates a new instance.
@param {string|Object} textOrConfig The source code text or config object.
@param {string} textOrConfig.text The source code text.
@param {ASTNode} textOrConfig.ast The Program node of the AST representing the code. This AST should be created from the text that BOM was stripped.
@param {boolean} textOrConfig.hasBOM Indicates if the text has a Unicode BOM.
@param {Object|null} textOrConfig.parserServices The parser services.
@param {ScopeManager|null} textOrConfig.scopeManager The scope of this source code.
@param {Object|null} textOrConfig.visitorKeys The visitor keys to traverse AST.
@param {ASTNode} [astIfNoConfig] The Program node of the AST representing the code. This AST should be created from the text that BOM was stripped.