Skip to content
Open
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
205 changes: 194 additions & 11 deletions src/Fable.Transforms/Rust/Fable2Rust.fs

Large diffs are not rendered by default.

144 changes: 120 additions & 24 deletions src/Fable.Transforms/Rust/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -490,8 +490,9 @@ let equals (com: ICompiler) ctx r (left: Expr) (right: Expr) =
| Array _ -> Helper.LibCall(com, "Array", "equals", t, [ left; right ], ?loc = r)
| List _ -> Helper.LibCall(com, "List", "equals", t, [ left; right ], ?loc = r)
| IEnumerable -> Helper.LibCall(com, "Seq", "equals", t, [ left; right ], ?loc = r)
// | MetaType ->
// Helper.LibCall(com, "Reflection", "equals", t, [left; right], ?loc=r)
// System.Type is erased to a boxed reflection object (dyn Any, no PartialEq),
// so type-identity equality is compared on the carried TypeId at runtime.
| MetaType -> Helper.LibCall(com, "Reflection", "typeEquals", t, [ left; right ], ?loc = r)
| HasReferenceEquality com _ -> referenceEquals com ctx r left right
| Nullable _ ->
// transforms null checks into option tests
Expand Down Expand Up @@ -1298,10 +1299,13 @@ let objects (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt
| "GetHashCode", Some arg, _ -> objectHash com ctx r arg |> Some
| "GetType", Some arg, _ ->
if arg.Type = Any then
"Types can only be resolved at compile time. At runtime this will be same as `typeof<obj>`"
|> addWarning com ctx.InlinePath r

makeTypeInfo r arg.Type |> Some
// Dynamic type of a boxed value: resolve via the runtime reflection
// registry (populated by typeof<T>). Returns the concrete type's info
// when registered, else the value as an opaque placeholder.
Helper.LibCall(com, "Reflection", "getTypeFromObj", t, [ arg ], ?loc = r)
|> Some
else
makeTypeInfo r arg.Type |> Some
| _ -> None

let valueTypes (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
Expand Down Expand Up @@ -2875,14 +2879,66 @@ let timers (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opti
let systemEnv
(com: ICompiler)
(ctx: Context)
(_: SourceLocation option)
(_: Type)
(r: SourceLocation option)
(t: Type)
(i: CallInfo)
(_: Expr option)
(_: Expr list)
(args: Expr list)
=
match i.CompiledName with
| "get_NewLine" -> Some(makeStrConst "\n")
| "GetEnvironmentVariable" ->
Helper.LibCall(com, "Environment", "getEnvironmentVariable", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "get_CurrentDirectory" ->
Helper.LibCall(com, "Environment", "getCurrentDirectory", t, [], ?loc = r)
|> Some
| _ -> None

let paths (com: ICompiler) (ctx: Context) r t (i: CallInfo) (_: Expr option) (args: Expr list) =
match i.CompiledName with
| "Combine" ->
match args with
| [ _; _ ] ->
Helper.LibCall(com, "Path", "combine2", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| [ _; _; _ ] ->
Helper.LibCall(com, "Path", "combine3", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| _ -> None
| "GetDirectoryName" ->
Helper.LibCall(com, "Path", "getDirectoryName", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "GetFileName" ->
Helper.LibCall(com, "Path", "getFileName", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "GetFileNameWithoutExtension" ->
Helper.LibCall(com, "Path", "getFileNameWithoutExtension", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "GetExtension" ->
Helper.LibCall(com, "Path", "getExtension", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "HasExtension" ->
Helper.LibCall(com, "Path", "hasExtension", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "GetTempPath" -> Helper.LibCall(com, "Path", "getTempPath", t, [], ?loc = r) |> Some
| "GetRandomFileName" -> Helper.LibCall(com, "Path", "getRandomFileName", t, [], ?loc = r) |> Some
| _ -> None

let files (com: ICompiler) (ctx: Context) r t (i: CallInfo) (_: Expr option) (args: Expr list) =
match i.CompiledName with
| "Exists" ->
Helper.LibCall(com, "File", "exists", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "WriteAllText" ->
Helper.LibCall(com, "File", "writeAllText", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "ReadAllText" ->
Helper.LibCall(com, "File", "readAllText", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "Delete" ->
Helper.LibCall(com, "File", "delete", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| _ -> None

// Initial support, making at least InvariantCulture compile-able
Expand Down Expand Up @@ -3258,19 +3314,37 @@ let uris
=
match i.CompiledName with
| ".ctor" ->
Helper.LibCall(com, "Uri", "Uri.create", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
match args with
| [ ExprType String ] ->
Helper.LibCall(com, "Uri", "create", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| [ ExprType String; _ ] ->
Helper.LibCall(com, "Uri", "createWithKind", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| [ _; ExprType String ] ->
Helper.LibCall(com, "Uri", "createFromString", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| [ _; _ ] ->
Helper.LibCall(com, "Uri", "createFromUri", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| _ -> None
| "TryCreate" ->
Helper.LibCall(com, "Uri", "Uri.tryCreate", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
match args with
| (ExprType String) :: _ ->
Helper.LibCall(com, "Uri", "tryCreateWithKind", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| _ ->
Helper.LibCall(com, "Uri", "tryCreateFromUri", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "ToString" -> toString com ctx r [ thisArg.Value ] |> Some
| "UnescapeDataString" ->
Helper.LibCall(com, "Util", "unescapeDataString", t, args, i.SignatureArgTypes)
Helper.LibCall(com, "Uri", "unescapeDataString", t, args, i.SignatureArgTypes)
|> Some
| "EscapeDataString" ->
Helper.LibCall(com, "Util", "escapeDataString", t, args, i.SignatureArgTypes)
Helper.LibCall(com, "Uri", "escapeDataString", t, args, i.SignatureArgTypes)
|> Some
| "EscapeUriString" ->
Helper.LibCall(com, "Util", "escapeUriString", t, args, i.SignatureArgTypes)
Helper.LibCall(com, "Uri", "escapeUriString", t, args, i.SignatureArgTypes)
|> Some
| "get_IsAbsoluteUri"
| "get_Scheme"
Expand All @@ -3280,11 +3354,7 @@ let uris
| "get_PathAndQuery"
| "get_Query"
| "get_Fragment"
| "get_OriginalString" ->
Naming.removeGetSetPrefix i.CompiledName
|> Naming.lowerFirst
|> getFieldWith r t thisArg.Value
|> Some
| "get_OriginalString" -> makeMemberCall com ctx r t i "Uri" i.CompiledName thisArg args |> Some
| _ -> None

let laziness (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
Expand Down Expand Up @@ -3617,6 +3687,8 @@ let private replacedModules =
Types.timespan, timeSpans
"System.Timers.Timer", timers
"System.Environment", systemEnv
"System.IO.File", files
"System.IO.Path", paths
"System.Globalization.CultureInfo", globalization
"System.Random", random
"System.Threading.CancellationToken", cancels
Expand Down Expand Up @@ -3695,13 +3767,34 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr
fsharpType com methName r t info args
else
fsharpValue com methName r t info args
| "Microsoft.FSharp.Reflection.UnionCaseInfo"
| "Microsoft.FSharp.Reflection.UnionCaseInfo" ->
// UnionCaseInfo from a NewUnionCase quotation deconstruction is a Rust-native
// FSharpUnionCaseInfo carrier; route member access to the quotation runtime.
match thisArg, info.CompiledName with
| Some c, "get_Name" -> Helper.LibCall(com, "quotation", "unionCaseName", t, [ c ], ?loc = r) |> Some
| Some c, "get_Tag" -> Helper.LibCall(com, "quotation", "unionCaseTag", t, [ c ], ?loc = r) |> Some
| _ -> None
| "System.Reflection.PropertyInfo"
| "System.Reflection.ParameterInfo"
| "System.Reflection.MethodBase"
| "System.Reflection.MethodInfo"
| "System.Reflection.MemberInfo" ->
// Name/DeclaringType are inherited from MemberInfo, so `mi.Name` / `mi.DeclaringType`
// on a Call quotation binding arrive here even though the binding's type is MethodInfo.
// Detect that carrier via the thisArg's Fable type and route to the quotation runtime:
// get_Name -> a dedicated accessor (not the generic reflection name<T>()); DeclaringType
// -> the declaring-type fullname boxed as System.Type, so mi.DeclaringType.FullName works.
let isMethodInfoCarrier (c: Expr) =
match c.Type with
| DeclaredType(e, _) -> e.FullName = "System.Reflection.MethodInfo"
| _ -> false

match thisArg, info.CompiledName with
| Some c, "get_Name" when isMethodInfoCarrier c ->
Helper.LibCall(com, "quotation", "methodName", t, [ c ], ?loc = r) |> Some
| Some c, "get_DeclaringType" when isMethodInfoCarrier c ->
Helper.LibCall(com, "quotation", "methodDeclaringType", t, [ c ], ?loc = r)
|> Some
| Some c, "get_Tag" -> makeStrConst "tag" |> getExpr r t c |> Some
| Some c, "get_ReturnType" -> makeStrConst "returnType" |> getExpr r t c |> Some
| Some c, "GetParameters" -> makeStrConst "parameters" |> getExpr r t c |> Some
Expand All @@ -3714,9 +3807,12 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr
match c with
| Value(TypeInfo(exprType, _), loc) ->
getTypeName com ctx loc exprType |> StringConstant |> makeValue r |> Some
| c -> Helper.LibCall(com, "Reflection", "name", t, [ c ], ?loc = r) |> Some
// Runtime PropertyInfo (e.g. a record field info): read its carried name.
// Distinct from the generic type-name helper `name<T>()` to avoid a clash.
| c -> Helper.LibCall(com, "Reflection", "propertyName", t, [ c ], ?loc = r) |> Some
| _ -> None
| _ -> None
// F# Quotations
| typeName -> Quotations.tryQuotationCall "quotation" com ctx r t info thisArg args typeName

let tryBaseConstructor com ctx (ent: EntityRef) (argTypes: Lazy<Type list>) genArgs args =
match ent.FullName with
Expand Down
11 changes: 7 additions & 4 deletions src/fable-library-rust/src/Async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,11 @@ pub mod Monitor_ {
LOCKS.get_or_init(|| RwLock::new(HashSet::new()))
}

pub fn enter<T>(o: LrcPtr<T>) {
let p = Arc::<T>::as_ptr(&o) as usize;
// `?Sized` so a boxed `obj` (LrcPtr<dyn Any>) can be locked on: casting to
// `*const ()` first discards the vtable of a fat pointer, leaving the data
// address, which is what identifies the lock.
pub fn enter<T: ?Sized>(o: LrcPtr<T>) {
let p = Arc::<T>::as_ptr(&o) as *const () as usize;
loop {
let otherHasLock = try_init_and_get_locks().read().unwrap().get(&p).is_some();
if otherHasLock {
Expand All @@ -178,8 +181,8 @@ pub mod Monitor_ {
}
}

pub fn exit<T>(o: LrcPtr<T>) {
let p = Arc::<T>::as_ptr(&o) as usize;
pub fn exit<T: ?Sized>(o: LrcPtr<T>) {
let p = Arc::<T>::as_ptr(&o) as *const () as usize;
let hasRemoved = try_init_and_get_locks().write().unwrap().remove(&p);
if (!hasRemoved) {
panic!("Not removed {}", p)
Expand Down
21 changes: 21 additions & 0 deletions src/fable-library-rust/src/Environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#[cfg(not(feature = "no_std"))]
pub mod Environment_ {
use crate::Native_::NullableRef;
use crate::String_::{fromString, string};

// .NET's Environment.GetEnvironmentVariable returns null when the
// variable is not set, so we map a missing/invalid value to a null string.
pub fn getEnvironmentVariable(name: string) -> string {
match std::env::var(name.as_str()) {
Ok(value) => fromString(value),
Err(_) => <string as NullableRef>::null(),
}
}

pub fn getCurrentDirectory() -> string {
match std::env::current_dir() {
Ok(path) => fromString(path.to_string_lossy().into_owned()),
Err(_) => string(""),
}
}
}
Loading
Loading