(* * * Copyright (c) 2001-2002, * George C. Necula * Scott McPeak * Wes Weimer * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. The names of the contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *) (* patch.ml *) (* CABS file patching *) open Cabs open Cabshelper open Trace open Pretty open Cabsvisit (* binding of a unification variable to a syntactic construct *) type binding = | BSpecifier of string * spec_elem list | BName of string * string | BExpr of string * expression (* thrown when unification fails *) exception NoMatch (* thrown when an attempt to find the associated binding fails *) exception BadBind of string (* trying to isolate performance problems; will hide all the *) (* potentially expensive debugging output behind "if verbose .." *) let verbose : bool = true (* raise NoMatch if x and y are not equal *) let mustEq (x : 'a) (y : 'a) : unit = begin if (x <> y) then ( if verbose then (trace "patchDebug" (dprintf "mismatch by structural disequality\n")); raise NoMatch ) end (* why isn't this in the core Ocaml library? *) let identity x = x let isPatternVar (s : string) : bool = begin ((String.length s) >= 1) && ((String.get s 0) = '@') end (* 's' is actually "@name(blah)"; extract the 'blah' *) let extractPatternVar (s : string) : string = (*(trace "patch" (dprintf "extractPatternVar %s\n" s));*) (String.sub s 6 ((String.length s) - 7)) (* a few debugging printers.. *) let printExpr (e : expression) = begin if (verbose && traceActive "patchDebug") then ( Cprint.print_expression e; Cprint.force_new_line (); Cprint.flush () ) end let printSpec (spec: spec_elem list) = begin if (verbose && traceActive "patchDebug") then ( Cprint.print_specifiers spec; Cprint.force_new_line (); Cprint.flush () ) end let printSpecs (pat : spec_elem list) (tgt : spec_elem list) = begin (printSpec pat); (printSpec tgt) end let printDecl (pat : name) (tgt : name) = begin if (verbose && traceActive "patchDebug") then ( Cprint.print_name pat; Cprint.force_new_line (); Cprint.print_name tgt; Cprint.force_new_line (); Cprint.flush () ) end let printDeclType (pat : decl_type) (tgt : decl_type) = begin if (verbose && traceActive "patchDebug") then ( Cprint.print_decl "__missing_field_name" pat; Cprint.force_new_line (); Cprint.print_decl "__missing_field_name" tgt; Cprint.force_new_line (); Cprint.flush () ) end let printDefn (d : definition) = begin if (verbose && traceActive "patchDebug") then ( Cprint.print_def d; Cprint.flush () ) end (* class to describe how to modify the tree for subtitution *) class substitutor (bindings : binding list) = object(self) inherit nopCabsVisitor as super (* look in the binding list for a given name *) method findBinding (name : string) : binding = begin try (List.find (fun b -> match b with | BSpecifier(n, _) -> n=name | BName(n, _) -> n=name | BExpr(n, _) -> n=name) bindings) with Not_found -> raise (BadBind ("name not found: " ^ name)) end method vexpr (e:expression) : expression visitAction = begin match e with | EXPR_PATTERN(name) -> ( match (self#findBinding name) with | BExpr(_, expr) -> ChangeTo(expr) (* substitute bound expression *) | _ -> raise (BadBind ("wrong type: " ^ name)) ) | _ -> DoChildren end (* use of a name *) method vvar (s:string) : string = begin if (isPatternVar s) then ( let nameString = (extractPatternVar s) in match (self#findBinding nameString) with | BName(_, str) -> str (* substitute *) | _ -> raise (BadBind ("wrong type: " ^ nameString)) ) else s end (* binding introduction of a name *) method vname (k: nameKind) (spec: specifier) (n: name) : name visitAction = begin match n with (s (*variable name*), dtype, attrs, loc) -> ( let replacement = (self#vvar s) in (* use replacer from above *) if (s <> replacement) then ChangeTo(replacement, dtype, attrs, loc) else DoChildren (* no replacement *) ) end method vspec (specList: specifier) : specifier visitAction = begin if verbose then (trace "patchDebug" (dprintf "substitutor: vspec\n")); (printSpec specList); (* are any of the specifiers SpecPatterns? we have to check the entire *) (* list, not just the head, because e.g. "typedef @specifier(foo)" has *) (* "typedef" as the head of the specifier list *) if (List.exists (fun elt -> match elt with | SpecPattern(_) -> true | _ -> false) specList) then begin (* yes, replace the existing list with one got by *) (* replacing all occurrences of SpecPatterns *) (trace "patchDebug" (dprintf "at least one spec pattern\n")); ChangeTo (List.flatten (List.map (* for each specifier element, yield the specifier list *) (* to which it maps; then we'll flatten the final result *) (fun elt -> match elt with | SpecPattern(name) -> ( match (self#findBinding name) with | BSpecifier(_, replacement) -> ( (trace "patchDebug" (dprintf "replacing pattern %s\n" name)); replacement ) | _ -> raise (BadBind ("wrong type: " ^ name)) ) | _ -> [elt] (* leave this one alone *) ) specList ) ) end else (* none of the specifiers in specList are patterns *) DoChildren end method vtypespec (tspec: typeSpecifier) : typeSpecifier visitAction = begin match tspec with | Tnamed(str) when (isPatternVar str) -> ChangeTo(Tnamed(self#vvar str)) | Tstruct(str, fields, extraAttrs) when (isPatternVar str) -> ( (trace "patchDebug" (dprintf "substituting %s\n" str)); ChangeDoChildrenPost(Tstruct((self#vvar str), fields, extraAttrs), identity) ) | Tunion(str, fields, extraAttrs) when (isPatternVar str) -> (trace "patchDebug" (dprintf "substituting %s\n" str)); ChangeDoChildrenPost(Tunion((self#vvar str), fields, extraAttrs), identity) | _ -> DoChildren end end (* why can't I have forward declarations in the language?!! *) let unifyExprFwd : (expression -> expression -> binding list) ref = ref (fun e e -> []) (* substitution for expressions *) let substExpr (bindings : binding list) (expr : expression) : expression = begin if verbose then (trace "patchDebug" (dprintf "substExpr with %d bindings\n" (List.length bindings))); (printExpr expr); (* apply the transformation *) let result = (visitCabsExpression (new substitutor bindings :> cabsVisitor) expr) in (printExpr result); result end let d_loc (_:unit) (loc: cabsloc) : doc = text loc.filename ++ chr ':' ++ num loc.lineno (* class to describe how to modify the tree when looking for places *) (* to apply expression transformers *) class exprTransformer (srcpattern : expression) (destpattern : expression) (patchline : int) (srcloc : cabsloc) = object(self) inherit nopCabsVisitor as super method vexpr (e:expression) : expression visitAction = begin (* see if the source pattern matches this subexpression *) try ( let bindings = (!unifyExprFwd srcpattern e) in (* match! *) (trace "patch" (dprintf "expr match: patch line %d, src %a\n" patchline d_loc srcloc)); ChangeTo(substExpr bindings destpattern) ) with NoMatch -> ( (* doesn't apply *) DoChildren ) end (* other constructs left unchanged *) end let unifyList (pat : 'a list) (tgt : 'a list) (unifyElement : 'a -> 'a -> binding list) : binding list = begin if verbose then (trace "patchDebug" (dprintf "unifyList (pat len %d, tgt len %d)\n" (List.length pat) (List.length tgt))); (* walk down the lists *) let rec loop pat tgt : binding list = match pat, tgt with | [], [] -> [] | (pelt :: prest), (telt :: trest) -> (unifyElement pelt telt) @ (loop prest trest) | _,_ -> ( (* no match *) if verbose then ( (trace "patchDebug" (dprintf "mismatching list length\n")); ); raise NoMatch ) in (loop pat tgt) end let gettime () : float = (Unix.times ()).Unix.tms_utime let rec applyPatch (patchFile : file) (srcFile : file) : file = begin let patch : definition list = (snd patchFile) in let srcFname : string = (fst srcFile) in let src : definition list = (snd srcFile) in (trace "patchTime" (dprintf "applyPatch start: %f\n" (gettime ()))); if (traceActive "patchDebug") then Cprint.out := stdout (* hack *) else (); (* more hackery *) unifyExprFwd := unifyExpr; (* patch a single source definition, yield transformed *) let rec patchDefn (patch : definition list) (d : definition) : definition list = begin match patch with | TRANSFORMER(srcpattern, destpattern, loc) :: rest -> ( if verbose then (trace "patchDebug" (dprintf "considering applying defn pattern at line %d to src at %a\n" loc.lineno d_loc (get_definitionloc d))); (* see if the source pattern matches the definition 'd' we have *) try ( let bindings = (unifyDefn srcpattern d) in (* we have a match! apply the substitutions *) (trace "patch" (dprintf "defn match: patch line %d, src %a\n" loc.lineno d_loc (get_definitionloc d))); (List.map (fun destElt -> (substDefn bindings destElt)) destpattern) ) with NoMatch -> ( (* no match, continue down list *) (*(trace "patch" (dprintf "no match\n"));*) (patchDefn rest d) ) ) | EXPRTRANSFORMER(srcpattern, destpattern, loc) :: rest -> ( if verbose then (trace "patchDebug" (dprintf "considering applying expr pattern at line %d to src at %a\n" loc.lineno d_loc (get_definitionloc d))); (* walk around in 'd' looking for expressions to modify *) let dList = (visitCabsDefinition ((new exprTransformer srcpattern destpattern loc.lineno (get_definitionloc d)) :> cabsVisitor) d ) in (* recursively invoke myself to try additional patches *) (* since visitCabsDefinition might return a list, I'll try my *) (* addtional patches on every yielded definition, then collapse *) (* all of them into a single list *) (List.flatten (List.map (fun d -> (patchDefn rest d)) dList)) ) | _ :: rest -> ( (* not a transformer; just keep going *) (patchDefn rest d) ) | [] -> ( (* reached the end of the patch file with no match *) [d] (* have to wrap it in a list ... *) ) end in (* transform all the definitions *) let result : definition list = (List.flatten (List.map (fun d -> (patchDefn patch d)) src)) in (*Cprint.print_defs result;*) if (traceActive "patchDebug") then ( (* avoid flush bug? yes *) Cprint.force_new_line (); Cprint.flush () ); (trace "patchTime" (dprintf "applyPatch finish: %f\n" (gettime ()))); (srcFname, result) end (* given a definition pattern 'pat', and a target concrete defintion 'tgt', *) (* determine if they can be unified; if so, return the list of bindings of *) (* unification variables in pat; otherwise raise NoMatch *) and unifyDefn (pat : definition) (tgt : definition) : binding list = begin match pat, tgt with | DECDEF((pspecifiers, pdeclarators), _), DECDEF((tspecifiers, tdeclarators), _) -> ( if verbose then (trace "patchDebug" (dprintf "unifyDefn of DECDEFs\n")); (unifySpecifiers pspecifiers tspecifiers) @ (unifyInitDeclarators pdeclarators tdeclarators) ) | TYPEDEF((pspec, pdecl), _), TYPEDEF((tspec, tdecl), _) -> ( if verbose then (trace "patchDebug" (dprintf "unifyDefn of TYPEDEFs\n")); (unifySpecifiers pspec tspec) @ (unifyDeclarators pdecl tdecl) ) | ONLYTYPEDEF(pspec, _), ONLYTYPEDEF(tspec, _) -> ( if verbose then (trace "patchDebug" (dprintf "unifyDefn of ONLYTYPEDEFs\n")); (unifySpecifiers pspec tspec) ) | _, _ -> ( if verbose then (trace "patchDebug" (dprintf "mismatching definitions\n")); raise NoMatch ) end and unifySpecifier (pat : spec_elem) (tgt : spec_elem) : binding list = begin if verbose then (trace "patchDebug" (dprintf "unifySpecifier\n")); (printSpecs [pat] [tgt]); if (pat = tgt) then [] else match pat, tgt with | SpecType(tspec1), SpecType(tspec2) -> (unifyTypeSpecifier tspec1 tspec2) | SpecPattern(name), _ -> (* record that future occurrances of @specifier(name) will yield this specifier *) if verbose then (trace "patchDebug" (dprintf "found specifier match for %s\n" name)); [BSpecifier(name, [tgt])] | _,_ -> ( (* no match *) if verbose then ( (trace "patchDebug" (dprintf "mismatching specifiers\n")); ); raise NoMatch ) end and unifySpecifiers (pat : spec_elem list) (tgt : spec_elem list) : binding list = begin if verbose then (trace "patchDebug" (dprintf "unifySpecifiers\n")); (printSpecs pat tgt); (* canonicalize the specifiers by sorting them *) let pat' = (List.stable_sort compare pat) in let tgt' = (List.stable_sort compare tgt) in (* if they are equal, they match with no further checking *) if (pat' = tgt') then [] else (* walk down the lists; don't walk the sorted lists because the *) (* pattern must always be last, if it occurs *) let rec loop pat tgt : binding list = match pat, tgt with | [], [] -> [] | [SpecPattern(name)], _ -> (* final SpecPattern matches anything which comes after *) (* record that future occurrences of @specifier(name) will yield this specifier *) if verbose then (trace "patchDebug" (dprintf "found specifier match for %s\n" name)); [BSpecifier(name, tgt)] | (pspec :: prest), (tspec :: trest) -> (unifySpecifier pspec tspec) @ (loop prest trest) | _,_ -> ( (* no match *) if verbose then ( (trace "patchDebug" (dprintf "mismatching specifier list length\n")); ); raise NoMatch ) in (loop pat tgt) end and unifyTypeSpecifier (pat: typeSpecifier) (tgt: typeSpecifier) : binding list = begin if verbose then (trace "patchDebug" (dprintf "unifyTypeSpecifier\n")); if (pat = tgt) then [] else match pat, tgt with | Tnamed(s1), Tnamed(s2) -> (unifyString s1 s2) | Tstruct(name1, None, _), Tstruct(name2, None, _) -> (unifyString name1 name2) | Tstruct(name1, Some(fields1), _), Tstruct(name2, Some(fields2), _) -> (* ignoring extraAttrs b/c we're just trying to come up with a list * of substitutions, and there's no unify_attributes function, and * I don't care at this time about checking that they are equal .. *) (unifyString name1 name2) @ (unifyList fields1 fields2 unifyField) | Tunion(name1, None, _), Tstruct(name2, None, _) -> (unifyString name1 name2) | Tunion(name1, Some(fields1), _), Tunion(name2, Some(fields2), _) -> (unifyString name1 name2) @ (unifyList fields1 fields2 unifyField) | Tenum(name1, None, _), Tenum(name2, None, _) -> (unifyString name1 name2) | Tenum(name1, Some(items1), _), Tenum(name2, Some(items2), _) -> (mustEq items1 items2); (* enum items *) (unifyString name1 name2) | TtypeofE(exp1), TtypeofE(exp2) -> (unifyExpr exp1 exp2) | TtypeofT(spec1, dtype1), TtypeofT(spec2, dtype2) -> (unifySpecifiers spec1 spec2) @ (unifyDeclType dtype1 dtype2) | _ -> ( if verbose then (trace "patchDebug" (dprintf "mismatching typeSpecifiers\n")); raise NoMatch ) end and unifyField (pat : field_group) (tgt : field_group) : binding list = begin match pat,tgt with (spec1, list1), (spec2, list2) -> ( (unifySpecifiers spec1 spec2) @ (unifyList list1 list2 unifyNameExprOpt) ) end and unifyNameExprOpt (pat : name * expression option) (tgt : name * expression option) : binding list = begin match pat,tgt with | (name1, None), (name2, None) -> (unifyName name1 name2) | (name1, Some(exp1)), (name2, Some(exp2)) -> (unifyName name1 name2) @ (unifyExpr exp1 exp2) | _,_ -> [] end and unifyName (pat : name) (tgt : name) : binding list = begin match pat,tgt with (pstr, pdtype, pattrs, ploc), (tstr, tdtype, tattrs, tloc) -> (mustEq pattrs tattrs); (unifyString pstr tstr) @ (unifyDeclType pdtype tdtype) end and unifyInitDeclarators (pat : init_name list) (tgt : init_name list) : binding list = begin (* if verbose then (trace "patchDebug" (dprintf "unifyInitDeclarators, pat %d, tgt %d\n" (List.length pat) (List.length tgt))); *) match pat, tgt with | ((pdecl, piexpr) :: prest), ((tdecl, tiexpr) :: trest) -> (unifyDeclarator pdecl tdecl) @ (unifyInitExpr piexpr tiexpr) @ (unifyInitDeclarators prest trest) | [], [] -> [] | _, _ -> ( if verbose then (trace "patchDebug" (dprintf "mismatching init declarators\n")); raise NoMatch ) end and unifyDeclarators (pat : name list) (tgt : name list) : binding list = (unifyList pat tgt unifyDeclarator) and unifyDeclarator (pat : name) (tgt : name) : binding list = begin if verbose then (trace "patchDebug" (dprintf "unifyDeclarator\n")); (printDecl pat tgt); match pat, tgt with | (pname, pdtype, pattr, ploc), (tname, tdtype, tattr, tloc) -> (mustEq pattr tattr); (unifyDeclType pdtype tdtype) @ (unifyString pname tname) end and unifyDeclType (pat : decl_type) (tgt : decl_type) : binding list = begin if verbose then (trace "patchDebug" (dprintf "unifyDeclType\n")); (printDeclType pat tgt); match pat, tgt with | JUSTBASE, JUSTBASE -> [] | PARENTYPE(pattr1, ptype, pattr2), PARENTYPE(tattr1, ttype, tattr2) -> (mustEq pattr1 tattr1); (mustEq pattr2 tattr2); (unifyDeclType ptype ttype) | ARRAY(ptype, pattr, psz), ARRAY(ttype, tattr, tsz) -> (mustEq pattr tattr); (unifyDeclType ptype ttype) @ (unifyExpr psz tsz) | PTR(pattr, ptype), PTR(tattr, ttype) -> (mustEq pattr tattr); (unifyDeclType ptype ttype) | PROTO(ptype, pformals, pva), PROTO(ttype, tformals, tva) -> (mustEq pva tva); (unifyDeclType ptype ttype) @ (unifySingleNames pformals tformals) | _ -> ( if verbose then (trace "patchDebug" (dprintf "mismatching decl_types\n")); raise NoMatch ) end and unifySingleNames (pat : single_name list) (tgt : single_name list) : binding list = begin if verbose then (trace "patchDebug" (dprintf "unifySingleNames, pat %d, tgt %d\n" (List.length pat) (List.length tgt))); match pat, tgt with | [], [] -> [] | (pspec, pdecl) :: prest, (tspec, tdecl) :: trest -> (unifySpecifiers pspec tspec) @ (unifyDeclarator pdecl tdecl) @ (unifySingleNames prest trest) | _, _ -> ( if verbose then (trace "patchDebug" (dprintf "mismatching single_name lists\n")); raise NoMatch ) end and unifyString (pat : string) (tgt : string) : binding list = begin (* equal? match with no further ado *) if (pat = tgt) then [] else (* is the pattern a variable? *) if (isPatternVar pat) then (* pat is actually "@name(blah)"; extract the 'blah' *) let varname = (extractPatternVar pat) in (* when substituted, this name becomes 'tgt' *) if verbose then (trace "patchDebug" (dprintf "found name match for %s\n" varname)); [BName(varname, tgt)] else ( if verbose then (trace "patchDebug" (dprintf "mismatching names: %s and %s\n" pat tgt)); raise NoMatch ) end and unifyExpr (pat : expression) (tgt : expression) : binding list = begin (* if they're equal, that's good enough *) if (pat = tgt) then [] else (* shorter name *) let ue = unifyExpr in (* because of the equality check above, I can omit some cases *) match pat, tgt with | UNARY(pop, pexpr), UNARY(top, texpr) -> (mustEq pop top); (ue pexpr texpr) | BINARY(pop, pexp1, pexp2), BINARY(top, texp1, texp2) -> (mustEq pop top); (ue pexp1 texp1) @ (ue pexp2 texp2) | QUESTION(p1, p2, p3), QUESTION(t1, t2, t3) -> (ue p1 t1) @ (ue p2 t2) @ (ue p3 t3) | CAST((pspec, ptype), piexpr), CAST((tspec, ttype), tiexpr) -> (mustEq ptype ttype); (unifySpecifiers pspec tspec) @ (unifyInitExpr piexpr tiexpr) | CALL(pfunc, pargs), CALL(tfunc, targs) -> (ue pfunc tfunc) @ (unifyExprs pargs targs) | COMMA(pexprs), COMMA(texprs) -> (unifyExprs pexprs texprs) | EXPR_SIZEOF(pexpr), EXPR_SIZEOF(texpr) -> (ue pexpr texpr) | TYPE_SIZEOF(pspec, ptype), TYPE_SIZEOF(tspec, ttype) -> (mustEq ptype ttype); (unifySpecifiers pspec tspec) | EXPR_ALIGNOF(pexpr), EXPR_ALIGNOF(texpr) -> (ue pexpr texpr) | TYPE_ALIGNOF(pspec, ptype), TYPE_ALIGNOF(tspec, ttype) -> (mustEq ptype ttype); (unifySpecifiers pspec tspec) | INDEX(parr, pindex), INDEX(tarr, tindex) -> (ue parr tarr) @ (ue pindex tindex) | MEMBEROF(pexpr, pfield), MEMBEROF(texpr, tfield) -> (mustEq pfield tfield); (ue pexpr texpr) | MEMBEROFPTR(pexpr, pfield), MEMBEROFPTR(texpr, tfield) -> (mustEq pfield tfield); (ue pexpr texpr) | GNU_BODY(pblock), GNU_BODY(tblock) -> (mustEq pblock tblock); [] | EXPR_PATTERN(name), _ -> (* match, and contribute binding *) if verbose then (trace "patchDebug" (dprintf "found expr match for %s\n" name)); [BExpr(name, tgt)] | a, b -> if (verbose && traceActive "patchDebug") then ( (trace "patchDebug" (dprintf "mismatching expression\n")); (printExpr a); (printExpr b) ); raise NoMatch end and unifyInitExpr (pat : init_expression) (tgt : init_expression) : binding list = begin (* Cprint.print_init_expression pat; Cprint.force_new_line (); Cprint.print_init_expression tgt; Cprint.force_new_line (); Cprint.flush (); *) match pat, tgt with | NO_INIT, NO_INIT -> [] | SINGLE_INIT(pe), SINGLE_INIT(te) -> (unifyExpr pe te) | COMPOUND_INIT(plist), COMPOUND_INIT(tlist) -> ( let rec loop plist tlist = match plist, tlist with | ((pwhat, piexpr) :: prest), ((twhat, tiexpr) :: trest) -> (mustEq pwhat twhat); (unifyInitExpr piexpr tiexpr) @ (loop prest trest) | [], [] -> [] | _, _ -> ( if verbose then (trace "patchDebug" (dprintf "mismatching compound init exprs\n")); raise NoMatch ) in (loop plist tlist) ) | _,_ -> ( if verbose then (trace "patchDebug" (dprintf "mismatching init exprs\n")); raise NoMatch ) end and unifyExprs (pat : expression list) (tgt : expression list) : binding list = (unifyList pat tgt unifyExpr) (* given the list of bindings 'b', substitute them into 'd' to yield a new definition *) and substDefn (bindings : binding list) (defn : definition) : definition = begin if verbose then (trace "patchDebug" (dprintf "substDefn with %d bindings\n" (List.length bindings))); (printDefn defn); (* apply the transformation *) match (visitCabsDefinition (new substitutor bindings :> cabsVisitor) defn) with | [d] -> d (* expect a singleton list *) | _ -> (failwith "didn't get a singleton list where I expected one") end (* end of file *)