Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hello WebAssembly! (MVP implementation) #10870

Merged
merged 48 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
cabdb42
Add Wasm32 LibC (based on musl)
lbguilherme Jul 1, 2021
76d5e22
Add Basic Wasm32 support
lbguilherme Jul 1, 2021
98c5447
Add LibC as a symbolic link
lbguilherme Jul 1, 2021
94109db
Merge branch 'master' into feat/webassembly
lbguilherme Jul 14, 2021
fa5077a
Move wasm to a Crystal::System implementation
lbguilherme Jul 14, 2021
fe511de
Enable smoke tests for wasm32-wasi
lbguilherme Jul 14, 2021
165b974
Revert change on unix Thread
lbguilherme Jul 14, 2021
be594ed
Smoke test: build compiler for the wasm test
lbguilherme Jul 14, 2021
ffe4b5b
Merge remote-tracking branch 'upstream/master' into feat/webassembly
lbguilherme Jul 20, 2021
598c99f
Mark stub methods with NotImplementedError or TODO
lbguilherme Jul 21, 2021
2d78c9b
Update src/crystal/system/wasm/process.cr
lbguilherme Jul 21, 2021
ffd9c12
Update wasi libc: it's now limited to what wasi actually provides. Mo…
lbguilherme Jul 22, 2021
9faf715
Use arc4random instead of getentropy
lbguilherme Jul 22, 2021
b0797c1
Remove Signal, Process and Sockets unimplemented method from Wasm32
lbguilherme Aug 2, 2021
aec74ad
Work around compiler bug in previous commit: BUG: `typeof(_io)` at ex…
lbguilherme Aug 2, 2021
463d4cd
fix: cleanup from previous commit
lbguilherme Aug 2, 2021
5faf601
Merge remote-tracking branch 'upstream/master' into feat/webassembly
lbguilherme Aug 2, 2021
3d91387
Merge remote-tracking branch 'upstream/master' into feat/webassembly
lbguilherme Aug 3, 2021
b1dd5f7
crystal tool format
lbguilherme Aug 3, 2021
00b10d6
fix: use correct .wasm extension
lbguilherme Dec 18, 2021
eab4739
Merge remote-tracking branch 'upstream/master' into feat/webassembly
lbguilherme Dec 18, 2021
4e33d92
Merge remote-tracking branch 'upstream/master' into feat/webassembly
lbguilherme Dec 23, 2021
e86b675
feat: implement Crystal::System::Fiber with malloc/free
lbguilherme Dec 23, 2021
ba5a531
fix: mark system_linger as not implemented
lbguilherme Dec 24, 2021
2582817
feat: add direct wasi integration for Dir.entries
lbguilherme Dec 24, 2021
4d5b061
feat: enable libpcre
lbguilherme Dec 24, 2021
5b4627f
chore: refactor Crystal::Program.object_extension
lbguilherme Dec 28, 2021
2519a6a
fix: mock Thread.current
lbguilherme Dec 28, 2021
fdef3ac
chore: change PREOPENS to @@preopens
lbguilherme Dec 28, 2021
10c6eb7
fix: print fatal errors
lbguilherme Dec 28, 2021
a0407ca
fix: use Sync logger until channels work
lbguilherme Dec 28, 2021
c3f035f
chore: rename wasierror.cr -> wasi_error.cr
lbguilherme Dec 28, 2021
3ba1b96
chore: preopen cleanup
lbguilherme Dec 28, 2021
e58cf26
fix: there is no need to initialize PREOPENS as early as possible if …
lbguilherme Dec 29, 2021
7bcd75a
fix: missing wasi require
lbguilherme Dec 29, 2021
cb2ea23
feat: generate better linker command
lbguilherme Dec 29, 2021
ad2e402
chore: stub Exception::CallStack#decode_backtrace until callback support
lbguilherme Dec 29, 2021
2e4b517
chore: remove some unused pieces from wasm libc
lbguilherme Dec 31, 2021
5faa13a
fix: use "out fd"
lbguilherme Jan 27, 2022
9de1150
feat: add wasi random_get to avoid relying on arc4random polyfill
lbguilherme Feb 8, 2022
2d3158e
Mark WASI as a UNIX target and use it as the system environment, inst…
lbguilherme Feb 8, 2022
ac74bad
Update spec/compiler/loader/unix_spec.cr
lbguilherme Feb 8, 2022
9f72ef9
Update spec/compiler/ffi/ffi_spec.cr
lbguilherme Feb 8, 2022
fea289a
Revert "Update spec/compiler/loader/unix_spec.cr"
lbguilherme Feb 8, 2022
53f6c25
Revert "Update spec/compiler/ffi/ffi_spec.cr"
lbguilherme Feb 8, 2022
5995375
fix: correct usage of some wasm32<->wasi flags
lbguilherme Feb 8, 2022
570eb32
Merge remote-tracking branch 'upstream/master' into feat/webassembly
lbguilherme Feb 11, 2022
b011761
fix: home_path for wasi
lbguilherme Feb 11, 2022
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
5 changes: 5 additions & 0 deletions .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
- aarch64-darwin
- arm-linux-gnueabihf
- i386-linux-musl
- wasm32-unknown-wasi
- x86_64-dragonfly
- x86_64-freebsd
- x86_64-netbsd
Expand All @@ -55,5 +56,9 @@ jobs:
- name: Download Crystal source
uses: actions/checkout@v2

- name: Make compiler (special case only for wasm32, until next release).
if: ${{ matrix.target == 'wasm32-unknown-wasi' }}
run: bin/ci with_build_env make

- name: Run smoke test
run: bin/ci with_build_env make smoke_test target=${{ matrix.target }}
12 changes: 7 additions & 5 deletions spec/std/string_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2646,12 +2646,14 @@ describe "String" do
string.should be(clone)
end

it "allocates buffer of correct size when UInt8 is given to new (#3332)" do
String.new(255_u8) do |buffer|
LibGC.size(buffer).should be >= 255
{255, 0}
{% unless flag?(:wasm32) %}
it "allocates buffer of correct size when UInt8 is given to new (#3332)" do
String.new(255_u8) do |buffer|
LibGC.size(buffer).should be >= 255
{255, 0}
end
end
end
{% end %}

it "raises on String.new if returned bytesize is greater than capacity" do
expect_raises ArgumentError, "Bytesize out of capacity bounds" do
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/crystal/codegen/target.cr
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ class Crystal::Codegen::Target
@environment.starts_with?("linux")
end

def wasm?
@architecture.starts_with?("wasm")
end
lbguilherme marked this conversation as resolved.
Show resolved Hide resolved

def bsd?
freebsd? || netbsd? || openbsd? || dragonfly?
end
Expand Down Expand Up @@ -144,6 +148,8 @@ class Crystal::Codegen::Target
if cpu.empty? && !features.includes?("fp") && armhf?
features += "+vfp2"
end
when "wasm32"
LLVM.init_webassembly
else
raise Target::Error.new("Unsupported architecture for target triple: #{self}")
end
Expand Down
3 changes: 3 additions & 0 deletions src/crystal/system.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ end
{% elsif flag?(:win32) %}
require "./system/win32/hostname"
require "./system/win32/cpucount"
{% elsif flag?(:wasm32) %}
require "./system/wasm/hostname"
require "./system/wasm/cpucount"
{% else %}
{% raise "No Crystal::System implementation available" %}
{% end %}
2 changes: 1 addition & 1 deletion src/crystal/system/dir.cr
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ module Crystal::System::Dir
# def self.delete(path : String) : Nil
end

{% if flag?(:unix) %}
{% if flag?(:unix) || flag?(:wasm32) %}
require "./unix/dir"
{% elsif flag?(:win32) %}
require "./win32/dir"
Expand Down
6 changes: 4 additions & 2 deletions src/crystal/system/env.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ module Crystal::System::Env
# def self.each(&block : String, String ->)
end

{% if flag?(:win32) %}
{% if flag?(:unix) || flag?(:wasm32) %}
require "./unix/env"
{% elsif flag?(:win32) %}
require "./win32/env"
{% else %}
require "./unix/env"
{% raise "No implementation of Crystal::System::Env available" %}
{% end %}
4 changes: 3 additions & 1 deletion src/crystal/system/event_loop.cr
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ struct Crystal::Event
# def add(time_span : Time::Span?) : Nil
end

{% if flag?(:unix) %}
{% if flag?(:wasm32) %}
require "./wasm/event_loop"
{% elsif flag?(:unix) %}
require "./unix/event_loop_libevent"
{% elsif flag?(:win32) %}
require "./win32/event_loop_iocp"
Expand Down
2 changes: 2 additions & 0 deletions src/crystal/system/fiber.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ end
require "./unix/fiber"
{% elsif flag?(:win32) %}
require "./win32/fiber"
{% elsif flag?(:wasm32) %}
require "./wasm/fiber"
{% else %}
{% raise "fiber not supported" %}
{% end %}
2 changes: 1 addition & 1 deletion src/crystal/system/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ module Crystal::System::File
# def file_descriptor_close
end

{% if flag?(:unix) %}
{% if flag?(:unix) || flag?(:wasm32) %}
require "./unix/file"
{% elsif flag?(:win32) %}
require "./win32/file"
Expand Down
8 changes: 6 additions & 2 deletions src/crystal/system/file_descriptor.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{% if flag?(:win32) %}
{% if flag?(:unix) %}
require "./unix/file_descriptor"
{% elsif flag?(:win32) %}
require "./win32/file_descriptor"
{% elsif flag?(:wasm32) %}
require "./wasm/file_descriptor"
{% else %}
require "./unix/file_descriptor"
{% raise "No Crystal::System::FileDescriptor implementation available" %}
{% end %}
2 changes: 1 addition & 1 deletion src/crystal/system/file_info.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% if flag?(:unix) %}
{% if flag?(:unix) || flag?(:wasm32) %}
require "./unix/file_info"
{% elsif flag?(:win32) %}
require "./win32/file_info"
Expand Down
2 changes: 2 additions & 0 deletions src/crystal/system/group.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{% if flag?(:unix) %}
require "./unix/group"
{% elsif flag?(:wasm32) %}
require "./wasm/group"
{% else %}
{% raise "No Crystal::System::Group implementation available" %}
{% end %}
2 changes: 2 additions & 0 deletions src/crystal/system/mime.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ end
require "./unix/mime"
{% elsif flag?(:win32) %}
require "./win32/mime"
{% elsif flag?(:wasm32) %}
require "./wasm/mime"
{% else %}
{% raise "No Crystal::System::Mime implementation available" %}
{% end %}
2 changes: 2 additions & 0 deletions src/crystal/system/process.cr
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ end
require "./unix/process"
{% elsif flag?(:win32) %}
require "./win32/process"
{% elsif flag?(:wasm32) %}
require "./wasm/process"
{% else %}
{% raise "No Crystal::System::Process implementation available" %}
{% end %}
2 changes: 2 additions & 0 deletions src/crystal/system/random.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ end

{% if flag?(:linux) %}
require "./unix/getrandom"
{% elsif flag?(:wasm32) %}
require "./wasm/getentropy"
lbguilherme marked this conversation as resolved.
Show resolved Hide resolved
{% elsif flag?(:openbsd) || flag?(:netbsd) %}
require "./unix/arc4random"
{% elsif flag?(:unix) %}
Expand Down
2 changes: 1 addition & 1 deletion src/crystal/system/socket.cr
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ module Crystal::System::Socket
# private def system_tcp_keepalive_count=(val : Int)
end

{% if flag?(:unix) %}
{% if flag?(:unix) || flag?(:wasm32) %}
require "./unix/socket"
{% else %}
{% raise "No Crystal::System::Socket implementation available" %}
Expand Down
2 changes: 2 additions & 0 deletions src/crystal/system/thread.cr
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ require "./thread_linked_list"
require "./unix/pthread_condition_variable"
{% elsif flag?(:win32) %}
require "./win32/thread"
{% elsif flag?(:wasm32) %}
require "./wasm/thread"
{% else %}
{% raise "thread not supported" %}
{% end %}
2 changes: 2 additions & 0 deletions src/crystal/system/thread_mutex.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ end
require "./unix/pthread_mutex"
{% elsif flag?(:win32) %}
require "./win32/thread_mutex"
{% elsif flag?(:wasm32) %}
require "./wasm/thread_mutex"
{% else %}
{% raise "thread not supported" %}
{% end %}
6 changes: 4 additions & 2 deletions src/crystal/system/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ module Crystal::System::Time
# def self.load_localtime : ::Time::Location?
end

{% if flag?(:win32) %}
{% if flag?(:unix) || flag?(:wasm32) %}
require "./unix/time"
{% elsif flag?(:win32) %}
require "./win32/time"
{% else %}
require "./unix/time"
{% raise "No implementation of Crystal::System::Time available" %}
{% end %}
2 changes: 2 additions & 0 deletions src/crystal/system/user.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{% if flag?(:unix) %}
require "./unix/user"
{% elsif flag?(:wasm32) %}
require "./wasm/user"
{% else %}
{% raise "No Crystal::System::User implementation available" %}
{% end %}
5 changes: 5 additions & 0 deletions src/crystal/system/wasm/cpucount.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Crystal::System
def self.cpu_count
1
end
end
40 changes: 40 additions & 0 deletions src/crystal/system/wasm/event_loop.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# :nodoc:
module Crystal::EventLoop
# Runs the event loop.
def self.run_once
end

# Create a new resume event for a fiber.
def self.create_resume_event(fiber : Fiber) : Crystal::Event
Crystal::Event.new
end

# Creates a timeout_event.
def self.create_timeout_event(fiber) : Crystal::Event
Crystal::Event.new
end

# Creates a write event for a file descriptor.
def self.create_fd_write_event(io : IO::Evented, edge_triggered : Bool = false) : Crystal::Event
Crystal::Event.new
end

# Creates a read event for a file descriptor.
def self.create_fd_read_event(io : IO::Evented, edge_triggered : Bool = false) : Crystal::Event
Crystal::Event.new
end
end

struct Crystal::Event
def add(timeout : LibC::Timeval? = nil)
end

def add(timeout : Time::Span)
end

def free
end

def delete
end
end
10 changes: 10 additions & 0 deletions src/crystal/system/wasm/fiber.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require "c/sys/mman"

module Crystal::System::Fiber
def self.allocate_stack(stack_size) : Void*
raise RuntimeError.new("Cannot allocate new fiber stack")
end

def self.free_stack(stack : Void*, stack_size) : Nil
end
end
8 changes: 8 additions & 0 deletions src/crystal/system/wasm/file_descriptor.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require "../unix/file_descriptor"

# :nodoc:
module Crystal::System::FileDescriptor
def self.from_stdio(fd)
return IO::FileDescriptor.new(fd).tap(&.flush_on_newline=(true))
end
end
72 changes: 72 additions & 0 deletions src/crystal/system/wasm/getentropy.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require "c/unistd"

lib LibC
fun getentropy(buffer : Void*, len : SizeT) : Int
end

module Crystal::System::Random
@@initialized = false
@@getentropy_available = false
@@urandom : ::File?

private def self.init
@@initialized = true

if LibC.getentropy(Bytes.new(16), 16) >= 0
@@getentropy_available = true
else
urandom = ::File.open("/dev/urandom", "r")
return unless urandom.info.type.character_device?

urandom.close_on_exec = true
urandom.sync = true # don't buffer bytes
@@urandom = urandom
end
end

# Reads n random bytes using the Linux `getentropy(2)` syscall.
def self.random_bytes(buf : Bytes) : Nil
init unless @@initialized

if @@getentropy_available
getentropy(buf)
elsif urandom = @@urandom
urandom.read_fully(buf)
else
raise "Failed to access secure source to generate random bytes!"
end
end

def self.next_u : UInt8
init unless @@initialized

if @@getentropy_available
buf = uninitialized UInt8[1]
getentropy(buf.to_slice)
buf.unsafe_as(UInt8)
elsif urandom = @@urandom
urandom.read_byte.not_nil!
else
raise "Failed to access secure source to generate random bytes!"
end
end

# Reads n random bytes using the Linux `getentropy(2)` syscall.
private def self.getentropy(buf)
# getentropy(2) may only read up to 256 bytes at once without being
# interrupted or returning early
chunk_size = 256

while buf.size > 0
if buf.size < chunk_size
chunk_size = buf.size
end

ret = LibC.getentropy(buf, chunk_size)

raise RuntimeError.from_errno("getentropy") if ret == -1

buf += chunk_size
end
end
end
9 changes: 9 additions & 0 deletions src/crystal/system/wasm/group.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Crystal::System::Group
private def from_name?(groupname : String)
nil
end

private def from_id?(groupid : String)
nil
end
end
5 changes: 5 additions & 0 deletions src/crystal/system/wasm/hostname.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Crystal::System
def self.hostname
""
end
end
4 changes: 4 additions & 0 deletions src/crystal/system/wasm/mime.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Crystal::System::MIME
def self.load
end
end
Loading