Writing ASYNC CPP nodejs (0.5.3+) modules - javascript

I´m searching for a way to build c++ modules for NodeJs with the current release (0.5.9).
By using the following tutorial and abstracting from nodes node_file.cc I was able to build a module by my self for node 0.5.3.
https://github.com/jedp/node-rot13/blob/master/src/rot13.cpp
But with node 0.5.4 some of the API´s must have changed because because I´m no longer able to warp functions with eio_* anymore.
Looking at node_file.cc I discovered that the eio_* wrapping is replaced by new ReqWrap classes.
For Example in this macro: https://gist.github.com/1303926
No I wonder what's the best way of writing async extensions?

Basically looking at the crypto node module solved my problem this module was not poluted with macros like the file module. I came up with a simple async module that calculates the sum of two integers:
#include <v8.h>
#include <node.h>
#include <stdlib.h>
#include <errno.h>
using namespace node;
using namespace v8;
struct Test_req
{
ssize_t result;
ssize_t int1;
ssize_t int2;
Persistent<Function> callback;
};
void TestWorker(uv_work_t* req)
{
Test_req* request = (Test_req*)req->data;
request->result = request->int1 + request->int2;
}
void TestAfter(uv_work_t* req)
{
HandleScope scope;
Test_req* request = (Test_req*)req->data;
delete req;
Handle<Value> argv[2];
// XXX: Error handling
argv[0] = Undefined();
argv[1] = Integer::New(request->result);
TryCatch try_catch;
request->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught())
{
FatalException(try_catch);
}
request->callback.Dispose();
delete request;
}
static Handle<Value> Test(const Arguments& args)
{
HandleScope scope;
if ( args.Length() < 3 || !args[0]->IsNumber() || !args[1]->IsNumber() )
{
return ThrowException(Exception::TypeError(String::New("Bad argument")));
}
ssize_t int1 ( args[0]->Int32Value() );
ssize_t int2 ( args[1]->Int32Value() );
if ( args[2]->IsFunction() )
{
Local<Function> callback = Local<Function>::Cast(args[2]);
Test_req* request = new Test_req;
request->callback = Persistent<Function>::New(callback);
request->int1 = int1;
request->int2 = int2;
uv_work_t* req = new uv_work_t();
req->data = request;
uv_queue_work(uv_default_loop(), req, TestWorker, TestAfter);
}
else
{
return ThrowException(Exception::TypeError(String::New("Callback missing")));
}
return Undefined();
}
extern "C"
{
static void init(Handle<Object> target)
{
HandleScope scope;
}
}
NODE_MODULE(node_AsyncTest, init);
On the node side you call the module like this:
var foo = process.binding('AsyncTest');
foo.Test(1,2,function(err,data){
console.log(err,data);
});
result:
undefined 3
Hope this is helpful ;)
Ps: Since the lack of node compiling extensions under windows. I´m building this directly into the core of the node windows port with the Visual Studio build solution.

Related

Hanging promise canceled

I'm trying to set Cloudflare's workers to track the circulation of some ERC20 tokens as an exercise to learn web3 and wasm. Thought it could be simple enough, but about 90% of the time so far has been trying to solve this elusive error
A hanging Promise was canceled. This happens when the worker runtime is waiting for a Promise from JavaScript to resolve but has detected that the Promise cannot possibly ever resolve because all code and events related to the Promise's request context have already finished.
I look for additional information online, but it seems my error is from a different type(?).
Here's a simple snippet of code to reproduce.
mod erc20_abi;
use erc20_abi::ERC20_ABI;
use cfg_if::cfg_if;
use ethers::{
contract::Contract,
core::{abi::Abi, types::Address},
prelude::{AbiError, U256},
providers::{Http, Provider},
};
use num_format::{Locale, ToFormattedString};
use std::convert::TryFrom;
use wasm_bindgen::prelude::*;
cfg_if! {
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
if #[cfg(feature = "wee_alloc")] {
extern crate wee_alloc;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
}
}
#[wasm_bindgen]
pub async fn handle() -> String {
let web3_ethereum = Provider::<Http>::try_from(WEB3_URL_ETHEREUM).unwrap();
let abi: Abi = serde_json::from_str(ERC20_ABI).unwrap();
let token_contract_ethereum = Contract::new(parse_address(ADDRESS_ETH),
abi, web3_ethereum);
let convert_wei_to_decimal = |bignumber: U256| -> String {
(bignumber.as_u128() / u128::pow(10, 18)).to_formatted_string(&Locale::en)
};
// I believe this is the problem, since just returning a String works fine.
let total_supply_ethereum = token_contract_ethereum
.method::<_, U256>("totalSupply", ())
.unwrap()
.call()
.await
.unwrap();
convert_wei_to_decimal(total_supply_ethereum)
}
fn parse_address(address: &str) -> Address {
address.parse::<Address>().unwrap()
}
This is the worker/workers.js file
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request))
})
const { handle } = wasm_bindgen;
const instance = wasm_bindgen(wasm);
/**
* Fetch and log a request
* #param {Request} request
*/
async function handleRequest(request) {
await instance;
const output = await handle();
let res = new Response(output, { status: 200 });
res.headers.set('Content-type', 'text/html');
return res;
}
Cargo.toml
[package]
name = "circulating-supply"
version = "0.1.0"
license = "GPL-3.0-or-later"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]
[profile.release]
opt-level = 's' # Optimize for size.
lto = true
panic = "abort"
codegen-units = 1
[dependencies]
ethers = { git = "https://github.com/gakonst/ethers-rs" }
serde_json = "1.0.68"
num-format = "0.4.0"
cfg-if = "1.0.0"
wee_alloc = { version = "0.4.5", optional = true }
wasm-bindgen = "0.2.78"
wasm-bindgen-futures = "0.4.28"
js-sys = "0.3.55"
wrangler dev will compile it fine, but going to http://127.0.0.1:8787 will result in Error 1101
In my case a dependency used sth. not available in wasm runtime.
I guess ethers cryptography dependencies also depend on sth. like getrandom.
Adding this to Cargo.toml solved my issue.
[target.wasm32-unknown-unknown.dependencies]
getrandom = { version = "0.1", features = ["wasm-bindgen"] }
This will force your dependencies based on getrandom use the wasm features of getrandom.

The `Module` variable's mechanism in Emscripten while compiling Pthread to workers

I am confused by the Module variable in Emsripten while compiling Pthread to Web Worker + Wasm
There is a simple Pthread code which just adds 1 in the shared variable sum in each thread. (simple.c is attached in the end.)
We can use the command to compile the Pthread code:
$ emcc simple.c -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=4 -o simple.html
Emscripten will generate simple.html, simple.js, simple.worker.js, and simple.wasm.
In simple.worker.js, there is a snippet:
// simple.worker.js
var Module = {};
// ...
Module['instantiateWasm'] = function(info, receiveInstance) {
// Instantiate from the module posted from the main thread.
// We can just use sync instantiation in the worker.
var instance = new WebAssembly.Instance(Module['wasmModule'], info);
// We don't need the module anymore; new threads will be spawned from the main thread.
Module['wasmModule'] = null;
receiveInstance(instance); // The second 'module' parameter is intentionally null here, we don't need to keep a ref to the Module object from here.
return instance.exports;
};
Note that it declares var Module = {} in the worker, and defines Module['instantiateWasm'].
However, Module['instantiateWasm'] is only called by simple.js, which the code snippet looks like:
//simple.js
var Module = {}
// ...
if (Module['instantiateWasm']) {
try {
var exports = Module['instantiateWasm'](info, receiveInstance);
return exports;
} catch(e) {
err('Module.instantiateWasm callback failed with error: ' + e);
return false;
}
}
// ...
As we can see, simple.js also declares var Module = {} too.
AFAIK, VAR global variables cannot be accessed across the main thread and its worker. I don't understand why simple.js can call Module['instantiateWasm'] as the Module of simple.js and the Module of simple.worker.js should be not the same thing.
Pthread Code:
// simple.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUMTHRDS 4
#define MAGNIFICATION 1e9
typedef struct
{
int thread_id;
double *sum;
} Arg;
pthread_t callThd[NUMTHRDS];
pthread_mutex_t mutexsum;
void *count_pi(void *arg)
{
Arg *data = (Arg *)arg;
int thread_id = data->thread_id;
double *sum = data->sum;
pthread_mutex_lock(&mutexsum);
*sum += 1;
pthread_mutex_unlock(&mutexsum);
printf("Thread %d: sum=%f\n", thread_id, *sum);
pthread_exit((void *)0);
}
int main(int argc, char *argv[])
{
pthread_mutex_init(&mutexsum, NULL);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
double *sum = malloc(sizeof(*sum));
*sum = 0;
Arg arg[NUMTHRDS];
for (int i = 0; i < NUMTHRDS; i++)
{
arg[i].thread_id = i;
arg[i].sum = sum;
pthread_create(&callThd[i], &attr, count_pi, (void *)&arg[i]);
}
pthread_attr_destroy(&attr);
void *status;
for (int i = 0; i < NUMTHRDS; i++)
{
pthread_join(callThd[i], &status);
}
printf("Final Sum = %f \n", *sum);
free(sum);
pthread_mutex_destroy(&mutexsum);
pthread_exit(NULL);
}
A main program sends self to worker.
// simple.js
// Ask the new worker to load up the Emscripten-compiled page. This is a heavy operation.
worker.postMessage({
'cmd': 'load',
// If the application main .js file was loaded from a Blob, then it is not possible
// to access the URL of the current script that could be passed to a Web Worker so that
// it could load up the same file. In that case, developer must either deliver the Blob
// object in Module['mainScriptUrlOrBlob'], or a URL to it, so that pthread Workers can
// independently load up the same main application file.
'urlOrBlob': Module['mainScriptUrlOrBlob'] || _scriptDir,
'wasmMemory': wasmMemory,
'wasmModule': wasmModule,
'DYNAMIC_BASE': DYNAMIC_BASE,
'DYNAMICTOP_PTR': DYNAMICTOP_PTR
});
and worker import that.
// simple.worker.js
if (typeof e.data.urlOrBlob === 'string') {
importScripts(e.data.urlOrBlob);
} else {
var objectUrl = URL.createObjectURL(e.data.urlOrBlob);
importScripts(objectUrl);
URL.revokeObjectURL(objectUrl);
}
Therefore Module isn't shared, but it is initialized independently.

How to call a webassembly method in vue.js?

I'm trying to transpose to vue.js this simple html page add.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<input type="button" value="Add" onclick="callAdd()" />
<script>
function callAdd() {
const result = Module.ccall('Add',
'number',
['number', 'number'],
[1, 2]);
console.log(`Result: ${result}`);
}
</script>
<script src="js_plumbing.js"></script>
</body>
</html>
which calls the Add function defined in add.c :
#include <stdlib.h>
#include <emscripten.h>
// If this is an Emscripten (WebAssembly) build then...
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#ifdef __cplusplus
extern "C" { // So that the C++ compiler does not rename our function names
#endif
EMSCRIPTEN_KEEPALIVE
int Add(int value1, int value2)
{
return (value1 + value2);
}
#ifdef __cplusplus
}
#endif
and converted to js_plumbing and js_plumbling.wasm files through the command:
emcc add.c -o js_plumbing.js -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap'] -s
ENVIRONMENT='web','worker'
In console of google chrome I get these errors:
GET http://localhost:8080/dist/js_plumbing.wasm 404 (Not Found) # js_plumbing.js?2b2c:1653
Where in js_plumbing_js :
// Prefer streaming instantiation if available.
function instantiateAsync() {
if (!wasmBinary &&
typeof WebAssembly.instantiateStreaming === 'function' &&
!isDataURI(wasmBinaryFile) &&
typeof fetch === 'function') {
fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) { // <---------------!!!
var result = WebAssembly.instantiateStreaming(response, info);
return result.then(receiveInstantiatedSource, function(reason) {
// We expect the most common failure cause to be a bad MIME type for the binary,
// in which case falling back to ArrayBuffer instantiation should work.
err('wasm streaming compile failed: ' + reason);
err('falling back to ArrayBuffer instantiation');
instantiateArrayBuffer(receiveInstantiatedSource);
});
});
} else {
return instantiateArrayBuffer(receiveInstantiatedSource);
}
}
In Google Chrome: createWasm # js_plumbing.js?2b2c:1680
line 1680 of js_plumbing.js:
instantiateAsync();
in Google Chrome: eval # js_plumbing.js?2b2c:1930
line 1930 of js_plumbing.js:
<pre><font color="#4E9A06">var</font> asm = createWasm();</pre>
And many other errors related to wasm :
So... how should I modify the callAdd() method in Result.vue in order to correctly execute the Add function in js_plumbing.js and in js_plumbing.wasm files?
methods: {
callAdd() {
const result = Module.ccall('Add',
'number',
['number', 'number'],
[1, 2]);
console.log('Result: ${result}');
}
}
Updates:
1 update)
I compiled the add.c with this command:
emcc add.c -o js_plumbing.mjs -s EXTRA_EXPORTED_RUNTIME_METHODS=
['ccall','cwrap'] -s ENVIRONMENT='web' .
Then created a js_plumbing.js file :
. import wasm from './js_plumbing.mjs';
const instance = wasm({
onRuntimeInitialized() {
console.log(instance._addTwoNumbers(3,2));
}
}) .
Doing npm run dev:
Failed to compile.
./src/components/js_plumbing.mjs 3:25
Module parse failed: Unexpected token (3:25)
You may need an appropriate loader to handle this file type, currently
no loaders are configured to process this file.
See https://webpack.js.org/concepts#loaders
|
| var Module = (function() {
> var _scriptDir = import.meta.url;
|
| return (
Update 2)
I solved the 404 error by putting the wasm file into a /div subfolder within the same folder of the index.html file.
Now I’m facing this problem: “Cannot read property ‘ccall’ of undefined”
But I compiled the add.c file, creating js_plumbing.js and js_plumbing.wasm files, with this command, which exports the methods ‘ccall’ and ‘cwrap’ :
emcc add.c -o js_plumbing.js -s EXTRA_EXPORTED_RUNTIME_METHODS=[‘ccall’,‘cwrap’] -s ENVIRONMENT=‘web’,‘worker’
3° Update)
I "solved" through a sort of an hack, which I do not like at all.
This is the Result.vue file:
<template>
<div>
<p button #click="callAdd">Add!</p>
<p>Result: {{ result }}</p>
</div>
</template>
<script>
import * as js_plumbing from './js_plumbing'
import Module from './js_plumbing'
export default {
data () {
return {
result: null
}
},
methods: {
callAdd () {
const result = js_plumbing.Module.ccall('Add',
'number',
['number', 'number'],
[1, 2]);
this.result = result;
}
}
}
</script>
which is exactly the same as the one used before
The only thing I've done to make it working, is to add export to the definition of Module in js_plumbing.js :
js_plumbing.js
// Copyright 2010 The Emscripten Authors. All rights reserved.
// Emscripten is available under two separate licenses, the MIT
license and the
// University of Illinois/NCSA Open Source License. Both these
licenses can be
// found in the LICENSE file.
// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
// 2. A function parameter, function(Module) { ..generated code.. }
// 3. pre-run appended it, var Module = {}; ..generated code..
// 4. External script tag defines var Module.
// We need to check if Module already exists (e.g. case 3 above).
// Substitution will be replaced with actual code on later stage of
the build,
// this way Closure Compiler will not mangle it (e.g. case 4. above).
// Note that if you want to run closure, and also to use Module
// after the generated code, you will need to define var Module =
{};
// before the code. Then that object will be used in the code, and you
// can continue to use Module afterwards as well.
export var Module = typeof Module !== 'undefined' ? Module : {};
But, as I said, I do not like this hack.
Any suggestions on how to make the Module exportable, thus importable, without manually adding 'export' in js_plumbing.js file?
First, the 404 error should be addressed. Does file /dist/js_plumbing.wasm exist? I've needed to copy .wasm files manually in the past because some automatic build systems (like Parcel) currently don't.
You can build with the MODULARIZE option to import into your build system.
addTwoNumbers.c
#include <emscripten.h>
EMSCRIPTEN_KEEPALIVE
int addTwoNumbers(int value1, int value2)
{
return (value1 + value2);
}
build command
$ emcc -o dist/addTwoNumbers.js -s MODULARIZE=1 src/addTwoNumbers.c
Vue Implementation
import myMathModule from './js_plumbing';
let instance = {
ready: new Promise(resolve => {
myMathModule({
onRuntimeInitialized() {
instance = Object.assign(this, {
ready: Promise.resolve()
});
resolve();
}
});
})
};
export default {
data () {
return {
result: null
};
},
methods: {
callAdd(a, b) {
instance.ready
.then(_ => this.result = instance._add(a, b));
}
}
};
Use the onRuntimeInitialized method to detect when the WASM module is ready. Your exported functions will have an underscore in front of them.
require() could possibly be used in place of import:
const wasmModule = require('./addTwoNumbers.js');
...

Calling callback inside callback in native Node

I'm a node & C++ newbie so be clement.
I'm writing a native node addon.
My addon start a webcam streaming (using UVC lib) and I want every frame to be available to node.
My CC addon do something like
uvc_start_streaming(devh, &ctrl, frameProcess, (void *) &args, 0)
Where:
devh: is the UVC device
ctrl: are device config
frameProcess: is my function callback to be called at every new frame
args: are arguments from javascript function.
The c++ callback is called every new frame and I want to simple print something like "new frame received" so my C++ is like:
void frameProcess(uvc_frame_t *frame, void *ptr) {
const FunctionCallbackInfo<Value> args = *((const FunctionCallbackInfo<Value>*)(ptr));
Isolate* isolate = args.GetIsolate();
Local<Function> cb = Local<Function>::Cast(args[0]);
const unsigned argc = 1;
Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "new frame received") };
cb->Call(Null(isolate), argc, argv);
}
void testStreaming (const FunctionCallbackInfo<Value>& args) {
...
res = uvc_start_streaming(devh, &ctrl, frameProcess, (void *) &args, 0);
puts("Streaming...");
Sleep(10000); /* stream for 10 seconds */
uvc_stop_streaming(devh);
puts("Done streaming.");
...
}
...
NODE_SET_METHOD(exports, "testStreaming", testDevice);
My js is something like:
'use strict';
var uvc = require('../build/Release/binding')
uvc.testStreaming(
function (x) {
console.log(x)
}
)
The problem is that node exit without any message or error when program reach cb->Call.
If I comment cb->Call row the program run for 10 seconds (continuosly calling the ) as programmed and then exit.
But if I uncomment cb->Call the program exit immediatly.
Your frameProcess() function should call v8::Function callback in the Node.js thread, see https://stackoverflow.com/a/28116160/1355844

PNaCl - How do i post message from index.html to PNaCl?

I run the hello_nacl example of nacl_sdk(pepper_39) and everything is fine.
But i try adding something on index.html to post message to PNaCl, it's not work and get error like this "NativeClient: NaCl module crashed".
This is my index.html, anyone can tell me what's wrong?
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
<script type="text/javascript">
var HelloTutorialModule = null;
function pageDidLoad() {
appendStatus('Page loaded');
HelloTutorialModule = document.getElementById('nacl_module');
HelloTutorialModule.postMessage('hello from HTML!!');
}
function appendStatus(opt_message) {
var statusField = document.getElementById('statusField');
if (statusField) {
var newElt = document.createElement("opt_message");
newElt.innerHTML = "<br>" + opt_message;
statusField.appendChild(newElt);
}
}
function handleMessage(message_event) {
appendStatus(message_event.data);
}
</script>
</head>
<body>
<div id="listener">
<script type="text/javascript">
var listener = document.getElementById('listener');
listener.addEventListener('message', handleMessage, true);
listener.addEventListener('load', pageDidLoad, true);
</script>
<h2>NaCl Module</h2>
<embed name="nacl_module"
id="nacl_module"
style="border-style: solid;"
width=200
height=200
src="newlib/hello_nacl.nmf"
type="application/x-nacl"/>
</div>
Thank you.
The following code is my hello_nacl.c
/* Copyright (c) 2012 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
// This project demonstrates how to migrate a Windows desktop app to Native
// Client, running first as a Win32 application (define STEP1), then as a PPAPI
// plugin (define STEP2 through STEP6), and finally as a Native Client module.
// Start with STEP1 defined and the defines for STEP2 through STEP6 commented
// out. For each step in the process, un-comment the next #define, leaving the
// previous ones on. Ready, set, port!
// *** SELECT THE WIN32 PLATFORM AND RUN WITH #define STEP1 ONLY ***
// #define STEP1
// Launches the original Windows desktop application, Hello World, which runs
// as WinMain. STEP1 encloses Windows-specific functions that are used with
// the WIN32 and PPAPI platforms. These will be removed when the full PPAPI
// port is finished (STEP6)
// *** SELECT THE PPAPI PLATFORM ***
#define STEP2
// What Changed: The platform launches Chrome, which will then load a Native
// Client Module. STEP2 encloses the Native Client module APIs needed to link
// any app to the browser. The module does nothing except report
// starting/ending the function Instance_DidCreate. The Windows app does not
// run because it is not being called.
#define STEP3
// What changed: Replace WinMain with WndProc, and call it from
// Instance_DidCreate, launching hello_nacl in its own window.
// Since WndProc spins in its message loop, the call to Instance_DidCreate
// never returns.
// Close the hello_nacl window and the module initialization will finish.
#define STEP4
// What changed: In WndProc replace the message loop with a callback function.
// Now the app window and the Native Client module are running concurrently.
#define STEP5
// What changed: Instance_DidCreate calls InitInstanceInBrowserWindow rather
// than InitInstanceInPCWindow.
// The InitInstanceInBrowserWindow uses postMessage to place text (now "Hello,
// Native Client") in the web page instead of opening and writing to a window.
#define STEP6
// What changed: All the Windows code is def'd out, to prove we are
// PPAPI-compliant. The functional code that is running is the same as STEP5.
// *** SELECT THE NACL64 PLATFORM AND RUN ***
// What changed: The code is the same as STEP6, but you are using the SDK
// toolchain to compile it into a nexe. The module is now running as a real
// Native Client executable in a NaCl sandbox, with nacl-gdb attached.
// *** RUN YOUR MODULE IN THE WILD ***
// You can run your nexe outside of Visual Studio, directly from Chrome by
// following these steps:
// - Build STEP6 and verify the file
// <project directory>/NaCl64/newlib/Debug/hello_nacl_64.nexe exists
// - Copy the folder <project directory> into your NaCl SDK's example
// directory.
// - Go to the NaCl SDK directory and launch the httpd.py server.
// - Launch Chrome, go to about:flags and enable the Native Client flag and
// relaunch Chrome
// - Point Chrome at http: //localhost:5103/hello_nacl
#ifdef STEP6
// remove Windows-dependent code.
#undef STEP1
#undef STEP3
#undef STEP4
#define NULL 0
#else
// includes for Windows APIs.
#include <windows.h>
#include <stdlib.h>
#include <tchar.h>
#endif
#ifdef STEP2
// includes for PPAPI
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_module.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/c/ppb.h"
#include "ppapi/c/ppb_instance.h"
#include "ppapi/c/ppb_messaging.h"
#include "ppapi/c/ppb_var.h"
#include "ppapi/c/ppb_core.h"
#include "ppapi/c/ppp.h"
#include "ppapi/c/ppp_instance.h"
#include "ppapi/c/ppp_messaging.h"
#include <string.h>
#include <stdio.h>
#include <time.h>
// Native Client APIs
static PPB_Messaging* ppb_messaging_interface = NULL;
static PPB_Var* ppb_var_interface = NULL;
static PPB_Core* ppb_core_interface = NULL;
PP_Instance myInstance;
int InitInstanceInPCWindow();
void InitInstanceInBrowserWindow();
#endif
#ifdef STEP4
// Implements message handling in a callback function.
void HelloWorldCallbackFun(void* user_data, int32_t result);
struct PP_CompletionCallback HelloWorldCallback = {
HelloWorldCallbackFun, NULL };
void HelloWorldCallbackFun(void* user_data, int32_t result) {
MSG uMsg;
if (PeekMessage(&uMsg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&uMsg);
DispatchMessage(&uMsg);
}
ppb_core_interface->CallOnMainThread(100, HelloWorldCallback, 0);
}
#endif
#ifdef STEP2
// The basic framework needed for all Native Client Modules. Handles creation
// of the module instance and initial handshake with the browser.
/**
* Creates new string PP_Var from C string. Useful utility for
* message-handling.
*/
static struct PP_Var CStrToVar(const char* str) { if (ppb_var_interface !=
NULL) { return ppb_var_interface->VarFromUtf8(str, strlen(str)); } return
PP_MakeUndefined(); }
void InitInstanceInBrowserWindow() {
// Pass the text to the browser page, there is no separate app window
// anymore. The text is added as a new element to the page, it does not
// appear in the module's embed view.
ppb_messaging_interface->PostMessage(myInstance, CStrToVar("Hello, Native Client! XDDXDXD"));
}
/**
* Called when the NaCl module is instantiated on the web page.
*/
static PP_Bool Instance_DidCreate(PP_Instance instance,
uint32_t argc,
const char* argn[],
const char* argv[]) {
myInstance = instance;
ppb_messaging_interface->PostMessage(instance,
CStrToVar("Start Instance_DidCreate"));
#ifdef STEP5
// Will be included in STEP5 and STEP6
// Uses messaging to relay text to the module's view on the web page
InitInstanceInBrowserWindow();
#else
#ifdef STEP3
// Will be included in STEP3 and STEP4 only
// Uses WndProc to place text in a window separate from the browser.
InitInstanceInPCWindow();
#endif
#endif
ppb_messaging_interface->PostMessage(instance,
CStrToVar("End Instance_DidCreate"));
return PP_TRUE;
}
/**
* Called when the NaCl module is destroyed.
*/
static void Instance_DidDestroy(PP_Instance instance) {
ppb_messaging_interface->PostMessage(instance,
CStrToVar("Instance_DidDestroy"));
}
/**
* Called when the position, the size, or the clip rect of the element in the
* browser that corresponds to this NaCl module has changed.
*/
static void Instance_DidChangeView(PP_Instance instance,
PP_Resource view_resource) {
ppb_messaging_interface->PostMessage(instance,
CStrToVar("Instance_DidChangeView"));
}
/**
* Notification that the given NaCl module has gained or lost focus.
*/
static void Instance_DidChangeFocus(PP_Instance instance,
PP_Bool has_focus) {
ppb_messaging_interface->PostMessage(instance,
CStrToVar("Instance_DidChangeFocus"));
}
/**
* Handler that gets called after a full-frame module is instantiated based on
* registered MIME types. This function is not called on NaCl modules. This
* function is essentially a place-holder for the required function pointer in
* the PPP_Instance structure.
*/
static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
PP_Resource url_loader) {
/* NaCl modules do not need to handle the document load function. */
ppb_messaging_interface->PostMessage(instance,
CStrToVar("Instance_HandleDocumentLoad...."));
return PP_FALSE;
}
static void HandleMessage(PP_Instance instance, struct PP_Var message){
//ppb_messaging_interface->PostMessage(instance, CStrToVar("Get Message From HTML"));
}
/**
* Entry points for the module.
* Initialize needed interfaces: PPB_Core, PPB_Messaging and PPB_Var.
*/
PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id,
PPB_GetInterface get_browser) {
ppb_messaging_interface = (PPB_Messaging*)
get_browser(PPB_MESSAGING_INTERFACE);
ppb_var_interface = (PPB_Var*)get_browser(PPB_VAR_INTERFACE);
ppb_core_interface = (PPB_Core*)get_browser(PPB_CORE_INTERFACE);
return PP_OK;
}
/**
* Returns an interface pointer for the interface of the given name, or NULL
* if the interface is not supported.
*/
PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
static PPP_Instance instance_interface = {
&Instance_DidCreate,
&Instance_DidDestroy,
&Instance_DidChangeView,
&Instance_DidChangeFocus,
&Instance_HandleDocumentLoad,
//&HandleMessage,
};
return &instance_interface;
}
return NULL;
}
/**
* Called before the plugin module is unloaded.
*/
PP_EXPORT void PPP_ShutdownModule() {
}
#endif
// **** Application Code ****
#ifdef STEP1
// Desktop Windows Hello World app. Native Client agnostic.
static TCHAR szWindowClass[] = _T("win32app");
static TCHAR szTitle[] = _T("hello_nacl");
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// WinMain
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
WNDCLASSEX wcex;
HWND hWnd;
MSG msg;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance,
MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex)) {
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("hello_nacl"),
0);
return 1;
}
hInst = hInstance;
hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 100,
NULL,
NULL,
hInstance,
NULL);
if (!hWnd) {
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("hello_nacl"),
0);
return 1;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
// WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps;
HDC hdc;
TCHAR greeting[] = _T("Hello, World!");
switch (message) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
#endif
#ifdef STEP3
// Replace WinMain with InitInstanceInPCWindow so the Native Client Module can
// launch the original application. Note the inclusion of a message-handling
// loop. STEP4 will replace the loop with a callback.
HINSTANCE g_hInstance = NULL;
HWND g_hWnd = NULL;
int InitInstanceInPCWindow() {
WNDCLASSEX winClass; MSG uMsg;
memset(&uMsg,0,sizeof(uMsg));
winClass.lpszClassName = _T("MY_WINDOWS_CLASS");
winClass.cbSize = sizeof(WNDCLASSEX);
winClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
winClass.lpfnWndProc = WndProc;
winClass.hInstance = g_hInstance;
winClass.hIcon = NULL;
winClass.hIconSm = NULL;
winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winClass.lpszMenuName = NULL;
winClass.cbClsExtra = 0;
winClass.cbWndExtra = 0;
if (!RegisterClassEx(&winClass))
return E_FAIL;
g_hWnd = CreateWindowEx(
0, _T("MY_WINDOWS_CLASS"),
_T("hello_nacl"), WS_OVERLAPPEDWINDOW,
0, 0, 640, 480, NULL, NULL, g_hInstance, NULL);
if (g_hWnd == NULL)
return E_FAIL;
ShowWindow(g_hWnd, 1);
UpdateWindow(g_hWnd);
#ifdef STEP4
// Skip the message loop, schedule a callback instead to periodically check
// for messages. Here we schedule at 100ms intervals.
ppb_core_interface->CallOnMainThread(100, HelloWorldCallback, 0);
return 0;
#else
// Main message loop, Windows style.
while(uMsg.message != WM_QUIT) {
if (PeekMessage(&uMsg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage( &uMsg );
DispatchMessage( &uMsg );
}
}
return uMsg.wParam;
#endif
}
#endif
There is a lot of extra code in this example, but here is the problem:
PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
static PPP_Instance instance_interface = {
&Instance_DidCreate,
&Instance_DidDestroy,
&Instance_DidChangeView,
&Instance_DidChangeFocus,
&Instance_HandleDocumentLoad,
//&HandleMessage,
};
return &instance_interface;
}
return NULL;
}
When you post a message to your module, this function is called requesting the PPP_Messaging;1.0 interface. This code is not handling that case, so it returns NULL to the caller, which then crashes.
This is not desirable behavior (it should probably produce an error instead), but it is not very surprising.
To fix this bug, you need to return an interface when PPP_MESSAGING_INTERFACE is requested:
...
} else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
static PPP_Messaging messaging_interface = {
&HandleMessage,
};
return &messaging_interface;
}

Categories