header {* \isaheader{Jinja VCG} *} theory JBC_VCG = JBC_SafetyLogic + VCG_Upgrades: section {* Control Flow Graph *} consts xcpt_cond::"jbc_prog \ cname \ pos \ expr" defs xcpt_cond_def: "xcpt_cond \ X p \ (case (cmd \ p) of None \ TT | Some c \ (case c of New C \ Eq (NewA 0) (Cn Null) | Getfield F C \ Eq (St 0) (Cn Null) | Putfield F C \ Eq (St 1) (Cn Null) | Checkcast C \ And ((Neg (Eq (St 0) (Cn Null)))#(map (\ Cl. Neg (Ty (St 0) (Class Cl))) [Cl \ (classnames (fst \)). (fst \) \ Cl \\<^sup>* C])) | Invoke M n \ Eq (St n) (Cn Null) | Throw \ (if X = NullPointer then Neg (And [Neg (Eq (St 0) (Cn Null)), Neg (Ty (St 0) (Class X))]) else Ty (St 0) (Class X)) | _ \ TT ))" consts succsInvoke::"(jbc_prog \ mname \ nat \ pos) \ (pos \ expr) list" recdef succsInvoke "{}" "succsInvoke (\,M,n,p) = (case anF \ p of None \ [] | Some A \ concat (map (\ tp. (case tp of Void \ [] | Boolean \ [] | Integer \ [] | NT \ [] | Class X \ [((fst (method (fst \) X M),M,0),And [Neg (xcpt_cond \ NullPointer p), Ty (St n) (Class X)])])) (extractTy (A,St n))))" constdefs succsNormal::"jbc_prog \ pos \ (pos \ expr) list" "succsNormal \ p \ (case cmd \ p of None \ [] | Some c \ (case c of Load n \ [(incA p,TT)] | Store n \ [(incA p,TT)] | Push v \ [(incA p,TT)] | New C \ [(incA p,Neg (xcpt_cond \ OutOfMemory p))] | Getfield F C \ [(incA p,Neg (xcpt_cond \ NullPointer p))] | Putfield F C \ [(incA p,Neg (xcpt_cond \ NullPointer p))] | Checkcast C \ [(incA p,Neg (xcpt_cond \ ClassCast p))] | Invoke M n \ succsInvoke (\,M,n,p) | Return \ map (\ p'. (incA p',Call (And [aF \ p',Pos p']))) (callers \ p) | Pop \ [(incA p,TT)] | IBin no \ [(incA p,TT)] | Goto t \ let (C,M,n)=p in [((C,M,nat ((int n)+t)),TT)] | CmpEq \ [(incA p,TT)] | IfIntCmp ro t \ let (C,M,n)=p in [((C,M,nat ((int n)+t)),Rel (St 1) ro (St 0)), (incA p, Neg (Rel (St 1) ro (St 0)))] | IfFalse t \ let (C,M,n)=p in [((C,M,nat ((int n)+t)),Eq (St 0) (Cn (Bool False))), (incA p, Neg (Eq (St 0) (Cn (Bool False))))] | Throw \ [] ))" consts match_ex_table_e :: "'m prog \ cname \ pc \ ex_table \ ex_entry option" primrec "match_ex_table_e P C pc [] = None" "match_ex_table_e P C pc (e#es) = (if matches_ex_entry P C pc e then Some e else match_ex_table_e P C pc es)" lemma match_ex_table_e_sim: "(match_ex_table_e P C pc et = Some e) \ (match_ex_table P C pc et = Some (snd (snd (snd e))))" proof (induct et) assume A: "(match_ex_table_e P C pc [] = Some e)" from A show "(match_ex_table P C pc [] = Some (snd (snd (snd e))))" by simp next fix a et assume hyp: "(match_ex_table_e P C pc et = Some e) \ (match_ex_table P C pc et = Some (snd (snd (snd e))))" assume A: "match_ex_table_e P C pc (a # et) = Some e" show "match_ex_table P C pc (a#et) = Some (snd (snd (snd e)))" proof (cases "matches_ex_entry P C pc a") case True from True A show ?thesis by simp next case False from False A hyp show ?thesis by simp qed qed lemma match_ex_table_e_sim2: "match_ex_table P C pc et = Some pc_h \ (\ e.(match_ex_table_e P C pc et = Some e) \ snd (snd (snd e)) = pc_h) " proof (induct et) assume A: "(match_ex_table P C pc [] = Some pc_h)" from A show "\ e. match_ex_table_e P C pc [] = Some e \ snd (snd (snd e)) = pc_h " by simp next fix a et assume hyp: "(match_ex_table P C pc et = Some pc_h) \ (\ e. match_ex_table_e P C pc et = Some e \ snd (snd (snd e)) = pc_h)" assume A: "match_ex_table P C pc (a # et) = Some pc_h" show "(\ e. match_ex_table_e P C pc (a#et) = Some e \ snd (snd (snd e)) = pc_h)" proof (cases "matches_ex_entry P C pc a") case True from True A show ?thesis by simp next case False from False A hyp show ?thesis by simp qed qed consts match_ex_table::"'m prog \ cname \ pc \ ex_table \ pc option" defs match_ex_table_def [simp]: "match_ex_table P C pc et == (case (JVMExceptions.match_ex_table P C pc et) of None \ None | Some h \ Some (fst h))" consts succsXpt::"(jbc_prog \ cname \ pos list) \ (pos \ expr) list" recdef succsXpt "measure (\(\,X,ps). length (domC \) - (length ps))" "succsXpt ((P,An),X,ps) = (case length (domC (P,An)) \ length ps of True \ map (\p. (p,TT)) (domC (P,An)) | False \ (case ps of [] \ map (\p. (p,TT)) (domC (P,An)) | p#pss \ let (C,M,pc)=p; et = ex_table_of P C M; A=aF (P,An) p in (case match_ex_table_e P X pc et of None \ concat (map (\p'. succsXpt ((P,An),X,p'#ps)) (callers (P,An) p)) | Some e \ (let (f,t,X',pc',d) = e in [((C,M,pc'),And ((if pss=[] then [] else [Catch X' A,Catch X (Pos p)])@ [xcpt_cond (P,An) X (last ps)]))]))))" constdefs succsExcept::"jbc_prog \ pos \ (pos \ expr) list" "succsExcept \ p \ (case cmd \ p of None \ [] | Some c \ (case c of Load n \ [] | Store n \ [] | Push v \ [] | New C \ succsXpt (\,OutOfMemory,[p]) | Getfield F C \ succsXpt (\,NullPointer,[p]) | Putfield F C \ succsXpt (\,NullPointer,[p]) | Checkcast C \ succsXpt (\,ClassCast,[p]) | Invoke M n \ succsXpt (\,NullPointer,[p]) | Return \ [] | Pop \ [] | IBin no \ [] | Goto t \ [] | CmpEq \ [] | IfIntCmp ro t \ [] | IfFalse t \ [] | Throw \ succsXpt (\,NullPointer,[p]) @ (case anF \ p of None \ [] | Some a \ concat (map (\ tp. (case tp of Void \ [] | Boolean \ [] | Integer \ [] | NT \ [] | Class X \ succsXpt (\,X,[p]))) (extractTy (a,St 0)))) ))" constdefs addPos::"pos \ ((pos \ expr) list) \ (pos \ expr) list" "addPos p es \ (map (\ (p',B). (p',And [Pos p,B])) es)" constdefs succsF::"jbc_prog \ pos \ (pos \ expr) list" "succsF \ p \ addPos p (succsNormal \ p @ succsExcept \ p)" section {* Static Semantics *} constdefs handlesEx'::"jvm_prog \ pos \ cname list" "handlesEx' P p \ remdups' (concat (map (\(C,(S,Fs,Ms)). concat (map (\(M,Ts,T,(mxs,mxl,is,et)). concat (map (\(b,e,cn,h,d). if p=(C,M,h) then [cn] else []) et)) Ms)) P))" constdefs handlesEx::"jvm_prog \ pos \ cname option" "handlesEx P p \ (case handlesEx' P p of [] \ None | cn#cns \ Some cn)" constdefs catchesEx::"jvm_prog \ cname \ pos \ bool" "catchesEx P X p \ (let (C,M,pc) = p; m = match_ex_table P X pc (ex_table_of P C M) in (case m of None \ False | Some pc' \ True))" constdefs sys_xcpt_of :: "instr \ cname" "sys_xcpt_of i \ (case i of New C \ OutOfMemory | Getfield F C \ NullPointer | Putfield F C \ NullPointer | Checkcast C \ ClassCast | Invoke M n \ NullPointer | Throw \ NullPointer | _ \ Exception)" text {* \clearpage *} constdefs wpF::"jbc_prog \ pos \ pos \ expr \ expr" "wpF \ p p' Q \ (let pm=map (\q. (Pos q,if q=p' then Pos p else FF)) (getPosEx Q) in (case cmd \ p of None \ FF | Some ins \ (case handlesEx (fst \) p' of None \ (case ins of Load n \ substE (pm@(map (\k. (St k,if k=0 then Rg n else St (k - 1))) (stkIds Q))) Q | Store n \ substE (pm@((Rg n,St 0)# map (\k. (St k, St (k+1))) (stkIds Q))) Q | Push v \ substE (pm@(map (\k. (St k,if k=0 then Cn v else St (k - 1))) (stkIds Q))) Q | New Cl \ (let em=(pm@(map (\k. (St k,if k=0 then NewA 0 else St (k - 1))) (stkIds Q))@ (map (\n. (NewA n, NewA (n+1))) (getNewEx Q))); gfe'=foldl (\mp hex. (case hex of GF F C ex \ (let ex'=substE mp ex in (Gf F C ex,IF ex' \ NewA 0 THEN Cn (the ((snd (blank (fst \) Cl))(F,C))) ELSE Gf F C ex')) | TY ex ty \ (let ex'=substE mp ex in (Ty ex ty,IF ex' \ NewA 0 THEN Cn (Bool ((Class Cl) = ty)) ELSE Ty ex' ty)))#mp) em (remdups' (getHeapEx Q)) in substE gfe' Q) | Getfield F C \ substE (pm@[(St 0,Gf F C (St 0))]) Q | Putfield F C \ (let em=pm@(map (\k. (St k,St (k+2))) (stkIds Q)); gfe'=foldl (\mp ex. let ex'=substE mp ex in (Gf F C ex,IF (ex' \ St 1) THEN St 0 ELSE Gf F C ex')#mp) em (remdups' (getGfEx F C Q)) in substE gfe' Q) | Checkcast C \ substE pm Q | Invoke M n \ substE (pm@(FrNr,FrNr \ (Cn (Intg 1)))# (map (\k. (Rg k,if k \ n then St (n-k) else (if k \ n + fst (snd (snd (snd (snd (method (fst \) (fst p') M))))) then Cn arb else none))) (rgIds Q))@ (map (\k. (St k,none)) (stkIds Q))@ (map (\ex. (Call ex,ex)) (getCallEx Q))@ (concat (map (\(cn',ex'). (if catchesEx (fst \) cn' p then [(Catch cn' ex',ex')] else [(Catch cn' ex', IF (FrNr \ Cn (Intg 1)) THEN ex' ELSE Catch cn' ex')])) (getCatchEx Q)))) Q | Return \ let (C,M,pc)=p; (P,An)=\; n = length (fst (snd (method P C M))) in substE (pm@(FrNr,FrNr \ (Cn (Intg 1)))# (map (\k. (St k,if 1 \ k then Call (St (n+k)) else St 0)) (stkIds Q))@ (map (\k. (Rg k,Call (Rg k))) (rgIds Q))@ (map (\ex. (Call ex,Call (Call ex))) (getCallEx Q))@ (map (\(cn',ex'). (Catch cn' ex',Call (Catch cn' ex'))) (getCatchEx Q))) Q | Pop \ substE (pm@(map (\k. (St k,St (k+1))) (stkIds Q))) Q | IBin no \ substE (pm@(map (\k. (St k,if k=0 then Num (St 1) no (St 0) else (St (k+1)))) (stkIds Q))) Q | Goto t \ substE pm Q | CmpEq \ substE (pm@(map (\k. (St k,if k=0 then (St 0)\(St 1) else (St (k+1)))) (stkIds Q))) Q | IfIntCmp ro t \ substE (pm@(map (\k. (St k,St (k+2))) (stkIds Q))) Q | IfFalse t \ substE (pm@(map (\k. (St k,St (k+1))) (stkIds Q))) Q | Throw \ FF ) | Some cn \ (let mp=pm@(map (\k. (St k,if 1\k then none else (if (ins = Throw) then (IF St 0 \ Cn (Null) THEN (Cn (Addr (addr_of_sys_xcpt NullPointer))) ELSE St 0) else Cn (Addr (addr_of_sys_xcpt (sys_xcpt_of ins)))))) (stkIds Q))@ (let (C,M,pc)=p; (C',M',pc')=p'; (P,An)=\ in (if match_ex_table P cn pc (ex_table_of P C M) = Some pc' then [] else let rgm=map (\k. (Rg k,Catch cn (Rg k))) (rgIds Q); om =map (\ex. (Call ex,Catch cn (Call ex))) (getCallEx Q); cm= map (\(cn',ex'). (Catch cn' ex', Catch cn (Catch cn' ex'))) (getCatchEx Q) in (FrNr,Catch cn FrNr)#rgm@om@cm)) in substE mp Q))))" section {* Welformedness *} constdefs pTy::"jvm_prog \ ty\<^isub>P" "pTy P \ (map_of2 (convert_pt (prog_kil P)))" consts throwChk::"jbc_prog \ instr option \ expr option \ pos \ bool" --{* Throw instructions require a type annotation (disjunction of Ty (St 0) (Class X)). The successor function uses these to find proper handlers. The initial intruction must not be Throw. In typesafe programs (no exception on top of initial stack) this cannot happen anyway. *} defs throwChk_def: "throwChk \ \ (\,ins,an,p). (case ins of None \ True | Some c \ (case c of Throw \ (case an of None \ False | Some A \ (if p=ipc \ then False else (case extractTy (A,St 0) of [] \ False | ty#tys \ (list_all (\ tp. (case tp of Void \ False | Boolean \ False | Integer \ False | NT \ False | Class X \ True)) (ty#tys))))) | _ \ True))" lemma throwChk_Throw_A [simp]: "throwChk (\, Some Throw, Some A, p) = (if p=ipc \ then False else (case extractTy (A,St 0) of [] \ False | ty#tys \ (list_all (\ tp. (case tp of Void \ False | Boolean \ False | Integer \ False | NT \ False | Class X \ True)) (ty#tys))))" (*<*) by (simp add: throwChk_def) (*>*) lemma throwChk_Throw_None [simp]: "throwChk (\,Some Throw,None,p) = False" (*<*) by (simp add: throwChk_def) (*>*) lemma throwChk_oth_None [simp]: "ins \ Some Throw \ throwChk (\,ins,None,p) = True" (*<*) apply (simp add: throwChk_def) apply (rule allI) apply (case_tac "a") apply simp+ done (*>*) lemma throwChk_oth_Some [simp]: "ins \ Some Throw \ throwChk (\,ins,Some A,p) = True" (*<*) apply (simp add: throwChk_def) apply (rule allI) apply (case_tac "a") apply simp+ done (*>*) consts invokeChk::"jbc_prog \ (instr option) \ (expr option) \ pos \ bool" --{* Invoke instructions require a type annotation (disjunction of Ty (St n) (Class X)). The successor function uses these to determine the potential method entry points. In addition the first instruction must not be Invoke. In typesafe programs this cannot happen anyway (no object reference on top of initial stack). We also forbid recursive calls of the main method (M ~= fst (snd (ipc Pi))). This ensures that the frame stack never becomes empty (A Return in method main stops execution). *} defs invokeChk_def: "invokeChk \ \ (\,ins,an,p). (case ins of None \ True | Some c \ case c of Invoke M n \ (case an of None \ False | Some A \ (if p=ipc \ then False else (case extractTy (A,St n) of [] \ False | ty#tys \ (list_all (\ tp. (case tp of Void \ False | Boolean \ False | Integer \ False | NT \ True | Class X \ has_method (fst \) X M)) (ty#tys)) \ M \ fst (snd (ipc \))))) | _ \ True)" lemma invokeChk_Invoke_A [simp]: "invokeChk (\,Some (Invoke M n),Some A,p) = (if p=ipc \ then False else (case extractTy (A,St n) of [] \ False | ty#tys \ (list_all (\ tp. (case tp of Void \ False | Boolean \ False | Integer \ False | NT \ True | Class X \ has_method (fst \) X M)) (ty#tys)) \ M \ fst (snd (ipc \))))" (*<*) by (simp add: invokeChk_def) (*>*) lemma invokeChk_Invoke_None [simp]: "invokeChk (\,Some (Invoke M n),None,p) = False" (*<*) by (simp add: invokeChk_def) (*>*) lemma invokeChk_oth_None [simp]: "(\ M n. ins \ Some (Invoke M n)) \ invokeChk (\,ins,None,p) = True" (*<*) apply (simp add: invokeChk_def) apply (rule allI) apply (case_tac "a") apply simp+ done (*>*) lemma invokeChk_oth_Some [simp]: "(\ M n. ins \ Some (Invoke M n)) \ invokeChk (\,ins,Some A,p) = True" (*<*) apply (simp add: invokeChk_def) apply (rule allI) apply (case_tac "a") apply simp+ done (*>*) consts checkPos :: "jbc_prog \ (pos list) \ bool" --{* checkPos ensures that targets of backward jumps (idx (domC Pi) p' <= idx (domC Pi) p) are annotated. This ensures termination of the generic VCG. In addition all successors must be in the code range (p' mem (domC Pi)) and successors from the normal successor function must not be entry points of handlers. Throw and Invoke instructions are checked for the extra requirements described above. *} primrec "checkPos \ [] = True" "checkPos \ (p#ps) = (if (let scsn = map fst (succsNormal \ p); scse = map fst (succsExcept \ p) in list_all (\p'. ((idx (domC \) p' \ idx (domC \) p) \ anF \ p' \ None) \ p' mem (domC \) \ (p' mem scsn \ handlesEx (fst \) p' = None) \ p'\ ipc \) (scsn @ scse) \ (set scse \ set (domC \)) ) \ throwChk (\,cmd \ p,anF \ p,p) \ invokeChk (\,cmd \ p,anF \ p,p) then (checkPos \ ps) else False)" lemma checkPos_split: "checkPos \ (l1@l2) = ((checkPos \ l1) \ (checkPos \ l2))" (*<*) by (induct_tac l1,simp+) (*>*) constdefs checkExTables ::"jbc_prog \ bool" (* "checkExTables \ \ list_all (\x. x) (concat (map (\(C,(S,Fs,Ms)). concat (map (\(M,Ts,T,(mxs,mxl,is,et)). (map (\(b,e,cn,h,d). d = 0 \ cn \ Object \ (C,M,h) \ set (domC \) \ remdups' (concat (map (\(b, e, cn, h',d). if h' = h then [cn] else []) et)) = [cn]) et)) Ms)) (fst \)))" *) "checkExTables \ \ list_all (\x. x) (concat (map (\(C,(S,Fs,Ms)). concat (map (\(M,Ts,T,(mxs,mxl,is,et)). (map (\(b,e,cn,h,d). d = 0 \ (C,M,h) \ set (domC \) \ remdups' (concat (map (\(b, e, cn, h',d). if h' = h then [cn] else []) et)) = [cn]) et)) Ms)) (fst \)))" --{* Programs are wellformed iff (1) all positions are wellformed (checkPos). That is - targets of backward jumps are annotated (enforces VCG termination). - Throw and Invoke instructions have type annotations (possibly inserted by the bytecode verifier). - the main method is not invoked (only automatically at start up). (2) all exception tables are wellformed. That is - remaining stack height d is 0 (no catch inside expressions). - the catching class is not Object. - handler entry points are within the code range. - handler entry points are distinct for each handler. (3) all classes and methods have distinct names. (4) the programs containts all system classes (object, exceptions). (5) the intial position is in the code range. (6) the main method has no arguments (type safety theorems from BV require this). *} constdefs wf :: "jbc_prog \ bool" "wf \ \ checkPos \ (domC \) \ checkExTables \ \ distinct (classnames (fst \)) \ distinct (methodnames (fst \)) \ (\cdl. fst \ = (SystemClasses @ cdl)) \ (ipc \ \ set (domC \)) \ wf_jvm_prog_phi (pTy (fst \)) (fst \) \ fst (snd (method (fst \) (fst (ipc \)) (fst (snd (ipc \))))) = []" section {* System Invariants *} text {* The following functions yield formulas that hold for all states reachable in wellformed programs *} subsection {* Position information *} constdefs inv_Pos::"jbc_prog \ pos \ expr" "inv_Pos \ p \ Pos p" subsection {* Frame Stack Size *} constdefs inv_FrNr:: "jbc_prog \ pos \ expr" "inv_FrNr \ \ (\(C,M,pc). if (let (C0,M0,pc0) = ipc \ in C=C0 \ M=M0) then Eq FrNr (Cn (Intg 1)) else (Rel (Cn (Intg 1)) Less FrNr))" subsection {* System Exception Types *} constdefs inv_ExTys::"jbc_prog \ pos \ expr" "inv_ExTys \ p \ And [Ty (Cn (Addr (addr_of_sys_xcpt NullPointer))) (Class NullPointer), Ty (Cn (Addr (addr_of_sys_xcpt ClassCast))) (Class ClassCast), Ty (Cn (Addr (addr_of_sys_xcpt OutOfMemory))) (Class OutOfMemory)]" subsection {* Bytecode Verifier Types *} constdefs conv_st::"jvm_prog \ ty\<^isub>i \ expr" "conv_st P \ (\ (st,rt). (let ex_st = map (\ n. STy P (St n) (st!n)) (upt 0 (length st)); ex_rt = map (\ n. (case rt!n of Err \ not_none (Rg n) | OK tp \ STy P (Rg n) tp)) (upt 0 (length rt)) in (And (ex_st @ ex_rt))))" constdefs annotate_types::"jvm_prog \ ((cname \ mname) \ (ty\<^isub>i' list)) list \ (pos \ expr)" "annotate_types P pt \ (\ (C,M,pc). (if (C,M) mem (methodnames P) then (case pt ? (C,M) of None \ FF | Some mt \ (if pc < length mt then (case mt ! pc of None \ FF | Some tyi \ conv_st P tyi) else TT)) else TT))" constdefs inv_Ty::"jbc_prog \ pos \ expr" "inv_Ty \ p \ annotate_types (fst \) (convert_pt (prog_kil (fst \))) p" section {* Instantiating the VCG *} constdefs vcg_jbc :: "jbc_prog \ expr" "vcg_jbc prg \ vcG And Imp FF ipc initF safeF succsF wpF domC domA anF prg" section {* Upgrade the VCG *} text {* Here we upgrade the VCG by instantiating it with successor functions that add invariants to branch conditions. *} constdefs upg::"(jbc_prog \ pos \ expr) \ (jbc_prog \ pos \ (pos \ expr) list) \ (jbc_prog \ pos \ (pos \ expr) list)" "upg iF sucF \ (\ \ p. map (\ (p',B). (p', And [B, iF \ p])) (sucF \ p))" subsection {* Upgrade Frame Stack Size *} constdefs succsFrNrF:: "jbc_prog \ pos \ (pos \ expr) list" "succsFrNrF \ upg inv_FrNr succsF" constdefs vcgFrNr :: "jbc_prog \ expr" "vcgFrNr prg \ vcG And Imp FF ipc initF safeF succsFrNrF wpF domC domA anF prg" subsection {* Upgrade System Exception Types. *} constdefs succsExTysF:: "jbc_prog \ pos \ (pos \ expr) list" "succsExTysF \ upg inv_ExTys succsFrNrF" constdefs vcgExTys :: "jbc_prog \ expr" "vcgExTys prg \ vcG And Imp FF ipc initF safeF succsExTysF wpF domC domA anF prg" subsection {* Upgrade Types *} constdefs succsTyF:: "jbc_prog \ pos \ (pos \ expr) list" "succsTyF \ upg inv_Ty succsExTysF" constdefs vcgTy :: "jbc_prog \ expr" "vcgTy \ \ (vcG And Imp FF ipc initF safeF succsTyF wpF domC domA anF \)" end