getditto / safer_ffi Goto Github PK
View Code? Open in Web Editor NEWWrite safer FFI code in Rust without polluting it with unsafe code
Home Page: http://getditto.github.io/safer_ffi
License: MIT License
Write safer FFI code in Rust without polluting it with unsafe code
Home Page: http://getditto.github.io/safer_ffi
License: MIT License
I feel like generated typedef should be either foobar_t
or Foobar
. Foobar_t
seems wrong, but that's just my opinion. I understand that the ability to rename types is planned but in the meantime maybe the default format could be altered.
I want to add error reporting in my API along the lines of
#[derive_ReprC]
#[repr(C)]
pub struct Error {
error_code: u32,
error_str: char_p::Box,
}
Error error;
api_func(&error);
printf("%d: %s\n", error.error_code, error.error_str);
free_error(&error); // to free the memory allocated for `error_str`
(just an example, api_func()
returns a result value too)
I tried various approaches on the Rust side, but I cannot make it work; at best it compiles, but in runtime I get a malloc: *** error for object <...>: pointer being freed was not allocated
error. For example, for this code:
#[ffi_export]
fn api_func(error: &mut Error) {
x.error_code = 0;
x.error_str = "error".to_string().try_into().unwrap();
}
or for the example in #30. What is the correct way of doing it in safer_ffi
? It seems that my problem is that I cannot figure out how to tell the compiler not to drop the newly created string.
(Ideally I'd want the reference to be nullable, so that one could call api_func(NULL)
in C, and the error reporting would be skipped, but I'll probably be able to deduce it)
Is there a way to use #[derive_reprC] with other derive attributes for enums?
When trying to compile:
#[derive_ReprC]
#[derive(PartialEq)]
#[repr(i32)]
pub enum Fmi2Status {
Fmi2OK,
Fmi2Warning,
Fmi2Discard,
Fmi2Error,
Fmi2Fatal,
Fmi2Pending,
}
I get:
63 | #[derive(PartialEq)]
| ^^^^^^ no rules expected this token in macro call
I have a function that returns a repr_c::Box<MyOpaqueType<AnotherOpaqueType>>
. It is generating on the C side as a * MyOpaqueType
. instead of a MyOpaqueType_AnotherOpaqueType
. This dilutes the type safety on the C side and makes it easier to accidentally cast a MyOpaqueType<A>
to a MyOpaqueType<B>
.
It would be cool if there was support for safer_ffi
opaque types that don't require boxing because they are Copy
. Ostensibly, one could just look at the size and alignment of the type and create a C type with the same layout. This is what autocxx
does and it seems to work well:
Generated rust code:
#[repr(C, align(4))]
pub struct HmdMatrix34_t {
_pinned: core::marker::PhantomData<core::marker::PhantomPinned>,
_non_send_sync: core::marker::PhantomData<[*const u8; 0]>,
_data: [u8; 48],
}
Corresponding C/C++ POD type:
struct HmdMatrix34_t
{
float m[3][4];
};
I'm assuming that this solution won't work for non-copy types (implying that they have a drop fn and cannot be memcopied), but in my API I have a bunch of opaque types that are Copy
, and I'l really like to avoid boxing all of them, and having the caller have to allocate and drop them all the time
It would be cool if we could tag things like this:
#[derive_ReprC]
#[ReprC::opaque]
pub type ContractDataHandle = arena::Index<ContractData>;
Any ideas how that would work? Would I still need to tag arena::Index
with #[ReprC]
?
#[derive_ReprC]
#[repr(C)]
#[derive(Copy, Clone)]
pub union Foobar {
foo: i32,
bar: f64,
}
This fails to compile with: error: `union`s are not supported yet.
A useful feature of cbindgen is the ability to specify a map of cfgs to defines so these can be emitted in the final header.
If possible this could be also a very useful feature for this project, if anything to make it a a more complete solution. Of course it's possible to do the header manually but that can get pretty cumbersome for bigger projects.
I ran into an annoying issue with BoxDynFnMut1
and friends when you try to pass around a struct with a non-'static
lifetime due to the lack of higher-kinded types.
Basically, I want to let users set their own logging function. In normal Rust you'd write something like this:
pub fn set_logger(log_func: Box<dyn Fn(&log::Record<'_>)>) { ... }
The equivalent using safer_ffi
would be (in theory) something like this:
#[ffi_export]
pub fn set_logger(log_func: for<'a> BoxDynFnMut1<(), LogRecord<'a>>) { ... }
#[derive_ReprC]
#[repr(C)]
pub struct LogRecord<'a> {
level: LogLevel,
target: str_ref<'a>,
}
Obviously for<'a> SomeStruct
isn't valid syntax and you get an "Error: expected trait, found struct BoxDynFnMut1
" error, however without the for<'a>
you get "Error: cannot infer an appropriate lifetime due to conflicting requirements" when you try to use it in practice.
This is a concrete example of the "improve support for closures" mentioned in #47.
Trying to compile this:
struct Shift{}
pub trait GenericCollectionShift<S: Sized + Send + Sync + PartialEq + AsRef<str>> {
}
impl GenericCollectionShift<char_p::Box> for Shift {
}
works on macOS but fails on Ubuntu.
I'm getting
116 | impl GenericCollectionShift<char_p::Box> for Shift {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsRef<str>` is not implemented for `char_p_boxed`
|
note: required by a bound in `GenericCollectionShift`
--> src/types.rs:13:71
|
13 | pub trait GenericCollectionShift<S: Sized + Send + Sync + PartialEq + AsRef<str>> {
| ^^^^^^^^^^ required by this bound in `GenericCollectionShift`
(Note, when tackled, each bullet point will have an associated specific issue or PR).
Out pointers to Option
ought to be more easily constructible;
We are missing a repr_c::Result
type;
We may want to loosen the alignment sanity-validity check requirement on the pointer part of a 0
-long slice;
We are missing repr_c::Arc
& co.;
The smart pointers, repr_c::Box<_>
, and the to-be-added repr_c::Arc<_>
, ought to forward as many traits as possible (e.g., PartialOrd
)
We need more ergonomics for the non-char_p
but still repr_c
string types, which are currently buried under the string
module, with no special mention anywhere;
More generally, we need more documentation, and more example for the most pervasive patterns!
cc @rodoyle @thombles @hamchapman, feel free to add other rough edges you may have found when working with safer-ffi
, be it from Rust or from other languages.
code (partial snippet, cant share all of it):
use crate::object::c_api::ObjectHandle as CObjectHandle;
#[ffi_export]
pub fn Baseline__object<'a>(baseline: &'a Baseline, handle: &CObjectHandle) -> &'a Object {
baseline.object(handle.inner).unwrap()
}
and
pub type ObjectHandle = arena::Index<Object>;
#[cfg(feature = "c_api")]
pub mod c_api {
use super::*;
use derive_more::From;
#[derive(From, Copy, Clone)]
#[derive_ReprC]
#[ReprC::opaque]
pub struct ObjectHandle {
pub inner: super::ObjectHandle,
}
}
error[E0599]: no method named
not
found for typebool
in the current scope
--> client/rust/src/baseline.rs:401:5
|
401 | #[ffi_export]
| ^^^^^^^^^^^^^ method not found inbool
|
::: /home/ryan/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/bit.rs:51:8
|
51 | fn not(self) -> Self::Output;
| --- the method is available forbool
here
|
= help: items from traits can only be used if the trait is in scope
= note: this error originates in the macro::safer_ffi::__ffi_export__
(in Nightly builds, run with -Z macro-backtrace for more info)
help: the following trait is implemented but not in scope; perhaps add ause
for it:
|
392 | use std::ops::Not;
|
Feature support for trait objects and improve support for closures, to use better types in our ffi function definitions, which make writing the SDK glue code easier for now (and automatable in the future). This will, among other things, get rid of 99% of the usages of unsafe in our code base!
ReprC
is only implemented for extern "C" fn (A1, ..., Ak) -> Ret
for any ReprC
type A1, ..., Ak
and Ret
, and for k ≤ 9
.
Sadly, a function such as extern "C" fn (&'_ i32)
does not match the k = 1, A1 = &'_ i32, Ret = ()
case, since A1
there is not a type (which lifetime '_
?).
Instead, such type is actually the type extern "C" for<'any> fn (&'any i32)
, that is, a higher-ranked type, which does not fit that generic impl
.
Granted, that fn()
type cannot be "reached" by the generic impls. But given the knowledge of the actual concrete types involved (e.g., &'_ i32 -> ()
), an impl
for that specific case could be generated.
Sadly, that would be for the downstream user to do, so they wouldn't be able to impl
that directly on the fn()
type. Instead, a newtype
wrapper would be necessary.
Hence the need for extending the functionality of #[derive_ReprC]
so that:
#[derive_ReprC]
#[repr(transparent)]
pub struct MyCb /* = */ (
pub extern "C" fn (&'_ i32),
);
is recognized, and thus allows people to have fields of type MyCb
and use MyCb
as the input or return type of an #[ffi_export]
-ed function.
Once we get to have extern "C" fn(...) -> _
involving higher-ranked lifetimes, another challenging part will be to manage to get ...dynFn...
closure types also involving such higher-ranked lifetimes.
That last part is very likely to take more time to implement, though.
On my system (Ubuntu Linux) I had to add two more libraries (-lpthread -ldl
) in the compilation/linking line for main.c
:
$ cc main.c -o main -L target/debug -ltest_ffi -lpthread -ldl
Maybe worth mentioning this in the README.md
file ...
I have some constants and enums that I would like in the C-headers, but they are not actually used by and ffi_export
ed function, so they don't end up showing up at the moment. Any chance this could be added?
The ffi_export
macro does not seem to support where
clause with lifetime bounds, like this:
See #34
I have reported it to official rust repository
Release that state of the code as a 0.1.0 release of safer-ffi, with a potential series of small blog posts about the stuff done there, and potential book / guide improvements.
Support renaming the types (at the C header layer) for:
Opaque types.
#[ReprC::opaque("name_in_the_c_header")]
everything else
Idea of the syntax: #[ReprC::rename("name_in_the_c_header")]
would probably override the syntax for opaque types for the sake of consistency
#[derive_ReprC(rename = "...")]
would also be possible, but the ::syn
-less equivalent of the ReprC! { ... }
macro would then be left out.
Hi, first of all thanks for the awesome work!
Now, during the use of this library I noticed that my generated header include guard was taking the name I put on the crate manifest and using it but leaving the hyphens as is.
For example, a crate named my-awesome-crate
, using the default builder, would generate a header with the include guard __RUST_MY-AWESOME-CRATE__
I'm receiving the following error in my IDE when using rust-analyzer with VSCode for the default example:
unresolved macro ::safer_ffi::__ffi_export__!
It compiles and runs fine however.
The package name is used as the C# namespace name but sometimes it would be useful to be able to customise the name used for the namespace so that it's different to the package name.
Such as:
repr_c::Box<dyn Fn…>
& repr_c::Arc<dyn Fn…>
- #134repr_c::Box<[_]>
repr_c::Box<dyn CustomReprCTrait>
I'm trying to define a struct that I'm using for serialization/deserialization with Serde like this:
use serde::{Deserialize, Serialize};
#[derive_ReprC]
#[repr(C)]
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub
struct Point {
#[serde(rename = "x_value")]
x: f64,
#[serde(rename = "y_value")]
y: f64,
}
However "safer_ffi" macros seem to generate a conflict since I get the following error at compile time:
cannot find attribute `serde` in this scope
I guess that since #[derive()] is not executed last, some definitions are missing in a previous stage?
Is it possible to add support for #[repr(C, Int)]
enums?
In the Type layout reference it says:
The representation of a repr(C) enum with fields is a repr(C) struct with two fields, also called a "tagged union" in C
I am not sure is this is guaranteed tho. If it is we should be able to transmute both ways right?
Playground test: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=fe82b419608d325d93481155c4458bc8
I was wondering if there is a way to automatically add a prefix to types/functions etc when generating the C definitions, sth like this:
#[ffi_export]
fn foo() {
// ..things
}
#[derive_reprC]
pub struct MyStruct { /* ... */ }
typedef struct { /* .. */ } PFX_MyStruct;
void pfx_foo() {
}
as currently I have to add the PFX
prefix manually to my types and functions in rust, which makes it very unidiomatic, and easy to forget.
I'm getting an error with this example, is there anything I can do about it?
use safer_ffi::prelude::*;
pub use ty::Void;
mod ty {
use safer_ffi::derive_ReprC;
#[derive_ReprC]
#[ReprC::opaque]
/// A type used to represent an untyped pointer
pub struct Void {
_private: (),
}
}
#[derive_ReprC]
#[repr(C)]
#[derive(Clone)]
pub struct CHostFunctionPointers {
/// Get the full CBOR serialized [`ScriptApi`] including components discovered and
/// implemented by other language adapters or the dynamite host.
pub get_full_api: extern "C" fn(dynamite: *const Void) -> repr_c::Vec<u8>,
/// Call a function provided by the scripting API
call_function: extern "C" fn(
dynamite: *const Void,
path: str::Ref,
args: c_slice::Ref<*const Void>,
) -> *const Void,
}
#[derive_ReprC]
#[repr(C)]
pub struct RemoteHostFunctions {
pub dynamite: *const Void,
pub pointers: CHostFunctionPointers,
}
error[E0277]: the trait bound `<CHostFunctionPointers as ReprC>::CLayout: CType` is not satisfied
--> src/lib.rs:32:1
|
32 | #[derive_ReprC]
| ^^^^^^^^^^^^^^^ the trait `CType` is not implemented for `<CHostFunctionPointers as ReprC>::CLayout`
|
= help: see issue #48214
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: implementation of `ReprC` is not general enough
--> src/lib.rs:32:1
|
32 | #[derive_ReprC]
| ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
|
= note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
= note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'0>, slice_ref<'_, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: implementation of `ReprC` is not general enough
--> src/lib.rs:32:1
|
32 | #[derive_ReprC]
| ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
|
= note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
= note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'_>, slice_ref<'0, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: implementation of `ReprC` is not general enough
--> src/lib.rs:32:1
|
32 | #[derive_ReprC]
| ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
|
= note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
= note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'0>, slice_ref<'_, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: implementation of `ReprC` is not general enough
--> src/lib.rs:32:1
|
32 | #[derive_ReprC]
| ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
|
= note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
= note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'_>, slice_ref<'0, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: implementation of `ReprC` is not general enough
--> src/lib.rs:32:1
|
32 | #[derive_ReprC]
| ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
|
= note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
= note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'0>, slice_ref<'_, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: implementation of `ReprC` is not general enough
--> src/lib.rs:32:1
|
32 | #[derive_ReprC]
| ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
|
= note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
= note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'_>, slice_ref<'0, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `<CHostFunctionPointers as ReprC>::CLayout: CType` is not satisfied
--> src/lib.rs:32:1
|
32 | #[derive_ReprC]
| ^^^^^^^^^^^^^^^ the trait `CType` is not implemented for `<CHostFunctionPointers as ReprC>::CLayout`
|
= help: see issue #48214
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: implementation of `ReprC` is not general enough
--> src/lib.rs:32:1
|
32 | #[derive_ReprC]
| ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
|
= note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
= note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'0>, slice_ref<'_, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: implementation of `ReprC` is not general enough
--> src/lib.rs:32:1
|
32 | #[derive_ReprC]
| ^^^^^^^^^^^^^^^ implementation of `ReprC` is not general enough
|
= note: `ReprC` would have to be implemented for the type `for<'r, 's> extern "C" fn(*const ty::Void, str_ref<'r>, slice_ref<'s, *const ty::Void>) -> *const ty::Void`
= note: ...but `ReprC` is actually implemented for the type `extern "C" fn(*const ty::Void, str_ref<'_>, slice_ref<'0, *const ty::Void>) -> *const ty::Void`, for some specific lifetime `'0`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 10 previous errors
For more information about this error, try `rustc --explain E0277`.
error: could not compile `ffi_example`
Apparently it works with feature(trivial_bounds)
.
Edit: I don't think the trivial_bounds
feature fixes everything, actually.
I would like to generate the headers as part of my build.rs
script, but it seems to not pick up the actual content currently, as it just generates the default header file, without my content.
Redesign the trait / crate architecture so as to setup the state to more easily add automatic glue code generation for higher-level languages:
move the header code from CType to ReprC;
parametrize the hard-coded C header code through a layer of "language syntax" indirection, since that layer of indirection ought to be the only thing "mobile" / changing from one SDK to another.
If the use of mini_paste isn't too performance critical perhaps moving to paste would make sense since mini_paste hasn't been updated in a couple years.
Compiling mini_paste v0.1.11
error[E0432]: unresolved imports `proc_macro::item`, `proc_macro::__expr_hack__`
--> /Users/sam/.cargo/registry/src/github.com-1ecc6299db9ec823/mini_paste-0.1.11/src/lib.rs:11:5
|
11 | item as __item__,
| ----^^^^^^^^^^^^
| |
| no `item` in the root
| help: a similar name exists in the module: `iter`
12 | __expr_hack__,
| ^^^^^^^^^^^^^ no `__expr_hack__` in the root
For more information about this error, try `rustc --explain E0432`.
error: could not compile `mini_paste` due to previous error
It is hard to bounce between Guide, Rust Gen Docs, and Github Repo.
More links the better, if you know of some link that we should add, just go ahead and add it!
The code below produces this warning:
warning: `extern` fn uses type `Option<slice_ref<Foo>>`, which is not FFI-safe
help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
use safer_ffi::prelude::*;
struct Foo<'a> {
name: Option<char_p::Ref<'a>>,
}
#[ffi_export]
fn foo<'a>(
p0: Option<char_p::Ref>, // this is fine...
p1: Option<c_slice::Ref<'a, Foo<'a>>>, // ...but this is not?
) -> i32 {
// do something...
}
What is the correct way to declare a mutable boxed type a function signature using #[ffi_export]?
Compiling with macro produces an error for the following piece of code:
#[ffi_export]
pub fn fmi2FreeInstance(mut slave: Option<repr_c::Box<Slave>>) {
match slave.as_mut() {
Some(s) => {
s.rpc.fmi2FreeInstance();
drop(slave)
}
None => {}
}
}
244 | pub fn fmi2FreeInstance(mut slave: Option<repr_c::Box<Slave>>) {
| ^^^^^ no rules expected this token in macro call
Without the macro the code compiles.
The blog post in page https://getditto.github.io/safer_ffi/example-ditto/_.html points to a wrong link 😲
I think it should be https://www.ditto.live/blog/introducing-safer-ffi
rather than https://www.ditto.live/blog/posts/introducing-safer-ffi
To enable things such as custom ABI, custom export_name
, and unsafe
-ly opting out of the sanity checks
The following example does not compile:
use safer_ffi::prelude::*;
#[ffi_export]
fn foobar(_: Option<repr_c::Box<dyn FooBar>>) {
todo!()
}
It complains that dyn FooBar
is not Sized but I fixed that with a quick hack, just adding ?Sized
to a few places.
This error remains:
the trait `ReprC` is not implemented for `(dyn FooBar + 'static)`
Could this work eventually or is there some limitation I haven't think of?
Such as:
C++, à la https://github.com/dtolnay/cxx
C# ?
Java ?
The idea would be that Rust can currently identify "constructors" (would require a special attribute on the fn new()
associated function), methods, and even the destructor, so it would be great if such semantically-rich semantics weren't lost by going through the flat C header layer, which currently requires people to manually recreate those semantics from scratch, out of SWIG or Cppsharp, etc. generated flat bindings.
Auto-generating stubs / shims for more higher-level classes would thus be a very neat thing to have.
#[wasm_bindgen]
does, for instance.Is there a way to generate header files for bindings which get cross-compiled? Currently, headers are generated by running a test on the host machine, but that assumes your test suite is executable.
I could bodge things together using a virtualisation layer like qemu
and having the VM print the generated header to stdout, but that isn't ideal.
My use case is:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.