Merged PR 148659: node binding for runtime apis

- node binding for runtime apis
This commit is contained in:
Asi Bross 2017-01-05 17:21:10 +00:00 коммит произвёл Asi Bross
Родитель 60af9d8341
Коммит 9ae68a15e6
13 изменённых файлов: 980 добавлений и 115 удалений

76
binding/node/addon.cpp Normal file
Просмотреть файл

@ -0,0 +1,76 @@
// Node uses deprecated V8 APIs
#pragma warning(push)
#pragma warning(disable: 4996)
#include <node.h>
#pragma warning(pop)
#include "napa/v8-helpers.h"
#include "napa-runtime.h"
#include "container-wrap.h"
#include <algorithm>
void Initialize(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto isolate = args.GetIsolate();
auto context = isolate->GetCurrentContext();
if (args.Length() <= 0)
{
// No settings provided
napa::runtime::InitializeFromConsole(0, nullptr);
}
else
{
CHECK_ARG(
isolate,
args[0]->IsString() || args[0]->IsObject(),
"first argument to initialize must be a string or an object");
if (args[0]->IsString())
{
// Settings provided as string
v8::String::Utf8Value settings(args[0]->ToString());
napa::runtime::Initialize(*settings);
}
else
{
// Settings provided as object
auto settingsObj = args[0]->ToObject(context).ToLocalChecked();
auto settingsMap = napa::v8_helpers::V8ObjectToMap<std::string>(isolate, settingsObj);
std::stringstream ss;
for (const auto& kv : settingsMap)
{
ss << " --" << kv.first << " " << kv.second;
}
napa::runtime::Initialize(ss.str());
}
}
}
void Shutdown(const v8::FunctionCallbackInfo<v8::Value>& args)
{
napa::runtime::Shutdown();
}
void CreateContainer(const v8::FunctionCallbackInfo<v8::Value>& args)
{
napa::binding::ContainerWrap::NewInstance(args);
}
void InitAll(v8::Local<v8::Object> exports)
{
napa::binding::ContainerWrap::Init(v8::Isolate::GetCurrent());
NODE_SET_METHOD(exports, "initialize", Initialize);
NODE_SET_METHOD(exports, "shutdown", Shutdown);
NODE_SET_METHOD(exports, "createContainer", CreateContainer);
}
NODE_MODULE(napa_wrap, InitAll)

Просмотреть файл

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(EnvironmentConfig)" />
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup>
<TargetName>napa_wrap</TargetName>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{AAB1C309-643B-45EC-A93A-902AD402E31D}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.props" />
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>4100</DisableSpecificWarnings>
<PreprocessorDefinitions>V8_IMMINENT_DEPRECATION_WARNINGS;BUILDING_NODE_EXTENSION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(NapaRoot)\vanilla\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="$(NapaRoot)\vanilla\src\napa\napa.vcxproj" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="addon.cpp" />
<ClCompile Include="container-wrap.cpp" />
</ItemGroup>
<ItemGroup>
<Binplace Include="$(OutputPath)\$(TargetFileName)">
<DestinationFolder>$(NapaVanillaBinplacePath)</DestinationFolder>
<DestinationFileName>$(TargetName).node</DestinationFileName>
</Binplace>
</ItemGroup>
<Import Project="$(PACKAGESROOT)\NodeJs.Library\exports.props" />
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,276 @@
// Node uses deprecated V8 APIs
#pragma warning(push)
#pragma warning(disable: 4996)
#include <node.h>
#pragma warning(pop)
#include "container-wrap.h"
#include "napa-runtime.h"
#include "napa/v8-helpers.h"
#include "node-async-handler.h"
#include <sstream>
#include <vector>
using namespace napa::binding;
v8::Persistent<v8::Function> ContainerWrap::_constructor;
ContainerWrap::ContainerWrap(std::unique_ptr<napa::runtime::Container> container) :
_container(std::move(container))
{
}
void ContainerWrap::Init(v8::Isolate* isolate)
{
// Prepare constructor template
v8::Local<v8::FunctionTemplate> functionTemplate = v8::FunctionTemplate::New(isolate, NewCallback);
functionTemplate->SetClassName(
v8::String::NewFromUtf8(isolate, "ContainerWrap", v8::NewStringType::kNormal).ToLocalChecked());
functionTemplate->InstanceTemplate()->SetInternalFieldCount(1);
// Prototype
NODE_SET_PROTOTYPE_METHOD(functionTemplate, "load", Load);
NODE_SET_PROTOTYPE_METHOD(functionTemplate, "loadSync", LoadSync);
NODE_SET_PROTOTYPE_METHOD(functionTemplate, "loadFile", LoadFile);
NODE_SET_PROTOTYPE_METHOD(functionTemplate, "loadFileSync", LoadFileSync);
NODE_SET_PROTOTYPE_METHOD(functionTemplate, "run", Run);
NODE_SET_PROTOTYPE_METHOD(functionTemplate, "runSync", RunSync);
// Set constructor method
_constructor.Reset(isolate, functionTemplate->GetFunction(isolate->GetCurrentContext()).ToLocalChecked());
}
void ContainerWrap::NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto isolate = args.GetIsolate();
int argc = args.Length();
std::vector<v8::Local<v8::Value>> argv;
argv.reserve(argc);
for (int i = 0; i < argc; ++i)
{
argv.emplace_back(args[i]);
}
v8::Local<v8::Function> cons = v8::Local<v8::Function>::New(isolate, _constructor);
v8::MaybeLocal<v8::Object> instance = cons->NewInstance(
isolate->GetCurrentContext(),
argc,
argv.data());
if (!instance.IsEmpty())
{
args.GetReturnValue().Set(instance.ToLocalChecked());
}
}
void ContainerWrap::NewCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto isolate = args.GetIsolate();
auto context = isolate->GetCurrentContext();
std::stringstream ss;
if (args.Length() > 0)
{
CHECK_ARG(isolate, args[0]->IsObject(), "first argument to createContainer must be an object");
v8::Local<v8::Object> settingsObj = args[0]->ToObject(context).ToLocalChecked();
auto settingsMap = napa::v8_helpers::V8ObjectToMap<std::string>(isolate, settingsObj);
for (const auto& kv : settingsMap)
{
ss << " --" << kv.first << " " << kv.second;
}
}
auto obj = new ContainerWrap(std::make_unique<napa::runtime::Container>(ss.str()));
obj->Wrap(args.This());
args.GetReturnValue().Set(args.This());
}
void ContainerWrap::Load(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto isolate = args.GetIsolate();
CHECK_ARG(isolate, args[0]->IsString(), "first parameter to container.load must be the javascript source");
CHECK_ARG(isolate, args[1]->IsFunction(), "second parameter to container.load must be the callback");
v8::String::Utf8Value source(args[0]->ToString());
auto callback = v8::Local<v8::Function>::Cast(args[1]);
auto handler = NodeAsyncHandler<NapaResponseCode>::New(
isolate,
callback,
[isolate](const NapaResponseCode& responseCode) {
std::vector<v8::Local<v8::Value>> res;
res.push_back(v8::Uint32::NewFromUnsigned(isolate, responseCode));
return res;
}
);
auto wrap = ObjectWrap::Unwrap<ContainerWrap>(args.Holder());
wrap->_container->Load(*source, [handler](NapaResponseCode responseCode) {
handler->DispatchCallback(std::move(responseCode));
});
}
void ContainerWrap::LoadSync(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto isolate = args.GetIsolate();
CHECK_ARG(isolate, args[0]->IsString(), "first parameter to container.loadSync must be the javascript source");
v8::String::Utf8Value source(args[0]->ToString());
auto wrap = ObjectWrap::Unwrap<ContainerWrap>(args.Holder());
wrap->_container->LoadSync(*source);
}
void ContainerWrap::LoadFile(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto isolate = args.GetIsolate();
CHECK_ARG(isolate, args[0]->IsString(), "first parameter to container.loadFile must be the javascript file");
CHECK_ARG(isolate, args[1]->IsFunction(), "second parameter to container.loadFile must be the callback");
v8::String::Utf8Value file(args[0]->ToString());
auto callback = v8::Local<v8::Function>::Cast(args[1]);
auto handler = NodeAsyncHandler<NapaResponseCode>::New(
isolate,
callback,
[isolate](const NapaResponseCode& responseCode) {
std::vector<v8::Local<v8::Value>> res;
res.push_back(v8::Uint32::NewFromUnsigned(isolate, responseCode));
return res;
}
);
auto wrap = ObjectWrap::Unwrap<ContainerWrap>(args.Holder());
wrap->_container->LoadFile(*file, [handler](NapaResponseCode responseCode) {
handler->DispatchCallback(std::move(responseCode));
});
}
void ContainerWrap::LoadFileSync(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto isolate = args.GetIsolate();
CHECK_ARG(isolate, args[0]->IsString(), "first parameter to container.loadFileSync must be the javascript file");
v8::String::Utf8Value file(args[0]->ToString());
auto wrap = ObjectWrap::Unwrap<ContainerWrap>(args.Holder());
wrap->_container->LoadFileSync(*file);
}
void ContainerWrap::Run(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto isolate = args.GetIsolate();
auto context = isolate->GetCurrentContext();
CHECK_ARG(isolate, args[0]->IsString(), "first parameter to container.run must be the function name");
CHECK_ARG(isolate, args[1]->IsArray(), "second parameter to container.run must be the arguments array");
CHECK_ARG(isolate, args[2]->IsFunction(), "third parameter to container.run must be the callback");
if (args.Length() > 3)
{
CHECK_ARG(isolate, args[3]->IsUint32(), "forth parameter to container.run must be the timeout");
}
v8::String::Utf8Value func(args[0]->ToString());
auto runArgs = napa::v8_helpers::V8ArrayToVector<std::string>(isolate, v8::Local<v8::Array>::Cast(args[1]));
auto callback = v8::Local<v8::Function>::Cast(args[2]);
auto handler = NodeAsyncHandler<napa::runtime::Response>::New(
isolate,
callback,
[isolate](const napa::runtime::Response& response) {
return std::vector<v8::Local<v8::Value>>({
v8::Uint32::NewFromUnsigned(isolate, response.code),
v8::String::NewFromUtf8(
isolate,
response.error.c_str(),
v8::NewStringType::kNormal).ToLocalChecked(),
v8::String::NewFromUtf8(
isolate,
response.returnValue.c_str(),
v8::NewStringType::kNormal).ToLocalChecked()
});
}
);
auto wrap = ObjectWrap::Unwrap<ContainerWrap>(args.Holder());
if (args.Length() > 3)
{
wrap->_container->Run(
*func,
runArgs,
[handler](napa::runtime::Response response) { handler->DispatchCallback(std::move(response)); },
args[3]->Uint32Value(context).FromJust()); // timeout
}
else
{
wrap->_container->Run(
*func,
runArgs,
[handler](napa::runtime::Response response) { handler->DispatchCallback(std::move(response)); });
}
}
void ContainerWrap::RunSync(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto isolate = args.GetIsolate();
auto context = isolate->GetCurrentContext();
CHECK_ARG(isolate, args[0]->IsString(), "first parameter to container.runSync must be the function name");
CHECK_ARG(isolate, args[1]->IsArray(), "second parameter to container.runSync must be the arguments array");
if (args.Length() > 2)
{
CHECK_ARG(isolate, args[2]->IsUint32(), "third parameter to container.runSync must be the timeout");
}
v8::String::Utf8Value func(args[0]->ToString());
auto runArgs = napa::v8_helpers::V8ArrayToVector<std::string>(isolate, v8::Local<v8::Array>::Cast(args[1]));
auto wrap = ObjectWrap::Unwrap<ContainerWrap>(args.Holder());
napa::runtime::Response response;
if (args.Length() > 2)
{
// Call with provided timeout
response = wrap->_container->RunSync(*func, runArgs, args[2]->Uint32Value(context).FromJust());
}
else
{
response = wrap->_container->RunSync(*func, runArgs);
}
auto returnObj = v8::Object::New(isolate);
returnObj->CreateDataProperty(
context,
v8::String::NewFromUtf8(isolate, "code", v8::NewStringType::kNormal).ToLocalChecked(),
v8::Uint32::NewFromUnsigned(isolate, response.code));
returnObj->CreateDataProperty(
context,
v8::String::NewFromUtf8(isolate, "error", v8::NewStringType::kNormal).ToLocalChecked(),
v8::String::NewFromUtf8(isolate, response.error.c_str(), v8::NewStringType::kNormal).ToLocalChecked());
returnObj->CreateDataProperty(
context,
v8::String::NewFromUtf8(isolate, "returnValue", v8::NewStringType::kNormal).ToLocalChecked(),
v8::String::NewFromUtf8(isolate, response.returnValue.c_str(), v8::NewStringType::kNormal).ToLocalChecked());
args.GetReturnValue().Set(returnObj);
}

Просмотреть файл

@ -0,0 +1,47 @@
#ifndef NAPA_BINDING_CONTAINER_WRAP_H
#define NAPA_BINDING_CONTAINER_WRAP_H
#include <memory>
#include <node_object_wrap.h>
// Forward declare container.
namespace napa
{
namespace runtime
{
class Container;
}
}
namespace napa
{
namespace binding
{
/// <summary>A node object wrap to expose container APIs to node. </summary>
class ContainerWrap : public node::ObjectWrap
{
public:
static void Init(v8::Isolate* isolate);
static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args);
private:
static v8::Persistent<v8::Function> _constructor;
std::unique_ptr<napa::runtime::Container> _container;
explicit ContainerWrap(std::unique_ptr<napa::runtime::Container> container);
static void NewCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Load(const v8::FunctionCallbackInfo<v8::Value>& args);
static void LoadSync(const v8::FunctionCallbackInfo<v8::Value>& args);
static void LoadFile(const v8::FunctionCallbackInfo<v8::Value>& args);
static void LoadFileSync(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Run(const v8::FunctionCallbackInfo<v8::Value>& args);
static void RunSync(const v8::FunctionCallbackInfo<v8::Value>& args);
};
}
}
#endif

Просмотреть файл

@ -0,0 +1,140 @@
#ifndef NAPA_BINDING_NODE_ASYNC_HANDLER_H
#define NAPA_BINDING_NODE_ASYNC_HANDLER_H
#include <uv.h>
#include <vector>
#include <functional>
#include <utility>
namespace napa
{
namespace binding
{
/// <summary>
/// Helper class to facilitate dispatcing async calls fron node addons.
/// The handler stores the provided V8 callback function persistently which enables calling
/// it when the underlying execution callback is called.
/// The dispatcing of the V8 callback happend on the node main thread.
/// </summary>
template <typename ResponseType>
class NodeAsyncHandler
{
public:
typedef std::function<std::vector<v8::Local<v8::Value>>(const ResponseType&)> CallbackArgsGeneratorFunction;
/// <summary>Non copyable and non movable. </summary>
NodeAsyncHandler(const NodeAsyncHandler&) = delete;
NodeAsyncHandler& operator=(const NodeAsyncHandler&) = delete;
NodeAsyncHandler(NodeAsyncHandler&&) = delete;
NodeAsyncHandler& operator=(NodeAsyncHandler&&) = delete;
/// <summary>
/// Factory method to create the handler.
/// Releasing the handler resources happens automatically when dispatcing is done.
/// In cases when an error occurs before a call to DispatchCallback was made, the user
/// should invoke the Release method.
/// </summary>
static NodeAsyncHandler<ResponseType>* New(
v8::Isolate* isolate,
const v8::Local<v8::Function>& callback,
CallbackArgsGeneratorFunction argsGeneratorFunc);
/// <summary>Releasing handler resources </summary>
static void Release(NodeAsyncHandler<ResponseType>* handler);
/// <summary>Invoking the stored V8 callback on node main thread. </summary>
void DispatchCallback(ResponseType&& response);
private:
NodeAsyncHandler() {}
// Prevent users from deleting the handler.
~NodeAsyncHandler() {}
static void AsyncCompletionCallback(uv_async_t* asyncHandle);
v8::Isolate* _isolate;
v8::Persistent<v8::Function> _callback;
CallbackArgsGeneratorFunction _argsGeneratorFunc;
uv_async_t _asyncHandle;
ResponseType _response;
};
template <typename ResponseType>
NodeAsyncHandler<ResponseType>* NodeAsyncHandler<ResponseType>::New(
v8::Isolate* isolate,
const v8::Local<v8::Function>& callback,
CallbackArgsGeneratorFunction argsGeneratorFunc)
{
auto handler = new NodeAsyncHandler<ResponseType>();
handler->_isolate = isolate;
// Store the callback in a persistent function.
handler->_callback.Reset(isolate, callback);
// Store the argument generator function.
handler->_argsGeneratorFunc = std::move(argsGeneratorFunc);
// Store the handler to enable retrieving it in the uv callbacks.
handler->_asyncHandle.data = handler;
// Initialize the uv async handle and set the callback function
// that will be executed on the node main thread.
uv_async_init(uv_default_loop(), &(handler->_asyncHandle), AsyncCompletionCallback);
return handler;
}
template <typename ResponseType>
void NodeAsyncHandler<ResponseType>::Release(NodeAsyncHandler<ResponseType>* handler)
{
// Free the persistent function callback.
handler->_callback.Reset();
// De-allocate the memory.
delete handler;
}
template <typename ResponseType>
void NodeAsyncHandler<ResponseType>::DispatchCallback(ResponseType&& response)
{
_response = std::forward<ResponseType>(response);
// Defer JS callback to the node main thread.
uv_async_send(&_asyncHandle);
}
template <typename ResponseType>
void NodeAsyncHandler<ResponseType>::AsyncCompletionCallback(uv_async_t* asyncHandle)
{
auto handler = reinterpret_cast<NodeAsyncHandler<ResponseType>*>(asyncHandle->data);
auto isolate = handler->_isolate;
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
auto callback = v8::Local<v8::Function>::New(isolate, handler->_callback);
auto args = handler->_argsGeneratorFunc(handler->_response);
// Call the user provided Javascript callback.
callback->Call(context, context->Global(), static_cast<int>(args.size()), args.data());
// Cleanup
uv_close(reinterpret_cast<uv_handle_t*>(asyncHandle), [](uv_handle_t* asyncHandle) {
auto handler = reinterpret_cast<NodeAsyncHandler<ResponseType>*>(asyncHandle->data);
Release(handler);
});
}
}
}
#endif // NAPA_BINDING_NODE_ASYNC_HANDLER_H

Просмотреть файл

@ -1,2 +1,8 @@
// This is the C style header file for Napa app engine.
// We are going to build binding on top of it. (Asi)
#ifndef NAPA_APP_C_H
#define NAPA_APP_C_H
#include "napa-runtime-c.h"
#endif

Просмотреть файл

@ -6,18 +6,16 @@
typedef NapaResponseCode napa_response_code;
/// <summary>
/// Represents napa response.
/// If response code is success then the output has a JSON format, otherwise the
/// output is the error message
/// </summary>
/// <summary>Represents napa response. </summary>
typedef struct {
napa_response_code code;
napa_string_ref output;
napa_string_ref error;
napa_string_ref return_value;
} napa_container_response;
/// <summary>Callback signature</summary>
typedef void(*napa_container_callback)(napa_container_response response, void* state);
/// <summary>Callback signatures</summary>
typedef void(*napa_container_run_callback)(napa_container_response response, void* context);
typedef void(*napa_container_load_callback)(napa_response_code code, void* context);
/// <summary>
/// Container handle type.
@ -54,17 +52,39 @@ EXTERN_C NAPA_API napa_response_code napa_container_set_global_value(
napa_string_ref key,
void* value);
/// <summary>Loads the content of the provided file into the container</summary>
/// <summary>Loads the content of the provided file into the container asynchronously</summary>
/// <param name="handle">The container handle</param>
/// <param name="file">The path to the JavaScript file</param>
EXTERN_C NAPA_API napa_response_code napa_container_load_file(
/// <param name="callback">A callback that is triggered when loading is done</param>
/// <param name="context">An opaque pointer that is passed back in the callback</param>
EXTERN_C NAPA_API void napa_container_load_file(
napa_container_handle handle,
napa_string_ref file,
napa_container_load_callback callback,
void* context);
/// <summary>Loads the content of the provided file into the container synchronously</summary>
/// <param name="handle">The container handle</param>
/// <param name="file">The path to the JavaScript file</param>
EXTERN_C NAPA_API napa_response_code napa_container_load_file_sync(
napa_container_handle handle,
napa_string_ref file);
/// <summary>Loads the provided source code into the container</summary>
/// <summary>Loads the provided source code into the container asynchronously</summary>
/// <param name="handle">The container handle</param>
/// <param name="source">The JavaScript source code</param>
EXTERN_C NAPA_API napa_response_code napa_container_load(
/// <param name="callback">A callback that is triggered when loading is done</param>
/// <param name="context">An opaque pointer that is passed back in the callback</param>
EXTERN_C NAPA_API void napa_container_load(
napa_container_handle handle,
napa_string_ref source,
napa_container_load_callback callback,
void* context);
/// <summary>Loads the provided source code into the container synchronously</summary>
/// <param name="handle">The container handle</param>
/// <param name="source">The JavaScript source code</param>
EXTERN_C NAPA_API napa_response_code napa_container_load_sync(
napa_container_handle handle,
napa_string_ref source);
@ -74,14 +94,14 @@ EXTERN_C NAPA_API napa_response_code napa_container_load(
/// <param name="argc">The number of arguments that are to be passed to the function</param>
/// <param name="argv">The arguments</param>
/// <param name="callback">A callback that is triggered when execution is done</param>
/// <param name="context">An opaque pointer that is returned with the callback</param>
/// <param name="context">An opaque pointer that is passed back in the callback</param>
/// <param name="timeout">Timeout in milliseconds - Use 0 for inifinite</param>
EXTERN_C NAPA_API napa_response_code napa_container_run(
EXTERN_C NAPA_API void napa_container_run(
napa_container_handle handle,
napa_string_ref func,
size_t argc,
napa_string_ref argv[],
napa_container_callback callback,
napa_container_run_callback callback,
void* context,
uint32_t timeout);

Просмотреть файл

@ -1,5 +1,5 @@
#ifndef NapaRuntime_H
#define NapaRuntime_H
#ifndef NAPA_RUNTIME_H
#define NAPA_RUNTIME_H
#include "napa-runtime-c.h"
@ -8,86 +8,38 @@
#include <string>
#include <vector>
namespace napa
{
namespace runtime
{
/// <summary> Initialize napa runtime with global scope settings </summary>
/// <see cref="napa_initialize" />
inline NapaResponseCode Initialize(const std::string& settings)
{
return napa_initialize(STD_STRING_TO_NAPA_STRING_REF(settings));
}
/// <summary> Initialize napa runtime using console provided arguments </summary>
/// <see cref="napa_initialize_from_console" />
inline NapaResponseCode InitializeFromConsole(int argc, char* argv[])
{
return napa_initialize_from_console(argc, argv);
}
/// <summary> Shut down napa runtime </summary>
/// <see cref="napa_shutdown" />
inline NapaResponseCode Shutdown()
{
return napa_shutdown();
}
/// <summary>Response</summary>
struct Response
{
/// <summary>Response code.</summary>
Response() : code(NAPA_RESPONSE_UNDEFINED)
{
}
Response(napa_container_response response) :
code(response.code),
error(NAPA_STRING_REF_TO_STD_STRING(response.error)),
returnValue(NAPA_STRING_REF_TO_STD_STRING(response.return_value))
{
}
/// <summary>Response code </summary>
NapaResponseCode code;
/// <summary>Napa output. Json format in case of success, error message otherwise</summary>
std::string output;
/// <summary>Error message. Empty when response code is success. </summary>
std::string error;
/// <summary>Napa return value </summary>
std::string returnValue;
};
/// <summary>Response callback signature</summary>
typedef std::function<void(Response)> ResponseCallback;
/// <summary>helper classes and functions in internal namespace</summary>
namespace internal
{
struct RunCompletionContext
{
RunCompletionContext(ResponseCallback callback)
: callback(std::move(callback))
{
}
/// <summary>Non copyable and non movable.</summary>
RunCompletionContext(const RunCompletionContext&) = delete;
RunCompletionContext& operator=(const RunCompletionContext&) = delete;
RunCompletionContext(RunCompletionContext&&) = delete;
RunCompletionContext& operator=(RunCompletionContext&&) = delete;
/// <summary>User callback.</summary>
ResponseCallback callback;
};
inline void RunCompletionHandler(napa_container_response response, void* context)
{
std::unique_ptr<RunCompletionContext> completionContext(
reinterpret_cast<RunCompletionContext*>(context));
completionContext->callback(
Response{ response.code, NAPA_STRING_REF_TO_STD_STRING(response.output) });
}
inline std::vector<napa_string_ref> ConvertToNapaRuntimeArgs(const std::vector<std::string>& args)
{
std::vector<napa_string_ref> res;
res.reserve(args.size());
for (const auto& arg : args)
{
res.emplace_back(STD_STRING_TO_NAPA_STRING_REF(arg));
}
return res;
}
}
typedef std::function<void(Response)> RunCallback;
typedef std::function<void(NapaResponseCode)> LoadCallback;
/// <summary> C++ class wrapper around napa runtime C APIs </summary>
class Container
@ -104,29 +56,35 @@ namespace runtime
/// <summary> Sets a value in container scope </summary>
/// <param name="key"> A unique identifier for the value </param>
/// <param name="value"> The value </param>
/// <see cref="NapaRuntime_SetGlobalValue" />
NapaResponseCode SetGlobalValue(const std::string& key, void* value);
/// <summary> Loads a JS file into the container </summary>
/// <summary> Loads a JS file into the container asynchronously </summary>
/// <param name="file"> The JS file </param>
/// <see cref="NapaRuntime_LoadFile" />
NapaResponseCode LoadFile(const std::string& file);
/// <param name="callback">A callback that is triggered when loading is done</param>
void LoadFile(const std::string& file, LoadCallback callback);
/// <summary> Loads a JS source into the container </summary>
/// <summary> Loads a JS file into the container synchronously </summary>
/// <param name="file"> The JS file </param>
NapaResponseCode LoadFileSync(const std::string& file);
/// <summary> Loads a JS source into the container asynchronously </summary>
/// <param name="source"> The JS source </param>
/// <see cref="NapaRuntime_Load" />
NapaResponseCode Load(const std::string& source);
/// <param name="callback">A callback that is triggered when loading is done</param>
void Load(const std::string& source, LoadCallback callback);
/// <summary> Loads a JS source into the container synchronously </summary>
/// <param name="source"> The JS source </param>
NapaResponseCode LoadSync(const std::string& source);
/// <summary> Runs a pre-loaded JS function asynchronously </summary>
/// <param name="func">The name of the function to run</param>
/// <param name="args">The arguments to the function</param>
/// <param name="callback">A callback that is triggered when execution is done</param>
/// <param name="timeout">Timeout in milliseconds - default is inifinite</param>
/// <see cref="NapaRuntime_Run" />
void Run(
const std::string& func,
const std::vector<std::string>& args,
ResponseCallback callback,
RunCallback callback,
uint32_t timeout = 0);
/// <summary> Runs a pre-loaded JS function synchronously </summary>
@ -142,6 +100,70 @@ namespace runtime
napa_container_handle _handle;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Implementation starts here
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Initialize napa runtime with global scope settings </summary>
inline NapaResponseCode Initialize(const std::string& settings)
{
return napa_initialize(STD_STRING_TO_NAPA_STRING_REF(settings));
}
/// <summary> Initialize napa runtime using console provided arguments </summary>
inline NapaResponseCode InitializeFromConsole(int argc, char* argv[])
{
return napa_initialize_from_console(argc, argv);
}
/// <summary> Shut down napa runtime </summary>
inline NapaResponseCode Shutdown()
{
return napa_shutdown();
}
/// <summary>Helper classes and functions in internal namespace</summary>
namespace internal
{
template <typename CallbackType>
struct AsyncCompletionContext
{
AsyncCompletionContext(CallbackType&& callback)
: callback(std::forward<CallbackType>(callback))
{
}
/// <summary>Non copyable and non movable.</summary>
AsyncCompletionContext(const AsyncCompletionContext&) = delete;
AsyncCompletionContext& operator=(const AsyncCompletionContext&) = delete;
AsyncCompletionContext(AsyncCompletionContext&&) = delete;
AsyncCompletionContext& operator=(AsyncCompletionContext&&) = delete;
/// <summary>User callback.</summary>
CallbackType callback;
};
template <typename CallbackType, typename ResponseType>
inline void CompletionHandler(ResponseType response, void* context)
{
std::unique_ptr<AsyncCompletionContext<CallbackType>> completionContext(
reinterpret_cast<AsyncCompletionContext<CallbackType>*>(context));
completionContext->callback(response);
}
inline std::vector<napa_string_ref> ConvertToNapaRuntimeArgs(const std::vector<std::string>& args)
{
std::vector<napa_string_ref> res;
res.reserve(args.size());
for (const auto& arg : args)
{
res.emplace_back(STD_STRING_TO_NAPA_STRING_REF(arg));
}
return res;
}
}
inline Container::Container(const std::string& settings)
{
@ -160,32 +182,54 @@ namespace runtime
return napa_container_set_global_value(_handle, STD_STRING_TO_NAPA_STRING_REF(key), value);
}
inline NapaResponseCode Container::LoadFile(const std::string& file)
inline void Container::LoadFile(const std::string& file, LoadCallback callback)
{
return napa_container_load_file(_handle, STD_STRING_TO_NAPA_STRING_REF(file));
auto context = new internal::AsyncCompletionContext<LoadCallback>(std::move(callback));
napa_container_load_file(
_handle,
STD_STRING_TO_NAPA_STRING_REF(file),
internal::CompletionHandler<LoadCallback, NapaResponseCode>,
context);
}
inline NapaResponseCode Container::Load(const std::string& source)
inline NapaResponseCode Container::LoadFileSync(const std::string& file)
{
return napa_container_load(_handle, STD_STRING_TO_NAPA_STRING_REF(source));
return napa_container_load_file_sync(_handle, STD_STRING_TO_NAPA_STRING_REF(file));
}
inline void Container::Load(const std::string& source, LoadCallback callback)
{
auto context = new internal::AsyncCompletionContext<LoadCallback>(std::move(callback));
napa_container_load(
_handle,
STD_STRING_TO_NAPA_STRING_REF(source),
internal::CompletionHandler<LoadCallback, napa_response_code>,
context);
}
inline NapaResponseCode Container::LoadSync(const std::string& source)
{
return napa_container_load_sync(_handle, STD_STRING_TO_NAPA_STRING_REF(source));
}
inline void Container::Run(
const std::string& func,
const std::vector<std::string>& args,
ResponseCallback callback,
RunCallback callback,
uint32_t timeout)
{
auto argv = internal::ConvertToNapaRuntimeArgs(args);
auto context = new internal::RunCompletionContext(std::move(callback));
auto context = new internal::AsyncCompletionContext<RunCallback>(std::move(callback));
napa_container_run(
_handle,
STD_STRING_TO_NAPA_STRING_REF(func),
argv.size(),
argv.data(),
internal::RunCompletionHandler,
internal::CompletionHandler<RunCallback, napa_container_response>,
context,
timeout);
}
@ -204,10 +248,10 @@ namespace runtime
argv.data(),
timeout);
return Response { response.code, NAPA_STRING_REF_TO_STD_STRING(response.output) };
return Response(response);
}
}
}
#endif // NapaRuntime_H
#endif // NAPA_RUNTIME_H

Просмотреть файл

@ -3,6 +3,7 @@
#include "stddef.h"
#include "stdint.h"
#include "string.h"
/// <summary>Simple non ownning string. Should only be used for binding.</summary>
typedef struct {
@ -10,9 +11,11 @@ typedef struct {
size_t size;
} napa_string_ref;
#define CREATE_NAPA_STRING_REF2(data, size) (napa_string_ref { (data), (size) })
#define CREATE_NAPA_STRING_REF(data) CREATE_NAPA_STRING_REF2(data, strlen(data))
#ifdef __cplusplus
#define CREATE_NAPA_STRING_REF(data, size) (napa_string_ref { (data), (size) })
#define STD_STRING_TO_NAPA_STRING_REF(str) (napa_string_ref { (str).data(), (str).size() })
#define NAPA_STRING_REF_TO_STD_STRING(str) (std::string((str).data, (str).size))

Просмотреть файл

@ -16,4 +16,5 @@
#error NAPA_RESPONSE_CODE_DEF must be defined before including response_code.inc
#endif
NAPA_RESPONSE_CODE_DEF( SUCCESS, "Success")
NAPA_RESPONSE_CODE_DEF( SUCCESS, "Success"),
NAPA_RESPONSE_CODE_DEF( UNDEFINED, "Undefined")

93
inc/napa/v8-helpers.h Normal file
Просмотреть файл

@ -0,0 +1,93 @@
#ifndef NAPA_V8_HELPERS_H
#define NAPA_V8_HELPERS_H
#include <sstream>
#include <unordered_map>
#define CHECK_ARG_COMMON(isolate, expression, message, result, function, line) \
if (!(expression)) \
{ \
std::stringstream temp; \
temp << function << ":" << line << " -- " << message; \
isolate->ThrowException(v8::Exception::TypeError( \
v8::String::NewFromUtf8(isolate, temp.str().c_str(), v8::NewStringType::kNormal).ToLocalChecked())); \
return result; \
}
#define CHECK_ARG(isolate, expression, message) \
CHECK_ARG_COMMON(isolate, expression, message, /* empty */, __FUNCTION__, __LINE__)
#define CHECK_ARG_WITH_RETURN(isolate, expression, message, result) \
CHECK_ARG_COMMON(isolate, expression, message, result, __FUNCTION__, __LINE__)
namespace napa
{
namespace v8_helpers
{
template <typename T>
inline T To(const v8::Local<v8::Value>& value)
{
static_assert(sizeof(T) == -1, "No specilization exists for this type");
}
template <>
inline std::string To(const v8::Local<v8::Value>& value)
{
v8::String::Utf8Value utf8Value(value);
return *utf8Value;
}
template <typename ValueType>
inline std::unordered_map<std::string, ValueType> V8ObjectToMap(
v8::Isolate* isolate,
const v8::Local<v8::Object>& obj)
{
auto context = isolate->GetCurrentContext();
std::unordered_map<std::string, ValueType> res;
auto maybeProps = obj->GetOwnPropertyNames(context);
if (!maybeProps.IsEmpty())
{
auto props = maybeProps.ToLocalChecked();
res.reserve(props->Length());
for (uint32_t i = 0; i < props->Length(); i++)
{
auto key = props->Get(context, i).ToLocalChecked();
auto value = obj->Get(context, key).ToLocalChecked();
v8::String::Utf8Value keyString(key->ToString());
res.emplace(*keyString, To<ValueType>(value));
}
}
return res;
}
template <typename ValueType>
inline std::vector<ValueType> V8ArrayToVector(
v8::Isolate* isolate,
const v8::Local<v8::Array>& array)
{
auto context = isolate->GetCurrentContext();
std::vector<ValueType> res;
res.reserve(array->Length());
for (uint32_t i = 0; i < array->Length(); i++)
{
res.emplace_back(To<ValueType>(array->Get(context, i).ToLocalChecked()));
}
return res;
}
}
}
#endif

Просмотреть файл

@ -39,5 +39,11 @@
<ClCompile Include="runtime.cpp" />
</ItemGroup>
<ItemGroup>
<Binplace Include="$(OutputPath)\$(TargetFileName)">
<DestinationFolder>$(NapaVanillaBinplacePath)</DestinationFolder>
</Binplace>
</ItemGroup>
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.targets" />
</Project>

Просмотреть файл

@ -2,9 +2,14 @@
#include "container.h"
#include <thread>
#include <iostream>
napa_container_handle napa_container_create()
{
std::cout << "napa_container_create()" << std::endl;
return reinterpret_cast<napa_container_handle>(new napa::runtime::internal::Container());
}
@ -12,6 +17,9 @@ napa_response_code napa_container_init(
napa_container_handle handle,
napa_string_ref settings)
{
std::cout << "napa_container_init()" << std::endl;
std::cout << "\tsettings: " << settings.data << std::endl;
return NAPA_RESPONSE_SUCCESS;
}
@ -20,43 +28,122 @@ napa_response_code napa_container_set_global_value(
napa_string_ref key,
void* value)
{
std::cout << "napa_container_set_global_value()" << std::endl;
std::cout << "\tkey: " << key.data << std::endl;
return NAPA_RESPONSE_SUCCESS;
}
napa_response_code napa_container_load_file(
void napa_container_load_file(
napa_container_handle handle,
napa_string_ref file,
napa_container_load_callback callback,
void* context)
{
std::cout << "napa_container_load_file()" << std::endl;
std::cout << "\tfile: " << file.data << std::endl;
// Mock async response
std::thread([callback, context]() {
std::this_thread::sleep_for(std::chrono::seconds(2));
callback(NAPA_RESPONSE_SUCCESS, context);
}).detach();
}
napa_response_code napa_container_load_file_sync(
napa_container_handle handle,
napa_string_ref file)
{
std::cout << "napa_container_load_file_sync()" << std::endl;
std::cout << "\tfile: " << file.data << std::endl;
return NAPA_RESPONSE_SUCCESS;
}
napa_response_code napa_container_load(
void napa_container_load(
napa_container_handle handle,
napa_string_ref source,
napa_container_load_callback callback,
void* context)
{
std::cout << "napa_container_load()" << std::endl;
std::cout << "\tsource: " << source.data << std::endl;
// Mock async response
std::thread([callback, context]() {
std::this_thread::sleep_for(std::chrono::seconds(2));
callback(NAPA_RESPONSE_SUCCESS, context);
}).detach();
}
napa_response_code napa_container_load_sync(
napa_container_handle handle,
napa_string_ref source)
{
std::cout << "napa_container_load_sync()" << std::endl;
std::cout << "\tsource: " << source.data << std::endl;
return NAPA_RESPONSE_SUCCESS;
}
napa_response_code napa_container_run(
void napa_container_run(
napa_container_handle handle,
napa_string_ref func,
uint32_t argc,
size_t argc,
napa_string_ref argv[],
napa_container_callback callback,
napa_container_run_callback callback,
void* context,
uint32_t timeout)
{
return NAPA_RESPONSE_SUCCESS;
std::cout << "napa_container_run()" << std::endl;
std::cout << "\tfunc: " << func.data << std::endl;
std::cout << "\targc: " << argc << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "\t\t[" << i << "] " << argv[i].data << std::endl;
}
std::cout << "\ttimeout: " << timeout << std::endl;
// Mock async response
std::thread([callback, context]() {
std::this_thread::sleep_for(std::chrono::seconds(2));
napa_container_response response;
response.code = NAPA_RESPONSE_SUCCESS;
response.error = CREATE_NAPA_STRING_REF("");
response.return_value = CREATE_NAPA_STRING_REF("{\"score\":2412}");
callback(response, context);
}).detach();
}
napa_container_response napa_container_run_sync(
napa_container_handle handle,
napa_string_ref func,
uint32_t argc,
size_t argc,
napa_string_ref argv[],
uint32_t timeout)
{
return napa_container_response{ NAPA_RESPONSE_SUCCESS, CREATE_NAPA_STRING_REF("", 0) };
std::cout << "napa_container_run_sync()" << std::endl;
std::cout << "\tfunc: " << func.data << std::endl;
std::cout << "\targc: " << argc << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "\t\t[" << i << "] " << argv[i].data << std::endl;
}
std::cout << "\ttimeout: " << timeout << std::endl;
return napa_container_response {
NAPA_RESPONSE_SUCCESS,
CREATE_NAPA_STRING_REF("test"),
CREATE_NAPA_STRING_REF("{\"score\":2412}")
};
}
napa_response_code napa_container_release(napa_container_handle handle)
@ -66,6 +153,9 @@ napa_response_code napa_container_release(napa_container_handle handle)
napa_response_code napa_initialize(napa_string_ref settings)
{
std::cout << "napa_initialize()" << std::endl;
std::cout << "\tsettings: " << settings.data << std::endl;
return NAPA_RESPONSE_SUCCESS;
}
@ -73,11 +163,20 @@ napa_response_code napa_initialize_from_console(
int argc,
char* argv[])
{
std::cout << "napa_initialize_from_console()" << std::endl;
std::cout << "\targc: " << argc << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "\t\t[" << i << "] " << argv[i] << std::endl;
}
return NAPA_RESPONSE_SUCCESS;
}
napa_response_code napa_shutdown()
{
std::cout << "napa_shutdown()" << std::endl;
return NAPA_RESPONSE_SUCCESS;
}
@ -94,6 +193,8 @@ const char* NAPA_RESPONSE_CODE_STRINGS[] = {
const char* napa_response_code_to_string(napa_response_code code)
{
std::cout << "napa_shutdownnapa_response_code_to_string()" << std::endl;
// TODO: assert code is in array boundaries
return NAPA_RESPONSE_CODE_STRINGS[code];