@@ -18,9 +18,8 @@ import {
1818 ProgramNode ,
1919} from "../ast/ast"
2020import { createBlock , createInstruction } from "../ast/helpers"
21- import { RootLayout } from "./constants"
2221import { getDisplayNumber , hasHint } from "../spec/helpers"
23- import { LayoutError , OperandError , UnknownOperandTypeError } from "./errors"
22+ import { OperandError , UnknownOperandTypeError } from "./errors"
2423
2524export interface DisassembleParams {
2625 /**
@@ -338,45 +337,52 @@ function processRefOrSliceOperand(
338337 }
339338}
340339
341- /**
342- * Checks if the root layout is valid.
343- *
344- * Valid layout is:
345- * - `SETCP`
346- * - `DICTPUSHCONST`
347- * - `DICTIGETJMPZ`
348- * - `THROWARG`
349- *
350- * This is the only layout that is supported by the decompiler.
351- * This layout is generated by the FunC and Tact compilers.
352- */
353- function checkLayout ( opcodes : DecompiledInstruction [ ] ) : void {
354- if ( opcodes . length !== RootLayout . instructionsCount ) {
355- throw new LayoutError ( RootLayout . instructionsCount , opcodes . length , {
356- instructions : opcodes . map ( op => op . op . definition . mnemonic ) ,
340+ function findDictOpcode ( opcodes : DecompiledInstruction [ ] ) : DecompiledInstruction | undefined {
341+ return opcodes . find ( it => it . op . definition . mnemonic === "DICTPUSHCONST" )
342+ }
343+
344+ function findRootMethods ( opcodes : DecompiledInstruction [ ] ) : MethodNode [ ] {
345+ const methods : MethodNode [ ] = [ ]
346+
347+ if ( opcodes [ 2 ] ?. op . definition . mnemonic === "PUSHCONT" ) {
348+ const cont = opcodes [ 2 ] . op . operands . at ( 0 )
349+ if ( ! cont || cont . type !== "subslice" ) {
350+ return methods
351+ }
352+
353+ const recvInternal = disassembleRawRoot ( cont . value )
354+ methods . push ( {
355+ type : "method" ,
356+ hash : recvInternal . hash ,
357+ offset : recvInternal . offset ,
358+ body : recvInternal ,
359+ id : 0 ,
357360 } )
358361 }
359362
360- const isValidLayout =
361- opcodes [ RootLayout . instructions . SETCP ] . op . definition . mnemonic === "SETCP" &&
362- opcodes [ RootLayout . instructions . DICTPUSHCONST ] . op . definition . mnemonic === "DICTPUSHCONST" &&
363- ( opcodes [ RootLayout . instructions . DICTIGETJMPZ ] . op . definition . mnemonic === "DICTIGETJMPZ" ||
364- opcodes [ RootLayout . instructions . DICTIGETJMPZ ] . op . definition . mnemonic ===
365- "DICTIGETJMP" ) &&
366- opcodes [ RootLayout . instructions . THROWARG ] . op . definition . mnemonic === "THROWARG"
367-
368- if ( ! isValidLayout ) {
369- throw new LayoutError ( RootLayout . instructionsCount , opcodes . length , {
370- expected : [ "SETCP" , "DICTPUSHCONST" , "DICTIGETJMPZ" , "THROWARG" ] ,
371- actual : opcodes . map ( op => op . op . definition . mnemonic ) ,
363+ if ( opcodes [ 6 ] ?. op . definition . mnemonic === "PUSHCONT" ) {
364+ const cont = opcodes [ 6 ] . op . operands . at ( 0 )
365+ if ( ! cont || cont . type !== "subslice" ) {
366+ return methods
367+ }
368+
369+ const recvExternal = disassembleRawRoot ( cont . value )
370+ methods . push ( {
371+ type : "method" ,
372+ hash : recvExternal . hash ,
373+ offset : recvExternal . offset ,
374+ body : recvExternal ,
375+ id : - 1 ,
372376 } )
373377 }
378+
379+ return methods
374380}
375381
376382/**
377383 * Disassembles the root cell into a list of instructions.
378384 *
379- * Use this function if you want to disassemble the whole BoC file.
385+ * Use this function if you want to disassemble the whole BoC file with dictionary unpacked .
380386 */
381387export function disassembleRoot (
382388 cell : Cell ,
@@ -388,29 +394,40 @@ export function disassembleRoot(
388394 } ,
389395) : ProgramNode {
390396 const opcodes = disassemble ( { source : cell } )
391- checkLayout ( opcodes )
392-
393- const dictOpcode = opcodes [ RootLayout . instructions . DICTPUSHCONST ] . op
394- const { procedures, methods} = deserializeDict ( dictOpcode . operands , options . computeRefs )
395397
396398 const args = {
397399 source : cell ,
398400 offset : { bits : 0 , refs : 9 } ,
399401 onCellReference : undefined ,
400402 }
403+
404+ const rootMethods = findRootMethods ( opcodes )
405+
406+ const dictOpcode = findDictOpcode ( opcodes )
407+ if ( ! dictOpcode ) {
408+ // Likely some non-Tact/FunC produced BoC
409+ return {
410+ type : "program" ,
411+ topLevelInstructions : opcodes . map ( op => processInstruction ( op , args ) ) ,
412+ procedures : [ ] ,
413+ methods : rootMethods ,
414+ withRefs : options . computeRefs ,
415+ }
416+ }
417+
418+ const { procedures, methods} = deserializeDict ( dictOpcode . op . operands , options . computeRefs )
419+
401420 return {
402421 type : "program" ,
403422 topLevelInstructions : opcodes . map ( op => processInstruction ( op , args ) ) ,
404423 procedures,
405- methods,
424+ methods : [ ... rootMethods , ... methods ] ,
406425 withRefs : options . computeRefs ,
407426 }
408427}
409428
410429/**
411- * Disassembles a cell without any additional checks for the layout.
412- *
413- * Use this function if your contract use non-usual layout.
430+ * Disassembles a cell without any additional unpacking of the dictionary.
414431 */
415432export function disassembleRawRoot ( cell : Cell ) : BlockNode {
416433 return disassembleAndProcess ( {
0 commit comments