dotnet-websharper / core

WebSharper - Full-stack, functional, reactive web apps and microservices in F# and C#
https://websharper.com
Apache License 2.0
597 stars 50 forks source link

InlineControl and F# 4.0 incompatibility #409

Closed Jand42 closed 6 years ago

Jand42 commented 9 years ago

Switching a new Client-Server Web Application to .NET 4.5+ and F# 4.0 in VS15 RC fails at runtime with stack trace

[Exception: Error in InlineControl at [...]\Main.fs: 47.35-47.48: Couldn't find address for method]
   <StartupCode$WebSharper-Web>.WebSharper-Html-Client-IControl-Requires@143.Invoke(String message) +63
   WebSharper.Web.InlineControl`1.WebSharper-Html-Client-IControl-Requires(Info meta) +391
   Microsoft.FSharp.Collections.map@111.DoMoveNext(b& ) +77
   Microsoft.FSharp.Collections.MapEnumerator`1.System-Collections-IEnumerator-MoveNext() +51
   Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers.takeOuter@713(ConcatEnumerator`2 x, Unit unitVar0) +37
   Microsoft.FSharp.Collections.map@111.DoMoveNext(b& ) +19
   Microsoft.FSharp.Collections.MapEnumerator`1.System-Collections-IEnumerator-MoveNext() +51
   Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers.takeOuter@713(ConcatEnumerator`2 x, Unit unitVar0) +37
   Microsoft.FSharp.Collections.DistinctBy@1485.GenerateNext(IEnumerable`1& next) +167
   Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1.MoveNextImpl() +46
   Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1.System-Collections-IEnumerator-MoveNext() +11
   Microsoft.FSharp.Collections.map@111.DoMoveNext(b& ) +19
   Microsoft.FSharp.Collections.MapEnumerator`1.System-Collections-IEnumerator-MoveNext() +51
   Microsoft.FSharp.Collections.SeqModule.ToList(IEnumerable`1 source) +192
   WebSharper.Sitelets.Content.writeResources(Env env, IEnumerable`1 controls, HtmlTextWriter tw) +136
   WebSharper.Sitelets.Content.getResourcesAndScripts(Env env, IEnumerable`1 controls) +142
   WebSharper.Sitelets.Run@573-6.Invoke(Unit unitVar) +103
   Microsoft.FSharp.Control.callA@851.Invoke(AsyncParams`1 args) +161
   Microsoft.FSharp.Control.AsyncIAsyncResult`1.GetResult() +2095038
   Microsoft.FSharp.Control.AsBeginEndHelpers.endAction(IAsyncResult iar) +71
   <StartupCode$FSharp-Core>.AsBeginEnd@1923.Invoke(IAsyncResult iar) +42
   WebSharper.Sitelets.HttpHandler.System-Web-IHttpAsyncHandler-EndProcessRequest(IAsyncResult result) +48
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9751057
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
granicz commented 9 years ago

This occurs for me even with F# 3.1.2 and .NET 4.5, using the following Warp app (with an extra ChartJs dependency):

open WebSharper

[<Client>]
module Client =
    open WebSharper.JavaScript
    open WebSharper.Html.Client
    open WebSharper.ChartJs

    let Main () =
        let n = ref 10

        let Push (chart : Chart.LineChart) (value : float) =
            chart.RemoveData()
            chart.AddData([| float value |], string (!n + 1)) 
            n := !n + 1

        let initialData =
            [| for x in 1 .. !n do
                    yield (string x, Math.Random()) |]

        let chart : Chart.LineChart option ref = ref None

        Div [
            Canvas [
                Width  "1200"
                Height "400"
            ]
            |>! OnAfterRender (fun canvas ->
                let canvas = As<CanvasElement> canvas
                let (labels, dataset) =
                    Array.unzip initialData

                let data =
                    LineChartData(
                        Labels   = labels,
                        Datasets = [| LineChartDataset(Data = dataset) |]
                    )

                Chart.Defaults.ShowTooltips <- false

                let options =
                    LineChartConfiguration(
                        BezierCurve = false,
                        DatasetFill = false
                    )

                chart := Some <| Chart(canvas.GetContext "2d").Line(data, options)
            )

            Div [
                Button [ Text "Push" ]
                |>! OnClick (fun _ _ ->
                    Push (!chart).Value <| Math.Random()
                )
            ]
        ]

open WebSharper.Html.Server

let MyApp =
    Warp.CreateSPA (fun ctx ->
        [Div [ClientSide <@ Client.Main() @>]]
    )

[<EntryPoint>]
Warp.RunAndWaitForInput(MyApp) |> ignore
granicz commented 9 years ago

When I move the local functions in main to toplevel, I get:

System.TypeLoadException: Method 'GenerateLegend' in type 'LineChart' from assem
bly 'WebSharper.ChartJs, Version=3.2.0.0, Culture=neutral, PublicKeyToken=dcd983
dec8f76a71' does not have an implementation.```
granicz commented 9 years ago

(Just to be sure, this is the code for the previous comment:

open WebSharper
open WebSharper.JavaScript
open WebSharper.Html.Client
open WebSharper.ChartJs

[<JavaScript>]
let n = ref 10

[<JavaScript>]
let chart : Chart.LineChart option ref = ref None

[<JavaScript>]
let Push (value : float) =
    (!chart).Value.RemoveData()
    (!chart).Value.AddData([| float value |], string (!n + 1)) 
    n := !n + 1

[<JavaScript>]
let DynamicChart () =
    let initialData =
        [| for x in 1 .. !n do
                yield (string x, Math.Random()) |]

    Div [
        Canvas [
            Width  "1200"
            Height "400"
        ]
        |>! OnAfterRender (fun canvas ->
            let canvas = As<CanvasElement> canvas
            let (labels, dataset) = Array.unzip initialData
            let data =
                LineChartData(
                    Labels   = labels,
                    Datasets = [| LineChartDataset(Data = dataset) |]
                )
            Chart.Defaults.ShowTooltips <- false
            let options =
                LineChartConfiguration(
                    BezierCurve = false,
                    DatasetFill = false
                )
            chart := Some <| Chart(canvas.GetContext "2d").Line(data, options)
        )
        Div [
            Button [ Text "Push" ]
            |>! OnClick (fun _ _ ->
                Push <| Math.Random()
            )
        ]
    ]

open WebSharper.Html.Server

let MyApp =
    Warp.CreateSPA (fun ctx ->
        [Div [ClientSide <@ DynamicChart() @>]]
    )

[<EntryPoint>]
Warp.RunAndWaitForInput(MyApp) |> ignore

)

Jand42 commented 9 years ago

Original F# 4.0 problem still persists. It seems that creating the types metadata for the assembly fails silently, no type lookup information is encoded.

@granicz Your issues here was a result of invalid IL generation by WIG on implementing interfaces (#437). This created a type load error when reflecting with System.Reflection used by WS.Warp (standard WS compilation only uses Mono.Cecil to read metadata).

granicz commented 9 years ago

(Sorry, in both of the above examples, the line

let canvas = As<CanvasElement> canvas

should read

let canvas = As<CanvasElement> canvas.Body

)

I now verified that both examples work with F# 3.1. So we need to get F# 4.0 sorted out.

Jand42 commented 8 years ago

General comment on "Error in InlineControl": if you encounter it, make sure that the targeted method has JavaScript attribute

Jand42 commented 6 years ago

This is now handled properly in WebSharper 4.