content: # Copyright 2015 Google Inc. All Rights Reserved.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  import struct
  
  from .. import ir, error
  from ..jvmops import *
  from . import options
  
  def _calcMinimumPositions(instrs):
      posd = {}
      pos = 0
      for ins in instrs:
          posd[ins] = pos
          if isinstance(ins, ir.LazyJumpBase):
              pos += ins.min
          elif isinstance(ins, ir.Switch):
              pad = (-pos-1) % 4
              pos += pad + ins.nopad_size
          else:
              pos += len(ins.bytecode)
      return posd, pos
  
  def optimizeJumps(irdata):
      # For jump offsets of more than +-32767, a longer form of the jump instruction
      # is required. This function finds the optimal jump widths by optimistically
      # starting with everything narrow and then iteratively marking instructions
      # as wide if their offset is too large (in rare cases, this can in turn cause
      # other jumps to become wide, hence iterating until convergence)
      instrs = irdata.flat_instructions
      jump_instrs = [ins for ins in instrs if isinstance(ins, ir.LazyJumpBase)]
  
      while 1:
          done = True
          posd, _ = _calcMinimumPositions(instrs)
  
          for ins in jump_instrs:
              if ins.min < ins.max and ins.widenIfNecessary(irdata.labels, posd):
                  done = False
          if done:
              break
  
      for ins in jump_instrs:
          assert ins.min <= ins.max
          ins.max = ins.min
  
  def createBytecode(irdata, opts):
      instrs = irdata.flat_instructions
      posd, end_pos = _calcMinimumPositions(instrs)
  
      bytecode = bytearray()
      for ins in instrs:
          if isinstance(ins, (ir.LazyJumpBase, ir.Switch)):
              ins.calcBytecode(posd, irdata.labels)
          bytecode += ins.bytecode
      assert len(bytecode) == end_pos
  
  
      if len(bytecode) > 65535:
          # If code is too long and optimization is off, raise exception so we can
          # retry with optimization. If it is still too long with optimization,
          # don't raise an error, since a class with illegally long code is better
          # than no output at all.
          if opts is not options.ALL:
              raise error.ClassfileLimitExceeded()
  
  
      prev_instr_map = dict(zip(instrs[1:], instrs))
      packed_excepts = []
      for s, e, h, c in irdata.excepts:
          # There appears to be a bug in the JVM where in rare cases, it throws
          # the exception at the address of the instruction _before_ the instruction
          # that actually caused the exception, triggering the wrong handler
          # therefore we include the previous (IR) instruction too
          # Note that this cannot cause an overlap because in that case the previous
          # instruction would just be a label and hence not change anything
          s = prev_instr_map.get(s, s)
  
          s_off = posd[s]
          e_off = posd[e]
          h_off = posd[h]
          assert s_off <= e_off
          if s_off < e_off:
              packed_excepts.append(struct.pack('>HHHH', s_off, e_off, h_off, c))
          else:
              print('Skipping zero width exception!')
              assert 0
  
      return bytes(bytecode), packed_excepts
  