Is there a way to compile a C file with inline Assembly Language with Web Assembly?
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.38.12
When trying to compile this:
int main(void) {
register int syscall_no asm("rax") = 1;
register int arg1 asm("rdi") = 1;
register char* arg2 asm("rsi") = "hello, world\n";
register int arg3 asm("rdx") = 14;
asm("syscall");
return 0;
}
These are the errors:
test.c:2:35: error: unknown register name 'rax' in asm
register int syscall_no asm("rax") = 1;
^
test.c:3:35: error: unknown register name 'rdi' in asm
register int arg1 asm("rdi") = 1;
^
test.c:4:35: error: unknown register name 'rsi' in asm
register char* arg2 asm("rsi") = "hello, world!\n";
^
test.c:5:35: error: unknown register name 'rdx' in asm
register int arg3 asm("rdx") = 14;
^
4 errors generated.
ERROR:root:compiler frontend failed to generate LLVM bitcode, halting
Also compiled this C file:
int main(){
int test();
test()
}
That is linked to this Assembly file:
SECTION .text
GLOBAL test
test:
mov rax,1
mov rdi,1
mov rsi,name
mov rdx,7
syscall
mov rax,60
mov rdi,0
syscall
SECTION .data
name DB "Hello",10
These are the errors:
warning: unexpected number of arguments 0 in call to 'test', should be 1
warning: unresolved symbol: test
Thank you.
asm local variables are only guaranteed to work with Extended asm statements in GNU C. You're using them with Basic asm, which may happen to work but is not guaranteed. (https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html
And it's pointless when you could just use constraints like "a"(1UL) to put a 1 in RAX. Also don't forget to declare clobbers on RCX and R11 because of how syscall works, and that RAX is also an output.
Anyway, of course x86-specific register names won't work when compiling to portable platform-neutral / architecture-neutral web assembly instead of directly to x86 asm.
A browser running on an ARM CPU won't have those registers available when JITing webasm into native code. Even a browser compiled for 32-bit x86 won't have those registers.
Also, a browser running on x86-64 Windows will have those registers available, but Linux system-call ABIs won't work.
AFAIK, you can't wrap target-specific inline asm and specific registers in web-asm even if your only intended target is one where they would work. The web-asm language isn't designed to support it.
When you run gcc or clang, the target is WASM, not x86.
Related
I'm experimenting with Rust, WebAssembly and C interoperability to eventually use the Rust (with static C dependency) library in the browser or Node.js. I'm using wasm-bindgen for the JavaScript glue code.
#![feature(libc, use_extern_macros)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
use std::os::raw::c_char;
use std::ffi::CStr;
extern "C" {
fn hello() -> *const c_char; // returns "hello from C"
}
#[wasm_bindgen]
pub fn greet() -> String {
let c_msg = unsafe { CStr::from_ptr(hello()) };
format!("{} and Rust!", c_msg.to_str().unwrap())
}
My first naive approach was to have a build.rs script that uses the gcc crate to generate a static library from the C code. Before introducing the WASM bits, I could compile the Rust program and see the hello from C output in the console, now I get an error from the compiler saying
rust-lld: error: unknown file type: hello.o
build.rs
extern crate gcc;
fn main() {
gcc::Build::new()
.file("src/hello.c")
.compile("libhello.a");
}
This makes sense, now that I think about it, since the hello.o file was compiled for my laptop's architecture not WebAssembly.
Ideally I'd like this to work out of the box adding some magic in my build.rs that would for example compile the C library to be a static WebAssembly library that Rust can use.
What I think that could work, but would like to avoid since it sounds more problematic, is using Emscripten to create a WASM library for the C code then compile the Rust library separately and glue them together in JavaScript.
TL;DR: Jump to "New week, new adventures" in order to get "Hello from C and Rust!"
The nice way would be creating a WASM library and passing it to the linker. rustc has an option for that (and there seem to be source-code directives too):
rustc <yourcode.rs> --target wasm32-unknown-unknown --crate-type=cdylib -C link-arg=<library.wasm>
The trick is that the library has to be a library, so it needs to contain reloc (and in practice linking) sections. Emscripten seems to have a symbol for that, RELOCATABLE:
emcc <something.c> -s WASM=1 -s SIDE_MODULE=1 -s RELOCATABLE=1 -s EMULATED_FUNCTION_POINTERS=1 -s ONLY_MY_CODE=1 -o <something.wasm>
(EMULATED_FUNCTION_POINTERS is included with RELOCATABLE, so it is not really necessary, ONLY_MY_CODE strips some extras, but it does not matter here either)
The thing is, emcc never generated a relocatable wasm file for me, at least not the version I downloaded this week, for Windows (I played this on hard difficulty, which retrospectively might have not been the best idea). So the sections are missing and rustc keeps complaining about <something.wasm> is not a relocatable wasm file.
Then comes clang, which can generate a relocatable wasm module with a very simple one-liner:
clang -c <something.c> -o <something.wasm> --target=wasm32-unknown-unknown
Then rustc says "Linking sub-section ended prematurely". Aw, yes (by the way, my Rust setup was brand new too). Then I read that there are two clang wasm targets: wasm32-unknown-unknown-wasm and wasm32-unknown-unknown-elf, and maybe the latter one should be used here. As my also brand new llvm+clang build runs into an internal error with this target, asking me to send an error report to the developers, it might be something to test on easy or medium, like on some *nix or Mac box.
Minimal success story: sum of three numbers
At this point I just added lld to llvm and succeeded with linking a test code manually from bitcode files:
clang cadd.c --target=wasm32-unknown-unknown -emit-llvm -c
rustc rsum.rs --target wasm32-unknown-unknown --crate-type=cdylib --emit llvm-bc
lld -flavor wasm rsum.bc cadd.bc -o msum.wasm --no-entry
Aw yes, it sums numbers, 2 in C and 1+2 in Rust:
cadd.c
int cadd(int x,int y){
return x+y;
}
msum.rs
extern "C" {
fn cadd(x: i32, y: i32) -> i32;
}
#[no_mangle]
pub fn rsum(x: i32, y: i32, z: i32) -> i32 {
x + unsafe { cadd(y, z) }
}
test.html
<script>
fetch('msum.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.compile(bytes))
.then(module => {
console.log(WebAssembly.Module.exports(module));
console.log(WebAssembly.Module.imports(module));
return WebAssembly.instantiate(module, {
env:{
_ZN4core9panicking5panic17hfbb77505dc622acdE:alert
}
});
})
.then(instance => {
alert(instance.exports.rsum(13,14,15));
});
</script>
That _ZN4core9panicking5panic17hfbb77505dc622acdE feels very natural (the module is compiled and instantiated in two steps in order to log the exports and imports, that is a way how such missing pieces can be found), and forecasts the demise of this attempt: the entire thing works because there is no other reference to the runtime library, and this particular method could be mocked/provided manually.
Side story: string
As alloc and its Layout thing scared me a little, I went with the vector-based approach described/used from time to time, for example here or on Hello, Rust!.
Here is an example, getting the "Hello from ..." string from the outside...
rhello.rs
use std::ffi::CStr;
use std::mem;
use std::os::raw::{c_char, c_void};
use std::ptr;
extern "C" {
fn chello() -> *mut c_char;
}
#[no_mangle]
pub fn alloc(size: usize) -> *mut c_void {
let mut buf = Vec::with_capacity(size);
let p = buf.as_mut_ptr();
mem::forget(buf);
p as *mut c_void
}
#[no_mangle]
pub fn dealloc(p: *mut c_void, size: usize) {
unsafe {
let _ = Vec::from_raw_parts(p, 0, size);
}
}
#[no_mangle]
pub fn hello() -> *mut c_char {
let phello = unsafe { chello() };
let c_msg = unsafe { CStr::from_ptr(phello) };
let message = format!("{} and Rust!", c_msg.to_str().unwrap());
dealloc(phello as *mut c_void, c_msg.to_bytes().len() + 1);
let bytes = message.as_bytes();
let len = message.len();
let p = alloc(len + 1) as *mut u8;
unsafe {
for i in 0..len as isize {
ptr::write(p.offset(i), bytes[i as usize]);
}
ptr::write(p.offset(len as isize), 0);
}
p as *mut c_char
}
Built as rustc rhello.rs --target wasm32-unknown-unknown --crate-type=cdylib
... and actually working with JavaScript:
jhello.html
<script>
var e;
fetch('rhello.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.compile(bytes))
.then(module => {
console.log(WebAssembly.Module.exports(module));
console.log(WebAssembly.Module.imports(module));
return WebAssembly.instantiate(module, {
env:{
chello:function(){
var s="Hello from JavaScript";
var p=e.alloc(s.length+1);
var m=new Uint8Array(e.memory.buffer);
for(var i=0;i<s.length;i++)
m[p+i]=s.charCodeAt(i);
m[s.length]=0;
return p;
}
}
});
})
.then(instance => {
/*var*/ e=instance.exports;
var ptr=e.hello();
var optr=ptr;
var m=new Uint8Array(e.memory.buffer);
var s="";
while(m[ptr]!=0)
s+=String.fromCharCode(m[ptr++]);
e.dealloc(optr,s.length+1);
console.log(s);
});
</script>
It is not particularly beautiful (actually I have no clue about Rust), but it does something what I expect from it, and even that dealloc might work (at least invoking it twice throws a panic).
There was an important lesson on the way: when the module manages its memory, its size may change which results in invalidating the backing ArrayBuffer object and its views. So that is why memory.buffer is checked multiple times, and checked after calling into wasm code.
And this is where I am stuck, because this code would refer to runtime libraries, and .rlib-s. The closest I could get to a manual build is the following:
rustc rhello.rs --target wasm32-unknown-unknown --crate-type=cdylib --emit obj
lld -flavor wasm rhello.o -o rhello.wasm --no-entry --allow-undefined
liballoc-5235bf36189564a3.rlib liballoc_system-f0b9538845741d3e.rlib
libcompiler_builtins-874d313336916306.rlib libcore-5725e7f9b84bd931.rlib
libdlmalloc-fffd4efad67b62a4.rlib liblibc-453d825a151d7dec.rlib
libpanic_abort-43290913ef2070ae.rlib libstd-dcc98be97614a8b6.rlib
libunwind-8cd3b0417a81fb26.rlib
Where I had to use the lld sitting in the depths of the Rust toolchain as .rlib-s are said to be interpreted, so they are bound to the Rust toolchain
--crate-type=rlib, #[crate_type = "rlib"] - A "Rust library" file will be produced. This is used as an intermediate artifact and can be thought of as a "static Rust library". These rlib files, unlike staticlib files, are interpreted by the Rust compiler in future linkage. This essentially means that rustc will look for metadata in rlib files like it looks for metadata in dynamic libraries. This form of output is used to produce statically linked executables as well as staticlib outputs.
Of course this lld does not eat the .wasm/.o files generated with clang or llc ("Linking sub-section ended prematurely"), perhaps the Rust-part also should be rebuilt with the custom llvm.
Also, this build seems to be missing the actual allocators, besides chello, there will be 4 more entries in the import table: __rust_alloc, __rust_alloc_zeroed, __rust_dealloc and __rust_realloc. Which in fact could be provided from JavaScript after all, just defeats the idea of letting Rust handle its own memory, plus an allocator was present in the single-pass rustc build... Oh, yes, this is where I gave up for this week (Aug 11, 2018, at 21:56)
New week, new adventures, with Binaryen, wasm-dis/merge
The idea was to modify the ready-made Rust code (having allocators and everything in place). And this one works. As long as your C code has no data.
Proof of concept code:
chello.c
void *alloc(int len); // allocator comes from Rust
char *chello(){
char *hell=alloc(13);
hell[0]='H';
hell[1]='e';
hell[2]='l';
hell[3]='l';
hell[4]='o';
hell[5]=' ';
hell[6]='f';
hell[7]='r';
hell[8]='o';
hell[9]='m';
hell[10]=' ';
hell[11]='C';
hell[12]=0;
return hell;
}
Not extremely usual, but it is C code.
rustc rhello.rs --target wasm32-unknown-unknown --crate-type=cdylib
wasm-dis rhello.wasm -o rhello.wast
clang chello.c --target=wasm32-unknown-unknown -nostdlib -Wl,--no-entry,--export=chello,--allow-undefined
wasm-dis a.out -o chello.wast
wasm-merge rhello.wast chello.wast -o mhello.wasm -O
(rhello.rs is the same one presented in "Side story: string")
And the result works as
mhello.html
<script>
fetch('mhello.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.compile(bytes))
.then(module => {
console.log(WebAssembly.Module.exports(module));
console.log(WebAssembly.Module.imports(module));
return WebAssembly.instantiate(module, {
env:{
memoryBase: 0,
tableBase: 0
}
});
})
.then(instance => {
var e=instance.exports;
var ptr=e.hello();
console.log(ptr);
var optr=ptr;
var m=new Uint8Array(e.memory.buffer);
var s="";
while(m[ptr]!=0)
s+=String.fromCharCode(m[ptr++]);
e.dealloc(optr,s.length+1);
console.log(s);
});
</script>
Even the allocators seem to do something (ptr readings from repeated blocks with/without dealloc show how memory does not leak/leaks accordingly).
Of course this is super-fragile and has mysterious parts too:
if the final merge is run with -S switch (generates source code instead of .wasm), and the result assembly file is compiled separately (using wasm-as), the result will be a couple bytes shorter (and those bytes are somewhere in the very middle of the running code, not in export/import/data sections)
the order of merge matters, file with "Rust-origin" has to come first. wasm-merge chello.wast rhello.wast [...] dies with an entertaining message
[wasm-validator error in module] unexpected false: segment offset should be reasonable, on
[i32] (i32.const 1)
Fatal: error in validating output
probably my fault, but I had to build a complete chello.wasm module (so, with linking). Compiling only (clang -c [...]) resulted in the relocatable module which was missed so much at the very beginning of this story, but decompiling that one (to .wast) lost the named export (chello()):
(export "chello" (func $chello)) disappears completely
(func $chello ... becomes (func $0 ..., an internal function (wasm-dis loses reloc and linking sections, putting only a remark about them and their size into the assembly source)
related to the previous one: this way (building a complete module) data from the secondary module can not be relocated by wasm-merge: while there is a chance for catching references to the string itself (const char *HELLO="Hello from C"; becomes a constant at offset 1024 in particular, and later referred as (i32.const 1024) if it is local constant, inside a function), it does not happen. And if it is a global constant, its address becomes a global constant too, number 1024 stored at offset 1040, and the string is going to be referred as (i32.load offset=1040 [...], which starts being difficult to catch.
For laughs, this code compiles and works too...
void *alloc(int len);
int my_strlen(const char *ptr){
int ret=0;
while(*ptr++)ret++;
return ret;
}
char *my_strcpy(char *dst,const char *src){
char *ret=dst;
while(*src)*dst++=*src++;
*dst=0;
return ret;
}
char *chello(){
const char *HELLO="Hello from C";
char *hell=alloc(my_strlen(HELLO)+1);
return my_strcpy(hell,HELLO);
}
... just it writes "Hello from C" in the middle of Rust's message pool, resulting in the printout
Hello from Clt::unwrap()` on an `Err`an value and Rust!
(Explanation: 0-initializers are not present in the recompiled code because of the optimization flag, -O)
And it also brings up the question about locating a libc (though defining them without my_, clang mentions strlen and strcpy as built-ins, also telling their correct singatures, it does not emit code for them and they become imports for the resulting module).
I'm on Ubuntu.
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.2 LTS
Release: 14.04
Codename: trusty
I installed Emscripten.
$ sudo apt-get install emscripten
I wrote the following C++ program:
#include <iostream>
int main(){
printf("hello world\n");
}
It compiles as expected.
$ emcc -O1 -s ASM_JS=1 main.cpp
$
However, when I write a similar program using the iostream facilities:
#include <iostream>
int main(){
std::cout << "hello world" << std::endl;
}
It fails to build.
$ emcc -O1 -s ASM_JS=1 main.cpp
aborting from js compiler due to exception: unknown vector type <4 x i8> | undefined
aborting from js compiler due to exception: unknown vector type <4 x i8> | undefined
aborting from js compiler due to exception: unknown vector type <4 x i8> | undefined
Traceback (most recent call last):
File "/usr/share/emscripten/emscripten.py", line 1352, in <module>
_main(environ=os.environ)
File "/usr/share/emscripten/emscripten.py", line 1340, in _main
temp_files.run_and_clean(lambda: main(
File "/usr/share/emscripten/tools/tempfiles.py", line 39, in run_and_clean
return func()
File "/usr/share/emscripten/emscripten.py", line 1348, in <lambda>
DEBUG_CACHE=DEBUG_CACHE,
File "/usr/share/emscripten/emscripten.py", line 1235, in main
jcache=jcache, temp_files=temp_files, DEBUG=DEBUG, DEBUG_CACHE=DEBUG_CACHE)
File "/usr/share/emscripten/emscripten.py", line 292, in emscript
assert len(output) == 2, 'Did not receive forwarded data in an output - process failed? We only got: ' + output[0][-3000:]
AssertionError: Did not receive forwarded data in an output - process failed? We only got: ((HEAP32[(($1)>>2)])|0);
$3=((($2)-(12))|0);
$4=$3;
$5=((HEAP32[(($4)>>2)])|0);
$6=$this;
$_sum=((($5)+(24))|0);
$7=(($6+$_sum)|0);
$8=$7;
$9=((HEAP32[(($8)>>2)])|0);
$10=($9|0)==0;
if ($10) {
STACKTOP=sp;return (($this)|0);
}
$12=(($__s)|0);
HEAP8[($12)]=0;
$13=(($__s+4)|0);
HEAP32[(($13)>>2)]=$this;
$_sum_i=((($5)+(16))|0);
$14=(($6+$_sum_i)|0);
$15=$14;
$16=((HEAP32[(($15)>>2)])|0);
$17=($16|0)==0;
do {
if ($17) {
$_sum1_i=((($5)+(72))|0);
$19=(($6+$_sum1_i)|0);
$20=$19;
$21=((HEAP32[(($20)>>2)])|0);
$22=($21|0)==0;
if (!($22)) {
$24=((__ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv($21))|0);
}
HEAP8[($12)]=1;
$26=((HEAP32[(($1)>>2)])|0);
$27=((($26)-(12))|0);
$28=$27;
$29=((HEAP32[(($28)>>2)])|0);
$_sum1=((($29)+(24))|0);
$30=(($6+$_sum1)|0);
$31=$30;
$32=((HEAP32[(($31)>>2)])|0);
$33=$32;
$34=$32;
$35=((HEAP32[(($34)>>2)])|0);
$36=(($35+24)|0);
$37=((HEAP32[(($36)>>2)])|0);
$38=((FUNCTION_TABLE_ii[($37)&{{{ FTM_ii }}}]($33))|0);
$39=($38|0)==-1;
if (!($39)) {
break;
}
$41=((HEAP32[(($1)>>2)])|0);
$42=((($41)-(12))|0);
$43=$42;
$44=((HEAP32[(($43)>>2)])|0);
$45=(($6+$44)|0);
$46=$45;
$_sum2=((($44)+(16))|0);
$47=(($6+$_sum2)|0);
$48=$47;
$49=((HEAP32[(($48)>>2)])|0);
$50=$49|1;
__ZNSt3__18ios_base5clearEj($46,$50);
}
} while(0);
__ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD2Ev($__s);
STACKTOP=sp;return (($this)|0);
}
function __ZNSt3__18ios_base33__set_badbit_and_consider_rethrowEv($this){
$this=($this)|0;
var $1=0,$2=0,$3=0,$4=0,$5=0,$6=0,$7=0,label=0;
$1=(($this+16)|0);
$2=((HEAP32[(($1)>>2)])|0);
$3=$2|1;
HEAP32[(($1)>>2)]=$3;
$4=(($this+20)|0);
$5=((HEAP32[(($4)>>2)])|0);
$6=$5&1;
$7=($6|0)==0;
if ($7) {
return;
} else {
___cxa_rethrow();
}
}
function __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev($this){
$this=($this)|0;
var $1=0,$2=0,label=0;
$1=(($this+8)|0);
__ZNSt3__18ios_baseD2Ev($1);
$2=$this;
__ZdlPv($2);
return;
}
function __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev($this){
$this=($this)|0;
var $1=0,label=0;
$1=(($this+8)|0);
__ZNSt3__18ios_baseD2Ev($1);
return;
}
function __ZTv0_n12_NSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev($this){
$this=($this)|0;
var $1=0,$2=0,$3=0,$4=0,$5=0,$6=0,$7=0,$_sum=0,$8=0,$9=0,label=0;
$1=$this;
$2=$this;
$3=((HEAP32[(($2)>>2)])|0);
$4=((($3)-(12))|0);
$5=$4;
$6=((HEAP32[(($5)>>2)])|0);
$7=(($1+$6)|0);
$_sum=((($6)+(8))|0);
$8=(($1+$_sum)|0);
$9=$8;
__ZNSt3__18ios_baseD2Ev($9);
__ZdlPv($7);
return;
}
function __ZTv0_n12_NSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev($this){
$this=($this)|0;
var $1=0,$2=0,$3=0,$4=0,$5=0,$6=0,$_sum=0,$7=0,$8=0,label=0;
$1=$this;
$2=$this;
$3=((HEAP32[(($2)>>2)])|0);
$4=((($3)-(12))|0);
$5=$4;
$6=((HEAP32[(($5)>>2)])|0);
$_sum=((($6)+(8))|0);
$7=(($1+$_sum)|0);
$8=$7;
__ZNSt3__18ios_baseD2Ev($8);
return;
}
Traceback (most recent call last):
File "/usr/bin/emcc", line 1864, in <module>
final = shared.Building.emscripten(final, append_ext=False, extra_args=extra_args)
File "/usr/share/emscripten/tools/shared.py", line 1276, in emscripten
assert os.path.exists(filename + '.o.js') and len(open(filename + '.o.js', 'r').read()) > 0, 'Emscripten failed to generate .js: ' + str(compiler_output)
AssertionError: Emscripten failed to generate .js:
I was under the impression that Emscripten has come a long way, and is capable of compiling entire C++ games for the browser! Is there some kind of flag or configuration I missed in order to sucessfully compile portions of the C++ standard library? I know that clang/gcc compilers will link against the C++ standard shared library by default. Does the issue have something to do with that you think?
I have also tried using the command em++ in place of emcc and recieved the same error message.
If relevant, here is the default configuration that was built when running emscripten for the first time:
$ cat ~/.emscripten
# Note: If you put paths relative to the home directory, do not forget os.path.expanduser
import os
# this helps projects using emscripten find it
EMSCRIPTEN_ROOT = os.path.expanduser(os.getenv('EMSCRIPTEN') or '/usr/share/emscripten') # directory
LLVM_ROOT = os.path.expanduser(os.getenv('LLVM') or '/usr/bin') # directory
PYTHON = os.path.expanduser(os.getenv('PYTHON') or '/usr/bin/python2') # executable
# See below for notes on which JS engine(s) you need
NODE_JS = os.path.expanduser(os.getenv('NODE') or '/usr/bin/node') # executable
SPIDERMONKEY_ENGINE = [os.path.expanduser(os.getenv('SPIDERMONKEY') or 'js')] # executable
V8_ENGINE = os.path.expanduser(os.getenv('V8') or 'd8') # executable
JAVA = 'java' # executable
TEMP_DIR = '/tmp'
CRUNCH = os.path.expanduser(os.getenv('CRUNCH') or 'crunch') # executable
#CLOSURE_COMPILER = '..' # define this to not use the bundled version
########################################################################################################
# Pick the JS engine to use for running the compiler. This engine must exist, or
# nothing can be compiled.
#
# Recommendation: If you already have node installed, use that. Otherwise, build v8 or
# spidermonkey from source. Any of these three is fine, as long as it's
# a recent version (especially for v8 and spidermonkey).
COMPILER_ENGINE = NODE_JS
#COMPILER_ENGINE = V8_ENGINE
#COMPILER_ENGINE = SPIDERMONKEY_ENGINE
# All JS engines to use when running the automatic tests. Not all the engines in this list
# must exist (if they don't, they will be skipped in the test runner).
#
# Recommendation: If you already have node installed, use that. If you can, also build
# spidermonkey from source as well to get more test coverage (node can't
# run all the tests due to node issue 1669). v8 is currently not recommended
# here because of v8 issue 1822.
JS_ENGINES = [NODE_JS] # add this if you have spidermonkey installed too, SPIDERMONKEY_ENGINE]
I can't seem to find a way to get the basic C++ program above to successfully compile.
First, your program compiles fine for me with emcc. Your original program (with printf) causes emcc to assume it is compiling C code and as such it will auto-include stdio.h as C does. Your second program is having more trouble, probably due to an install error (for me I had an issue like this because my version of LLVM doesn't line up with the exact version Emscripten needed.)
Unfortunately, my understanding is that this is a common problem with emscripten and auto installers like apt-get -- I had the problem with both port and brew on my Mac.
The solution is to get Emscripten through the SDK, which bundles all of the sensitive pieces together: https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html
Follow the instructions there carefully. The two problems I ran into were that it expects Python 2 to be called python2 on your system and the script to adjust your default path assumes you're using bash (if you are, it should work fine.)
I try using:
git clone git://github.com/v8/v8.git v8 && cd v8 or svn checkout http://v8.googlecode.com/svn/trunk/ v8
Use libs:
make dependencies
sudo apt-get install apt-file;
sudo apt-get install libc6-dev-i368 lib32stdc++6;
When i try compile a simple file as:
int main(int argc, char* argv[]) {
// Create a string containing the JavaScript source code.
String source = String::New("'Hello' + ', World'");
// Compile the source code.
Script script = Script::Compile(source);
// Run the script to get the result.
Value result = script->Run();
// Convert the result to an ASCII string and print it.
String::AsciiValue ascii(result);
printf("%s\n", *ascii);
return 0;
}
the command use :
g++ test.cpp -Ideps/v8/include -Ldeps/v8/ -lv8 -lpthread
I get the follow error output:
v8/src/
test.cpp: Na função ‘int main(int, char**)’:
test.cpp:4:3: error: ‘String’ was not declared in this scope
test.cpp:4:10: error: expected ‘;’ before ‘source’
test.cpp:7:3: error: ‘Script’ was not declared in this scope
test.cpp:7:10: error: expected ‘;’ before ‘script’
test.cpp:10:3: error: ‘Value’ was not declared in this scope
test.cpp:10:9: error: expected ‘;’ before ‘result’
test.cpp:13:3: error: ‘String’ is not a class or namespace
test.cpp:13:22: error: expected ‘;’ before ‘ascii’
test.cpp:14:19: error: ‘ascii’ was not declared in this scope
test.cpp:14:24: error: ‘printf’ was not declared in this scope
What is the issue? Can someone point me in the right direction?
Based on the answer from #Sim, here is the working version.
#include "v8.h"
using namespace v8;
int main(int argc, char* argv[])
{
// Get the default Isolate created at startup.
Isolate* isolate = Isolate::GetCurrent();
// Create a stack-allocated handle scope.
HandleScope handle_scope(isolate);
// Create a new context.
Handle<Context> context = Context::New(isolate);
// Here's how you could create a Persistent handle to the context, if needed.
Persistent<Context> persistent_context(isolate, context);
// Enter the created context for compiling and
// running the hello world script.
Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
Local<String> source = String::New("'Hello' + ', World'");
// Compile the source code.
Local<Script> script = Script::Compile(source);
// Run the script to get the result.
Local<Value> result = script->Run();
// Convert the result to an ASCII string and print it.
String::AsciiValue ascii(result);
printf("%s\n", *ascii);
return 0;
}
To compile and run it, assume you have v8 headers files in ../include and v8 libraries in ../lib
g++ -L../lib -I../include hello.cc -o a.out -lv8_base.x64 -lv8_snapshot -lpthread && ./a.out
The C++ code you are trying to compile is partial example ("pseudo code") taken from the V8 "Getting Started" guide. It can't work as it is. What you need to get this running is illustrated in the same document below:
Include v8.h header and link your project to the static (or shared) V8 library.
Import v8 namespace (using namespace v8);
Get a reference/pointer to the default Isolate (Isolate::GetCurrent()).
Create a HandleScope for local handles.
Create and enter the V8 execution context.
And only then you can use something like the code above.
Refer to the V8 Getting Started Guide for details.
I downloaded and built JS V8 for using in VS2010 in Release mode. Now I try run Hello World example:
#include "v8.h"
int _tmain(int argc, _TCHAR* argv[])
{
v8::HandleScope handle_scope;
v8::Persistent<v8::Context> context = v8::Context::New();
v8::Context::Scope context_scope(context);
v8::Handle<v8::String> source = v8::String::New("'Hello' + ', World'");
v8::Handle<v8::Script> script = v8::Script::Compile(source);
v8::Handle<v8::Value> result = script->Run();
context.Dispose();
v8::String::AsciiValue ascii (result);
printf ("%s\n", *ascii);
return 0;
}
I added Additional Dependencies:
"C:\v8\build\Release\lib\preparser_lib.lib"
"C:\v8\build\Release\lib\v8_base.lib"
When I try to compile and run the program, I encountered a linking error:
1>------ Build started: Project: V8_example, Configuration: Release Win32 ------
1>LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library
1>v8_base.lib(platform-win32.obj) : error LNK2001: unresolved external symbol __imp__inet_addr#4
...
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
When I have set "Ignore All Default Libraries: Yes(/NODEFAULTLIB)", these errors showed up:
1>------ Build started: Project: V8_example, Configuration: Release Win32 ------
1>v8_base.lib(strtod.obj) : error LNK2001: unresolved external symbol #__security_check_cookie#4
1>v8_base.lib(full-codegen-ia32.obj) : error LNK2001: unresolved external symbol #__security_check_cookie#4
...
1>c:\users\admin\documents\visual studio 2010\Projects\V8_example\Release\V8_example.exe : fatal error LNK1120: 141 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Has anyone tried to run this example or know how to fix these errors?
The error is caused by the missing symbol __imp__inet_addr#4, which is located in Ws2_32.lib.
Go to project Properties -> Linker -> Input -> Additional Dependencies. Simply add Ws2_32.lib and you're done.
I had to include the following libraries:
v8_base.lib;v8_snapshot.lib;ws2_32.lib;winmm.lib
DO NOT DEFINE /NODEFAULTLIB:LIBCMT this caused my build to fail.
If you're curious as to how I found out, I looked under the ALL.sln that is generated by GYP and checked the shell target. It's an executable that has to link with v8_base at some point, so it has the required Linker options. It was a bit difficult to find, however.
how about /NODEFAULTLIB:LIBCMT, to exclude only this single library?
also I believe you need to link v8_snapshot.lib or v8_nosnapshot.lib
or you build shared library and link to v8.lib
Is there a way to execute some code (in a file or from a string, doesn't really matter) before dropping into interactive mode in node.js?
For example, if I create a script __preamble__.js which contains:
console.log("preamble executed! poor guy!");
and a user types node __preamble__.js they get this output:
preamble executed! poor guy!
> [interactive mode]
Really old question but...
I was looking for something similar, I believe, and found out this.
You can open the REPL (typing node on your terminal) and then load a file.
Like this: .load ./script.js.
Press enter and the file content will be executed. Now everything created (object, variable, function) in your script will be available.
For example:
// script.js
var y = {
name: 'obj',
status: true
};
var x = setInterval(function () {
console.log('As time goes by...');
}, 5000);
On the REPL:
//REPL
.load ./script.js
Now you type on the REPL and interact with the "living code".
You can console.log(y) or clearInterval(x);
It will be a bit odd, cause "As time goes by..." keep showing up every five seconds (or so).
But it will work!
You can start a new repl in your Node software pretty easily:
var repl = require("repl");
var r = repl.start("node> ");
r.context.pause = pauseHTTP;
r.context.resume = resumeHTTP;
From within the REPL you can then call pause() or resume() and execute the functions pauseHTTP() and resumeHTTP() directly. Just assign whatever you want to expose to the REPL's context member.
This can be achieved with the current version of NodeJS (5.9.1):
$ node -i -e "console.log('A message')"
The -e flag evaluates the string and the -i flag begins the interactive mode.
You can read more in the referenced pull request
node -r allows you to require a module when REPL starts up. NODE_PATH sets the module search path. So you can run something like this on your command line:
NODE_PATH=. node -r myscript.js
This should put you in a REPL with your script loaded.
I've recently started a project to create an advanced interactive shell for Node and associated languages like CoffeeScript. One of the features is loading a file or string in the context of the interpreter at startup which takes into account the loaded language.
http://danielgtaylor.github.com/nesh/
Examples:
# Load a string (Javascript)
nesh -e 'var hello = function (name) { return "Hello, " + name; };'
# Load a string (CoffeeScript)
nesh -c -e 'hello = (name) -> "Hello, #{name}"'
# Load a file (Javascript)
nesh -e hello.js
# Load a file (CoffeeScript)
nesh -c -e hello.coffee
Then in the interpreter you can access the hello function.
Edit: Ignore this. #jaywalking101's answer is much better. Do that instead.
If you're running from inside a Bash shell (Linux, OS X, Cygwin), then
cat __preamble__.js - | node -i
will work. This also spews lots of noise from evaluating each line of preamble.js, but afterwords you land in an interactive shell in the context you want.
(The '-' to 'cat' just specifies "use standard input".)
Similar answer to #slacktracer, but if you are fine using global in your script, you can simply require it instead of (learning and) using .load.
Example lib.js:
global.x = 123;
Example node session:
$ node
> require('./lib')
{}
> x
123
As a nice side-effect, you don't even have to do the var x = require('x'); 0 dance, as module.exports remains an empty object and thus the require result will not fill up your screen with the module's content.
Vorpal.js was built to do just this. It provides an API for building an interactive CLI in the context of your application.
It includes plugins, and one of these is Vorpal-REPL. This lets you type repl and this will drop you into a REPL within the context of your application.
Example to implement:
var vorpal = require('vorpal')();
var repl = require('vorpal-repl');
vorpal.use(repl).show();
// Now you do your custom code...
// If you want to automatically jump
// into REPl mode, just do this:
vorpal.exec('repl');
That's all!
Disclaimer: I wrote Vorpal.
There isn't a way do this natively. You can either enter the node interactive shell node or run a script you have node myScrpt.js. #sarnold is right, in that if you want that for your app, you will need to make it yourself, and using the repl toolkit is helpful for that kind of thing
nit-tool lets you load a node module into the repl interactive and have access to inner module environment (join context) for development purposes
npm install nit-tool -g
First I tried
$ node --interactive foo.js
but it just runs foo.js, with no REPL.
If you're using export and import in your js, run npm init -y, then tell node that you're using modules with the "type": "module", line -
{
"name": "neomem",
"version": "1.0.0",
"description": "",
"type": "module",
"main": "home.js",
"keywords": [],
"author": "",
"license": "ISC"
}
Then you can run node and import a file with dynamic import -
$ node
Welcome to Node.js v18.1.0.
Type ".help" for more information.
> home = await import('./home.js')
[Module: null prototype] {
get: [AsyncFunction: get],
start: [AsyncFunction: start]
}
> home.get('hello')
Kind of a roundabout way of doing it - having a command line switch would be nice...