Why does the Mono.Cecil HelloWorld example fail with an exception? - f#

open System
open Mono.Cecil
open Mono.Cecil.Cil
let myHelloWorldApp =
AssemblyDefinition.CreateAssembly(
new AssemblyNameDefinition("HelloWorld", new Version(1, 0, 0, 0)), "HelloWorld", ModuleKind.Console)
let module_ = myHelloWorldApp.MainModule
// create the program type and add it to the module
let programType =
new TypeDefinition("HelloWorld", "Program",
Mono.Cecil.TypeAttributes.Class ||| Mono.Cecil.TypeAttributes.Public, module_.TypeSystem.Object)
module_.Types.Add(programType)
// add an empty constructor
let ctor =
new MethodDefinition(".ctor", Mono.Cecil.MethodAttributes.Public ||| Mono.Cecil.MethodAttributes.HideBySig
||| Mono.Cecil.MethodAttributes.SpecialName ||| Mono.Cecil.MethodAttributes.RTSpecialName, module_.TypeSystem.Void)
// create the constructor's method body
let il = ctor.Body.GetILProcessor()
il.Append(il.Create(OpCodes.Ldarg_0))
// call the base constructor
il.Append(il.Create(OpCodes.Call, module_.ImportReference(typeof<obj>.GetConstructor([||]))))
il.Append(il.Create(OpCodes.Nop))
il.Append(il.Create(OpCodes.Ret))
programType.Methods.Add(ctor)
// define the 'Main' method and add it to 'Program'
let mainMethod =
new MethodDefinition("Main",
Mono.Cecil.MethodAttributes.Public ||| Mono.Cecil.MethodAttributes.Static, module_.TypeSystem.Void)
programType.Methods.Add(mainMethod)
// add the 'args' parameter
let argsParameter =
new ParameterDefinition("args",
Mono.Cecil.ParameterAttributes.None, module_.ImportReference(typeof<string[]>))
mainMethod.Parameters.Add(argsParameter);
// create the method body
il = mainMethod.Body.GetILProcessor()
il.Append(il.Create(OpCodes.Nop))
il.Append(il.Create(OpCodes.Ldstr, "Hello World"))
let writeLineMethod =
il.Create(OpCodes.Call,
module_.ImportReference(typeof<Console>.GetMethod("WriteLine", [|typeof<string>|])))
// call the method
il.Append(writeLineMethod)
il.Append(il.Create(OpCodes.Nop))
il.Append(il.Create(OpCodes.Ret))
// set the entry point and save the module
myHelloWorldApp.EntryPoint <- mainMethod
I've borrowed the example from the answer to that question and rewritten it in F#. When I try to run it, I get the following error:
Unhandled Exception: System.TypeLoadException: Could not load type 'HelloWorld.Program' from assembly 'Hello
on=1.0.0.0, Culture=neutral, PublicKeyToken=null' because the method 'Main' has no implementation (no RVA).
What is wrong here?

My guess is that the problem is caused by the following line:
// create the method body
il = mainMethod.Body.GetILProcessor()
In C#, this would assign the IL processor for the Main method to the il variable, but in F#, this is just an equality test which results in false - and so the IL code that you wanted to generate for the Main method is added to the previous il processor for the constructor.
You should be able to fix this using variable shadowing:
// create the method body
let il = mainMethod.Body.GetILProcessor()

Related

XUnit/FsUnit framework throwing "Object reference not set to an instance of an objct"

I have written this simple test case for my code
module CustomerTests
open Xunit
open FsUnit
open MyProject.Customer
open MyProject.Customer.Domain
module ``When upgrading customer`` =
let customerVIP = {Id = 1; IsVip = true; Credit = 0.0M}
let customerSTD = {Id = 2; IsVip = false; Credit = 100.0M}
[<Fact>]
let ``should give VIP customer more credit`` () =
let expected = {customerVIP with Credit = customerVIP.Credit + 100.0M }
let actual = upgradeCustomer customerVIP
actual |> should equal expected
Very surprisingly this code fails with error
[xUnit.net 00:00:00.64] CustomerTests+When upgrading customer.should give VIP cstomer more credit [FAIL]
Failed CustomerTests+When upgrading customer.should give VIP cstomer more credit [3 ms]
Error Message:
System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace:
at CustomerTests.When upgrading customer.should give VIP cstomer more credit() in /Users/user/code/fsharp/CustomerProject/CustomerTests.fs:line 12
But line 12 is just a record being created so its not possible for that line to throw an Object reference not set to an instance of object. This is totally puzzling me.
In the dotnet fsi repl I can execute all my method and there is no object reference problem in my function which are being called from my tests here.
As this SO answer explains, XUnit loads the test in a way that skips initialization of those values. One easy fix is to use a class instead of a module:
type ``When upgrading customer``() =
let customerVIP = {Id = 1; isVip = true; Credit = 0.0M}
let customerSTD = {Id = 2; isVip = false; Credit = 100.0M}
[<Fact>]
let ``should give VIP cstomer more credit`` () =
let expected = {customerVIP with Credit = customerVIP.Credit + 100.0M }
let actual = upgradeCustomer customerVIP
actual |> should equal expected
That way, initialization of those values happens as it should.

Calling expand entity from SAP Gateway Client

I try to call a GET_EXPANDED_ENTITYSET method of SRA020_PO_TRACKING Project, po tracking project. The following is the method:
METHOD /iwbep/if_mgw_appl_srv_runtime~get_expanded_entityset.
DATA: lv_ponumber TYPE bapiekko-po_number,
lv_leading_msg TYPE boolean.
DATA: lo_api TYPE REF TO cl_sra020_po_tracking_api.
DATA: ls_po_details TYPE cl_sra020_po_tracking_api=>ts_podetails,
lt_return TYPE bapirettab,
lo_msgcontainer TYPE REF TO /iwbep/if_message_container.
DATA: lt_poitem_details TYPE cl_sra020_po_tracking_api=>tt_poitemdetail.
FIELD-SYMBOLS: <fs_return> TYPE bapiret2,
<fs_key_tab> TYPE /iwbep/s_mgw_name_value_pair.
lo_api = cl_sra020_po_tracking_api=>get_instance( ).
*========================================================================================
* Read Entities key values within oData service URL
*========================================================================================
IF it_key_tab IS NOT INITIAL.
LOOP AT it_key_tab ASSIGNING <fs_key_tab>.
CASE <fs_key_tab>-name.
WHEN if_sra020_po_tracking_constant=>cc_po_number.
lv_ponumber = <fs_key_tab>-value.
ENDCASE.
ENDLOOP.
ENDIF.
TRY.
CASE iv_entity_set_name.
WHEN if_sra020_po_tracking_constant=>cc_podetaileddatas.
CALL METHOD lo_api->get_po_details
EXPORTING
iv_item_additional_data = 'X' "#EC NOTEXT
iv_po_number = lv_ponumber
IMPORTING
es_po_details = ls_po_details
et_return = lt_return.
IF NOT lt_return IS INITIAL.
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception.
ENDIF.
lt_poitem_details = ls_po_details-poitemdetaildatas.
no_cache( ).
* Return the expanded clauses
APPEND cl_sra020_po_tracking_mpc=>gc_poitemdocflow TO et_expanded_tech_clauses.
APPEND cl_sra020_po_tracking_mpc=>gc_accounting TO et_expanded_tech_clauses.
APPEND cl_sra020_po_tracking_mpc=>gc_pricingconditions TO et_expanded_tech_clauses.
APPEND cl_sra020_po_tracking_mpc=>gc_confirmation TO et_expanded_tech_clauses.
CALL METHOD copy_data_to_ref
EXPORTING
is_data = lt_poitem_details
CHANGING
cr_data = er_entityset.
ENDCASE.
CATCH /iwbep/cx_mgw_busi_exception.
lo_msgcontainer = me->mo_context->get_message_container( ).
LOOP AT lt_return ASSIGNING <fs_return> WHERE type EQ 'E' OR type EQ 'A'. "#EC NOTEXT
IF <fs_return>-id EQ 'SRA020'.
lv_leading_msg = abap_true.
ELSE.
lv_leading_msg = abap_false.
ENDIF.
lo_msgcontainer->add_message(
iv_msg_type = <fs_return>-type
iv_msg_id = <fs_return>-id
iv_msg_number = <fs_return>-number
iv_msg_text = <fs_return>-message
iv_msg_v1 = <fs_return>-message_v1
iv_msg_v2 = <fs_return>-message_v2
iv_msg_v3 = <fs_return>-message_v3
iv_msg_v4 = <fs_return>-message_v4
iv_is_leading_message = lv_leading_msg
).
ENDLOOP.
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
textid = /iwbep/cx_mgw_busi_exception=>business_error
message_container = lo_msgcontainer.
ENDTRY.
ENDMETHOD.
Where
if_sra020_po_tracking_constant=>cc_po_number = PONumber
if_sra020_po_tracking_constant=>cc_podetaileddatas = PODetailedDatas
and PODetailedData entity has navigation properties of POItemDetailDatas,POItems,and POList
I try to call the method from Gateway Service Client by executing
/sap/opu/odata/SAP/SRA020_PO_TRACKING_SRV/PODetailedDatas?$expand=POItemDetailDatas,POItems,POList
However I got status code 400 Bad Request instead. What did I miss?
regards
Edit:
The message in the Bad Request says: Error while reading Purchase Order
edit 2:
association screen shot:
navigation screen shot:
Turns out I have to provide PONumber, so that:
PODetailedDatas(PONumber='4500000039')?$expand=POItems
works fine

checking an object in a nested link via dxl

I have the following situation.
I want to count in Module 1, how many objects are having links in links from Module 3.
example:
Module 1 Obj1 <- Module 2 Obj1 <- Module 3.Obj1
Module 1 Obj2 <- Module 2 Obj1 <- Module 3.Obj1
Module 1 Obj3 <- Module 2 Obj1 <- Module 3.Obj1
Module 1 Obj4 <- Module 2 Obj1
Module 1 Obj5 <- Module 2 Obj1
The count should return 3, in the above case.
Is it possible via DXL to follow a link, and then follow another link?
(not using the Wizard or DXL attributes)
Most important for me: knowing if somebody else did this and it's possible to do.
Please try the following DXL from within the module that has the incoming links. Before running the code:
make sure that you open the 'Edit DXL' window from the relevant module
set the string values assigned to global constant STR_LINK_MOD_FULLNAME (line 17) to the full pathname of the link module whose links you are interested in
set the string values assigned to global constant STR_SRC_MOD_FULLNAME (line 18) to the full pathname of the source formal module (Module 3, in your example) whose links you are interested in
You shouldn't need to change anything else to make it work.
N.B. I have not considered the implications of analysing links in all link modules by using the string "*" in place of a specific link module name in line 17 (see point 2, above).
I also haven't gone out of my way to explain the code, though I have tried to be nice and tidy up after myself where DOORS and DXL require it. Please feel free to reply with any questions on what I have done.
Kind regards,
Richard
//<CheckObjectInNestedLink.dxl>
/*
*/
///////////////
// Sanity check
if (null (current Module))
{
print "ERROR: this script must be run from a Formal Module."
}
///////////////////
// Global Constants
const string STR_LINK_MOD_FULLNAME = "/New Family Car Project/Admin/Satisfies" // the fullName of a single link module - results of using "*" have not been considered/tested
const string STR_SRC_MOD_FULLNAME = "/New Family Car Project/Architecture/Architectural Design" // The fullName of the desired source Formal Module
///////////////////
// Global Variables
Module modSource = null
Object objTarget = null
Object objSource = null
Link lkIn = null
Skip skLinkedMods = create()
Skip skObjsWithDesiredSource = create()
int intNoOfLinked = 0
//////////////////////
// Auxiliary Functions
void closeSourceMods ()
{
Module srcMod
for srcMod in skLinkedMods do
{
close(srcMod)
}
}
void openSourceMods (Object objWithLinks)
{
ModName_ srcModRef
Module srcMod
for srcModRef in (objWithLinks <- STR_LINK_MOD_FULLNAME) do
{
srcMod = read(fullName(srcModRef), false)
put(skLinkedMods, srcMod, srcMod)
}
}
void recurseFollowInLinks (Object objWithLinks)
{
openSourceMods(objWithLinks)
for lkIn in objWithLinks <- STR_LINK_MOD_FULLNAME do
{
openSourceMods(objWithLinks)
objSource = source(lkIn)
string strSrcModName = fullName(module(objSource))
if (strSrcModName == STR_SRC_MOD_FULLNAME)
{
bool blNewEntry = put(skObjsWithDesiredSource, objTarget, objTarget)
if (blNewEntry)
{
intNoOfLinked++
}
//print "put(skObjsWithDesiredSource, " identifier(objTarget) ", " identifier(objTarget) ")\n"
}
recurseFollowInLinks(objSource)
}
}
void checkObjectInNestedLink (Module modThis, string strSourceModuleFullname, string strLinkModuleFullName)
{
intNoOfLinked = 0
for objTarget in modThis do
{
recurseFollowInLinks(objTarget)
}
print "The following " intNoOfLinked " objects have direct or indirect links of type " STR_LINK_MOD_FULLNAME " from formal module " STR_SRC_MOD_FULLNAME ":\n"
for objTarget in skObjsWithDesiredSource do
{
print identifier(objTarget)
print "\n"
}
}
///////////////
// Main Program
checkObjectInNestedLink ((current Module), STR_SRC_MOD_FULLNAME, STR_LINK_MOD_FULLNAME)
closeSourceMods()
delete(skLinkedMods)
delete(skObjsWithDesiredSource)

How to refer to a type of the same name in a nested scope

I'm trying to access a type defined in the upper-level module:
module MainModule
type Data = { stuff }
module XmlDeserialization =
type Data() =
[<XmlAttribute("stuff")>]
member val stuff ...
member x.ToDomainType() =
{
stuff = x.stuff
} : MainModule.Data
The problem is, the last line doesn't compile because "the type 'MainModule' isn't defined."
I'm able to achieve what I want using namespaces instead, but is it possible to do this using modules?
You cannot reference MainModule within the body of MainModule itself, because at that point, the module is technically not defined yet. This can be reproduced with a smaller program:
module M =
type T = T
let x: T = T // OK
let y: M.T = T // Error: module M is not defined yet
The simplest solution for you would be to finish defining MainModule before you start defining XmlDeserialization:
module MainModule =
type Data = { stuff }
module XmlDeserialization =
type Data() =
[<XmlAttribute("stuff")>]
member val stuff ...
member x.ToDomainType() =
{
stuff = x.stuff
} : MainModule.Data // Works now
But if you insist that XmlDeserialization be nested under MainModule, and you insist that the types must have the same name, then you can work around the type shadowing by creating an alias of the original type before defining the overshadowing one:
module XmlDeserialization =
// Alias the original type
type MainModule_Data = Data
type Data() =
[<XmlAttribute("stuff")>]
member val stuff ...
member x.ToDomainType() =
{
stuff = x.stuff
} : MainModule_Data // Refer by alias: works now
In this specific case, it should just work if you remove the type annotation. The compiler will infer the record type from the fields of the record Data.
In general, you can declare a function to enforce a given record type, for example:
let private mainModuleData (instance : Data) = instance
If this function is defined before Data gets shadowed, even if the record fields are ambiguous, you can use the function to enforce the correct type:
member x.ToDomainType() =
mainModuleData
{ stuff = x.stuff }

How can I call ActiveX from SAP?

I have a custom ActiveX control that I would like to Call from SAP.
In this case I cannot use PI, what other options do I have?
See program SAPRDEMO_ACTIVEX_INTEGRATION as an example.
DATA: control TYPE REF TO i_oi_container_control,
ocx_document TYPE REF TO i_oi_document_proxy,
has_activex TYPE flag,
retcode TYPE soi_ret_string,
doc_handle TYPE cntl_handle,
document_type TYPE soi_document_type VALUE 'SAPActiveXDoc.Example1'.
"Test whether activeX is supported"
CALL FUNCTION 'GUI_HAS_ACTIVEX'
IMPORTING return = has_activex.
CHECK NOT has_activex IS INITIAL.
CALL METHOD control->init_control
EXPORTING r3_application_name = 'R/3 Basis'
inplace_enabled = 'X'
register_on_close_event = 'X'
register_on_custom_event = 'X'
parent = cl_gui_container=>default_screen
IMPORTING retcode = retcode.
CALL METHOD control->get_document_proxy
EXPORTING document_type = document_type
IMPORTING document_proxy = oxc_document
retcode = retcode.
CALL METHOD oxc_document->open_activex_document
IMPORTING retcode = retcode.
CALL METHOD ocx_document->get_document_handle
IMPORTING handle = doc_handle
retcode = retcode.
CALL FUNCTION 'CONTROL_CALL_METHOD'
EXPORTING h_control = doc_handle
method = 'MyMethod'
p_count = 0.
CALL METHOD ocx_document->clsoe_activex_document
IMPORTING retcode = retcode.
I've stripped out the screen processing & error handling of the example program in order to give an overview of main calls that are needed.

Resources