def makeAssemblyDebuggable(self, assembly_builder): ''' Make the assembly debuggable by adding specific attributes. ''' # http://blogs.msdn.com/rmbyers/archive/2005/06/26/432922.aspx da_type = typeof(DebuggableAttribute) da_ctor = da_type.GetConstructor(System.Array[System.Type]([typeof(DebuggableAttribute.DebuggingModes)])) da_builder = CustomAttributeBuilder(da_ctor, System.Array[System.Object]([DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.Default])) assembly_builder.SetCustomAttribute(da_builder)
def generateStaticDataInitialization(self, data_visitor, type_builder): """ Generate a type constructor to setup static data for DATA statements """ # static (type) constructor - initialise static DATA # TODO: Replace this with Dictionary<int, int> self.data_field = type_builder.DefineField('data', cts.string_array_type, FieldAttributes.Private | FieldAttributes.Static) self.data_line_number_map_field = type_builder.DefineField('dataLineNumbers', cts.int_int_dictionary_type, FieldAttributes.Private | FieldAttributes.Static) self.data_index_field = type_builder.DefineField('dataIndex', typeof(System.Int32), FieldAttributes.Private | FieldAttributes.Static) type_constructor_builder = type_builder.DefineTypeInitializer() generator = type_constructor_builder.GetILGenerator() data_local = generator.DeclareLocal(cts.string_array_type) data_index_local = generator.DeclareLocal(cts.int_int_dictionary_type) # Initialise the data field emitLdc_I4(generator, len(data_visitor.data)) # Load the array length onto the stack generator.Emit(OpCodes.Newarr, System.String) # New array with type information generator.Emit(OpCodes.Stloc, data_local) # Store array reference in local 0 for index, item in enumerate(data_visitor.data): generator.Emit(OpCodes.Ldloc, data_local) # Load the array onto the stack emitLdc_I4(generator, index) # Load the index onto the stack generator.Emit(OpCodes.Ldstr, item) # Load the string onto the stack generator.Emit(OpCodes.Stelem_Ref) # Assign to array element generator.Emit(OpCodes.Ldloc, data_local) # Load the array onto the stack generator.Emit(OpCodes.Stsfld, self.data_field) # Store it in the static field # Initialise the data index field - # this needs to be initialized with a Dictionary #generic_dictionary_type = typeof(System.Collections.Generic.Dictionary) #int_int_dictionary_type = generic_dictionary_type.MakeGenericType( # System.Array[System.Type]((typeof(System.Int32), typeof(System.Int32)))) int_int_dictionary_ctor_info = cts.int_int_dictionary_type.GetConstructor(System.Type.EmptyTypes) # Get the default constructor generator.Emit(OpCodes.Newobj, int_int_dictionary_ctor_info) generator.Emit(OpCodes.Stloc, data_index_local) # Store dictionary reference in local 1 add_method_info = cts.int_int_dictionary_type.GetMethod('Add') for line_number, index in data_visitor.index.items(): generator.Emit(OpCodes.Ldloc, data_index_local) # Load the dictionary onto the stack emitLdc_I4(generator, line_number) # Load the line_number onto the stack emitLdc_I4(generator, index) # Load the index onto the stack generator.Emit(OpCodes.Call, add_method_info) # Call Dictionary<int,int>.Add() generator.Emit(OpCodes.Ldloc, data_index_local) # Load the dictionary onto the stack generator.Emit(OpCodes.Stsfld, self.data_line_number_map_field) # Store it in the static field generator.Emit(OpCodes.Ret)
def generateAssembly(self, source_file, name, global_symbols, data_visitor, ordered_basic_blocks): """ :param source_file: The BASIC source file - for debugging information :param name: The name given to the assembly to be generated. :param ordered_basic_blocks: A mapping type where keys are the entry point name and values are a sequence of BasicBlocks for that method/program. """ # Generate an assembly # Generate a namespace # Generate a static class # Create global variables as members # For each entry point # - create a method # Mark the entry point to the assembly # We build the assembly in the current AppDomain domain = Thread.GetDomain() assembly_name = AssemblyName(name) assembly_builder = domain.DefineDynamicAssembly(assembly_name, AssemblyBuilderAccess.RunAndSave) self.makeAssemblyDebuggable(assembly_builder) module_builder = assembly_builder.DefineDynamicModule(name + ".exe", True) # pass True to track debug info owl_module = typeof(OwlRuntime.OwlModule) # Set the source file that we want to associate with this module self.doc = module_builder.DefineDocument(source_file, System.Guid.Empty, System.Guid.Empty, System.Guid.Empty) type_builder = module_builder.DefineType(name, TypeAttributes.Class | TypeAttributes.Public, object().GetType()) self.createAndAttachStaticEmitters(owl_module) self.createAndAttachGlobalEmitters(global_symbols, type_builder) if len(data_visitor.data) > 0: self.generateStaticDataInitialization(data_visitor, type_builder) # Generate all the unique method names # TODO: This would be sooo much easier if the entry_point.name # property had been set useful, and PROC and FN retained in identifier names everywhere! # TODO: Should also wrap the main program in DEF PROCMain - safely! for entry_name, basic_blocks in ordered_basic_blocks.items(): entry_point = basic_blocks[0].entryPoint if isinstance(entry_point, DefinitionStatement): self.createCtsMethodName(entry_point.name) else: # Main assert entry_name == '__owl__main' assert iter(entry_point.entryPoints).next().startswith('MAIN') self.createCtsMethodName('FNMain') #for owl_name, clr_name in self.owl_to_clr_method_names.items(): # print owl_name, " ==> ", clr_name # Generate all the empty methods, so we can retrieve them from the type builder for basic_blocks in ordered_basic_blocks.values(): self.generateMethod(type_builder, basic_blocks) # Generate the body of each method stop_on_error = False for basic_blocks in ordered_basic_blocks.values(): try: self.generateMethodBody(type_builder, basic_blocks) except CodeGenerationError, e: logging.critical("STOPPING %s\n\n\n", e) if stop_on_error: break