mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-10 10:02:38 +03:00
initial demo working!
This commit is contained in:
parent
5285884830
commit
e67645adcb
4
examples/jvm-interop/.gitignore
vendored
Normal file
4
examples/jvm-interop/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.log
|
||||
*.class
|
||||
*.o
|
||||
*.so
|
245
examples/jvm-interop/HelloJNI.c
Normal file
245
examples/jvm-interop/HelloJNI.c
Normal file
@ -0,0 +1,245 @@
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdalign.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include "json.h"
|
||||
#include "HelloJNI.h"
|
||||
|
||||
|
||||
void *roc_alloc(size_t size, unsigned int alignment)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void *roc_realloc(void *ptr, size_t new_size, size_t old_size,
|
||||
unsigned int alignment)
|
||||
{
|
||||
return realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
void roc_dealloc(void *ptr, unsigned int alignment)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void roc_panic(void *ptr, unsigned int alignment)
|
||||
{
|
||||
// TODO
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void *roc_memcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
return memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
void *roc_memset(void *str, int c, size_t n)
|
||||
{
|
||||
return memset(str, c, n);
|
||||
}
|
||||
|
||||
// Reference counting
|
||||
|
||||
// If the refcount is set to this, that means the allocation is
|
||||
// stored in readonly memory in the binary, and we must not
|
||||
// attempt to increment or decrement it; if we do, we'll segfault!
|
||||
const ssize_t REFCOUNT_READONLY = 0;
|
||||
const ssize_t REFCOUNT_ONE = (ssize_t)PTRDIFF_MIN;
|
||||
const size_t MASK = (size_t)PTRDIFF_MIN;
|
||||
|
||||
// Increment reference count, given a pointer to the first element in a collection.
|
||||
// We don't need to check for overflow because in order to overflow a usize worth of refcounts,
|
||||
// you'd need to somehow have more pointers in memory than the OS's virtual address space can hold.
|
||||
void incref(uint8_t* bytes, uint32_t alignment)
|
||||
{
|
||||
ssize_t *refcount_ptr = ((ssize_t *)bytes) - 1;
|
||||
ssize_t refcount = *refcount_ptr;
|
||||
|
||||
if (refcount != REFCOUNT_READONLY) {
|
||||
*refcount_ptr = refcount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement reference count, given a pointer to the first element in a collection.
|
||||
// Then call roc_dealloc if nothing is referencing this collection anymore.
|
||||
void decref(uint8_t* bytes, uint32_t alignment)
|
||||
{
|
||||
if (bytes == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t extra_bytes = (sizeof(size_t) >= (size_t)alignment) ? sizeof(size_t) : (size_t)alignment;
|
||||
ssize_t *refcount_ptr = ((ssize_t *)bytes) - 1;
|
||||
ssize_t refcount = *refcount_ptr;
|
||||
|
||||
if (refcount != REFCOUNT_READONLY) {
|
||||
*refcount_ptr = refcount - 1;
|
||||
|
||||
if (refcount == REFCOUNT_ONE) {
|
||||
void *original_allocation = (void *)(refcount_ptr - (extra_bytes - sizeof(size_t)));
|
||||
|
||||
roc_dealloc(original_allocation, alignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RocBytes (List U8)
|
||||
|
||||
struct RocBytes
|
||||
{
|
||||
uint8_t *bytes;
|
||||
size_t len;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
struct RocBytes init_rocbytes(uint8_t *bytes, size_t len)
|
||||
{
|
||||
if (len == 0)
|
||||
{
|
||||
struct RocBytes ret = {
|
||||
.len = 0,
|
||||
.bytes = NULL,
|
||||
.capacity = MASK,
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct RocBytes ret;
|
||||
size_t refcount_size = sizeof(size_t);
|
||||
uint8_t *new_content = ((uint8_t *)roc_alloc(len + refcount_size, alignof(size_t))) + refcount_size;
|
||||
|
||||
memcpy(new_content, bytes, len);
|
||||
|
||||
ret.bytes = new_content;
|
||||
ret.len = len;
|
||||
ret.capacity = len;
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// RocStr
|
||||
|
||||
struct RocStr
|
||||
{
|
||||
uint8_t *bytes;
|
||||
size_t len;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
struct RocStr init_rocstr(uint8_t *bytes, size_t len)
|
||||
{
|
||||
if (len == 0)
|
||||
{
|
||||
struct RocStr ret = {
|
||||
.len = 0,
|
||||
.bytes = NULL,
|
||||
.capacity = MASK,
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
else if (len < sizeof(struct RocStr))
|
||||
{
|
||||
// Start out with zeroed memory, so that
|
||||
// if we end up comparing two small RocStr values
|
||||
// for equality, we won't risk memory garbage resulting
|
||||
// in two equal strings appearing unequal.
|
||||
struct RocStr ret = {
|
||||
.len = 0,
|
||||
.bytes = NULL,
|
||||
.capacity = MASK,
|
||||
};
|
||||
|
||||
// Copy the bytes into the stack allocation
|
||||
memcpy(&ret, bytes, len);
|
||||
|
||||
// Record the string's length in the last byte of the stack allocation
|
||||
((uint8_t *)&ret)[sizeof(struct RocStr) - 1] = (uint8_t)len | 0b10000000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
// A large RocStr is the same as a List U8 (aka RocBytes) in memory.
|
||||
struct RocBytes roc_bytes = init_rocbytes(bytes, len);
|
||||
|
||||
struct RocStr ret = {
|
||||
.len = roc_bytes.len,
|
||||
.bytes = roc_bytes.bytes,
|
||||
.capacity = roc_bytes.capacity,
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_small_str(struct RocStr str) { return ((ssize_t)str.capacity) < 0; }
|
||||
|
||||
// Determine the length of the string, taking into
|
||||
// account the small string optimization
|
||||
size_t roc_str_len(struct RocStr str)
|
||||
{
|
||||
uint8_t *bytes = (uint8_t *)&str;
|
||||
uint8_t last_byte = bytes[sizeof(str) - 1];
|
||||
uint8_t last_byte_xored = last_byte ^ 0b10000000;
|
||||
size_t small_len = (size_t)(last_byte_xored);
|
||||
size_t big_len = str.len;
|
||||
|
||||
// Avoid branch misprediction costs by always
|
||||
// determining both small_len and big_len,
|
||||
// so this compiles to a cmov instruction.
|
||||
if (is_small_str(str))
|
||||
{
|
||||
return small_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
return big_len;
|
||||
}
|
||||
}
|
||||
|
||||
extern void roc__mainForHost_1_exposed_generic(struct RocBytes *ret, struct RocBytes *arg);
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_HelloJNI_sayHello
|
||||
(JNIEnv *env, jobject thisObj, jint num)
|
||||
{
|
||||
char native_string[256] = {0};
|
||||
sprintf(native_string, "%d", num);
|
||||
/* const char *native_string = (*env)->GetStringUTFChars(env, num, 0); */
|
||||
|
||||
struct RocBytes arg = init_rocbytes((uint8_t *)native_string, strlen(native_string));
|
||||
struct RocBytes ret = {0};
|
||||
|
||||
// Call the Roc function to populate `ret`'s bytes.
|
||||
roc__mainForHost_1_exposed_generic(&ret, &arg);
|
||||
printf("%s", (char *)ret.bytes);
|
||||
|
||||
struct json_value_s* root = json_parse((char *)ret.bytes, ret.len);
|
||||
if (root == NULL) {
|
||||
printf("Failed to parse JSON\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct json_string_s * json_string = json_value_as_string(root);
|
||||
if (json_string == NULL) {
|
||||
printf("JSON value is not a string\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(root);
|
||||
decref((void *)&ret, alignof(uint8_t *));
|
||||
|
||||
// new jstring assembled from the Roc string in ret
|
||||
return (*env)->NewStringUTF(env, json_string->string);
|
||||
|
||||
}
|
10
examples/jvm-interop/HelloJNI.java
Normal file
10
examples/jvm-interop/HelloJNI.java
Normal file
@ -0,0 +1,10 @@
|
||||
public class HelloJNI {
|
||||
static {
|
||||
System.loadLibrary("demo");
|
||||
}
|
||||
public native String sayHello(int num);
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(new HelloJNI().sayHello(420));
|
||||
}
|
||||
}
|
30
examples/jvm-interop/build.sh
Executable file
30
examples/jvm-interop/build.sh
Executable file
@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -euxo
|
||||
|
||||
# don't forget to validate that $JAVA_HOME is defined, the following would not work without it!
|
||||
# set it either globally or here
|
||||
# export JAVA_HOME=/your/java/installed/dir
|
||||
# in nixos, to set it globally, i needed to say `programs.java.enable = true;` in `/etc/nixos/configuration.nix`
|
||||
|
||||
|
||||
../../target/release/roc build --lib
|
||||
mv libhello.so.1.0 libhello.so.1
|
||||
ln -sf libhello.so.1 libhello.so
|
||||
|
||||
# for clang's compilation
|
||||
export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
|
||||
|
||||
# needs jdk10 +
|
||||
javac -h . HelloJNI.java
|
||||
|
||||
gcc \
|
||||
-c -fPIC \
|
||||
-I"$JAVA_HOME/include" \
|
||||
-I"$JAVA_HOME/include/linux" \
|
||||
-o demo.o HelloJNI.c
|
||||
|
||||
gcc -shared -o libdemo.so demo.o -L. -lhello
|
||||
|
||||
# then run
|
||||
# java -Djava.library.path=. HelloJNI
|
3451
examples/jvm-interop/json.h
Normal file
3451
examples/jvm-interop/json.h
Normal file
File diff suppressed because it is too large
Load Diff
15
examples/jvm-interop/main.roc
Normal file
15
examples/jvm-interop/main.roc
Normal file
@ -0,0 +1,15 @@
|
||||
app "libhello"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main : U64 -> Str
|
||||
main = \num ->
|
||||
if num == 0 then
|
||||
"I need a positive number here!"
|
||||
else
|
||||
str = Num.toStr num
|
||||
"The number was \(str), OH YEAH!!! 🤘🤘"
|
||||
|
||||
# main : Str -> Str
|
||||
# main = \name -> "Hello from roc \(name)!!!"
|
115
examples/jvm-interop/platform/host.c
Normal file
115
examples/jvm-interop/platform/host.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/shm.h> // shm_open
|
||||
#include <sys/mman.h> // for mmap
|
||||
#include <signal.h> // for kill
|
||||
|
||||
void* roc_alloc(size_t size, unsigned int alignment) { return malloc(size); }
|
||||
|
||||
void* roc_realloc(void* ptr, size_t new_size, size_t old_size,
|
||||
unsigned int alignment) {
|
||||
return realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
void roc_dealloc(void* ptr, unsigned int alignment) { free(ptr); }
|
||||
|
||||
void roc_panic(void* ptr, unsigned int alignment) {
|
||||
char* msg = (char*)ptr;
|
||||
fprintf(stderr,
|
||||
"Application crashed with message\n\n %s\n\nShutting down\n", msg);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void* roc_memcpy(void* dest, const void* src, size_t n) {
|
||||
return memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); }
|
||||
|
||||
int roc_shm_open(char* name, int oflag, int mode) {
|
||||
#ifdef _WIN32
|
||||
return 0;
|
||||
#else
|
||||
return shm_open(name, oflag, mode);
|
||||
#endif
|
||||
}
|
||||
void* roc_mmap(void* addr, int length, int prot, int flags, int fd, int offset) {
|
||||
#ifdef _WIN32
|
||||
return addr;
|
||||
#else
|
||||
return mmap(addr, length, prot, flags, fd, offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
int roc_getppid() {
|
||||
#ifdef _WIN32
|
||||
return 0;
|
||||
#else
|
||||
return getppid();
|
||||
#endif
|
||||
}
|
||||
|
||||
struct RocStr {
|
||||
char* bytes;
|
||||
size_t len;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
bool is_small_str(struct RocStr str) { return ((ssize_t)str.capacity) < 0; }
|
||||
|
||||
// Determine the length of the string, taking into
|
||||
// account the small string optimization
|
||||
size_t roc_str_len(struct RocStr str) {
|
||||
char* bytes = (char*)&str;
|
||||
char last_byte = bytes[sizeof(str) - 1];
|
||||
char last_byte_xored = last_byte ^ 0b10000000;
|
||||
size_t small_len = (size_t)(last_byte_xored);
|
||||
size_t big_len = str.len;
|
||||
|
||||
// Avoid branch misprediction costs by always
|
||||
// determining both small_len and big_len,
|
||||
// so this compiles to a cmov instruction.
|
||||
if (is_small_str(str)) {
|
||||
return small_len;
|
||||
} else {
|
||||
return big_len;
|
||||
}
|
||||
}
|
||||
|
||||
extern void roc__mainForHost_1_exposed_generic(struct RocStr *string);
|
||||
|
||||
int main() {
|
||||
|
||||
struct RocStr str;
|
||||
roc__mainForHost_1_exposed_generic(&str);
|
||||
|
||||
// Determine str_len and the str_bytes pointer,
|
||||
// taking into account the small string optimization.
|
||||
size_t str_len = roc_str_len(str);
|
||||
char* str_bytes;
|
||||
|
||||
if (is_small_str(str)) {
|
||||
str_bytes = (char*)&str;
|
||||
} else {
|
||||
str_bytes = str.bytes;
|
||||
}
|
||||
|
||||
// Write to stdout
|
||||
if (write(1, str_bytes, str_len) >= 0) {
|
||||
// Writing succeeded!
|
||||
|
||||
// NOTE: the string is a static string, read from in the binary
|
||||
// if you make it a heap-allocated string, it'll be leaked here
|
||||
return 0;
|
||||
} else {
|
||||
printf("Error writing to stdout: %s\n", strerror(errno));
|
||||
|
||||
// NOTE: the string is a static string, read from in the binary
|
||||
// if you make it a heap-allocated string, it'll be leaked here
|
||||
return 1;
|
||||
}
|
||||
}
|
12
examples/jvm-interop/platform/main.roc
Normal file
12
examples/jvm-interop/platform/main.roc
Normal file
@ -0,0 +1,12 @@
|
||||
platform "jvm-interop"
|
||||
requires {} { main : arg -> ret | arg has Decoding, ret has Encoding }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [Json]
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : List U8 -> List U8
|
||||
mainForHost = \json ->
|
||||
when Decode.fromBytes json Json.fromUtf8 is
|
||||
Ok arg -> Encode.toBytes (main arg) Json.toUtf8
|
||||
Err _ -> []
|
Loading…
Reference in New Issue
Block a user