def test_default_bytecode_transforms(): cl = ClassLoader(bytecode_transforms=[simple_swap]) cf = ClassFile.create('TestClass') cl.update(cf) test_method = cf.methods.create('test', '(V)V;', code=True) test_method.code.max_stack = 2 test_method.code.max_locals = 0 test_method.code.assemble(assemble([ ('iconst_0',), ('pop',), ('return',) ])) # Load from the ClassLoader to bind to it. cf = cl.load('TestClass') # Ensure the defaults apply. ins_iter = test_method.code.disassemble() ins = next(ins_iter) assert ins.mnemonic == 'bipush' assert len(ins.operands) == 1 assert ins.operands[0].value == 0 # Ensure we can override the default. ins_iter = test_method.code.disassemble(transforms=[]) ins = next(ins_iter) assert ins.mnemonic == 'iconst_0' assert len(ins.operands) == 0
def test_default_bytecode_transforms(): cl = ClassLoader(bytecode_transforms=[simple_swap]) cf = ClassFile.create('TestClass') cl.update(cf) test_method = cf.methods.create('test', '(V)V;', code=True) test_method.code.max_stack = 2 test_method.code.max_locals = 0 test_method.code.assemble( assemble([('iconst_0', ), ('pop', ), ('return', )])) # Load from the ClassLoader to bind to it. cf = cl.load('TestClass') # Ensure the defaults apply. ins_iter = test_method.code.disassemble() ins = next(ins_iter) assert ins.mnemonic == 'bipush' assert len(ins.operands) == 1 assert ins.operands[0].value == 0 # Ensure we can override the default. ins_iter = test_method.code.disassemble(transforms=[]) ins = next(ins_iter) assert ins.mnemonic == 'iconst_0' assert len(ins.operands) == 0
def test_load_from_class(): """Ensure we can add ClassFile's directly to the ClassLoader.""" cl = ClassLoader() cf = ClassFile.create('TestClass') cl.update(cf) assert cl.load('TestClass') is cf
def test_sourcefile_write(): """ Ensure SourceFileAttribute can be written and read back. """ cf_one = ClassFile.create(u'SourceFileTest') sfa = cf_one.attributes.create(SourceFileAttribute) sfa.source_file = cf_one.constants.create_utf8(u'SourceFileTest.java') fout = BytesIO() cf_one.save(fout) fin = BytesIO(fout.getvalue()) cf_two = ClassFile(fin) source_file = cf_two.attributes.find_one(name=u'SourceFile') assert(source_file.source_file.value == u'SourceFileTest.java')
def test_sourcefile_write(): """ Ensure SourceFileAttribute can be written and read back. """ cf_one = ClassFile.create(u'SourceFileTest') sfa = cf_one.attributes.create(SourceFileAttribute) sfa.source_file = cf_one.constants.create_utf8(u'SourceFileTest.java') fout = BytesIO() cf_one.save(fout) fin = BytesIO(fout.getvalue()) cf_two = ClassFile(fin) source_file = cf_two.attributes.find_one(name=u'SourceFile') assert (source_file.source_file.value == u'SourceFileTest.java')
def test_conversion(): cf = ClassFile.create('some_class') exception_name = 'some_name' exception_class = cf.constants.create_class(exception_name) method = cf.methods.create('some_method', '()V', code=True) method.code.assemble([]) method.code.exception_table.append( CodeException(start_pc=0, end_pc=2, handler_pc=3, catch_type=exception_class.index)) jvm_class = convert_class_file(cf) method = jvm_class.methods[key_from_method(method)] handlers = method.exception_handlers.handlers assert len(handlers) == 1 handler = handlers[0] assert handler.catch_type == exception_name
def test_printable_classes(): cf = ClassFile.create('HelloWorld') assert repr(cf) == '<ClassFile(this=\'HelloWorld\')>' assert repr(cf.version) == 'ClassVersion(major=50, minor=0)'
""" An example showing how to create a "Hello World" class from scratch. """ from jawa.cf import ClassFile from jawa.assemble import assemble cf = ClassFile.create('HelloWorld') main = cf.methods.create('main', '([Ljava/lang/String;)V', code=True) main.access_flags.acc_static = True main.code.max_locals = 1 main.code.max_stack = 2 main.code.assemble( assemble([('getstatic', cf.constants.create_field_ref('java/lang/System', 'out', 'Ljava/io/PrintStream;')), ('ldc', cf.constants.create_string('Hello World!')), ('invokevirtual', cf.constants.create_method_ref('java/io/PrintStream', 'println', '(Ljava/lang/String;)V')), ('return', )])) with open('HelloWorld.class', 'wb') as fout: cf.save(fout)
def create_method(self): """ Creates a Method that corresponds to the generated function call. It will be part of a class that implements the right interface, and will have the appropriate name and signature. """ assert self.stored_args != None if self.generated_method != None: return (self.generated_cf, self.generated_method) class_name = self._cf.this.name.value + "_lambda_" + str(self._ins.pos) self.generated_cf = ClassFile.create(class_name) # Jawa doesn't seem to expose this cleanly. Technically we don't need # to implement the interface because the caller doesn't actually care, # but it's better to implement it anyways for the future. # (Due to the hacks below, the interface isn't even implemented properly # since the method we create has additional parameters and is static.) iface_const = self.generated_cf.constants.create_class(self.implemented_iface) self.generated_cf._interfaces.append(iface_const.index) # HACK: This officially should use instantiated_desc.descriptor, # but instead use a combination of the stored arguments and the # instantiated descriptor to make packetinstructions work better # (otherwise we'd need to generate and load fields in a way that # packetinstructions understands) descriptor = "(" + self.dynamic_desc.args_descriptor + \ self.instantiated_desc.args_descriptor + ")" + \ self.instantiated_desc.returns_descriptor method = self.generated_cf.methods.create(self.dynamic_name, descriptor, code=True) self.generated_method = method # Similar hack: make the method static, so that packetinstructions # doesn't look for the corresponding instance. method.access_flags.acc_static = True # Third hack: the extra arguments are in the local variables/arguments # list, not on the stack. So we need to move them to the stack. # (In a real implementation, these would probably be getfield instructions) # Also, this uses aload for everything, instead of using the appropriate # instruction for each type. instructions = [] for i in range(len(method.args)): instructions.append(("aload", i)) cls_ref = self.generated_cf.constants.create_class(self.method_class) if self.ref_kind in FIELD_REFS: # This case is not currently hit, but provided for future use # (Likely method_name and method_descriptor would no longer be used though) ref = self.generated_cf.constants.create_field_ref( self.method_class, self.method_name, self.method_desc.descriptor) elif self.ref_kind == REF_invokeInterface: ref = self.generated_cf.constants.create_interface_method_ref( self.method_class, self.method_name, self.method_desc.descriptor) else: ref = self.generated_cf.constants.create_method_ref( self.method_class, self.method_name, self.method_desc.descriptor) # See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.5 if self.ref_kind == REF_getField: instructions.append(("getfield", ref)) elif self.ref_kind == REF_getStatic: instructions.append(("getstatic", ref)) elif self.ref_kind == REF_putField: instructions.append(("putfield", ref)) elif self.ref_kind == REF_putStatic: instructions.append(("putstatic", ref)) elif self.ref_kind == REF_invokeVirtual: instructions.append(("invokevirtual", ref)) elif self.ref_kind == REF_invokeStatic: instructions.append(("invokestatic", ref)) elif self.ref_kind == REF_invokeSpecial: instructions.append(("invokespecial", ref)) elif self.ref_kind == REF_newInvokeSpecial: instructions.append(("new", cls_ref)) instructions.append(("dup",)) instructions.append(("invokespecial", ref)) elif self.ref_kind == REF_invokeInterface: instructions.append(("invokeinterface", ref)) method.code.assemble(assemble(instructions)) return (self.generated_cf, self.generated_method)