Full Code of migueldeicaza/mono-wasm for AI

master c46c1e0185d1 cached
20 files
81.3 KB
22.4k tokens
67 symbols
1 requests
Download .txt
Repository: migueldeicaza/mono-wasm
Branch: master
Commit: c46c1e0185d1
Files: 20
Total size: 81.3 KB

Directory structure:
gitextract_u8dox53_/

├── .gitignore
├── LICENSE.txt
├── Makefile
├── README.md
├── boot.c
├── index.js
├── jsmin.c
├── mono-wasm.cpp
├── mscorlib.xml
├── sample/
│   ├── hello/
│   │   ├── .gitignore
│   │   ├── Makefile
│   │   ├── hello.cs
│   │   └── index.html
│   └── hello2/
│       ├── .gitignore
│       ├── Makefile
│       ├── hello.cs
│       └── index.html
└── tests/
    └── Mono.WebAssembly/
        ├── Makefile
        ├── index.html
        └── test.cs

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store
.gdb_history
build
dist
mscorlib.dll
*.o
.*.swo
.*.swp
mono-wasm
*.dSYM


================================================
FILE: LICENSE.txt
================================================
MIT License

Copyright (c) 2017 - present Microsoft Corporation

All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
MONO_RUNTIME_PATH = ../mono-runtime
MONO_COMPILER_PATH = ../mono-compiler
LIBC_PATH = ../libc
LLVM_PATH = ../llvm-build

CLANG = $(LLVM_PATH)/bin/clang

LIBC_CFLAGS = -fno-stack-protector -nostdinc -I$(LIBC_PATH)/include -I$(LIBC_PATH)/arch/wasm32 -target wasm32 -Wno-shift-op-parentheses -Wno-incompatible-library-redeclaration -Wno-bitwise-op-parentheses

LIBC_INTERNAL_CFLAGS = $(LIBC_CFLAGS) -I$(LIBC_PATH)/src/internal

MONO_CFLAGS = $(LIBC_CFLAGS) -I$(MONO_RUNTIME_PATH) -I$(MONO_RUNTIME_PATH)/mono -I$(MONO_RUNTIME_PATH)/eglib/src -DHAVE_CONFIG_H -D_THREAD_SAFE -DUSE_MMAP -DUSE_MUNMAP -std=gnu99 -fwrapv -DMONO_DLL_EXPORT -Wno-unused-value -Wno-tautological-compare -Wno-bitwise-op-parentheses

all: dist-install

build/libmini.bc: $(patsubst %, build/mini/%.bc, abcremoval alias-analysis aot-compiler aot-runtime branch-opts cfgdump cfold debug-mini debugger-agent decompose dominators driver dwarfwriter graph helpers image-writer jit-icalls linear-scan liveness lldb local-propagation memory-access method-to-ir mini-codegen mini-cross-helpers mini-wasm32 mini-exceptions mini-gc mini-generic-sharing mini-native-types mini-posix mini-runtime mini-trampolines mini seq-points simd-intrinsics ssa tasklets trace type-checking unwind xdebug)

build/libmetadata.bc: $(patsubst %, build/metadata/%.bc, appdomain assembly attach class-accessors class cominterop console-wasm coree custom-attrs debug-helpers debug-mono-ppdb debug-mono-symfile decimal-ms domain dynamic-image dynamic-stream environment exception file-mmap-posix file-mmap-windows filewatcher gc-stats gc handle icall image jit-info loader locales lock-tracer marshal mempool metadata-cross-helpers metadata-verify metadata method-builder monitor mono-basic-block mono-conc-hash mono-config mono-config-dirs mono-debug mono-endian mono-hash mono-mlist mono-perfcounters mono-route mono-security number-ms object opcodes profiler property-bag rand reflection remoting runtime security-core-clr security-manager seq-points-data sgen-bridge sgen-mono sgen-new-bridge sgen-old-bridge sgen-stw sgen-tarjan-bridge sgen-toggleref sre-encode sre-save sre string-icalls sysmath threadpool-io threadpool-worker-default threadpool threads verify w32error-unix w32event-unix w32file-unix-glob w32file-unix w32file w32handle-namespace w32handle w32mutex-unix w32process-unix-bsd w32process-unix-default w32process-unix-haiku w32process-unix-osx w32process-unix w32process w32semaphore-unix w32socket-unix w32socket)

build/libutils.bc: $(patsubst %, build/utils/%.bc, atomic mono-os-mutex bsearch mono-path checked-build mono-poll dlmalloc mono-proclib-windows hazard-pointer mono-proclib json mono-property-hash lock-free-alloc mono-publib lock-free-array-queue mono-rand-windows lock-free-queue mono-rand mono-sha1 mach-support mono-stdlib memfuncs mono-threads-android mono-codeman mono-threads-coop mono-conc-hashtable mono-threads-freebsd mono-context mono-threads-haiku mono-counters mono-threads-linux mono-dl-darwin mono-threads-mach-helper mono-dl-posix mono-threads-mach mono-dl-windows mono-threads-netbsd mono-dl mono-threads-openbsd mono-error mono-threads-posix-signals mono-filemap mono-threads-posix mono-threads-state-machine mono-hwcap mono-hwcap-wasm32 mono-threads-windows mono-internal-hash mono-threads mono-io-portability mono-time mono-linked-list-set mono-tls mono-log-android mono-uri mono-log-common mono-value-hash mono-log-darwin monobitset mono-log-posix networking-fallback mono-log-windows networking-missing mono-logger networking-posix mono-math networking-windows mono-md5 networking mono-mmap-windows os-event-unix mono-mmap parse mono-networkinterfaces strenc)

build/libsgen.bc: $(patsubst %, build/sgen/sgen-%.bc, alloc array-list cardtable debug descriptor fin-weak-hash gc gchandles gray hash-table internal layout-stats los marksweep memory-governor nursery-allocator pinning-stats pinning pointer-queue protocol qsort simple-nursery split-nursery thread-pool workers)

build/libeglib.bc: $(patsubst %, build/eglib/%.bc, garray goutput gbytearray gpath gdate-unix gpattern gdir-unix gptrarray gerror gqsort gfile-posix gqueue gfile-unix gshell gfile gslist ghashtable gspawn giconv gstr glist gstring gmarkup gtimer-unix gmem gunicode gmisc-unix gutf8 gmodule-unix)

build/libmono.bc: build/libmini.bc build/libmetadata.bc build/libutils.bc build/libsgen.bc build/libeglib.bc

build/libc.bc: $(patsubst %.c, build/libc/%.bc, $(shell (cd $(LIBC_PATH)/src && ls {ctype,env,errno,exit,internal,ldso,dlmalloc,fcntl,locale,math,prng,signal,stdio,string,stdlib,time,unistd}/*.c | grep -Ev "(pread|pwrite|sigaltstack|strtok_r)")) conf/sysconf.c thread/__lock.c misc/getrlimit.c mman/madvise.c stat/stat.c stat/fstat.c)

build/libmini.bc build/libmetadata.bc build/libeglib.bc build/libsgen.bc build/libutils.bc build/libmono.bc build/libc.bc:
	$(LLVM_PATH)/bin/llvm-link $^ -o $@

build/libc/%.bc: $(LIBC_PATH)/src/%.c
	@/bin/mkdir -p $(dir $@)
	$(CLANG) $(LIBC_INTERNAL_CFLAGS) $< -c -emit-llvm -o $@

build/mini/%.bc : $(MONO_RUNTIME_PATH)/mono/mini/%.c
	@/bin/mkdir -p $(dir $@)
	$(CLANG) -I$(MONO_RUNTIME_PATH)/mono/mini $(MONO_CFLAGS) $< -c -emit-llvm -o $@

build/metadata/%.bc : $(MONO_RUNTIME_PATH)/mono/metadata/%.c
	@/bin/mkdir -p $(dir $@)
	$(CLANG) -I$(MONO_RUNTIME_PATH)/mono/metadata $(MONO_CFLAGS) -DHAVE_SGEN_GC $< -c -emit-llvm -o $@

build/utils/%.bc : $(MONO_RUNTIME_PATH)/mono/utils/%.c
	@/bin/mkdir -p $(dir $@)
	$(CLANG) -I$(MONO_RUNTIME_PATH)/mono/utils $(MONO_CFLAGS) -DHAVE_SGEN_GC $< -c -emit-llvm -o $@

build/sgen/%.bc : $(MONO_RUNTIME_PATH)/mono/sgen/%.c
	@/bin/mkdir -p $(dir $@)
	$(CLANG) -I$(MONO_RUNTIME_PATH)/mono/sgen $(MONO_CFLAGS) -DHAVE_SGEN_GC $< -c -emit-llvm -o $@

build/eglib/%.bc : $(MONO_RUNTIME_PATH)/eglib/src/%.c
	@/bin/mkdir -p $(dir $@)
	$(CLANG) $(MONO_CFLAGS) $< -c -emit-llvm -o $@

mscorlib.dll: $(MONO_COMPILER_PATH)/mcs/class/lib/wasm/mscorlib.dll
	cp $< $@

build/boot.bc:        boot.c
	@/bin/mkdir -p $(dir $@)
	$(CLANG) $(MONO_CFLAGS) boot.c -c -emit-llvm -o build/boot.bc

build/runtime.bc:     build/boot.bc build/libc.bc build/libmono.bc
	@/bin/mkdir -p $(dir $@)
	$(LLVM_PATH)/bin/llvm-link build/libc.bc build/libmono.bc build/boot.bc -o build/runtime.bc

MONO_WASM_CXXFLAGS = -Wno-sign-compare -std=c++1y -UNDEBUG -fexceptions
MONO_WASM_LLVM_COMPONENTS = BitReader BitWriter Core IRReader Linker Object Support TransformUtils IPO webassembly Option

jsmin.o:        jsmin.c
	/usr/bin/clang -c jsmin.c -o jsmin.o

mono-wasm:      jsmin.o mono-wasm.cpp
	/usr/bin/clang++ $(shell $(LLVM_PATH)/bin/llvm-config --cxxflags --ldflags) -Wno-gnu $(MONO_WASM_CXXFLAGS) -I$(shell $(LLVM_PATH)/bin/llvm-config --src-root)/tools/lld/include -g mono-wasm.cpp -o mono-wasm -lncurses -lz jsmin.o $(shell $(LLVM_PATH)/bin/llvm-config --libs $(MONO_WASM_LLVM_COMPONENTS)) -llldCommon -llldCore -llldDriver -llldReaderWriter -llldWasm

dist-install:   mono-wasm build/runtime.bc mscorlib.dll
	rm -rf dist
	mkdir -p dist/bin
	cp mono-wasm dist/bin
	cp $(MONO_COMPILER_PATH)/mono/mini/mono dist/bin/monoc
	mkdir -p dist/lib
	cp mscorlib.dll dist/lib
	cp mscorlib.xml dist/lib
	cp build/runtime.bc dist/lib
	cp index.js dist/lib

need-version:
ifndef VERSION
    $(error VERSION is undefined)
endif

make-dist-dir:
DIST_DIR = mono-wasm-macos-$(VERSION)

release:	need-version make-dist-dir dist-install
	mkdir -p releases
	(cd releases \
	  && mkdir $(DIST_DIR) \
	  && ditto ../dist $(DIST_DIR)/dist \
	  && ditto ../sample $(DIST_DIR)/sample \
	  && for i in `ls $(DIST_DIR)/sample`; do (cd $(DIST_DIR)/sample/$$i && make clean); done \
	  && zip -r $(DIST_DIR).zip $(DIST_DIR))

clean:
	/bin/rm -rf build dist mscorlib.dll jsmin.o mono-wasm


================================================
FILE: README.md
================================================
# mono-wasm

This project is a proof-of-concept aiming at building C# applications into WebAssembly, by using Mono and compiling/linking everything statically into one .wasm file that can be easily delivered to browsers.

The process does not use Emscripten (or Binaryen) but instead uses the experimental WebAssembly backend of LLVM with `clang` and `lld` to generate the final .wasm code. The goal is to use as few dependencies as possible. At the moment the only dependencies are LLVM, `clang` and `lld` trunk.

`mono-wasm` supports 2 build modes: one that links all the LLVM bitcode into one module then performs a WebAssembly codegen on it, and one that compiles project dependencies into WebAssembly incrementally (the runtime and the mscorlib assembly) then uses `lld` to link into a final .wasm file. The later is experimental but will become the default as it allows build times lesser than a second.

The .wasm file is loaded from JavaScript (see `index.js`), which also exposes proper callbacks for system calls that the C library will be calling into. These syscalls are responsible for heap management, I/O, etc.

This project is a work in progress. Feel free to ping me if you have questions or feedback: laurent.sansonetti@microsoft.com

## Related repositories

* [mono-wasm-mono](https://github.com/lrz/mono-wasm-mono): a fork of Mono with changes for a wasm32 target, used to build the runtime and compiler.
* [mono-wasm-libc](https://github.com/lrz/mono-wasm-libc): a fork of the WebAssembly/musl C library with tweaks for our version of Mono and our JS glue.

## Current status

This is a work in progress, but you can see `sample/hello/hello.cs` running here: www.hipbyte.com/~lrz/mono-wasm-hello

### Binary releases

Binary releases are avalable here (for testing only): https://github.com/lrz/mono-wasm/releases

## How does it work?

An ASCII graph is worth a thousand words:

```
+----------------+-------------+  +---------------------+
|  Mono runtime  |  C library  |  |    C# assemblies    | <-------+
+----------------+-------------+  +----------+----------+         |
           clang |                           | mono               |
  -target=wasm32 |                           | -aot=llvmonly      |
                 v                           v                    |
+-------------------------------------------------------+         | load
|                       LLVM bitcode                    |         | metadata
+----------------------------+--------------------------+         | (runtime)
                             | mono-wasm                          |
                             | (bitcode -> wasm)                  | 
                             v                                    | 
+-------------------------------------------------------+         |
|                        index.wasm                     |---------+
+----------------------------------------+--------------+               
                 ^                       | libc                         
   load, compile |                       | syscalls                     
    + run main() |                       v                             
+----------------+--------------------------------------+         +-----------+ 
|                         index.js                      | <-----> |  Browser  |
+-------------------------------------------------------+         +-----------+
```

## Build instructions

We will assume that you want to build everything in the ~/src/mono-wasm directory.

```
$ mkdir ~/src/mono-wasm
$ cd ~/src/mono-wasm
$ git clone git@github.com:lrz/mono-wasm.git build
```

### LLVM+clang+lld with WebAssembly target

We need a copy of the LLVM tooling (clang and lld included) with the experimental WebAssembly target enabled. Make sure to build a Release build (as indicated below) otherwise the WASM codegen will be significantly slower.

```
$ cd ~/src/mono-wasm
$ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
$ cd llvm/tools
$ svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
$ svn co http://llvm.org/svn/llvm-project/lld/trunk lld
$ cd ../..
$ mkdir llvm-build
$ cd llvm-build
$ cmake -G "Unix Makefiles" -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly -DCMAKE_BUILD_TYPE=Release ../llvm
$ make
```

After you did this you should have the LLVM static libraries for the WebAssembly target:

```
$ ls ~/src/mono-wasm/llvm-build/lib | grep WebAssembly
libLLVMWebAssemblyAsmPrinter.a
libLLVMWebAssemblyCodeGen.a
libLLVMWebAssemblyDesc.a
libLLVMWebAssemblyDisassembler.a
libLLVMWebAssemblyInfo.a
```

You should also have the `~/src/mono-wasm/llvm-build/bin/clang` program built with the wasm32 target:

```
$ ~/src/mono-wasm/llvm-build/bin/clang --version
clang version 5.0.0 (trunk 306818)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Users/lrz/src/mono-wasm/llvm-build/bin

  Registered Targets:
[...]
    wasm32     - WebAssembly 32-bit
    wasm64     - WebAssembly 64-bit
```

You should also have the wasm lld (linker) library:

```
$ ls ~/src/mono-wasm/llvm-build/lib/liblldWasm.a
/Users/lrz/src/mono-wasm/llvm-build/lib/liblldWasm.a
```

### Mono compiler

We need a build a copy of the Mono compiler that we will use to generate LLVM bitcode from assemblies. We are building this for 32-bit Intel (i386) because the Mono compiler assumes way too many things from the host environment when generating the bitcode, so we want to match the target architecture (which is also 32-bit).

First, you need to build a copy of the Mono fork of LLVM. We are building it for both 32-bit and 64-bit Intel so that we can easily switch the Mono compiler back to 64-bit later, and we manually have to copy the headers to the build directory as the Mono build system doesn't support external LLVM builds.

```
$ cd ~/src/mono-wasm
$ git clone git@github.com:mono/llvm.git llvm-mono
$ mkdir llvm-mono-build
$ cd llvm-mono-build
$ cmake -G "Unix Makefiles" -DCMAKE_OSX_ARCHITECTURES="i386;x86_64" ../llvm-mono
$ ditto ../llvm-mono/include include
$ make
```

Now, we can now build the Mono compiler itself.

```
$ cd ~/src/mono-wasm
$ git clone git@github.com:lrz/mono-wasm-mono.git mono-compiler
$ cd mono-compiler
$ ./autogen.sh --host=i386-darwin --with-cross-offsets=offsets-wasm32.h CFLAGS="-DCOMPILE_WASM32 -DMONO_CROSS_COMPILE" CXXFLAGS="-DCOMPILE_WASM32 -DMONO_CROSS_COMPILE" --disable-boehm --with-sigaltstack=no --enable-llvm --enable-llvm-runtime --with-llvm=../llvm-mono-build --disable-btls --with-runtime_preset=testing_aot_full
$ cd eglib
$ make
$ cd ../mono
$ make
```

At the end of this process you should have a `mono` executable installed as `~/src/mono-wasm/mono-compiler/mono/mini/mono` built for the i386 architecture.

```
$ file ~/src/mono-wasm/mono-compiler/mono/mini/mono
mono/mini/mono: Mach-O executable i386
```

Now let's build the `mscorlib.dll` assembly for the WebAssembly profile. We can't use the mono runtime we just built as it's full AOT, so we use assume you have a normal `mono` runtime in your PATH that we can use. Clearly a hack, but in the meantime it works.

```
$ cd ~/src/mono-wasm/mono-compiler/mcs/class/corlib
$ make V=1 PROFILE=wasm RUNTIME=mono STRING_REPLACER=true SN=true
```

After this you should have the assembly file created in the proper location:

```
$ file ~/src/mono-wasm/mono-compiler/mcs/class/lib/wasm/mscorlib.dll 
/Users/lrz/src/mono-wasm/mono-compiler/mcs/class/lib/wasm/mscorlib.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
```

### Mono runtime

Now we can prepare the Mono runtime. We have to clone a new copy of the source code. We are not building the runtime code using the Mono autotools system, so we have to copy header files that are normally generated.

```
$ cd ~/src/mono-wasm
$ git clone git@github.com:lrz/mono-wasm-mono.git mono-runtime
$ cd mono-runtime
$ cp config-wasm32.h config.h
$ cp eglib/src/eglib-config-wasm32.h eglib/src/eglib-config.h
```

### C library

Similarly as above, we clone a copy of the C library that we will be using.

```
$ cd ~/src/mono-wasm
$ git clone git@github.com:lrz/mono-wasm-libc.git libc
```

### OK ready!

We are ready to build our Hello World.

First, we need to build everything into the `dist` directory:

```
$ cd ~/src/mono-wasm/build
$ vi Makefile               # make sure the *_PATH variables point to proper locations, should be the case if you followed these instructions
$ make
```

This will build the mono runtime and the libc as LLVM bitcode using our version of clang, then link everything into a `runtime.bc` file. This will also build the `mono-wasm` tool which links against the LLVM and lld libraries. Finally, we will copy the Mono compiler and its `mscorlib.dll` file.

```
$ find dist -type f
dist/bin/monoc
dist/bin/mono-wasm
dist/lib/runtime.bc
dist/lib/index.js
dist/lib/mscorlib.dll
dist/lib/mscorlib.xml
```

Once done, you can build the Hello World sample:

```
$ cd sample/hello
$ make
```

## TODO

TODO (now):

* fix garbage collection (need to figure out how to scan the stack)
* ship a first 'alpha' release

TODO (later):

* put mscorlib on a diet (currently 'hello world' is 10MB) by removing more functionality within the `wasm.make` profile and doing more aggressive IL linking
* work on patches for mono based on the changes made in the fork
* merge the WebAssembly LLVM code into the mono/llvm fork so that the Mono compiler can target wasm32 directly, and that we can merge the code into `mono-wasm` (we won't have to ship the Mono compiler separately as `monoc`)
* improve the C# -> JS interop by doing a full C# API replication in JS like embeddinator 4000
* investigate: threads, sockets, debugger, stack unwinding, simd and atomic operations, etc.

## License

This work is distributed under the terms of the MIT license. See the LICENSE.txt file for more information.


================================================
FILE: boot.c
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See the LICENSE.txt file in the project root
// for the license information.

#include <mono/mini/mini.h>
#include <mono/metadata/assembly.h>
#include <locale.h>

void mono_wasm_aot_init(void);

__attribute__ ((__visibility__ ("default")))
int
mono_wasm_main(char *main_assembly_name, int debug)
{
    g_log("mono-wasm", G_LOG_LEVEL_INFO, "booting main()");

    setlocale(LC_ALL, "");

    g_setenv("LANG", "en_US", 1);
    g_setenv("MONO_PATH", ".", 1);
    g_setenv("MONO_LOG_LEVEL", debug ? "debug" : "error", 1);

    g_log_set_always_fatal(G_LOG_LEVEL_ERROR);
    g_log_set_fatal_mask(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR);

    g_set_prgname("hello");

    mono_wasm_aot_init();

    g_log("mono-wasm", G_LOG_LEVEL_INFO, "initializing mono runtime");
    mono_jit_set_aot_mode(MONO_AOT_MODE_LLVMONLY);
    MonoDomain *domain = mono_jit_init_version("hello", "v4.0.30319");

    g_log("mono-wasm", G_LOG_LEVEL_INFO, "opening main assembly `%s'",
            main_assembly_name);
    MonoAssembly *assembly = mono_assembly_open(main_assembly_name, NULL);
    g_assert(assembly != NULL);

    g_log("mono-wasm", G_LOG_LEVEL_INFO, "running Main()");
    int mono_argc = 1;
    char *mono_argv[] = { main_assembly_name, NULL };
    return mono_jit_exec(domain, assembly, mono_argc, mono_argv);
}


================================================
FILE: index.js
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See the LICENSE.txt file in the project root
// for the license information.

// JavaScript WASM support for libc+mono. Inspired from WebAssembly/musl's
// wasm.js file.

Error.stackTraceLimit = Infinity; // print the entire callstack on errors

var dump_cross_offsets = false;
var debug_logs = false;
var functions = { env: {} };
var instance;
var heap;
var heap_size;

var browser_environment = (typeof window != "undefined");

function heap_get_short(ptr) {
  var d = 0;
  d += (heap[ptr + 0] << 0);
  d += (heap[ptr + 1] << 8);
  return d;
}

function heap_get_int(ptr) {
  var d = 0;
  d += (heap[ptr + 0] << 0);
  d += (heap[ptr + 1] << 8);
  d += (heap[ptr + 2] << 16);
  d += (heap[ptr + 3] << 24);
  return d;
}

function heap_get_long(ptr) {
  var d = 0;
  d += (heap[ptr + 0] << 0);
  d += (heap[ptr + 1] << 8);
  d += (heap[ptr + 2] << 16);
  d += (heap[ptr + 3] << 24);
  d += (heap[ptr + 4] << 32);
  d += (heap[ptr + 5] << 40);
  d += (heap[ptr + 6] << 48);
  d += (heap[ptr + 7] << 56);
  return d;
}

function heap_set_int(ptr, d) {
  heap[ptr + 0] = ((d & 0x000000ff) >> 0);
  heap[ptr + 1] = ((d & 0x0000ff00) >> 8);
  heap[ptr + 2] = ((d & 0x00ff0000) >> 16);
  heap[ptr + 3] = ((d & 0xff000000) >> 24);
  return d;
}

function heap_set_long(ptr, d) {
  heap[ptr + 0] = ((d & 0x00000000000000ff) >> 0);
  heap[ptr + 1] = ((d & 0x000000000000ff00) >> 8);
  heap[ptr + 2] = ((d & 0x0000000000ff0000) >> 16);
  heap[ptr + 3] = ((d & 0x00000000ff000000) >> 24);
  heap[ptr + 4] = ((d & 0x000000ff00000000) >> 32);
  heap[ptr + 5] = ((d & 0x0000ff0000000000) >> 40);
  heap[ptr + 6] = ((d & 0x00ff000000000000) >> 48);
  heap[ptr + 7] = ((d & 0xff00000000000000) >> 56);
  return d;
}

function heap_get_string(ptr, len=-1) {
  var str = '';
  var i = 0;
  while (true) {
    var c = heap[ptr + i];
    if (c == 0) {
      break;
    }
    if (i == len) {
      break;
    }
    str += String.fromCharCode(c);
    i++;
  }
  return str;
}

function heap_get_mono_string(ptr)
{
  var str_length = heap_get_int(ptr + 8)
  var str_chars = ptr + 12
  var str = ''
  for (var i = 0; i < str_length; i++) {
    var c = heap_get_short(str_chars + (i * 2))
    str += String.fromCharCode(c);
  }
  return str;
}

function heap_set_string(ptr, str) {
  for (var i = 0; i < str.length; i++) {
    heap[ptr + i] = str.charCodeAt(i);
  }
  heap[ptr + str.length] = 0
}

function heap_malloc_string(str) {
  var ptr = instance.exports.malloc(str.length + 1)
  heap_set_string(ptr, str)
  return ptr
}

function heap_human(size) {
  var suffixes = ['B', 'K', 'M', 'G']
  var suffix;
  for (var i in suffixes) {
    suffix = suffixes[i]
    if (size < 1000) {
      break
    }
    size /= 1000
  }
  return size.toFixed(2) + suffix
}

function log(str) {
  browser_environment ? console.log(str) : print(str)
}

function debug(str) {
  if (debug_logs) {
    log(">> " + str);
  }
}

function error(str) {
  log("!! " + str + ": " + new Error().stack);
}

function TerminateWasmException(value) {
  this.message = 'Terminating WebAssembly';
  this.value = value;
  this.toString = function() { return this.message + ': ' + this.value; };
}

function NotYetImplementedException(what) {
  this.message = 'Not yet implemented';
  this.what = what;
  this.toString = function() { return this.message + ': ' + this.what; };
}

// TODO: these missing (imported) functions shouldn't be called from the runtime.
var missing_functions=["__addtf3","__clone","__divdc3","__divtf3","__eqtf2","__extenddftf2","__extendsftf2","__fixtfdi","__fixtfsi","__fixunstfsi","__floatsitf","__floatunsitf","__getf2","__lsysinfo","__lttf2","__mmap","__multf3","__munmap","__netf2","__randname","__set_thread_area","__subtf3","__synccall","__syscall","__syscall0","__syscall1","__syscall2","__syscall3","__syscall4","__syscall5","__syscall6","__syscall_cp","__trunctfdf2","__trunctfsf2","__unordtf2","__wait","_pthread_cleanup_pop","_pthread_cleanup_push","accept","bind","btowc","cabs","chmod","closedir","closelog","connect","execv","execve","execvp","feclearexcept","fegetround","feraiseexcept","fesetround","fetestexcept","fork","freeaddrinfo","getaddrinfo","getgrgid_r","getgrnam_r","getnameinfo","getpeername","getpriority","getprotobyname","getpwnam_r","getpwuid_r","getrusage","getsockname","getsockopt","htons","ioctl","listen","longjmp","lstat","mbrtowc","mbsinit","mbsnrtowcs","mbstowcs","mbtowc","mincore","mkdir","mkdtemp","mkstemp","mmap","mono_arch_cleanup","mono_arch_context_get_int_reg","mono_arch_create_generic_trampoline","mono_arch_create_rgctx_lazy_fetch_trampoline","mono_arch_create_specific_trampoline","mono_arch_find_imt_method","mono_arch_find_static_call_vtable","mono_arch_flush_register_windows","mono_arch_free_jit_tls_data","mono_arch_get_argument_info","mono_arch_get_call_filter","mono_arch_get_delegate_invoke_impl","mono_arch_get_delegate_virtual_invoke_impl","mono_arch_get_gsharedvt_arg_trampoline","mono_arch_get_gsharedvt_call_info","mono_arch_get_gsharedvt_trampoline","mono_arch_get_restore_context","mono_arch_get_rethrow_exception","mono_arch_get_static_rgctx_trampoline","mono_arch_get_this_arg_from_call","mono_arch_get_throw_corlib_exception","mono_arch_get_throw_exception","mono_arch_get_unbox_trampoline","mono_arch_handle_exception","mono_arch_ip_from_context","mono_arch_patch_callsite","mono_arch_patch_plt_entry","mono_arch_regname","mono_arch_unwind_frame","mono_interp_frame_iter_init","mono_interp_frame_iter_next","mono_interp_run_finally","mono_interp_set_resume_state","mono_monoctx_to_sigctx","mono_mprotect","mono_sigctx_to_monoctx","mono_vfree","mono_w32file_get_volume_information","mono_wasm_js_eval_imp","mono_wasm_throw_exception","msync","munmap","opendir","openlog","posix_spawn","posix_spawn_file_actions_adddup2","posix_spawn_file_actions_destroy","posix_spawn_file_actions_init","pthread_attr_destroy","pthread_attr_getstacksize","pthread_attr_init","pthread_attr_setdetachstate","pthread_attr_setstacksize","pthread_barrier_init","pthread_barrier_wait","pthread_cond_broadcast","pthread_cond_destroy","pthread_cond_init","pthread_cond_signal","pthread_cond_timedwait","pthread_cond_wait","pthread_condattr_destroy","pthread_condattr_init","pthread_condattr_setclock","pthread_create","pthread_exit","pthread_getschedparam","pthread_getspecific","pthread_join","pthread_key_create","pthread_key_delete","pthread_kill","pthread_mutex_destroy","pthread_mutex_init","pthread_mutex_lock","pthread_mutex_trylock","pthread_mutex_unlock","pthread_mutexattr_destroy","pthread_mutexattr_init","pthread_mutexattr_settype","pthread_once","pthread_self","pthread_setcancelstate","pthread_setschedparam","pthread_setspecific","pthread_sigmask","readdir","recvfrom","recvmsg","sched_get_priority_max","sched_yield","select","sem_destroy","sem_init","sem_post","sem_timedwait","sem_trywait","sem_wait","send","sendmsg","sendto","setjmp","setpriority","setsockopt","shutdown","socket","statvfs","syslog","uname","utimensat","waitpid","wcsrtombs","wctomb","mono_arch_build_imt_trampoline"];
// TODO: these missing (imported) globals should also be removed from the runtime.
var missing_globals=["_ZTIPi"];

// variables generated by `mono-wasm':
//   files: an array of IL assemblies files
if (typeof files == "undefined") {
  var files = [];
}

for (var i in missing_functions) {
  f = missing_functions[i];
  functions['env'][f] = (function(f) { 
    return function() {
      error("Not Yet Implemented: " + f)
      throw new NotYetImplementedException(f);
    }
  })(f);
}

var do_nothing_functions = ['pthread_mutexattr_init', 'pthread_mutexattr_settype', 'pthread_mutex_init', 'pthread_mutexattr_destroy', 'pthread_mutex_lock', 'pthread_mutex_unlock', 'pthread_condattr_init', 'pthread_condattr_setclock', 'pthread_cond_init', 'pthread_condattr_destroy', 'pthread_sigmask', '_pthread_cleanup_push', '_pthread_cleanup_pop', 'pthread_self', 'pthread_create', 'pthread_mutex_trylock', 'pthread_attr_init', 'pthread_attr_setstacksize', 'pthread_attr_getstacksize', 'pthread_attr_destroy', 'sem_init', 'sem_wait', 'sem_post', 'mono_console_init', '__munmap', 'pthread_cond_broadcast', 'pthread_mutex_destroy', 'pthread_cond_destroy', 'mono_mprotect', 'sched_yield']

for (var i in do_nothing_functions) {
  f = do_nothing_functions[i];
  functions['env'][f] = function() { }
}

// A (way too) simple implementation for thread-local variables. Should be
// removed once we enable the relevant code in the libc.
var tls_variables = {}

functions['env']['pthread_key_create'] = function(key_ptr, destructor) {
  key = Object.keys(tls_variables).length;
  tls_variables[key] = 0;
  heap_set_int(key_ptr, key);
  return 0;
}

functions['env']['pthread_getspecific'] = function(key) {
  var value = tls_variables[key];
  debug('pthread_getspecific(' + key + ') -> ' + value);
  return value;
}

functions['env']['pthread_setspecific'] = function(key, value) {
  debug('pthread_setspecific(' + key + ', ' + value + ')');
  tls_variables[key] = value;
  return 0;
}

for (var i in missing_globals) {
  g = missing_globals[i];
  functions['env'][g] = 0;
}

// Temporary entry-point for exceptions raised by Mono. We assume that all
// exceptions are fatal at this point.

functions['env']['mono_wasm_throw_exception'] = function(exc) {
  var class_str = heap_get_mono_string(heap_get_int(exc + 8))
  var message_str = heap_get_mono_string(heap_get_int(exc + 12))
  msg = ('Mono Exception: ' + (class_str.length > 0 ? class_str + ': ' : '')
          + message_str)
  error(msg)
  throw new TerminateWasmException(msg)
}

// Implementation of the C# WebAssembly API.

functions['env']['mono_wasm_js_eval_imp'] = function(expr, exception_raised) {
  var str = heap_get_string(expr);
  var res = undefined;
  try {
    res = eval(str);
  }
  catch (e) {
    heap_set_int(exception_raised, 1);
    res = e;
  }
  return heap_malloc_string(String(res));
}

var mono_wasm_refs = {};
var mono_wasm_ref_counter = 0;

Object.defineProperty(Object.prototype, "__mono_wasm_ref__", {
  writable: true
});

function mono_wasm_wrap_obj(obj) {
  var ref = undefined;
  if (obj != null) {
    ref = obj.__mono_wasm_ref__;
    if (ref == undefined) {
      obj.__mono_wasm_ref__ = ref = mono_wasm_ref_counter++;
    }
    mono_wasm_refs[ref] = obj;
  }
  return ref;
}

function mono_wasm_unwrap_obj(ref) {
  return mono_wasm_refs[ref];
}

// Implementation of the JS/Mono API.

function _MonoDomain() {
  return instance.exports.mono_domain_get();
}

function _MonoGPtrToArray(ptr) {
  var pdata = heap_get_int(ptr + 0);
  var plen = heap_get_int(ptr + 4);
  var ary = [];
  for (var i = 0; i < plen; i++) {
    ary.push(heap_get_int(pdata + (i * 4)));
  }
  return ary;
}

function _MonoAssemblies() {
  var ptr = instance.exports.mono_domain_get_assemblies(_MonoDomain(), false);
  return _MonoGPtrToArray(ptr);
}

function _MonoImage(assembly) {
  return instance.exports.mono_assembly_get_image(assembly);
}

function MonoClass(namespace, name) {
  var namespace_str = heap_malloc_string(namespace);
  var name_str = heap_malloc_string(name);
  var assemblies = _MonoAssemblies();
  var klass = undefined;
  for (var i in assemblies) {
    var assembly = assemblies[i];
    var image = _MonoImage(assembly);
    var klass = instance.exports.mono_class_from_name(image, namespace_str,
            name_str);
    if (klass) {
      break;
    }
  }
  instance.exports.free(namespace_str);
  instance.exports.free(name_str);
  return klass;
}

function MonoMethod(klass, name, is_static) {
  var flags = (is_static ? 0x10 : 0);
  var name_str = heap_malloc_string(name);
  var method = instance.exports.mono_class_get_method_from_name_flags(klass,
          name_str, -1, flags);
  instance.exports.free(name_str);
  return method;
}

function MonoInvoke(obj, method, params) {
  var sig = instance.exports.mono_method_signature(method);
  var argc = instance.exports.mono_signature_get_param_count(sig);
  if (params.length != argc) {
    throw "invalid number of parameters";
  }
  var argv = 0;
  if (argc > 0) {
    argv = instance.exports.malloc(4 * argc);
    for (var i in params) {
      var param = params[i];
      var arg = undefined;
      if (Number.isInteger(param)) {
        arg = instance.exports.malloc(4);
        heap_set_int(arg, param);
      }
      else if (typeof param === 'string') {
        var param_str = heap_malloc_string(param);        
        arg = instance.exports.mono_string_new(_MonoDomain(), param_str)
        instance.exports.free(param_str);
      }
      else {
        throw "unsupported param type";
      }
      heap_set_int(argv + (i * 4), arg);
    }
  }
  var res = instance.exports.mono_runtime_invoke(method, obj, argv);
  if (argc > 0) {
    //for (var i = 0; i < argc; i++) {
    //  instance.exports.free(heap_get_int(argv + (i * 4)))
    //}
    instance.exports.free(argv);
  }
  //if (res) {
  //  res = instance.exports.mono_object_unbox(res);
  //}
  return res;
}

// System calls.

var fds = {}
fds[0] = undefined
fds[1] = undefined
fds[2] = undefined

var out_buffer = '';

function out_buffer_add(ptr, len) {
  out_buffer += heap_get_string(ptr, len)
}

function out_buffer_flush() {
  if (out_buffer.charAt(out_buffer.length - 1) == '\n') {
    log(out_buffer.substr(0, out_buffer.length - 1))
    out_buffer = ''
  }
}

var files_content = {}
var syscalls = {}

syscalls[3] = function SYS_read(fd, buf, len) {
  var obj = fds[fd]
  if (obj) {
    debug('read(' + fd + ') -> ' + len) 
    offset = obj['offset']
    buffer = obj['content']
    heap.set(buffer.subarray(offset, offset + len), buf)   
    return len
  }
  error('read() called with invalid fd ' + fd)
  return -1
}

syscalls[4] = function SYS_write(fd, buf, len) {
  if (fd == 1 || fd == 2) {
    out_buffer_add(buf, len)
    out_buffer_flush()
    return len
  }
  error('write() called with invalid fd ' + fd)
}

syscalls[6] = function SYS_close(fd) {
  var obj = fds[fd]
  if (obj) {
    fds[fd] = undefined
    return 0
  }
  error('close() called with invalid fd ' + fd)
  return -1
}

syscalls[20] = function SYS_getpid() {
  return 42
}

var brk_current = 0
syscalls[45] = function SYS_brk(inc) {
  if (inc == 0) {
    brk_current = heap_size;
    debug("brk: current heap " + heap_human(brk_current))
    return brk_current;
  }
  if (brk_current + inc > heap_size) {
    var delta = inc - (heap_size - brk_current)
    brk_current += inc
    var new_pages_needed = Math.ceil(delta / 65536.0)
    var memory = instance.exports.memory
    var n = memory.grow(new_pages_needed);
    var new_heap_size = memory.buffer.byteLength
    debug("brk: pages " + n + " -> " + (n + new_pages_needed) + " (+" + new_pages_needed + "), heap " + heap_human(heap_size) + " -> " + heap_human(new_heap_size) + " (+" + heap_human(new_heap_size - heap_size) + ")")
    heap = new Uint8Array(memory.buffer)
    heap_size = new_heap_size
  }
  return inc
}

syscalls[54] = function SYS_ioctl(fd, req, arg) {
  // TODO
  return 0
}

syscalls[55] = function SYS_fcntl(fd, cmd, arg) {
  if (cmd == 3) {
    // F_GETFL
    if (fd == 1 || fd == 2) {
      return 1 // O_WRONLY
    }
    if (fd == 0 || fds[fd]) {
      return 0 // O_RDONLY
    }
  }
  error('fcntl() called with invalid fd ' + fd + ' and/or cmd ' + cmd)
  return -1
}

syscalls[76] = function SYS_getrlimit(resource, rlim) {
  // TODO
  return 0
}

syscalls[85] = function SYS_readlink(path, buf, buflen) {
  // TODO
  debug('readlink("' + heap_get_string(path) + '")')
  return -1
}

syscalls[146] = function SYS_writev(fd, iovs, iov_count) {
  if (fd == 1 || fd == 2) {
    var all_lens = 0
    for (var i = 0; i < iov_count; i++) {
      var base = heap_get_int(iovs + (i * 8))
      var len = heap_get_int(iovs + 4 + (i * 8))
      debug("write fd: " + fd + ", base: " + base + ", len: " + len)
      out_buffer_add(base, len)
      all_lens += len
    }
    out_buffer_flush()
    return all_lens
  }
  error("can only write on stdout and stderr") 
  return -1
}

var sizeof_k_sigaction = 20
var signals = {} // maps signal numbers to k_sigaction UInt8Array
syscalls[174] = function SYS_sigaction(sig, act, oact, mask_len) {
  if (mask_len != 8) {
    error('mask_len should be 8 (is ' + mask_len + ')')
    mask_len = 8
  }
  sig_act = (signals[sig] || new Uint8Array(sizeof_k_sigaction))
  if (oact != 0) {
    heap.set(sig_act, oact)    
  }
  if (act != 0) {
    sig_act.set(heap.slice(act, sizeof_k_sigaction)) 
  }
  return 0
}

syscalls[106] = function SYS_stat(path, s) {
  var path_str = heap_get_string(path)
  debug('stat("' + path_str + '")')
  if (path_str == "/") {
    heap_set_int(s + 16, 0040000)   // st_mode -> S_IFDIR
    return 0
  }
  for (var i in files) {
    var file = "/" + files[i];
    if (path_str == file) {
      heap_set_int(s + 16, 0100000)   // st_mode -> S_IFREG
      return 0
    }
  }
  return -1
}

syscalls[108] = function SYS_fstat(fd, s) {
  var obj = fds[fd]
  if (obj) {
    var st_size = obj['content'].length
    debug('fstat(' + fd + ') -> { st_size: ' + st_size + ' }')
    heap_set_int(s + 40, st_size) // st_size
    return 0
  }
  error('fstat() called with invalid fd ' + fd)
  return -1
}

syscalls[140] = function SYS_lseek(fd, unused, offset, result, whence) {
  var obj = fds[fd]
  if (obj) {
    if (whence == 0) {
      // SEEK_SET
      obj['offset'] = offset
    }
    else if (whence == 1) {
      // SEEK_CUR
      offset = obj['offset']
    }
    else {
      error('lseek() called with invalid whence ' + whence)
      return -1
    }
    debug('lseek(' + fd + ', ...) -> ' + offset)
    heap_set_long(result, offset)
    return 0
  }
  error('lseek() called with invalid fd ' + fd)
  return -1
}

syscalls[175] = function SYS_sigprocmask(action, mask, set, sig_n) {
  // TODO
  return 0
}

syscalls[183] = function SYS_getcwd(buf, buflen) {
  if (buflen > 1) {
    heap_set_string(buf, "/")
    return 0
  }
  error('getcwd() called with buflen ' + buflen)
  return -1
}

var process_tid = 42 // Should fix this once we get multithreading
syscalls[224] = function SYS_gettid() {
  return process_tid
}

syscalls[219] = function SYS_madvise(addr, len, advice) {
  if (advice == 4) {
    // TODO
    return 0
  }
  return -1
}

syscalls[238] = function SYS_tkill(tid, signal) {
  if (tid == process_tid) {
    if (signal == 6) {
      // SIGABRT
      error("received SIGABRT")
      throw new TerminateWasmException('SIGABRT');
    }
    error('tkill() with unsupported signal: ' + signal)
  }
  else {
    error('tkill() with wrong tid: ' + tid)
  }
  return -1
}

syscalls[252] = function SYS_exit(code) {
  log("exit(" + code + "): " + new Error().stack)
  throw new TerminateWasmException('exit(' + code + ')');
}

syscalls[265] = function SYS_clock_gettime(clock_id, timespec) {
  // TODO should switch to something else with a higher resolution + support
  // the different CLOCK_ ids.
  if (timespec) {
    var ms = new Date().getTime()
    var sec = Math.floor(ms / 1000)
    var nsec = (ms % 1000) * 1000000
    debug("clock_gettime: msec: " + ms + " -> sec: " + sec + ", nsec: "
            + nsec)
    heap_set_int(timespec, sec)        // tv_sec
    heap_set_int(timespec + 4, nsec)   // tv_nsec
  }
  return 0;
}

syscalls[266] = function SYS_clock_getred(clock_id, timespec) {
  if (timespec) {
    // Our gettime JS implementation has a 1ms resolution.
    heap_set_int(timespec, 0)           // tv_sec
    heap_set_int(timespec + 4, 1000000) // tv_nsec
  }
  return 0
}

syscalls[295] = function SYS_openat(at, filename, flags, mode) {
  if (at == -100) {
    // AT_FDCWD
    if (flags == 0100000) {
      var filename_str = heap_get_string(filename)
      var fd = -1
      if (filename_str.charAt(0) == '/') {
          filename_str = filename_str.substr(1)
      }
      if (files.indexOf(filename_str) != -1) {
        var obj = {};
        obj['offset'] = 0;
        obj['path'] = filename_str;
        var buf = files_content[filename_str]
        if (!buf) {
          buf = new Uint8Array(readbuffer(filename_str))
          files_content[filename_str] = buf
        } 
        obj['content'] = buf
        fd = Object.keys(fds).length;
        fds[fd] = obj;
      }
      debug('open("' + filename_str + '") -> ' + fd);
      return fd
    }
  }
  error('openat() called with at ' + at + ' and flags ' + flags)
  return -1
}

syscalls[340] = function SYS_prlimit64(pid, resource, new_rlim, old_rlim) {
  // TODO
  return 0
}

syscalls[375] = function SYS_membarrier() {
  return 0
}

function route_syscall() {
  n = arguments[0]
  argv = [].slice.call(arguments, 1)
  f = syscalls[n]
  name = f ? f.name : n
  debug('syscall(' + name + (argv.length > 0 ? ', ' + argv.join(', ') : '')
              + ')')
  if (!f) {
    error('unimplemented syscall ' + n + ' called')
    return -1
  }
  return f.apply(this, argv)
}

for (var i in [0, 1, 2, 3, 4, 5, 6]) {
  functions['env']['__syscall' + i] = route_syscall
}
functions['env']['__syscall_cp'] = route_syscall

function run_wasm_code() {
  heap = new Uint8Array(instance.exports.memory.buffer);
  heap_size = instance.exports.memory.buffer.byteLength;
  
  if (dump_cross_offsets) {
    // We don't care about freeing the memory as we exit soon after.
    instance.exports.setenv(heap_malloc_string('DUMP_CROSS_OFFSETS'),
            heap_malloc_string('1'), 1)
  }
 
  debug("running main()")
  var ret = instance.exports.mono_wasm_main(heap_malloc_string(files[0]),
          debug_logs);
  debug('main() returned: ' + ret);
}

if (browser_environment) {
  fetch('index.wasm').then(function(response) {
    return response.arrayBuffer()
  }).then(function(buf) {
    return WebAssembly.compile(buf)
  }).then(function(mod) {
    return WebAssembly.instantiate(mod, functions)
  }).then(function(i) {
    instance = i
    var files_promises = [];
    files.forEach(function(url, i) {
      files_promises.push(
        fetch(url).then(function(res){
          return res.arrayBuffer();
        }).then(function(buf){
          files_content[url] = new Uint8Array(buf)
        })
      );
    });
    Promise.all(files_promises).then(function() {
      run_wasm_code();
      document.dispatchEvent(new Event('WebAssemblyContentLoaded'));
    });
  })
}
else {
  var module = new WebAssembly.Module(read('index.wasm', 'binary'))
  instance = new WebAssembly.Instance(module, functions)
  run_wasm_code()
}


================================================
FILE: jsmin.c
================================================
/* jsmin.c
   2013-03-29

Copyright (c) 2002 Douglas Crockford  (www.crockford.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

The Software shall be used for Good, not Evil.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <stdlib.h>
#include <stdio.h>

FILE *jsmin_in = NULL;
FILE *jsmin_out = NULL;

static int   theA;
static int   theB;
static int   theLookahead = EOF;
static int   theX = EOF;
static int   theY = EOF;


static void
error(const char* s)
{
    fputs("JSMIN Error: ", stderr);
    fputs(s, stderr);
    fputc('\n', stderr);
    exit(1);
}

/* isAlphanum -- return true if the character is a letter, digit, underscore,
        dollar sign, or non-ASCII character.
*/

static int
isAlphanum(int c)
{
    return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
        (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' ||
        c > 126);
}


/* get -- return the next character from stdin. Watch out for lookahead. If
        the character is a control character, translate it to a space or
        linefeed.
*/

static int
get()
{
    int c = theLookahead;
    theLookahead = EOF;
    if (c == EOF) {
        c = getc(jsmin_in);
    }
    if (c >= ' ' || c == '\n' || c == EOF) {
        return c;
    }
    if (c == '\r') {
        return '\n';
    }
    return ' ';
}


/* peek -- get the next character without getting it.
*/

static int
peek()
{
    theLookahead = get();
    return theLookahead;
}


/* next -- get the next character, excluding comments. peek() is used to see
        if a '/' is followed by a '/' or '*'.
*/

static int
next()
{
    int c = get();
    if  (c == '/') {
        switch (peek()) {
        case '/':
            for (;;) {
                c = get();
                if (c <= '\n') {
                    break;
                }
            }
            break;
        case '*':
            get();
            while (c != ' ') {
                switch (get()) {
                case '*':
                    if (peek() == '/') {
                        get();
                        c = ' ';
                    }
                    break;
                case EOF:
                    error("Unterminated comment.");
                }
            }
            break;
        }
    }
    theY = theX;
    theX = c;
    return c;
}


/* action -- do something! What you do is determined by the argument:
        1   Output A. Copy B to A. Get the next B.
        2   Copy B to A. Get the next B. (Delete A).
        3   Get the next B. (Delete B).
   action treats a string as a single character. Wow!
   action recognizes a regular expression if it is preceded by ( or , or =.
*/

static void
action(int d)
{
    switch (d) {
    case 1:
        putc(theA, jsmin_out);
        if (
            (theY == '\n' || theY == ' ') &&
            (theA == '+' || theA == '-' || theA == '*' || theA == '/') &&
            (theB == '+' || theB == '-' || theB == '*' || theB == '/')
        ) {
            putc(theY, jsmin_out);
        }
    case 2:
        theA = theB;
        if (theA == '\'' || theA == '"' || theA == '`') {
            for (;;) {
                putc(theA, jsmin_out);
                theA = get();
                if (theA == theB) {
                    break;
                }
                if (theA == '\\') {
                    putc(theA, jsmin_out);
                    theA = get();
                }
                if (theA == EOF) {
                    error("Unterminated string literal.");
                }
            }
        }
    case 3:
        theB = next();
        if (theB == '/' && (
            theA == '(' || theA == ',' || theA == '=' || theA == ':' ||
            theA == '[' || theA == '!' || theA == '&' || theA == '|' ||
            theA == '?' || theA == '+' || theA == '-' || theA == '~' ||
            theA == '*' || theA == '/' || theA == '{' || theA == '\n'
        )) {
            putc(theA, jsmin_out);
            if (theA == '/' || theA == '*') {
                putc(' ', jsmin_out);
            }
            putc(theB, jsmin_out);
            for (;;) {
                theA = get();
                if (theA == '[') {
                    for (;;) {
                        putc(theA, jsmin_out);
                        theA = get();
                        if (theA == ']') {
                            break;
                        }
                        if (theA == '\\') {
                            putc(theA, jsmin_out);
                            theA = get();
                        }
                        if (theA == EOF) {
                            error("Unterminated set in Regular Expression literal.");
                        }
                    }
                } else if (theA == '/') {
                    switch (peek()) {
                    case '/':
                    case '*':
                        error("Unterminated set in Regular Expression literal.");
                    }
                    break;
                } else if (theA =='\\') {
                    putc(theA, jsmin_out);
                    theA = get();
                }
                if (theA == EOF) {
                    error("Unterminated Regular Expression literal.");
                }
                putc(theA, jsmin_out);
            }
            theB = next();
        }
    }
}


/* jsmin -- Copy the input to the output, deleting the characters which are
        insignificant to JavaScript. Comments will be removed. Tabs will be
        replaced with spaces. Carriage returns will be replaced with linefeeds.
        Most spaces and linefeeds will be removed.
*/

void
jsmin(void)
{
    if (peek() == 0xEF) {
        get();
        get();
        get();
    }
    theA = '\n';
    action(3);
    while (theA != EOF) {
        switch (theA) {
        case ' ':
            action(isAlphanum(theB) ? 1 : 2);
            break;
        case '\n':
            switch (theB) {
            case '{':
            case '[':
            case '(':
            case '+':
            case '-':
            case '!':
            case '~':
                action(1);
                break;
            case ' ':
                action(3);
                break;
            default:
                action(isAlphanum(theB) ? 1 : 2);
            }
            break;
        default:
            switch (theB) {
            case ' ':
                action(isAlphanum(theA) ? 1 : 3);
                break;
            case '\n':
                switch (theA) {
                case '}':
                case ']':
                case ')':
                case '+':
                case '-':
                case '"':
                case '\'':
                case '`':
                    action(1);
                    break;
                default:
                    action(isAlphanum(theA) ? 1 : 3);
                }
                break;
            default:
                action(1);
                break;
            }
        }
    }
}


================================================
FILE: mono-wasm.cpp
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See the LICENSE.txt file in the project root
// for the license information.

#include <mach/mach_time.h>
#include <sys/stat.h>
#include <dirent.h>
#include <libgen.h>

#include <string>
#include <vector>
#include <memory>

#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Linker/Linker.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO/FunctionImport.h"
#include "llvm/Transforms/IPO/Internalize.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"

#include "lld/Common/Driver.h"

#define ERROR(...) \
    do { \
        fprintf(stderr, __VA_ARGS__); \
        exit(1); \
    } \
    while (0)

#define _PATH_CHECK(path, iftype, what, must_exist) \
    ({ \
        struct stat s; \
        bool exists = false; \
        if (stat(path, &s) != 0) { \
            if (must_exist) { \
                ERROR("path `%s' does not exist\n", path); \
            } \
        } \
        else { \
            if ((s.st_mode & S_IFMT) != iftype) { \
                ERROR("path `%s' is not a %s\n", path, what); \
            } \
            exists = true; \
        } \
        exists; \
    })

#define _FILE_CHECK(path, must_exist) \
    _PATH_CHECK(path, S_IFREG, "file", must_exist)
#define FILE_MUST_EXIST(path) _FILE_CHECK(path, true)
#define FILE_MAY_EXIST(path) _FILE_CHECK(path, false)

#define _DIR_CHECK(path, must_exist) \
    _PATH_CHECK(path, S_IFDIR, "directory", must_exist)
#define DIR_MUST_EXIST(path) _DIR_CHECK(path, true)
#define DIR_MAY_EXIST(path) _DIR_CHECK(path, false)

static int
timespec_cmp (struct timespec a, struct timespec b)
{
    return a.tv_sec < b.tv_sec
        ? -1
        : (a.tv_sec > b.tv_sec
                ? 1 : a.tv_nsec - b.tv_nsec);
}

#define FILE_IS_OLDER(source_path, dest_path) \
    ({ \
        struct stat source_s; \
        assert(stat(source_path, &source_s) == 0); \
        struct stat dest_s; \
        stat(dest_path, &dest_s) == 0 \
            ? timespec_cmp(source_s.st_mtimespec, dest_s.st_mtimespec) > 0 \
            : true; \
    })

static char libdir_path[PATH_MAX] = { 0 };
static char bindir_path[PATH_MAX] = { 0 };

static void
setup_paths(const char *arg0)
{
    char path[PATH_MAX];
    snprintf(path, sizeof path, "%s/../../lib", arg0);
    if (realpath(path, libdir_path) == NULL) {
        ERROR("can't resolve `lib' directory\n");
    }
    DIR_MUST_EXIST(libdir_path);

    snprintf(path, sizeof path, "%s/..", arg0);
    if (realpath(path, bindir_path) == NULL) {
        ERROR("can't resolve `bin' directory\n");
    }
    DIR_MUST_EXIST(bindir_path);
}

static void
diagnostic_handler(const llvm::DiagnosticInfo &DI, void *ctx)
{
    switch (DI.getSeverity()) {
        case llvm::DS_Error:
            llvm::errs() << "ERROR: ";
            break;
        case llvm::DS_Warning:
            llvm::errs() << "WARNING: ";
            break;
        default:
            break;
    }

    llvm::DiagnosticPrinterRawOStream DP(llvm::errs());
    DI.print(DP);
    llvm::errs() << '\n';
}

static void
assembly_link(std::vector<std::string> &assembly_paths,
        const char *output_path)
{
    auto dest_base = std::string(output_path) + "/";

    if (DIR_MAY_EXIST(output_path)) {
        bool need_link = false;
        for (auto assembly_path : assembly_paths) {
            auto linked_path = dest_base + assembly_path;
            if (FILE_IS_OLDER(assembly_path.c_str(), linked_path.c_str())) {
                need_link = true;
                break;
            }
        }
        if (!need_link) {
            goto skip_link;
        }
    }

    char cmd[PATH_MAX];
    snprintf(cmd, sizeof cmd, "monolinker -d %s -c link -l none -o %s",
            libdir_path, output_path);

    for (auto assembly_path : assembly_paths) {
        strlcat(cmd, " -a ", sizeof cmd);
        strlcat(cmd, assembly_path.c_str(), sizeof cmd);
    }

    if (system(cmd) != 0) {
        ERROR("monolinker pass failed (command was: %s)\n", cmd);
    }

skip_link:
    char *first_assembly = strdup(basename((char *)assembly_paths[0].c_str()));
    assert(first_assembly != NULL);
    assembly_paths.clear();
    DIR *dir = opendir(output_path);
    assert(dir != NULL);
    struct dirent *entry;
    int i = 0, first_assembly_i = -1;
    while ((entry = readdir(dir)) != NULL) {
        const char *s = entry->d_name;
        size_t sl = strlen(s);
        if (sl > 4) {
            const char *sp = s + sl - 4;
            if (strcmp(sp, ".exe") == 0 || strcmp(sp, ".dll") == 0) {
                auto linked_path = dest_base + s;
                assembly_paths.push_back(linked_path);
                if (strcmp(s, first_assembly) == 0) {
                    first_assembly_i = i;
                }
                i++;
            }
        }
    }
    closedir(dir);
    // The first assembly path must remain the same given to the command line.
    assert (first_assembly_i >= 0);
    if (first_assembly_i > 0) {
        std::iter_swap(assembly_paths.begin() + first_assembly_i,
                assembly_paths.begin());
    }
    free(first_assembly);
}

static std::string
assembly_compile(std::string assembly_path, const char *build_dir,
        std::string bitcode_path)
{
    static char monoc_path[PATH_MAX] = { '\0' };
    if (monoc_path[0] == '\0') {
        snprintf(monoc_path, sizeof monoc_path, "%s/monoc", bindir_path);
        FILE_MUST_EXIST(monoc_path);
    }

    if (FILE_IS_OLDER(assembly_path.c_str(), bitcode_path.c_str())) {
        char cmd[PATH_MAX];
        snprintf(cmd, sizeof cmd,
                "MONO_PATH=\"%s\" MONO_ENABLE_COOP=1 " \
                "%s --aot=asmonly,llvmonly,static,llvm-outfile=%s %s " \
                ">& /dev/null",
                build_dir, monoc_path, bitcode_path.c_str(),
                assembly_path.c_str());

        if (system(cmd) != 0) {
            ERROR("bitcode compilation for `%s' failed " \
                    "(command was: %s)\n", assembly_path.c_str(), cmd);
        }
    }

    return bitcode_path;
}

static std::unique_ptr<llvm::Module>
bitcode_link(std::vector<std::string> &paths, llvm::LLVMContext &context)
{
    auto module = llvm::make_unique<llvm::Module>("index.bc", context);
    llvm::Linker linker(*module);

    for (auto path : paths) {
        llvm::SMDiagnostic err;
        auto file_module = llvm::parseIRFile(path, err, context);
        if (!file_module) {
            ERROR("bitcode parsing error: %s:%d: %s\n",
                    err.getFilename().str().c_str(), err.getLineNo(),
                    err.getMessage().str().c_str());
        }

        if (linker.linkInModule(std::move(file_module),
                    llvm::Linker::Flags::OverrideFromSrc)) {
            ERROR("linking %s failed\n", path.c_str());
        }
    }

    return module;
}

static llvm::Module *
aot_init_gen(std::vector<std::string> &assembly_paths, llvm::Module *module,
        llvm::LLVMContext &context)
{
    if (module == NULL) {
        module = new llvm::Module("aot_init.bc", context);
    }

    auto ptr_ty = llvm::PointerType::getUnqual(llvm::Type::getInt8Ty(context));

    auto register_f = module->getFunction("mono_aot_register_module");
    if (register_f == NULL) {
        std::vector<llvm::Type *> types;
        types.push_back(ptr_ty);
        auto c = module->getOrInsertFunction("mono_aot_register_module",
                llvm::FunctionType::get(llvm::Type::getVoidTy(context),
                    types, false));
        register_f = llvm::cast<llvm::Function>(c);
    }

    auto c = module->getOrInsertFunction("mono_wasm_aot_init",
            llvm::FunctionType::get(llvm::Type::getVoidTy(context), false));
    auto f = llvm::cast<llvm::Function>(c);
    auto bb = llvm::BasicBlock::Create(context, "entry", f);

    for (auto path : assembly_paths) {
        // /<build-dir>/foo.{exe,dll} -> mono_aot_module_foo_info
        assert(path.size() > 4);
        assert(path[path.size() - 4] == '.');
        size_t beg = path.rfind('/');
        assert(beg != std::string::npos);
        beg++;

        auto name = std::string("mono_aot_module_")
            + path.substr(beg, path.size() - beg - 4) + "_info";

        auto aot_info = module->getGlobalVariable(name.c_str());
        if (aot_info == NULL) {
            auto c = module->getOrInsertGlobal(name.c_str(), ptr_ty);
            aot_info = llvm::cast<llvm::GlobalVariable>(c);
        }

        llvm::CallInst::Create(register_f,
                new llvm::LoadInst(aot_info, "", bb), "", bb);
    }

    llvm::ReturnInst::Create(context, bb);

    return module;
}

static void
wasm_codegen(llvm::Module *module, llvm::CodeGenOpt::Level opt_level,
        llvm::LLVMContext &context, std::string wasm_path)
{
    static bool init_done = false;
    if (!init_done) {
        LLVMInitializeWebAssemblyTarget();
        LLVMInitializeWebAssemblyTargetMC();
        LLVMInitializeWebAssemblyTargetInfo();
        LLVMInitializeWebAssemblyAsmPrinter();
        init_done = true;
    }

    // Important to generate a proper wasm object file.
    module->setTargetTriple("wasm32-unknown-unknown-wasm");

    std::string err;
    auto triple = llvm::Triple(module->getTargetTriple());
    auto target = llvm::TargetRegistry::lookupTarget("wasm32", triple, err);
    if (target == NULL) {
        ERROR("can't lookup wasm32 target: %s\n", err.c_str());
    }

    std::string cpu_str = "";
    std::string features_str = "";
    llvm::TargetOptions options;
    options.MCOptions.AsmVerbose = false;

    auto target_machine = target->createTargetMachine(triple.getTriple(),
            cpu_str, features_str, options, llvm::None,
            llvm::CodeModel::Large, opt_level);

    if (target_machine == NULL) {
        ERROR("couldn't allocate target machine\n");
    }

    llvm::legacy::PassManager pm;
    pm.add(new llvm::TargetLibraryInfoWrapperPass(
                llvm::TargetLibraryInfoImpl(triple)));

    module->setDataLayout(target_machine->createDataLayout());

    std::error_code EC;
    llvm::raw_fd_ostream dest(wasm_path, EC, llvm::sys::fs::F_None);
    if (EC) {
        ERROR("error when opening file %s: %s\n", wasm_path.c_str(),
                EC.message().c_str());
    }

    if (target_machine->addPassesToEmitFile(pm, dest,
                llvm::TargetMachine::CGFT_ObjectFile)) {
        ERROR("target does not support assembly generation\n");
    }

    pm.run(*module);
    dest.flush();
}

static void
wasm_codegen2(std::string &bitcode_path, llvm::CodeGenOpt::Level opt,
        llvm::LLVMContext &context, std::string wasm_path)
{
    if (FILE_IS_OLDER(bitcode_path.c_str(), wasm_path.c_str())) {
        llvm::SMDiagnostic err;
        auto module = llvm::parseIRFile(bitcode_path, err, context);
        if (!module) {
            ERROR("parsing bitcode file `%s' failed: %s:%d: %s\n",
                    bitcode_path.c_str(), err.getFilename().str().c_str(),
                    err.getLineNo(), err.getMessage().str().c_str());
        }

        wasm_codegen(module.get(), opt, context, wasm_path);
    }
}

static void
wasm_link(std::vector<std::string> &paths, std::string output,
        bool strip_debug_info)
{
    std::vector<const char *> args;
    args.push_back("wasm-lld");
    for (auto path : paths) {
        args.push_back(strdup(path.c_str()));
    }
    args.push_back("-o");
    args.push_back(output.c_str());
    args.push_back("--allow-undefined");
    args.push_back("--no-entry");
    if (strip_debug_info) {
        args.push_back("--strip-debug");
    }

    if (!lld::wasm::link(args, false)) {
        ERROR("failed to link wasm files\n");
    }

    for (int i = 0; i < paths.size(); i++) {
        free((char *)args[i + 1]);
    }
}

static void
assembly_strip(std::vector<std::string> &paths, const char *output_path)
{
    for (auto path : paths) {
        const char *base = strrchr(path.c_str(), '/');
        assert(base != NULL);

        char cmd[PATH_MAX];
        snprintf(cmd, sizeof cmd, "mono-cil-strip %s %s%s >& /dev/null",
                path.c_str(), output_path, base);

        if (system(cmd) != 0) {
            ERROR("IL strip for `%s' failed (command was: %s)\n",
                    path.c_str(), cmd);
        }
    }
}

extern "C" {
    extern FILE *jsmin_in;
    extern FILE *jsmin_out;
    void jsmin(void);
}

static void
js_gen(std::vector<std::string> &assembly_paths, const char *output_path)
{
    auto index_js = std::string(libdir_path) + "/index.js";
    FILE_MUST_EXIST(index_js.c_str());

    auto output_index_js = std::string(output_path) + "/index.js";
    FILE *output = fopen(output_index_js.c_str(), "w+");
    if (output == NULL) {
        ERROR("can't open `%s': %s\n", output_index_js.c_str(),
                strerror(errno));
    }

    fprintf(output, "var files=[");
    for (auto path : assembly_paths) {
        const char *base = strrchr(path.c_str(), '/');
        assert(base != NULL);
        fprintf(output, "\"%s\",", base + 1);
    }
    fprintf(output, "];");

    jsmin_in = fopen(index_js.c_str(), "r");
    jsmin_out = output;

    jsmin();

    fclose(jsmin_in);
    fclose(output);

    jsmin_in = NULL;
    jsmin_out = NULL;
}

static std::string
swap_extension(std::string path, const char *new_extension)
{
    auto pos = path.rfind('.');
    if (pos != std::string::npos) {
        assert(pos > 0);
        path = path.substr(0, pos);
    }
    return path + new_extension;
}

int
main(int argc, char **argv)
{
    if (argc < 2) {
        ERROR("Usage: %s [options] <input files>\n\n" \
                "Options:\n" \
                "    -b <directory>        Specify build directory\n" \
                "                          (default is `./build')\n" \
                "    -o <directory>        Specify output directory\n" \
                "    -On                   Specify optimization level\n" \
                "                          (0, 1, 2, 3, default is 2)\n" \
                "    --strip-debug         Strip debugging information\n" \
                "    -v                    Verbose output\n" \
                "    -i                    Incremental build (experimental)\n",
                argv[0]);
    }

    const char *build_path = "./build";
    const char *output_path = NULL;
    llvm::CodeGenOpt::Level opt = llvm::CodeGenOpt::Default;
    bool strip_debug_info = false;
    bool verbose = false;
    bool incremental = false;
    std::vector<std::string> assembly_paths, bitcode_paths, wasm_paths;
    for (int i = 1; i < argc; i++) {
        const char *arg = argv[i];
        if (arg[0] == '-') {
            if (arg[1] == 'b' && arg[2] == '\0') {
                i++;
                if (i >= argc) {
                    ERROR("expected value for `-b' option\n");
                }
                build_path = argv[i];
            }
            else if (arg[1] == 'o' && arg[2] == '\0') {
                i++;
                if (i >= argc) {
                    ERROR("expected value for `-o' option\n");
                }
                output_path = argv[i];
            }
            else if (arg[1] == 'v' && arg[2] == '\0') {
                verbose = true;
            }
            else if (arg[1] == 'i' && arg[2] == '\0') {
                incremental = true;
            }
            else if (arg[1] == 'O' && arg[3] == '\0') {
                switch (arg[2]) {
                    case '0':
                        opt = llvm::CodeGenOpt::None;
                        break;
                    case '1':
                        opt = llvm::CodeGenOpt::Less;
                        break;
                    case '2':
                        opt = llvm::CodeGenOpt::Default;
                        break;
                    case '3':
                        opt = llvm::CodeGenOpt::Aggressive;
                        break;
                    default:
                        ERROR("malformed `-On' option\n");
                }
            }
            else if (strcmp(arg, "--strip-debug") == 0) {
                strip_debug_info = true;
            }
            else {
                ERROR("invalid `%s' option\n", arg);
            }
        }
        else {
            assembly_paths.push_back(arg);
        }
    }
    if (output_path == NULL) {
        ERROR("`-o' option required\n");
    }
    if (assembly_paths.size() == 0) {
        ERROR("at least one input file is required\n");
    }

    setup_paths(argv[0]);

    if (!DIR_MAY_EXIST(output_path)) {
        if (mkdir(output_path, 0755) != 0) {
            ERROR("can't create output directory `%s': %s\n",
                    output_path, strerror(errno));
        }
    }

    auto output_wasm = std::string(output_path) + "/index.wasm";

    llvm::LLVMContext context;
    context.setDiagnosticHandlerCallBack(diagnostic_handler, NULL, true);

    uint64_t start = 0, total = 0, delta = 0;
    mach_timebase_info_data_t timebase_info;
    mach_timebase_info(&timebase_info);

#define T_MEASURE(what, code) \
    start = mach_absolute_time(); \
    if (verbose) { \
        std::string _what = what; \
        printf("%s ... ", _what.c_str()); \
    } \
    code; \
    delta = mach_absolute_time() - start; \
    total += delta; \
    if (verbose) { \
        printf("%.3fs\n", (((double)(delta) * timebase_info.numer) \
                    / (timebase_info.denom * 1000000000))); \
    }

    T_MEASURE("IL link", assembly_link(assembly_paths, build_path));

    bitcode_paths.push_back(std::string(libdir_path) + "/runtime.bc");
    for (auto assembly_path : assembly_paths) {
        auto bitcode_path = swap_extension(assembly_path, ".bc");
        T_MEASURE(std::string("IL/IR compile ") + assembly_path,
                assembly_compile(assembly_path, build_path, bitcode_path));
        bitcode_paths.push_back(bitcode_path);
    }

    if (incremental) {
        for (auto bitcode_path : bitcode_paths) {
            auto wasm_path = swap_extension(bitcode_path, ".wasm");
            T_MEASURE(std::string("IR/WASM codegen ")
                    + bitcode_path.c_str(),
                    wasm_codegen2(bitcode_path, opt, context, wasm_path));
            wasm_paths.push_back(wasm_path);
        }

        auto path = std::string(build_path) + "/aot_init.wasm";
        auto aot_init_mod = aot_init_gen(assembly_paths, NULL, context);
        wasm_codegen(aot_init_mod, opt, context, path);
        wasm_paths.push_back(path);
        delete aot_init_mod;
    }
    else {
        T_MEASURE("IR link",
                auto module = bitcode_link(bitcode_paths, context));

        aot_init_gen(assembly_paths, module.get(), context);

        auto path = std::string(build_path) + "/index.wasm";
        T_MEASURE("IR/WASM codegen",
                wasm_codegen(module.get(), opt, context, path));
        wasm_paths.push_back(path);
    }

    T_MEASURE("WASM link",
            wasm_link(wasm_paths, output_wasm, strip_debug_info));

    T_MEASURE("IL strip", assembly_strip(assembly_paths, output_path));

    T_MEASURE("JS gen", js_gen(assembly_paths, output_path));

#undef T_MEASURE

    return 0;
}


================================================
FILE: mscorlib.xml
================================================
<linker>
  <assembly fullname="mscorlib">
  </assembly>
</linker>


================================================
FILE: sample/hello/.gitignore
================================================
build
output
hello.exe


================================================
FILE: sample/hello/Makefile
================================================
all: hello.exe output/index.wasm output/index.html

hello.exe:      hello.cs
	mcs -nostdlib -noconfig -r:../../dist/lib/mscorlib.dll hello.cs -out:hello.exe

output/index.wasm:      hello.exe
	../../dist/bin/mono-wasm -i hello.exe -o output

output/index.html:      index.html
	cp index.html output

clean:
	rm -rf build output hello.exe


================================================
FILE: sample/hello/hello.cs
================================================
class Hello {
  static int Main(string[] args) {
    System.Console.WriteLine("hello world!");
    return 0;
  }
}


================================================
FILE: sample/hello/index.html
================================================
<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>WebAssembly Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <script src="index.js"></script>
  </body>
</html>


================================================
FILE: sample/hello2/.gitignore
================================================
build
output
hello.exe


================================================
FILE: sample/hello2/Makefile
================================================
all: hello.exe output/index.wasm output/index.html

hello.exe:      hello.cs
	mcs -nostdlib -noconfig -r:../../dist/lib/mscorlib.dll hello.cs -out:hello.exe

output/index.wasm:      hello.exe
	../../dist/bin/mono-wasm -i hello.exe -o output

output/index.html:      index.html
	cp index.html output

clean:
	rm -rf build output hello.exe


================================================
FILE: sample/hello2/hello.cs
================================================
using Mono.WebAssembly;
using System;

class Hello
{
    static int Factorial(int n)
    {
        if (n == 0) {
            return 1;
        }
        return n * Factorial(n - 1);
    }

    // This function is called from the browser by JavaScript.
    // Here we calculate the factorial of the given number then use the
    // Mono.WebAssembly API to retrieve the element from the DOM and set its
    // innerText property to the factorial result.
    static void FactorialInElement(int n, string element_id)
    {
        Console.WriteLine(
                "Calculating factorial of {0} into DOM element {1}",
                n, element_id);

        int f = Factorial(n);

        var elem = HtmlPage.Document.GetElementById(element_id);
        elem.InnerText = f.ToString();
    }

    static void PrintHtmlElements(HtmlElement elem, int level)
    {
        string str = "";
        for (int i = 0; i < level; i++) {
            str += "  ";
        }

        str += $"<{elem.TagName}";

        foreach (var name in elem.AttributeNames) {
            var value = elem.GetAttribute(name);
            str += $" {name}='{value}'";
        }

        str += ">";

        Console.WriteLine(str);

        var list = elem.Children;
        for (int i = 0; i < list.Count; i++) {
            var child = list[i];
            PrintHtmlElements(child, level + 1);
        }
    }

    static int Main(string[] args)
    {
        int f = Factorial(6);
        HtmlPage.Window.Alert($"Hello world! factorial(6) -> {f}");

        var bi = HtmlPage.BrowserInformation;
        Console.WriteLine($"BrowserInformation: Name {bi.Name} BrowserVersion {bi.BrowserVersion} UserAgent {bi.UserAgent} Platform {bi.Platform} CookiesEnabled {bi.CookiesEnabled} ProductName {bi.ProductName}");

        var d = HtmlPage.Document;
        Console.WriteLine($"Document Location: {d.Location}");

        PrintHtmlElements(d.DocumentElement, 0);

        var p = d.CreateElement("p");
        p.InnerText = "This text was added at runtime.";
        d.Body.AppendChild(p);

        if (args.Length > 0) FactorialInElement(0, ""); // this is a hack so that the linker does not remove the FactorialInElement() method

        return f;
    }
}


================================================
FILE: sample/hello2/index.html
================================================
<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>WebAssembly Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <p>
      <form name="factorial_form" onSubmit="return factorial_submit()">
        Number: <input type="text" name="input" value="6"/>
        <br/><br/>
        Result: <span id="factorial_result">...</span>
        <br/><br/>
        <input name="Submit"  type="submit" value="Calculate"/>
      </form>
    </p>
    <script src="index.js"></script>
    <script>
      function factorial_submit() {
        // This function is called by the form. We now call into the
        // Hello.FactorialInElement() C# method, passing the input (a number)
        // and the element ID which should be set with the result (here, the
        // <span> element defined above. 
        var input = document.forms["factorial_form"]["input"].value;
        var klass = MonoClass("", "Hello");
        if (klass) {
          var method = MonoMethod(klass, "FactorialInElement", true);
          if (method) {
            MonoInvoke(0, method, [parseInt(input), "factorial_result"]);
          }
        }
        return false;
      }

      document.addEventListener('WebAssemblyContentLoaded', function() {
        // Calculate the initial form value.
        factorial_submit();
      }, false);
    </script>
  </body>
</html>



================================================
FILE: tests/Mono.WebAssembly/Makefile
================================================
all: run

test.exe:      test.cs
	mcs -nostdlib -noconfig -r:../../dist/lib/mscorlib.dll test.cs -out:test.exe

output/index.wasm:      test.exe
	../../dist/bin/mono-wasm -g test.exe -o output

run:    output/index.wasm
	cp index.html output
	(cd output && python -m SimpleHTTPServer 9000)


================================================
FILE: tests/Mono.WebAssembly/index.html
================================================
<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>WebAssembly Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <script src="index.js"></script>
    <span id="span-id">span-id text</span>
    <div class="my-class container">
      <div class="my-class">my-class-container</div>
      <span>text</span>
    </div>
    <p/>
    <div class="my-class"></div>
    <p/>
    <div class="my-class"></div>
    <p/>
  </body>
</html>



================================================
FILE: tests/Mono.WebAssembly/test.cs
================================================
using Mono.WebAssembly;
using System;

class Test
{
    int count = 0;
    int failures = 0;

    void _assert(bool condition, string msg)
    {
        if (condition) {
            count++;        
        }
        else {
            Console.WriteLine(msg);
            failures++;
        }
    }

    void assert(bool condition)
    {
        _assert(condition, $"assertion {count} failed");
    }

    void assert_Equals(object obj1, object obj2)
    {
        _assert(((obj1 == null && obj2 == null) || obj1.Equals(obj2)),
                $"assertion {count} failed: `{obj1}' should be equal to `{obj2}'");
    }

    void test_Runtime()
    {
        assert_Equals(Runtime.JavaScriptEval("1+2") , "3");

        assert_Equals(Runtime.JavaScriptEval("var x = 42; x"), "42");

        assert_Equals(Runtime.JavaScriptEval(
                    "(function(x, y) { return x + y; })(40, 2);"), "42");
    }

    void test_BrowserInformation()
    {
        var bi = HtmlPage.BrowserInformation;

        assert_Equals(bi.Name, "Netscape");
        assert(bi.BrowserVersion.Contains("5.0"));
        assert(bi.UserAgent.Contains("Firefox")
                || bi.UserAgent.Contains("Chrome")
                || bi.UserAgent.Contains("Safari"));
        assert_Equals(bi.Platform, "MacIntel");
        assert_Equals(bi.CookiesEnabled, true);
        assert_Equals(bi.ProductName, "Mozilla");
    }

    void test_HtmlDocument()
    {
        var doc = HtmlPage.Document;

        var root = doc.DocumentElement;
        assert_Equals(root.TagName, "HTML");
        assert_Equals(doc.GetElementsByTagName("html")[0], root);
        assert_Equals(root.Parent, null);

        var body = doc.Body;
        assert_Equals(body.TagName, "BODY");
        assert_Equals(doc.GetElementsByTagName("body")[0], body);
        assert_Equals(body.Parent, root);

        // We can't use `Contains()' or even `foreach' yet due to a compiler
        // limitation.
        bool found_body_in_root_children = false;
        var root_children = root.Children;
        for (int i = 0, len = root_children.Count; i < len; i++) {
            var child = root_children[i];
            if (child.Equals(body)) {
                found_body_in_root_children = true;
                break;
            }
        }
        assert(found_body_in_root_children);

        var span_id = doc.GetElementById("span-id");
        assert(span_id != null);
        assert_Equals(span_id.TagName, "SPAN");
        assert_Equals(span_id.Id, "span-id");
        assert_Equals(span_id.Parent, body);
        assert_Equals(span_id.InnerText, "span-id text");

        assert_Equals(doc.GetElementById("does-not-exist"), null);

        var elem = doc.CreateElement("span");
        elem.Id = "span-id2";
        assert_Equals(elem.TagName, "SPAN");
        assert_Equals(elem.Parent, null);
        body.AppendChild(elem);
        assert_Equals(elem.Parent, body);
        assert_Equals(doc.GetElementById("span-id2"), elem);
        body.RemoveChild(elem);
        assert_Equals(elem.Parent, null);
        assert_Equals(doc.GetElementById("span-id2"), null);
    }

    void test_HtmlNode()
    {
        var doc = HtmlPage.Document;

        assert_Equals(doc.GetElementsByTagName("does-not-exist").Count, 0);
        assert_Equals(doc.Body.GetElementsByTagName("does-not-exist").Count, 0);

        assert_Equals(doc.GetElementsByTagName("p").Count, 3);
        assert_Equals(doc.Body.GetElementsByTagName("p").Count, 3);

        assert_Equals(doc.GetElementsByClassName("my-class").Count, 4);
        assert_Equals(doc.Body.GetElementsByClassName("my-class").Count, 4);
        var ary = doc.GetElementsByClassName("container");
        var ary2 = doc.Body.GetElementsByClassName("container");
        assert_Equals(ary.Count, 1);
        assert_Equals(ary2.Count, 1);
        var container = ary[0];
        assert_Equals(container, ary2[0]);
        assert_Equals(container.GetElementsByClassName("my-class").Count, 1);
        assert_Equals(container.InnerText, "my-class-container\ntext");

        var ary3 = container.GetElementsByTagName("span");
        assert_Equals(ary3.Count, 1);
        ary3[0].InnerText = "42";
        assert_Equals(container.InnerText, "my-class-container\n42");

        assert_Equals(doc.QuerySelector(".does-not-exist"), null);
        assert_Equals(doc.Body.QuerySelector(".does-not-exist"), null);

        var elem = doc.QuerySelector(".my-class");
        assert(elem != null);
        assert_Equals(elem, doc.QuerySelector(".container"));
        assert_Equals(elem, doc.Body.QuerySelector(".my-class"));
        assert_Equals(elem, doc.Body.QuerySelector(".container"));

        var ary4 = doc.QuerySelectorAll(".my-class");
        var ary5 = doc.Body.QuerySelectorAll(".my-class");
        assert_Equals(ary4.Count, 4);
        assert_Equals(ary5.Count, 4);
        assert_Equals(ary4[0], elem);
        assert_Equals(ary5[0], elem);

        assert_Equals(doc.QuerySelector(".container span"), ary3[0]);
    }

    void test_HtmlElement()
    {
        var doc = HtmlPage.Document;

        var elem = doc.CreateElement("div");
        assert_Equals(elem.TagName, "DIV");
        assert_Equals(elem.ClassName, "");
        assert_Equals(elem.Id, "");
        assert_Equals(elem.Parent, null);
        assert_Equals(elem.Children.Count, 0);
        assert_Equals(elem.AttributeNames.Length, 0);
        assert_Equals(elem.InnerText, "");

        elem.InnerText = "foo";
        assert_Equals(elem.InnerText, "foo");

        assert_Equals(elem.GetAttribute("does-not-exist"), null);

        elem.ClassName = "my-class";
        assert_Equals(elem.ClassName, "my-class");
        assert_Equals(elem.AttributeNames.Length, 1);
        assert_Equals(elem.AttributeNames[0], "class");
        assert_Equals(elem.GetAttribute("class"), "my-class");

        elem.SetAttribute("id", "my-id");
        assert_Equals(elem.Id, "my-id");
        assert_Equals(elem.AttributeNames.Length, 2);

        elem.RemoveAttribute("id");
        assert_Equals(elem.Id, "");
        assert_Equals(elem.AttributeNames.Length, 1);

        var ary = doc.GetElementsByTagName("script");
        assert_Equals(ary.Count, 1);
        var elem2 = ary[0];
        assert_Equals(elem2.AttributeNames.Length, 1);
        assert_Equals(elem.AttributeNames[0], "src");
        assert_Equals(elem.GetAttribute("src"), "index.js");
    }

    void run_tests()
    {
        test_Runtime();
        test_BrowserInformation();
        test_HtmlDocument();
        test_HtmlNode();
        test_HtmlElement();

        if (failures == 0) {
            Console.WriteLine("All tests ({0}) successful", count);
        }
        else {
            Console.WriteLine("Tests ran with {0} failures", failures);
        }
    }

    static void Main()
    {
        var r = new Test();
        r.run_tests();
    }
}
Download .txt
gitextract_u8dox53_/

├── .gitignore
├── LICENSE.txt
├── Makefile
├── README.md
├── boot.c
├── index.js
├── jsmin.c
├── mono-wasm.cpp
├── mscorlib.xml
├── sample/
│   ├── hello/
│   │   ├── .gitignore
│   │   ├── Makefile
│   │   ├── hello.cs
│   │   └── index.html
│   └── hello2/
│       ├── .gitignore
│       ├── Makefile
│       ├── hello.cs
│       └── index.html
└── tests/
    └── Mono.WebAssembly/
        ├── Makefile
        ├── index.html
        └── test.cs
Download .txt
SYMBOL INDEX (67 symbols across 7 files)

FILE: boot.c
  function mono_wasm_main (line 11) | __attribute__ ((__visibility__ ("default")))

FILE: index.js
  function heap_get_short (line 19) | function heap_get_short(ptr) {
  function heap_get_int (line 26) | function heap_get_int(ptr) {
  function heap_get_long (line 35) | function heap_get_long(ptr) {
  function heap_set_int (line 48) | function heap_set_int(ptr, d) {
  function heap_set_long (line 56) | function heap_set_long(ptr, d) {
  function heap_get_string (line 68) | function heap_get_string(ptr, len=-1) {
  function heap_get_mono_string (line 85) | function heap_get_mono_string(ptr)
  function heap_set_string (line 97) | function heap_set_string(ptr, str) {
  function heap_malloc_string (line 104) | function heap_malloc_string(str) {
  function heap_human (line 110) | function heap_human(size) {
  function log (line 123) | function log(str) {
  function debug (line 127) | function debug(str) {
  function error (line 133) | function error(str) {
  function TerminateWasmException (line 137) | function TerminateWasmException(value) {
  function NotYetImplementedException (line 143) | function NotYetImplementedException(what) {
  function mono_wasm_wrap_obj (line 239) | function mono_wasm_wrap_obj(obj) {
  function mono_wasm_unwrap_obj (line 251) | function mono_wasm_unwrap_obj(ref) {
  function _MonoDomain (line 257) | function _MonoDomain() {
  function _MonoGPtrToArray (line 261) | function _MonoGPtrToArray(ptr) {
  function _MonoAssemblies (line 271) | function _MonoAssemblies() {
  function _MonoImage (line 276) | function _MonoImage(assembly) {
  function MonoClass (line 280) | function MonoClass(namespace, name) {
  function MonoMethod (line 299) | function MonoMethod(klass, name, is_static) {
  function MonoInvoke (line 308) | function MonoInvoke(obj, method, params) {
  function out_buffer_add (line 357) | function out_buffer_add(ptr, len) {
  function out_buffer_flush (line 361) | function out_buffer_flush() {
  function route_syscall (line 654) | function route_syscall() {
  function run_wasm_code (line 673) | function run_wasm_code() {

FILE: jsmin.c
  function error (line 40) | static void
  function isAlphanum (line 53) | static int
  function get (line 67) | static int
  function peek (line 88) | static int
  function next (line 100) | static int
  function action (line 145) | static void
  function jsmin (line 234) | void

FILE: mono-wasm.cpp
  function timespec_cmp (line 81) | static int
  function setup_paths (line 103) | static void
  function diagnostic_handler (line 120) | static void
  function assembly_link (line 139) | static void
  function assembly_compile (line 205) | static std::string
  function bitcode_link (line 233) | static std::unique_ptr<llvm::Module>
  function wasm_codegen (line 308) | static void
  function wasm_codegen2 (line 366) | static void
  function wasm_link (line 383) | static void
  function assembly_strip (line 409) | static void
  function js_gen (line 433) | static void
  function swap_extension (line 466) | static std::string
  function main (line 477) | int

FILE: sample/hello/hello.cs
  class Hello (line 1) | class Hello {
    method Main (line 2) | static int Main(string[] args) {

FILE: sample/hello2/hello.cs
  class Hello (line 4) | class Hello
    method Factorial (line 6) | static int Factorial(int n)
    method FactorialInElement (line 18) | static void FactorialInElement(int n, string element_id)
    method PrintHtmlElements (line 30) | static void PrintHtmlElements(HtmlElement elem, int level)
    method Main (line 55) | static int Main(string[] args)

FILE: tests/Mono.WebAssembly/test.cs
  class Test (line 4) | class Test
    method _assert (line 9) | void _assert(bool condition, string msg)
    method assert (line 20) | void assert(bool condition)
    method assert_Equals (line 25) | void assert_Equals(object obj1, object obj2)
    method test_Runtime (line 31) | void test_Runtime()
    method test_BrowserInformation (line 41) | void test_BrowserInformation()
    method test_HtmlDocument (line 55) | void test_HtmlDocument()
    method test_HtmlNode (line 103) | void test_HtmlNode()
    method test_HtmlElement (line 148) | void test_HtmlElement()
    method run_tests (line 188) | void run_tests()
    method Main (line 204) | static void Main()
Condensed preview — 20 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (88K chars).
[
  {
    "path": ".gitignore",
    "chars": 82,
    "preview": ".DS_Store\n.gdb_history\nbuild\ndist\nmscorlib.dll\n*.o\n.*.swo\n.*.swp\nmono-wasm\n*.dSYM\n"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1110,
    "preview": "MIT License\n\nCopyright (c) 2017 - present Microsoft Corporation\n\nAll rights reserved.\n\nPermission is hereby granted, fre"
  },
  {
    "path": "Makefile",
    "chars": 7681,
    "preview": "MONO_RUNTIME_PATH = ../mono-runtime\nMONO_COMPILER_PATH = ../mono-compiler\nLIBC_PATH = ../libc\nLLVM_PATH = ../llvm-build\n"
  },
  {
    "path": "README.md",
    "chars": 9860,
    "preview": "# mono-wasm\n\nThis project is a proof-of-concept aiming at building C# applications into WebAssembly, by using Mono and c"
  },
  {
    "path": "boot.c",
    "chars": 1389,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See the LICENSE.txt file"
  },
  {
    "path": "index.js",
    "chars": 22436,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See the LICENSE.txt file"
  },
  {
    "path": "jsmin.c",
    "chars": 7949,
    "preview": "/* jsmin.c\n   2013-03-29\n\nCopyright (c) 2002 Douglas Crockford  (www.crockford.com)\n\nPermission is hereby granted, free "
  },
  {
    "path": "mono-wasm.cpp",
    "chars": 20018,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See the LICENSE.txt file"
  },
  {
    "path": "mscorlib.xml",
    "chars": 66,
    "preview": "<linker>\n  <assembly fullname=\"mscorlib\">\n  </assembly>\n</linker>\n"
  },
  {
    "path": "sample/hello/.gitignore",
    "chars": 23,
    "preview": "build\noutput\nhello.exe\n"
  },
  {
    "path": "sample/hello/Makefile",
    "chars": 338,
    "preview": "all: hello.exe output/index.wasm output/index.html\n\nhello.exe:      hello.cs\n\tmcs -nostdlib -noconfig -r:../../dist/lib/"
  },
  {
    "path": "sample/hello/hello.cs",
    "chars": 115,
    "preview": "class Hello {\n  static int Main(string[] args) {\n    System.Console.WriteLine(\"hello world!\");\n    return 0;\n  }\n}\n"
  },
  {
    "path": "sample/hello/index.html",
    "chars": 342,
    "preview": "<!DOCTYPE html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <title"
  },
  {
    "path": "sample/hello2/.gitignore",
    "chars": 23,
    "preview": "build\noutput\nhello.exe\n"
  },
  {
    "path": "sample/hello2/Makefile",
    "chars": 338,
    "preview": "all: hello.exe output/index.wasm output/index.html\n\nhello.exe:      hello.cs\n\tmcs -nostdlib -noconfig -r:../../dist/lib/"
  },
  {
    "path": "sample/hello2/hello.cs",
    "chars": 2229,
    "preview": "using Mono.WebAssembly;\nusing System;\n\nclass Hello\n{\n    static int Factorial(int n)\n    {\n        if (n == 0) {\n       "
  },
  {
    "path": "sample/hello2/index.html",
    "chars": 1510,
    "preview": "<!DOCTYPE html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <title"
  },
  {
    "path": "tests/Mono.WebAssembly/Makefile",
    "chars": 290,
    "preview": "all: run\n\ntest.exe:      test.cs\n\tmcs -nostdlib -noconfig -r:../../dist/lib/mscorlib.dll test.cs -out:test.exe\n\noutput/i"
  },
  {
    "path": "tests/Mono.WebAssembly/index.html",
    "chars": 604,
    "preview": "<!DOCTYPE html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <title"
  },
  {
    "path": "tests/Mono.WebAssembly/test.cs",
    "chars": 6883,
    "preview": "using Mono.WebAssembly;\nusing System;\n\nclass Test\n{\n    int count = 0;\n    int failures = 0;\n\n    void _assert(bool cond"
  }
]

About this extraction

This page contains the full source code of the migueldeicaza/mono-wasm GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 20 files (81.3 KB), approximately 22.4k tokens, and a symbol index with 67 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!