# 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
[docs]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
[docs]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
[docs]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))
[docs]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
[docs]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()