MCPcopy Create free account
hub / github.com/CashScript/cashscript / replaceOps

Function replaceOps

packages/utils/src/script.ts:276–423  ·  view source on GitHub ↗
(
  script: Script,
  locationData: FullLocationData,
  logs: LogEntry[],
  requires: RequireStatement[],
  sourceTags: SourceTagEntry[],
  constructorParamLength: number,
  optimisations: string[][],
)

Source from the content-addressed store, hash-verified

274}
275
276function replaceOps(
277 script: Script,
278 locationData: FullLocationData,
279 logs: LogEntry[],
280 requires: RequireStatement[],
281 sourceTags: SourceTagEntry[],
282 constructorParamLength: number,
283 optimisations: string[][],
284): ReplaceOpsResult {
285 let asm = scriptToAsm(script);
286 let newLocationData = [...locationData];
287 let newLogs = [...logs];
288 let newRequires = [...requires];
289 let newSourceTags = [...sourceTags];
290
291 optimisations.forEach(([pattern, replacement]) => {
292 let processedAsm = '';
293 let asmToSearch = asm;
294
295 // We add a space or end of string to the end of the pattern to ensure that we match the whole pattern
296 // (no partial matches)
297 const regex = new RegExp(`${pattern}(\\s|$)`, 'g');
298
299 let matchIndex = asmToSearch.search(regex);
300 while (matchIndex !== -1) {
301 // We add the part before the match to the processed asm
302 processedAsm = mergeAsm(processedAsm, asmToSearch.slice(0, matchIndex));
303
304 // We count the number of spaces in the processed asm + 1, which is equal to the script index
305 // We do the same thing to calculate the number of opcodes in the pattern and replacement
306 const scriptIndex = processedAsm === '' ? 0 : [...processedAsm.matchAll(/\s+/g)].length + 1;
307 const patternLength = [...pattern.matchAll(/\s+/g)].length + 1;
308 const replacementLength = replacement === '' ? 0 : [...replacement.matchAll(/\s+/g)].length + 1;
309
310 // We get the locationData entries for every opcode in the pattern
311 const patternLocations = newLocationData.slice(scriptIndex, scriptIndex + patternLength);
312
313 // We get the lowest start location and highest end location of the pattern
314 const lowestStart = getLowestStartLocation(patternLocations);
315 const highestEnd = getHighestEndLocation(patternLocations);
316
317 // Initially we set the position hint to END if any of the pattern locations have a position hint of END
318 // It turned out that this was not the correct approach in the case of OP_NOT OP_IF => OP_NOTIF,
319 // because OP_IF and OP_NOTIF are START opcodes, and OP_NOT is an END opcode.
320 // After reviewing the entire list of optimisations, we set the position hint to the last location's position hint
321 // which we believe to be the correct approach, but it is hard to reason about.
322 // We've also consulted with AI (o3-max) to help us reason about this, and it seems to be the correct approach.
323 const positionHint = patternLocations.at(-1)?.positionHint ?? PositionHint.START;
324
325 // We merge the lowest start and highest end locations into a single location data entry
326 const mergedLocation = {
327 location: {
328 start: lowestStart.location.start,
329 end: highestEnd.location.end,
330 },
331 positionHint,
332 };
333

Callers 1

optimiseBytecodeFunction · 0.85

Calls 5

scriptToAsmFunction · 0.85
mergeAsmFunction · 0.85
getLowestStartLocationFunction · 0.85
getHighestEndLocationFunction · 0.85
asmToScriptFunction · 0.85

Tested by

no test coverage detected