1959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle/* 2959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Copyright (C) 2014 The Android Open Source Project 3959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * 4959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Licensed under the Apache License, Version 2.0 (the "License"); 5959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * you may not use this file except in compliance with the License. 6959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * You may obtain a copy of the License at 7959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * 8959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * http://www.apache.org/licenses/LICENSE-2.0 9959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * 10959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Unless required by applicable law or agreed to in writing, software 11959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * distributed under the License is distributed on an "AS IS" BASIS, 12959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * See the License for the specific language governing permissions and 14959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * limitations under the License. 15959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 16959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 17959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kylepackage dexfuzz.program.mutators; 18959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 19959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.Log; 20959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.MutationStats; 21959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.program.MInsn; 22959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.program.MTryBlock; 23959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.program.MutatableCode; 24959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.program.Mutation; 25959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 26959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.List; 27959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.Random; 28959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 29959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kylepublic class TryBlockShifter extends CodeMutator { 30959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle /** 31959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Every CodeMutator has an AssociatedMutation, representing the 32959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * mutation that this CodeMutator can perform, to allow separate 33959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * generateMutation() and applyMutation() phases, allowing serialization. 34959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 35959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public static class AssociatedMutation extends Mutation { 36959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public int tryIdx; 37959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public boolean shiftingTryBlock; // false => shifting handler 38959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public boolean shiftingStart; // false => shifting end (try block only) 39959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public boolean shiftingHandlerCatchall; 40959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public int shiftingHandlerIdx; 41959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public int newShiftedInsnIdx; 42959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 43959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle @Override 44959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public String getString() { 45959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle String result = String.format("%d %s %s %s %d %d", 46959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle tryIdx, 47959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle (shiftingTryBlock ? "T" : "F"), 48959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle (shiftingStart ? "T" : "F"), 49959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle (shiftingHandlerCatchall ? "T" : "F"), 50959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle shiftingHandlerIdx, 51959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle newShiftedInsnIdx 52959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle ); 53959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return result; 54959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 55959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 56959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle @Override 57959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public void parseString(String[] elements) { 58959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle tryIdx = Integer.parseInt(elements[2]); 59959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle shiftingTryBlock = elements[3].equals("T"); 60959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle shiftingStart = elements[4].equals("T"); 61959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle shiftingHandlerCatchall = elements[5].equals("T"); 62959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle shiftingHandlerIdx = Integer.parseInt(elements[6]); 63959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle newShiftedInsnIdx = Integer.parseInt(elements[7]); 64959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 65959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 66959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 67959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // The following two methods are here for the benefit of MutationSerializer, 68959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // so it can create a CodeMutator and get the correct associated Mutation, as it 69959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // reads in mutations from a dump of mutations. 70959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle @Override 71959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public Mutation getNewMutation() { 72959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return new AssociatedMutation(); 73959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 74959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 75959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public TryBlockShifter() { } 76959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 77959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public TryBlockShifter(Random rng, MutationStats stats, List<Mutation> mutations) { 78959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle super(rng, stats, mutations); 79959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle likelihood = 40; 80959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 81959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 82959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle @Override 83959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle protected boolean canMutate(MutatableCode mutatableCode) { 84959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mutatableCode.triesSize > 0) { 85959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return true; 86959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 87959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 88959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.debug("Method contains no tries."); 89959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return false; 90959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 91959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 92959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle @Override 93959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle protected Mutation generateMutation(MutatableCode mutatableCode) { 94959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Pick a random try. 95959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int tryIdx = rng.nextInt(mutatableCode.triesSize); 96959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MTryBlock tryBlock = mutatableCode.mutatableTries.get(tryIdx); 97959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 98959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle boolean shiftingTryBlock = rng.nextBoolean(); 99959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle boolean shiftingStart = false; 100959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle boolean shiftingHandlerCatchall = false; 101959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int shiftingHandlerIdx = -1; 102959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (shiftingTryBlock) { 103959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // We're shifting the boundaries of the try block 104959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // determine if we shift the start or the end. 105959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle shiftingStart = rng.nextBoolean(); 106959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else { 107959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // We're shifting the start of a handler of the try block. 108959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (tryBlock.handlers.isEmpty()) { 109959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // No handlers, so we MUST mutate the catchall 110959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle shiftingHandlerCatchall = true; 111959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (tryBlock.catchAllHandler != null) { 112959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // There is a catchall handler, so potentially mutate it. 113959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle shiftingHandlerCatchall = rng.nextBoolean(); 114959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 115959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // If we're not going to shift the catchall handler, then 116959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // pick an explicit handler to shift. 117959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (!shiftingHandlerCatchall) { 118959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle shiftingHandlerIdx = rng.nextInt(tryBlock.handlers.size()); 119959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 120959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 121959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 122959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Get the original instruction wherever we're shifting. 123959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn oldInsn = null; 124959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (shiftingTryBlock && shiftingStart) { 125959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle oldInsn = tryBlock.startInsn; 126959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (shiftingTryBlock && !(shiftingStart)) { 127959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle oldInsn = tryBlock.endInsn; 128959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (!(shiftingTryBlock) && shiftingHandlerCatchall) { 129959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle oldInsn = tryBlock.catchAllHandler; 130959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (!(shiftingTryBlock) && !(shiftingHandlerCatchall) 131959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle && (shiftingHandlerIdx != -1)) { 132959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle oldInsn = tryBlock.handlers.get(shiftingHandlerIdx); 133959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else { 134959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit("Faulty logic in TryBlockShifter!"); 135959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 136959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 137959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Find the index of this instruction. 138959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int oldInsnIdx = mutatableCode.getInstructionIndex(oldInsn); 139959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 140959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int newInsnIdx = oldInsnIdx; 141959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 142959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int delta = 0; 143959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 144959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Keep searching for a new index. 145959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle while (newInsnIdx == oldInsnIdx) { 146959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Vary by +/- 2 instructions. 147959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle delta = 0; 148959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle while (delta == 0) { 149959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle delta = (rng.nextInt(5) - 2); 150959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 151959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 152959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle newInsnIdx = oldInsnIdx + delta; 153959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 154959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Check the new index is legal. 155959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (newInsnIdx < 0) { 156959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle newInsnIdx = 0; 157959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (newInsnIdx >= mutatableCode.getInstructionCount()) { 158959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle newInsnIdx = mutatableCode.getInstructionCount() - 1; 159959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 160959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 161959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 162959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle AssociatedMutation mutation = new AssociatedMutation(); 163959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutation.setup(this.getClass(), mutatableCode); 164959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutation.tryIdx = tryIdx; 165959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutation.shiftingTryBlock = shiftingTryBlock; 166959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutation.shiftingStart = shiftingStart; 167959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutation.shiftingHandlerCatchall = shiftingHandlerCatchall; 168959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutation.shiftingHandlerIdx = shiftingHandlerIdx; 169959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutation.newShiftedInsnIdx = newInsnIdx; 170959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return mutation; 171959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 172959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 173959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle @Override 174959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle protected void applyMutation(Mutation uncastMutation) { 175959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Cast the Mutation to our AssociatedMutation, so we can access its fields. 176959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle AssociatedMutation mutation = (AssociatedMutation) uncastMutation; 177959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MutatableCode mutatableCode = mutation.mutatableCode; 178959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 179959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MTryBlock tryBlock = mutatableCode.mutatableTries.get(mutation.tryIdx); 180959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 181959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn newInsn = 182959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.getInstructionAt(mutation.newShiftedInsnIdx); 183959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 184959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Find the right mutatable instruction in try block, and point it at the new instruction. 185959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mutation.shiftingTryBlock && mutation.shiftingStart) { 186959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle tryBlock.startInsn = newInsn; 187959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.info("Shifted the start of try block #" + mutation.tryIdx 188959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle + " to be at " + newInsn); 189959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (mutation.shiftingTryBlock && !(mutation.shiftingStart)) { 190959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle tryBlock.endInsn = newInsn; 191959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.info("Shifted the end of try block #" + mutation.tryIdx 192959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle + " to be at " + newInsn); 193959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (!(mutation.shiftingTryBlock) && mutation.shiftingHandlerCatchall) { 194959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle tryBlock.catchAllHandler = newInsn; 195959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.info("Shifted the catch all handler of try block #" + mutation.tryIdx 196959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle + " to be at " + newInsn); 197959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (!(mutation.shiftingTryBlock) && !(mutation.shiftingHandlerCatchall) 198959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle && (mutation.shiftingHandlerIdx != -1)) { 199959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle tryBlock.handlers.set(mutation.shiftingHandlerIdx, newInsn); 200959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.info("Shifted handler #" + mutation.shiftingHandlerIdx 201959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle + " of try block #" + mutation.tryIdx + " to be at " + newInsn); 202959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else { 203959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit("faulty logic in TryBlockShifter"); 204959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 205959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 206959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle stats.incrementStat("Shifted boundary in a try block"); 207959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 208959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle} 209