Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

Each release has a `## [version]` section. When a `v*.*.*` tag is pushed, the Publish workflow copies that section into the GitHub release, and it fails if the section is missing.

## [Unreleased]

### Docs

- Caveats document 15-argument `TypeLoadException`, C# optional parameters, and `tryGet` / `exists` on a null target (#110).

## [6.0.0]

### Breaking
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ You can still `open Dynamitey` for `Build`, `Dynamic.Curry`, `DynamicObjects.Dic

- The DLR cannot see **explicit interface members** (same as C# `dynamic`).
- **Not trim-safe or NativeAOT-safe.**
- **15 or more arguments** throw `TypeLoadException` (ImpromptuInterface was never shipped). Fourteen work.
- **C# optional parameters** must all be passed. The DLR does not fill defaults.
- `tryGet` / `exists` on a **null target** throw `NullReferenceException` (not `None` / `false`).
- Do not build member names from untrusted input. [SECURITY.md](SECURITY.md).
- Historical: .NET Core 2.0.0–2.0.2 broke `dynamic` on nested types inside generics ([#11](https://github.com/fsprojects/FSharp.Interop.Dynamic/issues/11)). Current TFMs are fine.

Expand Down
12 changes: 12 additions & 0 deletions docfx/docs/caveats.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ Annotate `'T` / `unit` so the binder and the conversion agree.

`Dyn.exists "x"` is true if `InvokeGet` succeeded, including when the value is null. `tryGet` returns `Some null` for a reference type. That is different from missing.

## Null target

`Dyn.tryGet` and `Dyn.exists` on a **null target** throw `NullReferenceException`. That is not a binder miss, so it is not `None` / `false`. Same for `Dyn.set`. A present null *value* is still `Some null` / `exists` true. Changing the null-target contract is [#111](https://github.com/fsprojects/FSharp.Interop.Dynamic/issues/111).

## More than 14 arguments

A call with **15 or more** arguments throws `TypeLoadException: Cannot Emit long delegates without ImpromptuInterface installed`. Fourteen arguments work. Dynamitey 3.0.3 never shipped ImpromptuInterface with this package. A fix belongs in a newer Dynamitey, not in a vendor here ([#29](https://github.com/fsprojects/FSharp.Interop.Dynamic/issues/29)).

## C# optional parameters

The DLR does not fill CLR optional defaults. `obj?Opt(5)` and `namedArg "a" 5` both fail (`No overload takes 1 arguments`) when `Opt` is `Opt(int a, int b = 0)`. Pass every argument. Named args work when the full set is present.

## Function-typed results delay the call

If F# infers `'TResult` as a function, `?` / `Dyn.get` / `Dyn.invokeMember` return a callable. A missing member in that mode used to look like `Some (fun …)` until you applied it. `tryGet` does `InvokeGet` first, so a missing member is `None` even when `'T` is a function.
Expand Down
2 changes: 1 addition & 1 deletion docfx/docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ let _: unit = items?Add("x")
- [Operators](operators.md) — `?`, `?<-`, `!?` in detail
- [Dyn](dyn.md) — the functions behind those operators
- [tryGet and exists](tryget.md) — the issue #27 surface
- [Caveats](caveats.md) — explicit interface members, AOT, conversion vs missing
- [Caveats](caveats.md) — explicit interface members, AOT, conversion vs missing, 15-arg, optionals, null target
3 changes: 2 additions & 1 deletion docfx/docs/tryget.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Lookup is `Dynamic.InvokeGet` only. Then the existing conversion / callable wrap
| Member present, value cannot convert to `'T` | throws (`InvalidCastException` or a binder exception) — **not** `None` |
| Member missing, `'T` is a function type | `None` immediately (does not return a lazy callable) |
| Getter throws something else | that exception propagates |
| Target is null | `NullReferenceException` (not `None`). [#110](https://github.com/fsprojects/FSharp.Interop.Dynamic/issues/110), [#111](https://github.com/fsprojects/FSharp.Interop.Dynamic/issues/111) |

## `Dyn.exists`

Expand All @@ -35,7 +36,7 @@ o |> Dyn.exists "myProp" // true
o |> Dyn.exists "nope" // false
```

True when `InvokeGet` succeeds. A present null is still present. A throwing getter is not reported as missing.
True when `InvokeGet` succeeds. A present null is still present. A throwing getter is not reported as missing. A **null target** throws `NullReferenceException`, not `false`.

`exists` does not convert the value. That is the difference from `tryGet >> Option.isSome` when conversion would fail: `exists` can be true while `tryGet` as `int` throws.

Expand Down