Files
xo-cash-utils/source/cash-assembly/identifier-collisions.ts
T

60 lines
2.9 KiB
TypeScript

import { generateBytecodeMap, IdentifierResolutionType, OpcodesBchSpec } from '@bitauth/libauth';
import { CashAssemblyIdentifierCollisionError } from './errors.ts';
import type { CompileCashAssemblyContext } from './types.ts';
/**
* Set of opcode names that the compiler recognizes.
*/
const COMPILER_OPCODE_NAMES: ReadonlySet<string> = new Set(Object.keys(generateBytecodeMap(OpcodesBchSpec)));
/**
* Parameters for {@link assertNoIdentifierCollisions} function.
*/
export type AssertNoIdentifierCollisionsParameters = Pick<CompileCashAssemblyContext, 'variables' | 'templateScripts'>;
/**
* Libauth resolves opcodes, then variables, then scripts, and the first match silently hides the
* rest, leading to unexpected compilation results and no errors. This function throws when one identifier matches more than one resolution type.
*
* @param {AssertNoIdentifierCollisionsParameters} parameters - Provided variables and optional template scripts.
* @throws {@link CashAssemblyIdentifierCollisionError} - When one identifier matches more than one resolution type.
*/
export const assertNoIdentifierCollisions = (parameters: AssertNoIdentifierCollisionsParameters): void => {
const { variables, templateScripts } = parameters;
// Create a set of the script identifiers from the template scripts map.
const scriptIdentifiers = new Set(Object.keys(templateScripts ?? {}));
const variableNames = new Set(Object.keys(variables));
// Combine both, the variableNames and the scriptIdentifiers, to get a set of all identifiers.
const declaredIdentifiers = new Set([ ...variableNames, ...scriptIdentifiers ]);
// This loop will check for more than one occurrence of an identifier in OP_CODES, variables, and scripts
for (const identifier of declaredIdentifiers) {
// Resolution types is a native construct from libauth.
const resolutionTypes: IdentifierResolutionType[] = [];
// If the identifier is an opcode, then push it to the resolution types array, this will also be
// detected by either or both of the variableNames and scriptIdentifiers checks.
if (COMPILER_OPCODE_NAMES.has(identifier) === true) {
resolutionTypes.push(IdentifierResolutionType.opcode);
}
// If the identifier is in variables, then push it to the resolution types array.
if (variableNames.has(identifier) === true) {
resolutionTypes.push(IdentifierResolutionType.variable);
}
// If the identifier is a script identifier, then push it to the resolution types array.
if (scriptIdentifiers.has(identifier) === true) {
resolutionTypes.push(IdentifierResolutionType.script);
}
// If there are more than one resolution types, then throw an error.
if (resolutionTypes.length > 1) {
throw new CashAssemblyIdentifierCollisionError(identifier, resolutionTypes);
}
}
};