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.
  
  from .. import flags
  from ..byteio import Writer
  from . import constantpool, writebytecode, error
  from .optimization import options
  
  def writeField(pool, stream, field):
      stream.u16(field.access & flags.FIELD_FLAGS)
      stream.u16(pool.utf8(field.id.name))
      stream.u16(pool.utf8(field.id.desc))
      if field.constant_value is not None:
          stream.u16(1)
          stream.u16(pool.utf8(b"ConstantValue"))
          stream.u32(2)
  
          ctype, val = field.constant_value
          # Ignore dalvik constant type and use actual field type instead
          index = {
              b'Z': pool.int,
              b'B': pool.int,
              b'S': pool.int,
              b'C': pool.int,
              b'I': pool.int,
              b'F': pool.float,
              b'J': pool.long,
              b'D': pool.double,
              b'Ljava/lang/String;': pool.string,
              b'Ljava/lang/Class;': pool.class_,
          }[field.id.desc](val)
          stream.u16(index)
      else:
          stream.u16(0) # no attributes
  
  def writeMethod(pool, stream, method, code_attr_data):
      stream.u16(method.access & flags.METHOD_FLAGS)
      stream.u16(pool.utf8(method.id.name))
      stream.u16(pool.utf8(method.id.desc))
  
      if code_attr_data is not None:
          code_attr_data = code_attr_data.toBytes()
          stream.u16(1)
          stream.u16(pool.utf8(b"Code"))
          stream.u32(len(code_attr_data))
          stream.write(code_attr_data)
      else:
          stream.u16(0) # no attributes
  
  def writeMethods(pool, stream, methods, opts):
      code_irs = []
      for method in methods:
          code_irs.append(writebytecode.getCodeIR(pool, method, opts=opts))
      code_attrs = writebytecode.finishCodeAttrs(pool, code_irs, opts=opts)
  
      stream.u16(len(methods))
      for method in methods:
          writeMethod(pool, stream, method, code_attrs.get(method))
  
  def classFileAfterPool(cls, opts):
      stream = Writer()
      if opts.split_pool:
          pool = constantpool.SplitConstantPool()
      else:
          pool = constantpool.SimpleConstantPool()
  
      cls.parseData()
      access = cls.access & flags.CLASS_FLAGS
      if not access & flags.ACC_INTERFACE:
          # Not necessary for correctness, but this works around a bug in dx
          access |= flags.ACC_SUPER
  
      stream.u16(access) # access
      stream.u16(pool.class_(cls.name)) # this
      super_ = pool.class_(cls.super) if cls.super is not None else 0
      stream.u16(super_) # super
  
      # interfaces
      stream.u16(len(cls.interfaces))
      for interface in cls.interfaces:
          stream.u16(pool.class_(interface))
  
      # fields
      stream.u16(len(cls.data.fields))
      for field in cls.data.fields:
          writeField(pool, stream, field)
  
      # methods
      writeMethods(pool, stream, cls.data.methods, opts=opts)
  
      # attributes
      stream.u16(0)
      return pool, stream
  
  def toClassFile(cls, opts):
      stream = Writer()
      stream.u32(0xCAFEBABE)
      # bytecode version 49.0
      stream.u16(0)
      stream.u16(49)
  
      # Optimistically try translating without optimization to speed things up
      # if the resulting code is too big, retry with optimization
      try:
          pool, rest_stream = classFileAfterPool(cls, opts=opts)
      except error.ClassfileLimitExceeded:
          # print('Retrying {} with optimization enabled'.format(cls.name))
          pool, rest_stream = classFileAfterPool(cls, opts=options.ALL)
  
      # write constant pool
      pool.write(stream)
      # write rest of file
      stream.write(rest_stream.toBytes())
      return stream.toBytes()
  