mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
Merge pull request #5238 from roc-lang/glue-towards-ts
Move RustGlue.roc closer to what we have on the ts branch
This commit is contained in:
commit
0bb8c60b93
11 changed files with 198 additions and 365 deletions
|
@ -41,6 +41,8 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
|||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_alloc").unwrap();
|
||||
fn_val.set_linkage(Linkage::Internal);
|
||||
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let size_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
@ -135,6 +137,8 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
|||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_dealloc").unwrap();
|
||||
fn_val.set_linkage(Linkage::Internal);
|
||||
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
|
5
crates/glue/platform/File.roc
Normal file
5
crates/glue/platform/File.roc
Normal file
|
@ -0,0 +1,5 @@
|
|||
interface File
|
||||
exposes [File]
|
||||
imports []
|
||||
|
||||
File : { name : Str, content : Str }
|
11
crates/glue/platform/InternalTypeId.roc
Normal file
11
crates/glue/platform/InternalTypeId.roc
Normal file
|
@ -0,0 +1,11 @@
|
|||
interface InternalTypeId
|
||||
exposes [InternalTypeId, fromNat, toNat]
|
||||
imports []
|
||||
|
||||
InternalTypeId : Nat
|
||||
|
||||
toNat : InternalTypeId -> Nat
|
||||
toNat = \x -> x
|
||||
|
||||
fromNat : Nat -> InternalTypeId
|
||||
fromNat = \x -> x
|
|
@ -1,68 +1,8 @@
|
|||
platform "roc-lang/glue"
|
||||
requires {} { makeGlue : List Types -> Result (List File) Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [makeGlueForHost]
|
||||
interface Shape
|
||||
exposes [Shape, RocNum, RocTagUnion, RocStructFields, RocFn, RocSingleTagPayload]
|
||||
imports [TypeId.{ TypeId }]
|
||||
|
||||
makeGlueForHost : List Types -> Result (List File) Str
|
||||
makeGlueForHost = \x -> makeGlue x
|
||||
|
||||
File : { name : Str, content : Str }
|
||||
|
||||
# TODO move into separate Target.roc interface once glue works across interfaces.
|
||||
Target : {
|
||||
architecture : Architecture,
|
||||
operatingSystem : OperatingSystem,
|
||||
}
|
||||
|
||||
Architecture : [
|
||||
Aarch32,
|
||||
Aarch64,
|
||||
Wasm32,
|
||||
X86x32,
|
||||
X86x64,
|
||||
]
|
||||
|
||||
OperatingSystem : [
|
||||
Windows,
|
||||
Unix,
|
||||
Wasi,
|
||||
]
|
||||
|
||||
# TODO change this to an opaque type once glue supports abilities.
|
||||
TypeId : Nat
|
||||
# has [
|
||||
# Eq {
|
||||
# isEq: isEqTypeId,
|
||||
# },
|
||||
# Hash {
|
||||
# hash: hashTypeId,
|
||||
# }
|
||||
# ]
|
||||
# isEqTypeId = \@TypeId lhs, @TypeId rhs -> lhs == rhs
|
||||
# hashTypeId = \hasher, @TypeId id -> Hash.hash hasher id
|
||||
# TODO: switch AssocList uses to Dict once roc_std is updated.
|
||||
Tuple1 : [T Str TypeId]
|
||||
Tuple2 : [T TypeId (List TypeId)]
|
||||
|
||||
Types : {
|
||||
# These are all indexed by TypeId
|
||||
types : List RocType,
|
||||
sizes : List U32,
|
||||
aligns : List U32,
|
||||
|
||||
# Needed to check for duplicates
|
||||
typesByName : List Tuple1,
|
||||
|
||||
## Dependencies - that is, which type depends on which other type.
|
||||
## This is important for declaration order in C; we need to output a
|
||||
## type declaration earlier in the file than where it gets referenced by another type.
|
||||
deps : List Tuple2,
|
||||
target : Target,
|
||||
}
|
||||
|
||||
RocType : [
|
||||
Shape : [
|
||||
RocStr,
|
||||
Bool,
|
||||
RocResult TypeId TypeId,
|
22
crates/glue/platform/Target.roc
Normal file
22
crates/glue/platform/Target.roc
Normal file
|
@ -0,0 +1,22 @@
|
|||
interface Target
|
||||
exposes [Target, Architecture, OperatingSystem]
|
||||
imports []
|
||||
|
||||
Target : {
|
||||
architecture : Architecture,
|
||||
operatingSystem : OperatingSystem,
|
||||
}
|
||||
|
||||
Architecture : [
|
||||
Aarch32,
|
||||
Aarch64,
|
||||
Wasm32,
|
||||
X86x32,
|
||||
X86x64,
|
||||
]
|
||||
|
||||
OperatingSystem : [
|
||||
Windows,
|
||||
Unix,
|
||||
Wasi,
|
||||
]
|
5
crates/glue/platform/TypeId.roc
Normal file
5
crates/glue/platform/TypeId.roc
Normal file
|
@ -0,0 +1,5 @@
|
|||
interface TypeId
|
||||
exposes [TypeId]
|
||||
imports [InternalTypeId.{ InternalTypeId }]
|
||||
|
||||
TypeId : InternalTypeId
|
61
crates/glue/platform/Types.roc
Normal file
61
crates/glue/platform/Types.roc
Normal file
|
@ -0,0 +1,61 @@
|
|||
interface Types
|
||||
exposes [Types, shape, size, alignment, target, walkShapes]
|
||||
imports [Shape.{ Shape }, TypeId.{ TypeId }, Target.{ Target }, InternalTypeId]
|
||||
|
||||
# TODO: switch AssocList uses to Dict once roc_std is updated.
|
||||
Tuple1 : [T Str TypeId]
|
||||
Tuple2 : [T TypeId (List TypeId)]
|
||||
|
||||
Types := {
|
||||
# These are all indexed by TypeId
|
||||
types : List Shape,
|
||||
sizes : List U32,
|
||||
aligns : List U32,
|
||||
|
||||
# Needed to check for duplicates
|
||||
typesByName : List Tuple1,
|
||||
|
||||
## Dependencies - that is, which type depends on which other type.
|
||||
## This is important for declaration order in C; we need to output a
|
||||
## type declaration earlier in the file than where it gets referenced by another type.
|
||||
deps : List Tuple2,
|
||||
target : Target,
|
||||
}
|
||||
|
||||
target : Types -> Target
|
||||
target = \@Types types -> types.target
|
||||
|
||||
walkShapes : Types, state, (state, Shape, TypeId -> state) -> state
|
||||
walkShapes = \@Types { types: shapes }, originalState, update ->
|
||||
List.walk shapes { index: 0, state: originalState } \{ index, state }, elem ->
|
||||
id = InternalTypeId.fromNat index
|
||||
|
||||
{ index: index + 1, state: update state elem id }
|
||||
|> .state
|
||||
|
||||
shape : Types, TypeId -> Shape
|
||||
shape = \@Types types, id ->
|
||||
when List.get types.types (InternalTypeId.toNat id) is
|
||||
Ok answer -> answer
|
||||
Err OutOfBounds ->
|
||||
idStr = Num.toStr (InternalTypeId.toNat id)
|
||||
|
||||
crash "TypeId #\(idStr) was not found in Types. This should never happen, and means there was a bug in `roc glue`. If you have time, please open an issue at <https://github.com/roc-lang/roc/issues>"
|
||||
|
||||
alignment : Types, TypeId -> U32
|
||||
alignment = \@Types types, id ->
|
||||
when List.get types.aligns (InternalTypeId.toNat id) is
|
||||
Ok answer -> answer
|
||||
Err OutOfBounds ->
|
||||
idStr = Num.toStr (InternalTypeId.toNat id)
|
||||
|
||||
crash "TypeId #\(idStr) was not found in Types. This should never happen, and means there was a bug in `roc glue`. If you have time, please open an issue at <https://github.com/roc-lang/roc/issues>"
|
||||
|
||||
size : Types, TypeId -> U32
|
||||
size = \@Types types, id ->
|
||||
when List.get types.sizes (InternalTypeId.toNat id) is
|
||||
Ok answer -> answer
|
||||
Err OutOfBounds ->
|
||||
idStr = Num.toStr (InternalTypeId.toNat id)
|
||||
|
||||
crash "TypeId #\(idStr) was not found in Types. This should never happen, and means there was a bug in `roc glue`. If you have time, please open an issue at <https://github.com/roc-lang/roc/issues>"
|
9
crates/glue/platform/main.roc
Normal file
9
crates/glue/platform/main.roc
Normal file
|
@ -0,0 +1,9 @@
|
|||
platform "roc-lang/glue"
|
||||
requires {} { makeGlue : List Types -> Result (List File) Str }
|
||||
exposes [Shape, File, Types, TypeId, Target]
|
||||
packages {}
|
||||
imports [Types.{ Types }, File.{ File }]
|
||||
provides [makeGlueForHost]
|
||||
|
||||
makeGlueForHost : List Types -> Result (List File) Str
|
||||
makeGlueForHost = \types -> makeGlue types
|
|
@ -1,12 +1,14 @@
|
|||
app "rust-glue"
|
||||
packages { pf: "RocType.roc" }
|
||||
imports []
|
||||
packages { pf: "../platform/main.roc" }
|
||||
imports [pf.Types.{ Types }, pf.File.{ File }, pf.TypeId.{ TypeId }]
|
||||
provides [makeGlue] to pf
|
||||
|
||||
makeGlue = \types ->
|
||||
makeGlue : List Types -> Result (List File) Str
|
||||
makeGlue = \typesByArch ->
|
||||
modFileContent =
|
||||
List.walk types "" \content, { target } ->
|
||||
archStr = archName target.architecture
|
||||
List.walk typesByArch "" \content, types ->
|
||||
arch = (Types.target types).architecture
|
||||
archStr = archName arch
|
||||
|
||||
Str.concat
|
||||
content
|
||||
|
@ -18,15 +20,15 @@ makeGlue = \types ->
|
|||
|
||||
"""
|
||||
|
||||
types
|
||||
|> List.map typesWithDict
|
||||
typesByArch
|
||||
|> List.map convertTypesToFile
|
||||
|> List.append { name: "mod.rs", content: modFileContent }
|
||||
|> Ok
|
||||
|
||||
convertTypesToFile : Types -> File
|
||||
convertTypesToFile = \types ->
|
||||
content =
|
||||
walkWithIndex types.types fileHeader \buf, id, type ->
|
||||
Types.walkShapes types fileHeader \buf, type, id ->
|
||||
when type is
|
||||
Struct { name, fields } ->
|
||||
generateStruct buf types id name fields Public
|
||||
|
@ -84,13 +86,16 @@ convertTypesToFile = \types ->
|
|||
# These types don't need to be declared in Rust.
|
||||
# TODO: Eventually we want to generate roc_std. So these types will need to be emitted.
|
||||
buf
|
||||
archStr = archName types.target.architecture
|
||||
|
||||
arch = (Types.target types).architecture
|
||||
archStr = archName arch
|
||||
|
||||
{
|
||||
name: "\(archStr).rs",
|
||||
content,
|
||||
}
|
||||
|
||||
generateStruct : Str, Types, TypeId, _, _, _ -> Str
|
||||
generateStruct = \buf, types, id, name, structFields, visibility ->
|
||||
escapedName = escapeKW name
|
||||
repr =
|
||||
|
@ -108,7 +113,7 @@ generateStruct = \buf, types, id, name, structFields, visibility ->
|
|||
Public -> "pub"
|
||||
Private -> ""
|
||||
|
||||
structType = getType types id
|
||||
structType = Types.shape types id
|
||||
|
||||
buf
|
||||
|> generateDeriveStr types structType IncludeDebug
|
||||
|
@ -221,7 +226,7 @@ generateNonRecursiveTagUnion = \buf, types, id, name, tags, discriminantSize, di
|
|||
"""
|
||||
|> Str.concat "// TODO: NonRecursive TagUnion constructor impls\n\n"
|
||||
|> \b ->
|
||||
type = getType types id
|
||||
type = Types.shape types id
|
||||
if cannotDeriveCopy types type then
|
||||
# A custom drop impl is only needed when we can't derive copy.
|
||||
b
|
||||
|
@ -285,7 +290,7 @@ generateTagUnionDropPayload = \buf, types, selfMut, tags, discriminantName, disc
|
|||
buf
|
||||
|> writeTagImpls tags discriminantName indents \name, payload ->
|
||||
when payload is
|
||||
Some id if cannotDeriveCopy types (getType types id) ->
|
||||
Some id if cannotDeriveCopy types (Types.shape types id) ->
|
||||
"unsafe {{ core::mem::ManuallyDrop::drop(&mut \(selfMut).\(name)) }},"
|
||||
|
||||
_ ->
|
||||
|
@ -313,6 +318,7 @@ writeTagImpls = \buf, tags, discriminantName, indents, f ->
|
|||
|> writeIndents indents
|
||||
|> Str.concat "}\n"
|
||||
|
||||
generateTagUnionSizer : Str, Types, TypeId, _ -> Str
|
||||
generateTagUnionSizer = \buf, types, id, tags ->
|
||||
if List.len tags > 1 then
|
||||
# When there's a discriminant (so, multiple tags) and there is
|
||||
|
@ -355,7 +361,7 @@ generateUnionField = \types ->
|
|||
typeStr = typeName types id
|
||||
escapedFieldName = escapeKW fieldName
|
||||
|
||||
type = getType types id
|
||||
type = Types.shape types id
|
||||
fullTypeStr =
|
||||
if cannotDeriveCopy types type then
|
||||
# types with pointers need ManuallyDrop
|
||||
|
@ -614,40 +620,40 @@ cannotDeriveCopy = \types, type ->
|
|||
Unit | Unsized | EmptyTagUnion | Bool | Num _ | TagUnion (Enumeration _) | Function _ -> Bool.false
|
||||
RocStr | RocList _ | RocDict _ _ | RocSet _ | RocBox _ | TagUnion (NullableUnwrapped _) | TagUnion (NullableWrapped _) | TagUnion (Recursive _) | TagUnion (NonNullableUnwrapped _) | RecursivePointer _ -> Bool.true
|
||||
TagUnion (SingleTagStruct { payload: HasNoClosure fields }) ->
|
||||
List.any fields \{ id } -> cannotDeriveCopy types (getType types id)
|
||||
List.any fields \{ id } -> cannotDeriveCopy types (Types.shape types id)
|
||||
|
||||
TagUnion (SingleTagStruct { payload: HasClosure fields }) ->
|
||||
List.any fields \{ id } -> cannotDeriveCopy types (getType types id)
|
||||
List.any fields \{ id } -> cannotDeriveCopy types (Types.shape types id)
|
||||
|
||||
TagUnion (NonRecursive { tags }) ->
|
||||
List.any tags \{ payload } ->
|
||||
when payload is
|
||||
Some id -> cannotDeriveCopy types (getType types id)
|
||||
Some id -> cannotDeriveCopy types (Types.shape types id)
|
||||
None -> Bool.false
|
||||
|
||||
RocResult okId errId ->
|
||||
cannotDeriveCopy types (getType types okId)
|
||||
|| cannotDeriveCopy types (getType types errId)
|
||||
cannotDeriveCopy types (Types.shape types okId)
|
||||
|| cannotDeriveCopy types (Types.shape types errId)
|
||||
|
||||
Struct { fields: HasNoClosure fields } | TagUnionPayload { fields: HasNoClosure fields } ->
|
||||
List.any fields \{ id } -> cannotDeriveCopy types (getType types id)
|
||||
List.any fields \{ id } -> cannotDeriveCopy types (Types.shape types id)
|
||||
|
||||
Struct { fields: HasClosure fields } | TagUnionPayload { fields: HasClosure fields } ->
|
||||
List.any fields \{ id } -> cannotDeriveCopy types (getType types id)
|
||||
List.any fields \{ id } -> cannotDeriveCopy types (Types.shape types id)
|
||||
|
||||
cannotDeriveDefault = \types, type ->
|
||||
when type is
|
||||
Unit | Unsized | EmptyTagUnion | TagUnion _ | RocResult _ _ | RecursivePointer _ | Function _ -> Bool.true
|
||||
RocStr | Bool | Num _ | Struct { fields: HasClosure _ } | TagUnionPayload { fields: HasClosure _ } -> Bool.false
|
||||
RocList id | RocSet id | RocBox id ->
|
||||
cannotDeriveDefault types (getType types id)
|
||||
cannotDeriveDefault types (Types.shape types id)
|
||||
|
||||
RocDict keyId valId ->
|
||||
cannotDeriveCopy types (getType types keyId)
|
||||
|| cannotDeriveCopy types (getType types valId)
|
||||
cannotDeriveCopy types (Types.shape types keyId)
|
||||
|| cannotDeriveCopy types (Types.shape types valId)
|
||||
|
||||
Struct { fields: HasNoClosure fields } | TagUnionPayload { fields: HasNoClosure fields } ->
|
||||
List.any fields \{ id } -> cannotDeriveDefault types (getType types id)
|
||||
List.any fields \{ id } -> cannotDeriveDefault types (Types.shape types id)
|
||||
|
||||
hasFloat = \types, type ->
|
||||
hasFloatHelp types type (Set.empty {})
|
||||
|
@ -664,40 +670,40 @@ hasFloatHelp = \types, type, doNotRecurse ->
|
|||
|
||||
Unit | Unsized | EmptyTagUnion | RocStr | Bool | TagUnion (Enumeration _) | Function _ -> Bool.false
|
||||
RocList id | RocSet id | RocBox id ->
|
||||
hasFloatHelp types (getType types id) doNotRecurse
|
||||
hasFloatHelp types (Types.shape types id) doNotRecurse
|
||||
|
||||
RocDict id0 id1 | RocResult id0 id1 ->
|
||||
hasFloatHelp types (getType types id0) doNotRecurse
|
||||
|| hasFloatHelp types (getType types id1) doNotRecurse
|
||||
hasFloatHelp types (Types.shape types id0) doNotRecurse
|
||||
|| hasFloatHelp types (Types.shape types id1) doNotRecurse
|
||||
|
||||
Struct { fields: HasNoClosure fields } | TagUnionPayload { fields: HasNoClosure fields } ->
|
||||
List.any fields \{ id } -> hasFloatHelp types (getType types id) doNotRecurse
|
||||
List.any fields \{ id } -> hasFloatHelp types (Types.shape types id) doNotRecurse
|
||||
|
||||
Struct { fields: HasClosure fields } | TagUnionPayload { fields: HasClosure fields } ->
|
||||
List.any fields \{ id } -> hasFloatHelp types (getType types id) doNotRecurse
|
||||
List.any fields \{ id } -> hasFloatHelp types (Types.shape types id) doNotRecurse
|
||||
|
||||
TagUnion (SingleTagStruct { payload: HasNoClosure fields }) ->
|
||||
List.any fields \{ id } -> hasFloatHelp types (getType types id) doNotRecurse
|
||||
List.any fields \{ id } -> hasFloatHelp types (Types.shape types id) doNotRecurse
|
||||
|
||||
TagUnion (SingleTagStruct { payload: HasClosure fields }) ->
|
||||
List.any fields \{ id } -> hasFloatHelp types (getType types id) doNotRecurse
|
||||
List.any fields \{ id } -> hasFloatHelp types (Types.shape types id) doNotRecurse
|
||||
|
||||
TagUnion (Recursive { tags }) ->
|
||||
List.any tags \{ payload } ->
|
||||
when payload is
|
||||
Some id -> hasFloatHelp types (getType types id) doNotRecurse
|
||||
Some id -> hasFloatHelp types (Types.shape types id) doNotRecurse
|
||||
None -> Bool.false
|
||||
|
||||
TagUnion (NonRecursive { tags }) ->
|
||||
List.any tags \{ payload } ->
|
||||
when payload is
|
||||
Some id -> hasFloatHelp types (getType types id) doNotRecurse
|
||||
Some id -> hasFloatHelp types (Types.shape types id) doNotRecurse
|
||||
None -> Bool.false
|
||||
|
||||
TagUnion (NullableWrapped { tags }) ->
|
||||
List.any tags \{ payload } ->
|
||||
when payload is
|
||||
Some id -> hasFloatHelp types (getType types id) doNotRecurse
|
||||
Some id -> hasFloatHelp types (Types.shape types id) doNotRecurse
|
||||
None -> Bool.false
|
||||
|
||||
TagUnion (NonNullableUnwrapped { payload }) ->
|
||||
|
@ -706,7 +712,7 @@ hasFloatHelp = \types, type, doNotRecurse ->
|
|||
else
|
||||
nextDoNotRecurse = Set.insert doNotRecurse payload
|
||||
|
||||
hasFloatHelp types (getType types payload) nextDoNotRecurse
|
||||
hasFloatHelp types (Types.shape types payload) nextDoNotRecurse
|
||||
|
||||
TagUnion (NullableUnwrapped { nonNullPayload }) ->
|
||||
if Set.contains doNotRecurse nonNullPayload then
|
||||
|
@ -714,7 +720,7 @@ hasFloatHelp = \types, type, doNotRecurse ->
|
|||
else
|
||||
nextDoNotRecurse = Set.insert doNotRecurse nonNullPayload
|
||||
|
||||
hasFloatHelp types (getType types nonNullPayload) nextDoNotRecurse
|
||||
hasFloatHelp types (Types.shape types nonNullPayload) nextDoNotRecurse
|
||||
|
||||
RecursivePointer payload ->
|
||||
if Set.contains doNotRecurse payload then
|
||||
|
@ -722,10 +728,10 @@ hasFloatHelp = \types, type, doNotRecurse ->
|
|||
else
|
||||
nextDoNotRecurse = Set.insert doNotRecurse payload
|
||||
|
||||
hasFloatHelp types (getType types payload) nextDoNotRecurse
|
||||
hasFloatHelp types (Types.shape types payload) nextDoNotRecurse
|
||||
|
||||
typeName = \types, id ->
|
||||
when getType types id is
|
||||
when Types.shape types id is
|
||||
Unit -> "()"
|
||||
Unsized -> "roc_std::RocList<u8>"
|
||||
EmptyTagUnion -> "std::convert::Infallible"
|
||||
|
@ -785,27 +791,12 @@ typeName = \types, id ->
|
|||
TagUnion (SingleTagStruct { name }) -> escapeKW name
|
||||
Function { functionName } -> escapeKW functionName
|
||||
|
||||
getType = \types, id ->
|
||||
when List.get types.types id is
|
||||
Ok type -> type
|
||||
Err _ -> crash "unreachable"
|
||||
|
||||
getSizeRoundedToAlignment = \types, id ->
|
||||
alignment = getAlignment types id
|
||||
alignment = Types.alignment types id
|
||||
|
||||
getSizeIgnoringAlignment types id
|
||||
Types.size types id
|
||||
|> roundUpToAlignment alignment
|
||||
|
||||
getSizeIgnoringAlignment = \types, id ->
|
||||
when List.get types.sizes id is
|
||||
Ok size -> size
|
||||
Err _ -> crash "unreachable"
|
||||
|
||||
getAlignment = \types, id ->
|
||||
when List.get types.aligns id is
|
||||
Ok align -> align
|
||||
Err _ -> crash "unreachable"
|
||||
|
||||
roundUpToAlignment = \width, alignment ->
|
||||
when alignment is
|
||||
0 -> width
|
||||
|
@ -931,14 +922,3 @@ escapeKW = \input ->
|
|||
"r#\(input)"
|
||||
else
|
||||
input
|
||||
|
||||
# This is a temporary helper until roc_std::roc_dict is update.
|
||||
# after that point, Dict will be passed in directly.
|
||||
typesWithDict = \{ types, sizes, aligns, typesByName, deps, target } -> {
|
||||
types,
|
||||
sizes,
|
||||
aligns,
|
||||
typesByName: Dict.fromList typesByName,
|
||||
deps: Dict.fromList deps,
|
||||
target,
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
//! the plan is to support any language via a plugin model.
|
||||
pub mod enums;
|
||||
pub mod load;
|
||||
pub mod roc_helpers;
|
||||
pub mod roc_type;
|
||||
pub mod rust_glue;
|
||||
pub mod structs;
|
||||
|
@ -14,3 +13,34 @@ pub mod types;
|
|||
pub mod glue;
|
||||
|
||||
pub use load::generate;
|
||||
|
||||
// required because we use roc_std here
|
||||
mod roc_externs {
|
||||
use core::ffi::c_void;
|
||||
|
||||
/// # Safety
|
||||
/// This just delegates to libc::malloc, so it's equally safe.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
libc::malloc(size)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This just delegates to libc::realloc, so it's equally safe.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
libc::realloc(c_ptr, new_size)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This just delegates to libc::free, so it's equally safe.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
libc::free(c_ptr)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,234 +0,0 @@
|
|||
use libc::{c_char, c_int, c_void, size_t};
|
||||
#[cfg(unix)]
|
||||
use libc::{c_uint, mode_t, off_t, pid_t};
|
||||
|
||||
use roc_std::RocStr;
|
||||
|
||||
// These are required to ensure rust adds these functions to the final binary even though they are never used.
|
||||
#[used]
|
||||
pub static ROC_ALLOC: unsafe extern "C" fn(usize, u32) -> *mut c_void = roc_alloc;
|
||||
|
||||
#[used]
|
||||
pub static ROC_REALLOC: unsafe extern "C" fn(*mut c_void, usize, usize, u32) -> *mut c_void =
|
||||
roc_realloc;
|
||||
|
||||
#[used]
|
||||
pub static ROC_DEALLOC: unsafe extern "C" fn(*mut c_void, u32) = roc_dealloc;
|
||||
|
||||
#[used]
|
||||
pub static ROC_PANIC: unsafe extern "C" fn(&RocStr, u32) = roc_panic;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[used]
|
||||
pub static ROC_GETPPID: unsafe extern "C" fn() -> pid_t = roc_getppid;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[used]
|
||||
pub static ROC_MMAP: unsafe extern "C" fn(
|
||||
*mut c_void,
|
||||
size_t,
|
||||
c_int,
|
||||
c_int,
|
||||
c_int,
|
||||
off_t,
|
||||
) -> *mut c_void = roc_mmap;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[used]
|
||||
pub static ROC_SHM_OPEN: unsafe extern "C" fn(*const c_char, c_int, mode_t) -> c_int = roc_shm_open;
|
||||
|
||||
#[used]
|
||||
pub static ROC_MEMCPY: unsafe extern "C" fn(*mut c_void, *mut c_void, usize) -> *mut c_void =
|
||||
roc_memcpy;
|
||||
|
||||
#[used]
|
||||
pub static ROC_MEMSET: unsafe extern "C" fn(*mut c_void, i32, usize) -> *mut c_void = roc_memset;
|
||||
|
||||
/// # Safety
|
||||
/// This just delegates to libc::malloc, so it's equally safe.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
libc::malloc(size)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This just delegates to libc::realloc, so it's equally safe.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
libc::realloc(c_ptr, new_size)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This just delegates to libc::free, so it's equally safe.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
libc::free(c_ptr)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_panic(msg: &RocStr, tag_id: u32) {
|
||||
match tag_id {
|
||||
0 => {
|
||||
eprintln!("Roc crashed with:\n\n\t{}\n", msg.as_str());
|
||||
|
||||
print_backtrace();
|
||||
std::process::exit(1);
|
||||
}
|
||||
1 => {
|
||||
eprintln!("The program crashed with:\n\n\t{}\n", msg.as_str());
|
||||
|
||||
print_backtrace();
|
||||
std::process::exit(1);
|
||||
}
|
||||
code => {
|
||||
eprintln!("Roc crashed with error code:\n\n\t{}\n", code);
|
||||
|
||||
print_backtrace();
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_getppid() -> pid_t {
|
||||
unsafe { libc::getppid() }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This just delegates to libc::mmap, and so is equally safe.
|
||||
#[cfg(unix)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_mmap(
|
||||
addr: *mut c_void,
|
||||
len: size_t,
|
||||
prot: c_int,
|
||||
flags: c_int,
|
||||
fd: c_int,
|
||||
offset: off_t,
|
||||
) -> *mut c_void {
|
||||
libc::mmap(addr, len, prot, flags, fd, offset)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This just delegates to libc::shm_open, and so is equally safe.
|
||||
#[cfg(unix)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_shm_open(name: *const c_char, oflag: c_int, mode: mode_t) -> c_int {
|
||||
libc::shm_open(name, oflag, mode as c_uint)
|
||||
}
|
||||
|
||||
fn print_backtrace() {
|
||||
eprintln!("Here is the call stack that led to the crash:\n");
|
||||
|
||||
let mut entries = Vec::new();
|
||||
|
||||
#[derive(Default)]
|
||||
struct Entry {
|
||||
pub fn_name: String,
|
||||
pub filename: Option<String>,
|
||||
pub line: Option<u32>,
|
||||
pub col: Option<u32>,
|
||||
}
|
||||
|
||||
backtrace::trace(|frame| {
|
||||
backtrace::resolve_frame(frame, |symbol| {
|
||||
if let Some(fn_name) = symbol.name() {
|
||||
let fn_name = fn_name.to_string();
|
||||
|
||||
if should_show_in_backtrace(&fn_name) {
|
||||
let mut entry: Entry = Entry {
|
||||
fn_name: format_fn_name(&fn_name),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(path) = symbol.filename() {
|
||||
entry.filename = Some(path.to_string_lossy().into_owned());
|
||||
};
|
||||
|
||||
entry.line = symbol.lineno();
|
||||
entry.col = symbol.colno();
|
||||
|
||||
entries.push(entry);
|
||||
}
|
||||
} else {
|
||||
entries.push(Entry {
|
||||
fn_name: "???".to_string(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
true // keep going to the next frame
|
||||
});
|
||||
|
||||
for entry in entries {
|
||||
eprintln!("\t{}", entry.fn_name);
|
||||
|
||||
if let Some(filename) = entry.filename {
|
||||
eprintln!("\t\t{filename}");
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("\nOptimizations can make this list inaccurate! If it looks wrong, try running without `--optimize` and with `--linker=legacy`\n");
|
||||
}
|
||||
|
||||
fn should_show_in_backtrace(fn_name: &str) -> bool {
|
||||
let is_from_rust = fn_name.contains("::");
|
||||
let is_host_fn = fn_name.starts_with("roc_panic")
|
||||
|| fn_name.starts_with("_Effect_effect")
|
||||
|| fn_name.starts_with("_roc__")
|
||||
|| fn_name.starts_with("rust_main")
|
||||
|| fn_name == "_main";
|
||||
|
||||
!is_from_rust && !is_host_fn
|
||||
}
|
||||
|
||||
fn format_fn_name(fn_name: &str) -> String {
|
||||
// e.g. convert "_Num_sub_a0c29024d3ec6e3a16e414af99885fbb44fa6182331a70ab4ca0886f93bad5"
|
||||
// to ["Num", "sub", "a0c29024d3ec6e3a16e414af99885fbb44fa6182331a70ab4ca0886f93bad5"]
|
||||
let mut pieces_iter = fn_name.split('_');
|
||||
|
||||
if let (_, Some(module_name), Some(name)) =
|
||||
(pieces_iter.next(), pieces_iter.next(), pieces_iter.next())
|
||||
{
|
||||
display_roc_fn(module_name, name)
|
||||
} else {
|
||||
"???".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn display_roc_fn(module_name: &str, fn_name: &str) -> String {
|
||||
let module_name = if module_name == "#UserApp" {
|
||||
"app"
|
||||
} else {
|
||||
module_name
|
||||
};
|
||||
|
||||
let fn_name = if fn_name.parse::<u64>().is_ok() {
|
||||
"(anonymous function)"
|
||||
} else {
|
||||
fn_name
|
||||
};
|
||||
|
||||
format!("\u{001B}[36m{module_name}\u{001B}[39m.{fn_name}")
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This just delegates to libc::memcpy, so it's equally safe.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
|
||||
libc::memcpy(dst, src, n)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This just delegates to libc::memset, so it's equally safe.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
|
||||
libc::memset(dst, c, n)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue