I was hoping to be able to write a very small Rust package and compile it to wasm, that would be able to read the system RAM/CPU resources.
Is this due to browser protection for the PC?
I can use the dependencies sysinfo dependency and run it no problem in Rust but once compiled into wasm, the values are no longer available. They just return back as 0.
use sysinfo::SystemExt;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: String);
}
#[wasm_bindgen]
pub fn specs() {
let mut system = sysinfo::System::new();
// First we update all information of our system struct.
system.refresh_all();
// And finally the RAM:
let total_ram = system.get_total_memory();
let used_ram = system.get_used_memory();
let available_ram = total_ram - used_ram;
log(format!("Total memory: {} kB", total_ram)); <--- Work in Rust but return 0 in wasm
log(format!("Used memory: {} kB", used_ram)); <--- Work in Rust but return 0 in wasm
log(format!("Available memory: {} kB", available_ram)); <--- Work in Rust but return 0 in wasm
}
Web assembly is unsupported by sysinfo. This triggers a dummy implementation returning 0s.
As can be seen here: https://github.com/GuillaumeGomez/sysinfo/blob/6315474256150e5f6e23bc80c0fd922b40998a58/src/unknown/system.rs#L96
Regarding the near future I would not get my hopes up to see support for web assembly. At least to me it would be also unclear to me what the meaning of the values should be. Would these values refer to the entire system, or just the memory available to Web Assembly?
Related
I am trying to work out how to call a JavaScript function using Rust and wasm-bindgen. Due to lack of browser support, I cannot use wasm-bindgen with ES6 modules together with a Web Worker.
As far as I can tell, declaring that a JavaScript function exists for me to call on the Rust side is straightforward
#[wasm_bindgen]
extern {
fn logProgress(percent: f64);
}
I have no idea where to define the JavaScript implementation however. If I try to just call a Rust function from JavaScript that calls the undefined logProgress then I get a runtime error: Error: logProgress is not defined
I can see from the wasm-bindgen docs that if I was using wasm-bindgen with ES6 modules then I could change the rust code to
#[wasm_bindgen(module = "/progress.js")]
extern {
fn logProgress(percent: f64);
}
and declare the JavaScript function in progress.js
export function logProgress(percent) {
console.log(percent)
// actual implementation would not just log
}
Since I am instead importing my Rust APIs via the wasm_bindgen global, I presume I should be able to define the implementation somewhere around the same part in my Web Worker, but I have searched through a lot of docs and can't find anything on how to do this.
importScripts('foo_wasm.js')
wasm_bindgen('foo_wasm_bg.wasm').then(fooWasmModule => {
memory = fooWasmModule.memory
const { Foo, Bar, Baz, foobar } = wasm_bindgen;
// JS has 'imported' the Rust structs and functions
// How to 'export' the JS functions to Rust?
}
I've made a compiler from my programming language (called AEC) targeting WebAssembly. However, that compiler only produces WebAssembly Binary Toolkit (WABT) compatible code, as I couldn't find sufficient documentation for the assembly compatible with Emscripten. So, how can I call the functions in that language from Emscripten-compatible C or C++?
Previously, I made a compiler for that language targeting x86, and it was rather easy to use C++ to interact with code written in that language. The same, however, doesn't seem to be true when using WebAssembly.
Let's say I have this code in AEC:
Function plusOne(Integer32 integer) Which Returns Integer32 Does
Return integer + 1;
EndFunction
I compile it as follows:
[teo.samarzija#teos-acer-laptop debug]$ ../AECforWebAssembly/aec *.aec
Running the tests...
All the tests passed.
Reading the file...
All characters read!
Tokenizing the program...
Finished tokenizing the program!
Parsing the program...
Finished parsing the program!
Compiling the program...
Compilation finished!
Saving the assembly in file "plusOne.wat"...
Assembly successfully saved, quitting now.
[teo.samarzija#teos-acer-laptop debug]$ wat2wasm plusOne.wat
Now, let's say I want to call it from a C code such as this one:
#include <stdio.h>
extern int plusOne(int);
int main() {
printf("plusOne(4)=%d\n",plusOne(4));
return 0;
}
How do I compile the C program to be able to do that? If I try it in an analogous way I could do it with my AEC compiler targeting x86 assembly, I get this error:
[teo.samarzija#teos-acer-laptop debug]$ emcc -o test.html test.c plusOne.wasm
emcc: error: plusOne.wasm: Input file has an unknown suffix, don't know what to do with it!
So, what should I do?
The following NodeJS code accomplishes what I want:
const FileSystem = require('fs');
const wasmFileContent = FileSystem.readFileSync("plusOne.wasm");
let stack_pointer = new WebAssembly.Global({value : 'i32', mutable : true}, 0);
let memory = new WebAssembly.Memory({initial : 1, maximum : 1});
let importObject = {
JavaScript : {stack_pointer : stack_pointer, memory : memory}
};
WebAssembly.instantiate(wasmFileContent, importObject).then((results) => {
const exports = results.instance.exports;
let plusOne = exports.plusOne;
console.log("plusOne(4)=", plusOne(4));
});
So, how can I do that in C or C++ using Emscripten?
I am attempting to embed v8 into my application, I am messing about seeing what is included in the V8 environment (duktape does not include a console implementation) and it seems like v8 does include an implementation however when I call console.log it does not print anything, instead it just prints undefined (which i assume to be the return value of console.log) So how would one go about linking the default std::cout output with console.log.
This is my code currently, I am working with the default hello world code very slightly modified.
int main(int argc, char* argv[]) {
// Initialize V8.
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
// Create a new Isolate and make it the current one.
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
// Create a stack-allocated handle scope.
v8::HandleScope handle_scope(isolate);
// Create a new context.
v8::Local<v8::Context> context = v8::Context::New(isolate);
// Enter the context for compiling and running the hello world script.
v8::Context::Scope context_scope(context);
{
// Create a string containing the JavaScript source code.
v8::Local<v8::String> source =
v8::String::NewFromUtf8(isolate, R"(
console.log("does not print?")
)",
v8::NewStringType::kNormal)
.ToLocalChecked();
// Compile the source code.
v8::Local<v8::Script> script =
v8::Script::Compile(context, source).ToLocalChecked();
// Run the script to get the result.
v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
// Convert the result to an UTF8 string and print it.
v8::String::Utf8Value utf8(isolate, result);
printf("%s\n", *utf8);
}
}
// Dispose the isolate and tear down V8.
isolate->Dispose();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete create_params.array_buffer_allocator;
std::cin.get();
return 0;
}
and I am using the prebuilt v8 binaries here
Try the following:
#include "src/debug/interface-types.h"
define your own "console delegate" class, deriving from debug::ConsoleDelegate
override any methods you're interested in, e.g. void Log(const debug::ConsoleCallArguments& args, const v8::debug::ConsoleContext&) override;
instantiate it and call debug::SetConsoleDelegate(isolate, &your_console_delegate); after creating your Isolate
To see an example, start at https://cs.chromium.org/chromium/src/v8/src/d8/d8-console.h?l=14&gsn=D8Console and trace where it's used.
So for anyone in the future that is dealing with this, this is the process I used to fix it.
download the source from here, only the src folder is needed.
extract it and link it in to your project where ever you put vendor code in addition to the bundle.
put it in the a src folder because otherwise its includes don't work
you will need to make a bunch of include directories for it to compile, mine include v8/src, and v8
make sure to link it with the nuget package, you may not have to do this, one machine needed it the other didn't.
you do not need to generate builtins-generated/bytecodes-builtins-list.h
I'm maintaining a Node.js add-on in C++.
I need to call a SQL parser (a very non-standard variant of SQL, actually) inside an asynchronously called C++ function, but unfortunately (for me) that parser has been implemented in Javascript using some Node.js libraries (the function require is being used); this is an example of the kind of javascript code I need to execute:
require("./util/SqlParser")("SELECT 1 FROM DUAL").getSyntaxTree()
I've tried writing something like this:
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::TryCatch trycatch;
v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate
,
"require(\"./util/SqlParser\")(\"SELECT 1 FROM DUAL\").getSyntaxTree()"
,
v8::NewStringType::kNormal).ToLocalChecked()
;
v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();
v8::MaybeLocal<v8::Value> result = script->Run(context);
if( result.IsEmpty() )
{
v8::Local<v8::Value> exc = trycatch.Exception();
if( !exc.IsEmpty() )
{
auto msg = exc->ToString();
if( !msg.IsEmpty() )
throw std::string( *v8::String::Utf8Value(msg) );
}
throw "unknown error in called js function";
}
But unfortunately this doesn't work. The net outcome is the error message "ReferenceError: require is not defined": apparently, my context doesn't know anything about Node.js.
I tried to wrap the above expression in a js function parseSqlText, but this function is equally unknown to the script executor (I get the error message "ReferenceError: parseSqlText is not defined").
My question is: is there any way to get around this?
Any help would be very appreciated. I would be very glad to avoid reimplementing that parser in C++... which at present seems the only viable way to do what I need.
First, v8 is not Node.js, Node.js is built on top of v8.
v8 is a javascript engine
Thus Node.js libraries are not carried with v8 by it-self
The N-API is the answer to question. Although, normally it is used to write native C++ plugins for Node.js. The github thread belwo has some examples. Additionally, the node.js docs for N-API have also been linked.
Github Thread
Node N-API Docs
EDIT: It would seem the work has been done already, here in this repository. The writer made it so it appears to abstract all the prep work of v8 and be one line for you.
#include "node_embed.h"
int main(int argc, char** argv) {
node_context *context = nodeSetup(argc, argv);
if (context) {
nodeExecuteString(context, "let foo = 1", "__init__");
nodeExecuteString(context, "foo = 2", "__init__");
nodeExecuteString(context, "console.log(foo)", "__init__");
return nodeTeardown(context);
} else {
return 12;
}
}
The above is from the repository and will allow you to run that snippet of code.
I suggest that you go find an SQL parser that is already written in C++. I found one immediately in a ten-second search on github.com.
In my opinion, there is zero technical justification for bringing the JavaScript language into this scenario, just to be able to "parse SQL." You will not have to "write from scratch" anything at all. It will just be an object – pluck it "off the shelf," instantiate an instance of it, and use it.
I am writing a Scala application (that is supposed to run on Hadoop using Spark) and my users are to execute JavaScript snippets that they upload and I want to provide access to certain helper functions written in Scala (like "make an HTTP call" etc.) to these JavaScript users. So what I do is writing a big JavaScriptHelpers object and then give access to that object using
engine = scriptEngineManager.getEngineByName("JavaScript")
engine.put("jql", JavaScriptHelpers)
so users can say jql.httpPost(...) from within JavaScript. The Scala code that makes this possible looks as follows:
def httpPost(where: String, params: Object): Try[String] = {
params match {
// JavaScript string becomes Java's String:
case body: String =>
// ...
// JavaScript object becomes Java's ScriptableObject
case obj: ScriptableObject =>
val params = convertToMap(obj)
// ...
}
}
protected def convertToMap(obj: ScriptableObject): Map[String, String] = {
(for (key <- obj.getIds().toList) yield {
(key.toString, obj.get(key) match {
case s: String =>
s
case d: java.lang.Double if d.toString.endsWith(".0") =>
d.toInt.toString
case other => other.toString
})
}).toMap
}
The only way I found to access information stored in JavaScript objects is to look at them as an instance of sun.org.mozilla.javascript.ScriptableObject. Now this works like a charm on my local OpenJDK installation
java version "1.7.0_75"
OpenJDK Runtime Environment (fedora-2.5.4.2.fc20-x86_64 u75-b13)
OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode)
but when I run the same code on my Hadoop cluster, which is running
java version "1.7.0_67"
Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)
then I get:
java.lang.NoClassDefFoundError: sun/org/mozilla/javascript/ScriptableObject
sun.org.mozilla.javascript.internal.JavaMembers.discoverAccessibleMethods(JavaMembers.java:383)
sun.org.mozilla.javascript.internal.JavaMembers.discoverAccessibleMethods(JavaMembers.java:335)
sun.org.mozilla.javascript.internal.JavaMembers.reflect(JavaMembers.java:455)
sun.org.mozilla.javascript.internal.JavaMembers.<init>(JavaMembers.java:76)
sun.org.mozilla.javascript.internal.JavaMembers.lookupClass(JavaMembers.java:847)
sun.org.mozilla.javascript.internal.NativeJavaObject.initMembers(NativeJavaObject.java:88)
sun.org.mozilla.javascript.internal.NativeJavaObject.<init>(NativeJavaObject.java:78)
sun.org.mozilla.javascript.internal.NativeJavaObject.<init>(NativeJavaObject.java:68)
...
and looking at the version of Rhino that Oracle bundles with the JDK 7 as downloadable from http://www.oracle.com/technetwork/opensource/jdk7-source-1634015.html it seems like all sun.org.mozilla.javascript.* classes have been moved to sun.org.mozilla.javascript.internal.*.
Now how do I deal with that situation? Is there any Rhino-independent way of accessing the fields of a JavaScript object in Java? Or, how can I force the Oracle JVM to use the ...javascript.internal.ScriptableObject while using ...javascript.ScriptableObject in my local environment?
Any help much appreciated.
You can use function overloading instead.
// `params` matches a JavaScript string
def httpPost(where: String, params: String): Try[String] = {
// ...
}
// `params` matches a JavaScript object
def httpPost(where: String, params: java.util.Map[_, _]): Try[String] = {
// ...
}
This solution worked on my environment (Oracle JDK 8 and OpenJDK 1.7).