Bug 53515

Summary: F#: Loading images from files causes a null reference exception
Product: Forms Reporter: Rob Lyndon <rob.lyndon>
Component: AndroidAssignee: Bugzilla <bugzilla>
Status: CONFIRMED ---    
Severity: normal CC: david.ortinau, david, jas, jimmy.garrido, lassana.nd, rui.marinho, v-sapaun
Priority: High    
Version: 2.3.3   
Target Milestone: ---   
Hardware: PC   
OS: Windows   
Tags: ac android image f# Is this bug a regression?: ---
Last known good build:
Attachments: Attached IDE logs
repro project

Description Rob Lyndon 2017-03-20 10:52:54 UTC
1. Create a new project, of type "Visual F# -> Android -> Blank App (Android)"

2. Add the Xamarin.Forms Nuget package.

3. Replace the code in MainActivity.fs with

namespace <<MyProjectName>>

open System

open Xamarin.Forms.Platform.Android

open Android.App
open Android.Content
open Android.OS
open Android.Runtime
open Android.Views
open Android.Widget

type MainPage() =
    inherit Xamarin.Forms.ContentPage()
    let layout = new Xamarin.Forms.StackLayout()
    do layout.Children.Add(new Xamarin.Forms.Button(Image = new Xamarin.Forms.FileImageSource(File = "Icon.jpg")))
    do base.Content <- layout

type App() =
    inherit Xamarin.Forms.Application()
    do base.MainPage <- new MainPage()

[<Activity (Label = "FSharpResourcesDemo", MainLauncher = true)>]
type MainActivity () =
    inherit FormsApplicationActivity()
    override this.OnCreate (bundle) =
        base.OnCreate (bundle)
        Xamarin.Forms.Forms.Init(this, bundle)
        this.LoadApplication(new App())

Run the app.


Note that ImageSource.FromFile() breaks in exactly the same way.
Comment 1 Rob Lyndon 2017-03-20 11:00:34 UTC
Also, ImageSource.FromResource() seems to be working correctly.

For convenience, I have added a project called FSharpResourcesDemo to my open source project, https://github.com/SpiegelSoft/Astrid. If you clone this solution, you can set this project to be your startup project.

I'm giving a presentation at the F# Exchange on the 7th of April, so a proposed fix or a valid workaround by then would be very useful.
Comment 2 Rob Lyndon 2017-03-20 11:02:27 UTC
There is a partial workaround: by adding

type Drawable() = inherit Resource_Drawable()
type Id() = inherit Resource_Id()

to your MainActivity file, the error happens further down the stack, but it doesn't come anywhere near solving the problem.
Comment 3 Rob Lyndon 2017-03-20 11:36:26 UTC
I've found the cause.

In the Resource.designer.fs project, members are defined as follows:

    static member abc_ab_share_pack_mtrl_alpha = 2130837504

In F#, these are interpreted as properties, rather than fields.

So here is the fix for the Xamarin Forms project:

In the ResourceManager class in the Xamarin.Forms.Core project, replace the method

public static void Init(Assembly masterAssembly)
	DrawableClass = masterAssembly.GetTypes().FirstOrDefault(x => x.Name == "Drawable");
	ResourceClass = masterAssembly.GetTypes().FirstOrDefault(x => x.Name == "Id");


public static void Init(Assembly masterAssembly)
	DrawableClass = masterAssembly.GetTypes().FirstOrDefault(x => x.Name == "Drawable" || x.Name == "Resource_Drawable");
	ResourceClass = masterAssembly.GetTypes().FirstOrDefault(x => x.Name == "Id" || x.Name == "Resource_Id");

and then replace the GetId() method

static int GetId(Type type, string propertyName)
	FieldInfo[] props = type.GetFields();
	FieldInfo prop = props.Select(p => p).FirstOrDefault(p => p.Name == propertyName);
	if (prop != null)
		return (int)prop.GetValue(type);
	return 0;


static int GetId(Type type, string propertyName)
	FieldInfo[] props = type.GetFields();
	FieldInfo prop = type.GetFields().FirstOrDefault(p => p.Name == propertyName) || type.GetProperties().FirstOrDefault(p => p.Name == propertyName);
	if (prop != null)
		return (int)prop.GetValue(type);
	return 0;

I'll submit a pull request.
Comment 4 Rob Lyndon 2017-03-20 11:37:16 UTC
Apologies -- in the code above, replace || with the null propagation operator ??
Comment 5 Rob Lyndon 2017-03-20 18:06:02 UTC
The final implementation is 

static int GetId(Type type, string propertyName)
	object value = type.GetFields().FirstOrDefault(p => p.Name == propertyName)?.GetValue(type)
		?? type.GetProperties().FirstOrDefault(p => p.Name == propertyName)?.GetValue(type);
	if (value != null)
		return (int)value;
	return 0;

The pull request is https://github.com/xamarin/Xamarin.Forms/pull/825
Comment 6 Rui Marinho 2017-04-12 11:08:11 UTC
Should be fixed on 2.3.5-pre1
Comment 8 Saurabh Paunikar 2017-07-19 11:00:17 UTC
Created attachment 23652 [details]
Attached IDE logs
Comment 9 Jimmy [MSFT] 2017-07-19 17:33:41 UTC
Created attachment 23665 [details]
repro project

I've attached a project that uses Forms 2.3.5-pre6 and still reproduces the issue so I am re-confirming this report.

### Version Tests   BAD
2.3.5-pre6  BAD