зеркало из https://github.com/microsoft/napajs.git
Merged PR 249141: Use transport context in execute
Use transport context in execute
This commit is contained in:
Родитель
2cb740d3c4
Коммит
93f19e06ca
|
@ -17,4 +17,6 @@
|
|||
<< std::endl; \
|
||||
std::terminate(); \
|
||||
} \
|
||||
} while (false)
|
||||
} while (false)
|
||||
|
||||
#define NAPA_FAIL(message) NAPA_ASSERT(false, message)
|
|
@ -68,6 +68,14 @@ EXTERN_C NAPA_API napa_zone_handle napa_zone_create(napa_string_ref id);
|
|||
/// </remarks>
|
||||
EXTERN_C NAPA_API napa_zone_handle napa_zone_get(napa_string_ref id);
|
||||
|
||||
/// <summary> Retrieves the current zone. </summary>
|
||||
/// <returns> The zone handle if this thread is associated with one, null otherwise. </returns>
|
||||
/// <remarks>
|
||||
/// This function returns a handle that must be release when it's no longer needed.
|
||||
/// Napa keeps track of all zone handles and destroys the zone when all handles have been released.
|
||||
/// </remarks>
|
||||
EXTERN_C NAPA_API napa_zone_handle napa_zone_get_current();
|
||||
|
||||
/// <summary> Releases the zone handle. When all handles for a zone are released the zone is destoryed. </summary>
|
||||
/// <param name="handle"> The zone handle. </param>
|
||||
EXTERN_C NAPA_API napa_response_code napa_zone_release(napa_zone_handle handle);
|
||||
|
|
11
inc/napa.h
11
inc/napa.h
|
@ -112,6 +112,17 @@ namespace napa {
|
|||
return std::unique_ptr<ZoneProxy>(new ZoneProxy(id, handle));
|
||||
}
|
||||
|
||||
/// <summary> Creates a proxy to the current zone, throws if non is associated with this thread. </summary>
|
||||
static std::unique_ptr<ZoneProxy> GetCurrent() {
|
||||
auto handle = napa_zone_get_current();
|
||||
if (!handle) {
|
||||
throw std::runtime_error("The calling thread is not associated with a zone");
|
||||
}
|
||||
|
||||
auto zoneId = NAPA_STRING_REF_TO_STD_STRING(napa_zone_get_id(handle));
|
||||
return std::unique_ptr<ZoneProxy>(new ZoneProxy(std::move(zoneId), handle));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// <summary> Private constructor to create a C++ zone proxy from a C handle. </summary>
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
function __napa_function_dispatcher__(func, args, contextHandle) {
|
||||
var transport = require('napajs').transport;
|
||||
|
||||
var transportContext = transport.createTransportContext(contextHandle);
|
||||
var args = args.map((arg) => { return transport.unmarshall(arg, transportContext); });
|
||||
|
||||
return transport.marshall(func.apply(this, args), transportContext);
|
||||
}
|
||||
|
||||
function __napa_module_dispatcher__(moduleName, functionName, args, contextHandle) {
|
||||
var module = require(moduleName);
|
||||
var func = module[functionName];
|
||||
if (!func) {
|
||||
throw new Error("Cannot find function '" + functionName + "' in module '" + moduleName + "'");
|
||||
}
|
||||
|
||||
return __napa_function_dispatcher__(func, args, contextHandle);
|
||||
}
|
|
@ -2,9 +2,10 @@
|
|||
<Import Project="$(EnvironmentConfig)" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectFile Include="lib\scripts.proj" />
|
||||
<ProjectFile Include="napa\addon.vcxproj" />
|
||||
<ProjectFile Include="node\addon.vcxproj" />
|
||||
<ProjectFile Include="package\create-package.proj" />
|
||||
<ProjectFile Include="package\napajs.proj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(ExtendedTargetsPath)\Traversal.targets" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/// <summary> Barrel is a container that creates boundary for sharing objects across isolates. </summary>
|
||||
/// TODO: @dapeng, implement add-on and connect with this class.
|
||||
declare class Barrel {
|
||||
export declare class Barrel {
|
||||
/// <summary> ID of this barrel. </summary>
|
||||
readonly id: number;
|
||||
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
import * as zone from "./zone";
|
||||
import * as transport from "../transport";
|
||||
|
||||
declare var __in_napa: boolean;
|
||||
|
||||
interface ExecuteRequest {
|
||||
module: string;
|
||||
function: string;
|
||||
arguments: any[];
|
||||
timeout: number;
|
||||
transportContext: transport.TransportContext;
|
||||
}
|
||||
|
||||
/// <summary> Zone consists of Napa isolates. </summary>
|
||||
export class NapaZone implements zone.Zone {
|
||||
private _nativeZone;
|
||||
|
@ -14,11 +23,11 @@ export class NapaZone implements zone.Zone {
|
|||
return this._nativeZone.getId();
|
||||
}
|
||||
|
||||
public load(source: string) : Promise<zone.ResponseCode> {
|
||||
public broadcast(source: string) : Promise<zone.ResponseCode> {
|
||||
if (typeof __in_napa !== undefined) {
|
||||
// Napa does not support async yet, we wrap this sync call in a promise
|
||||
// to provide a uniform API.
|
||||
return Promise.resolve(this._nativeZone.loadSync(source));
|
||||
return Promise.resolve(this._nativeZone.broadcastSync(source));
|
||||
}
|
||||
|
||||
return new Promise<zone.ResponseCode>(resolve => {
|
||||
|
@ -26,20 +35,33 @@ export class NapaZone implements zone.Zone {
|
|||
});
|
||||
}
|
||||
|
||||
public execute(module: string, func: string, args: any[], timeout?: number) : Promise<zone.ResponseValue> {
|
||||
// Convert all arguments to strings
|
||||
args = args.map(x => { return JSON.stringify(x); });
|
||||
public execute(module: string, func: string, args: any[], timeout?: number) : Promise<zone.ResponseValue> {
|
||||
let transportContext: transport.TransportContext = transport.createTransportContext();
|
||||
let request : ExecuteRequest = {
|
||||
module: module,
|
||||
function: func,
|
||||
arguments: args.map(arg => { return transport.marshall(arg, transportContext); }),
|
||||
timeout: timeout,
|
||||
transportContext: transportContext
|
||||
};
|
||||
|
||||
if (typeof __in_napa !== undefined) {
|
||||
// Napa does not support async yet, we wrap this sync call in a promise
|
||||
// to provide a uniform API.
|
||||
return Promise.resolve(this._nativeZone.executeSync(module, func, args, timeout));
|
||||
let response = this._nativeZone.executeSync(request);
|
||||
if (response.code === 0) {
|
||||
transportContext = transport.createTransportContext(response.contextHandle);
|
||||
return Promise.resolve(transport.unmarshall(response.returnValue, transportContext));
|
||||
} else {
|
||||
return Promise.reject(response.errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise<zone.ResponseValue>((resolve, reject) => {
|
||||
this._nativeZone.execute(module, func, args, (response) => {
|
||||
if (response.code === 0) {
|
||||
resolve(response.returnValue);
|
||||
transportContext = transport.createTransportContext(response.contextHandle);
|
||||
resolve(transport.unmarshall(response.returnValue, transportContext));
|
||||
} else {
|
||||
reject(response.errorMessage);
|
||||
}
|
||||
|
@ -47,23 +69,5 @@ export class NapaZone implements zone.Zone {
|
|||
});
|
||||
}
|
||||
|
||||
public executeAll(module: string, func: string, args: any[]) : Promise<zone.ResponseCode> {
|
||||
// Convert all arguments to strings
|
||||
args = args.map(x => { return JSON.stringify(x); });
|
||||
|
||||
if (typeof __in_napa !== undefined) {
|
||||
// Napa does not support async yet, we wrap this sync call in a promise
|
||||
// to provide a uniform API.
|
||||
return Promise.resolve(this._nativeZone.executeAllSync(module, func, args));
|
||||
}
|
||||
|
||||
return new Promise<zone.ResponseCode>(resolve => {
|
||||
this._nativeZone.executeAll(module, func, args, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
public destory(): void {
|
||||
this._nativeZone.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ export class NodeZone implements zone.Zone {
|
|||
return "node";
|
||||
}
|
||||
|
||||
public load(source: string) : Promise<zone.ResponseCode> {
|
||||
public broadcast(source: string) : Promise<zone.ResponseCode> {
|
||||
// TODO @asib: add implementation
|
||||
return undefined;
|
||||
}
|
||||
|
@ -16,13 +16,4 @@ export class NodeZone implements zone.Zone {
|
|||
// TODO @asib: add implementation
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public executeAll(module: string, func: string, args: string[]) : Promise<zone.ResponseCode> {
|
||||
// TODO @asib: add implementation
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public destory(): void {
|
||||
// TODO @asib: add implementation
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,12 +68,10 @@ export function getZone(id: string) : zone.Zone {
|
|||
}
|
||||
|
||||
/// <summary> Returns the current zone. </summary>
|
||||
export let currentZone: zone.Zone = getCurrentZone();
|
||||
|
||||
function getCurrentZone() : zone.Zone {
|
||||
export function getCurrentZone() : zone.Zone {
|
||||
if (typeof __in_napa !== undefined) {
|
||||
return new napa.NapaZone(addon.getCurrentZone());
|
||||
}
|
||||
|
||||
|
||||
return new node.NodeZone();
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ export interface Zone {
|
|||
/// <summary> The zone id. </summary>
|
||||
readonly id: string;
|
||||
|
||||
/// <summary> Loads the source code into the global scope in all zone workers. </summary>
|
||||
/// <summary> Compiles and run the provided source code on all zone workers. </summary>
|
||||
/// <param name="source"> A valid javascript source code. </param>
|
||||
load(source: string) : Promise<ResponseCode>;
|
||||
broadcast(source: string) : Promise<ResponseCode>;
|
||||
|
||||
/// <summary> Executes the function on one of the zone workers. </summary>
|
||||
/// <param name="module"> The module that contains the function to execute. </param>
|
||||
|
@ -30,14 +30,5 @@ export interface Zone {
|
|||
/// <param name="args"> The arguments that will pass to the function. </param>
|
||||
/// <param name="timeout"> The timeout of the execution in milliseconds, defaults to infinity. </param>
|
||||
execute(module: string, func: string, args: any[], timeout?: number) : Promise<ResponseValue>;
|
||||
|
||||
/// <summary> Executes the function on all zone workers. </summary>
|
||||
/// <param name="module"> The module that contains the function to execute. </param>
|
||||
/// <param name="func"> The function to execute. </param>
|
||||
/// <param name="args"> The arguments that will pass to the function. </param>
|
||||
executeAll(module: string, func: string, args: any[]) : Promise<ResponseCode>;
|
||||
|
||||
/// <summary> Destroys the zone, cleaning all resources associated with it. </summary>
|
||||
destory(): void;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(EnvironmentConfig)" />
|
||||
<Import Project="$(NapaBuildRoot)\node\npm.targets" />
|
||||
|
||||
<ItemGroup>
|
||||
<QCustomInput Include="$(NapaBuildRoot)\**\*" />
|
||||
<QCustomInput Include="..\package.json" />
|
||||
<QCustomInput Include="..\readme.md" />
|
||||
<QCustomInput Include="**\*.ts" />
|
||||
<QCustomInput Include="**\*.js" />
|
||||
<QCustomInput Include="tsconfig.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageJsonDirectory>..\</PackageJsonDirectory>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="BuildScripts" AfterTargets="AfterBuild">
|
||||
<CallTarget Targets="NpmInstall" />
|
||||
|
||||
<Message Text="Compile scripts." />
|
||||
<Exec Command="$(TSCompile) --outDir $(IntermediateOutputPath)" />
|
||||
|
||||
<CallTarget Targets="NpmCleanup" />
|
||||
</Target>
|
||||
|
||||
<Import Project="$(ExtendedTargetsPath)\NoTarget.targets" />
|
||||
</Project>
|
|
@ -33,8 +33,8 @@ export interface TransportContext {
|
|||
var addon = require('../../bin/addon');
|
||||
|
||||
/// <summary> Create a transport context. </summary>
|
||||
export function createTransportContext(): TransportContext {
|
||||
return new addon.TransportContextWrap();
|
||||
export function createTransportContext(handle? : memory.Handle): TransportContext {
|
||||
return new addon.TransportContextWrap(handle);
|
||||
}
|
||||
|
||||
/// <summary> Interface for transportable non-built-in JavaScript types. </summary>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"outDir": "./objd/amd64"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
"objd",
|
||||
"obj"
|
||||
]
|
||||
}
|
|
@ -66,6 +66,14 @@ void GetZone(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void GetCurrentZone(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
#ifdef NAPA_MODULE_EXTENSION
|
||||
JS_ASSERT(v8::Isolate::GetCurrent(), false, "napa.getCurrentZone cannot be called from inside napa");
|
||||
#else
|
||||
ZoneWrap::NewInstance(ZoneWrap::ConstructorType::CURRENT, args);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GetLoggingProvider(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
LoggingProviderWrap::NewInstance(args);
|
||||
}
|
||||
|
@ -116,6 +124,7 @@ void InitAll(v8::Local<v8::Object> exports) {
|
|||
|
||||
NAPA_SET_METHOD(exports, "createZone", CreateZone);
|
||||
NAPA_SET_METHOD(exports, "getZone", GetZone);
|
||||
NAPA_SET_METHOD(exports, "getCurrentZone", GetCurrentZone);
|
||||
|
||||
NAPA_SET_METHOD(exports, "getLoggingProvider", GetLoggingProvider);
|
||||
NAPA_SET_METHOD(exports, "getResponseCodeString", GetResponseCodeString);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "node-async-handler.h"
|
||||
|
||||
#include <napa.h>
|
||||
#include <napa-assert.h>
|
||||
#include <napa/v8-helpers.h>
|
||||
|
||||
#include <sstream>
|
||||
|
@ -63,7 +64,9 @@ void ZoneWrap::NewCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|||
|
||||
std::unique_ptr<napa::ZoneProxy> zoneProxy;
|
||||
auto constructorType = static_cast<ConstructorType>(args[0]->Uint32Value(context).FromJust());
|
||||
if (constructorType == ConstructorType::CREATE) {
|
||||
|
||||
switch (constructorType) {
|
||||
case napa::binding::ZoneWrap::ConstructorType::CREATE: {
|
||||
CHECK_ARG(isolate, args[1]->IsString(), "first argument to createZone must be a string");
|
||||
v8::String::Utf8Value zoneId(args[1]->ToString());
|
||||
|
||||
|
@ -80,11 +83,26 @@ void ZoneWrap::NewCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|||
}
|
||||
|
||||
zoneProxy = std::make_unique<napa::ZoneProxy>(*zoneId, ss.str());
|
||||
} else { // ConstructorType::GET
|
||||
break;
|
||||
}
|
||||
case napa::binding::ZoneWrap::ConstructorType::GET: {
|
||||
CHECK_ARG(isolate, args[1]->IsString(), "first argument to getZone must be a string");
|
||||
v8::String::Utf8Value zoneId(args[1]->ToString());
|
||||
|
||||
zoneProxy = napa::ZoneProxy::Get(*zoneId);
|
||||
try {
|
||||
zoneProxy = napa::ZoneProxy::Get(*zoneId);
|
||||
} catch (const std::exception &ex) {
|
||||
JS_ASSERT(isolate, false, ex.what());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case napa::binding::ZoneWrap::ConstructorType::CURRENT: {
|
||||
zoneProxy = napa::ZoneProxy::GetCurrent();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NAPA_FAIL("Non existing constructor type for ZoneWrap");
|
||||
}
|
||||
|
||||
auto obj = new ZoneWrap(std::move(zoneProxy));
|
||||
|
|
|
@ -17,8 +17,9 @@ namespace binding {
|
|||
public:
|
||||
|
||||
enum class ConstructorType : uint32_t {
|
||||
CREATE = 0, // Creates a new zone
|
||||
GET = 1 // Retrieves an existing zone
|
||||
CREATE = 0, // Creates a new zone
|
||||
GET = 1, // Retrieves an existing zone
|
||||
CURRENT = 2 // Retrieves the current zone
|
||||
};
|
||||
|
||||
static void Init(v8::Isolate* isolate);
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
"name": "napajs",
|
||||
"version": "0.1.0",
|
||||
"author": "napajs",
|
||||
"main": "./bin/addon",
|
||||
"types": "./types/napajs.d.ts",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"dependencies": {
|
||||
"@napajs/logger": ">= 0.0.1",
|
||||
"commander": ">= 2.9.0"
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
|
||||
import logger = require('@napajs/logger');
|
||||
|
||||
export declare function initialize(): void;
|
||||
export declare function initialize(settings: string): void;
|
||||
export declare function initialize(settings: object): void;
|
||||
export declare function shutdown(): void;
|
||||
|
||||
type ResponseCode = number;
|
||||
|
||||
export declare class Response {
|
||||
code: ResponseCode;
|
||||
errorMessage: string;
|
||||
returnValue: string;
|
||||
}
|
||||
|
||||
export interface Container {
|
||||
load(source: string, callback: (responseCode: ResponseCode) => void): void;
|
||||
loadSync(source: string): ResponseCode;
|
||||
|
||||
loadFile(file: string, callback: (responseCode: ResponseCode) => void): void;
|
||||
loadFileSync(file: string): ResponseCode;
|
||||
|
||||
run(functionName: string, args: string[], callback: (response: Response) => void, timeout?: number): void;
|
||||
runSync(functionName: string, args: string[], timeout?: number): Response;
|
||||
|
||||
runAll(functionName: string, args: string[], callback: (responseCode: ResponseCode) => void): void;
|
||||
runAllSync(functionName: string, args: string[]): ResponseCode;
|
||||
}
|
||||
|
||||
export declare function createContainer(settings?: object): Container;
|
||||
|
||||
export declare function getLoggingProvider(): logger.LoggingProvider;
|
||||
|
||||
export declare function getResponseCodeString(code: number): string;
|
|
@ -4,11 +4,13 @@
|
|||
|
||||
<ItemGroup>
|
||||
<QCustomProjectReference Include="$(NapaVanillaRoot)\lib\js-core-modules.proj" />
|
||||
<QCustomProjectReference Include="..\lib\scripts.proj" />
|
||||
<QCustomProjectReference Include="..\napa\addon.vcxproj" />
|
||||
<QCustomProjectReference Include="..\node\addon.vcxproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<QCustomInput Include="$(NapaBuildRoot)\**\*" />
|
||||
<QCustomInput Include="$(NapaVanillaRoot)\inc\**\*" />
|
||||
<QCustomInput Include="..\package.json" />
|
||||
<QCustomInput Include="..\README.md" />
|
||||
|
@ -21,28 +23,38 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(NapaBuildRoot)\node\npm.targets" />
|
||||
|
||||
|
||||
<Target Name="CreateNpmPackage" AfterTargets="AfterBuild">
|
||||
<Message Text="Create NPM package." />
|
||||
|
||||
<Copy SourceFiles="..\node\$(O)\addon.node" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="..\napa\$(O)\addon.napa" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="$(NapaVanillaRoot)\src\$(O)\napa.dll" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="$(NapaVanillaRoot)\src\module\core-modules.json" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="napajs.d.ts" DestinationFolder="$(PackageOutPath)\types" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="napa-cl.js" DestinationFolder="$(PackageOutPath)\tools" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="napa-cl.bat" DestinationFolder="$(PackageOutPath)\tools" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="..\package.json" DestinationFolder="$(PackageOutPath)" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="..\README.md" DestinationFolder="$(PackageOutPath)" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="$(NapaVanillaRoot)\src\$(O)\napa.dll" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="$(NapaVanillaRoot)\src\$(O)\napa.lib" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="$(NapaVanillaRoot)\src\module\core-modules.json" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
|
||||
<!-- Core modules -->
|
||||
<ItemGroup>
|
||||
<JsCoreModuleFiles Include="$(NapaVanillaRoot)\lib\$(O)\**\*"/>
|
||||
<JsCoreModuleFiles Include="$(NapaVanillaRoot)\lib\$(O)\**\*.js"/>
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(JsCoreModuleFiles)" DestinationFiles="@(JsCoreModuleFiles->'$(PackageOutPath)\lib\%(RecursiveDir)%(Filename)%(Extension)')" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="@(JsCoreModuleFiles)" DestinationFiles="@(JsCoreModuleFiles->'$(PackageOutPath)\bin\lib\%(RecursiveDir)%(Filename)%(Extension)')" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
|
||||
<!-- Compiled scripts -->
|
||||
<ItemGroup>
|
||||
<IncFiles Include="$(NapaVanillaRoot)\inc\**\*.js"/>
|
||||
<JsFiles Include="..\lib\$(O)\**\*.js"/>
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(JsFiles)" DestinationFiles="@(JsFiles->'$(PackageOutPath)\lib\%(RecursiveDir)%(Filename)%(Extension)')" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
|
||||
<!-- Declarations -->
|
||||
<ItemGroup>
|
||||
<DeclarationFiles Include="..\lib\$(O)\**\*.d.ts"/>
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(DeclarationFiles)" DestinationFiles="@(DeclarationFiles->'$(PackageOutPath)\types\%(RecursiveDir)%(Filename)%(Extension)')" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
|
||||
<!-- Include files -->
|
||||
<ItemGroup>
|
||||
<IncFiles Include="$(NapaVanillaRoot)\inc\**\*"/>
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(IncFiles)" DestinationFiles="@(IncFiles->'$(PackageOutPath)\inc\%(RecursiveDir)%(Filename)%(Extension)')" ContinueOnError="false" SkipUnchangedFiles="true" />
|
||||
|
|
@ -47,6 +47,18 @@ napa_zone_handle napa_zone_get(napa_string_ref id) {
|
|||
return new napa_zone { std::move(zoneId), std::move(zone) };
|
||||
}
|
||||
|
||||
napa_zone_handle napa_zone_get_current() {
|
||||
NAPA_ASSERT(_initialized, "Napa wasn't initialized");
|
||||
|
||||
auto zone = reinterpret_cast<ZoneImpl*>(napa::module::WorkerContext::Get(napa::module::WorkerContextItem::ZONE));
|
||||
if (zone == nullptr) {
|
||||
LOG_WARNING("Api", "Trying to get current zone from a thread that is not associated with a zone");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return napa_zone_get(STD_STRING_TO_NAPA_STRING_REF(zone->GetId()));
|
||||
}
|
||||
|
||||
napa_response_code napa_zone_init(napa_zone_handle handle, napa_string_ref settings) {
|
||||
NAPA_ASSERT(_initialized, "Napa wasn't initialized");
|
||||
NAPA_ASSERT(handle, "Zone handle is null");
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
|
||||
<!-- Core modules -->
|
||||
<Import Project="module\core-modules\core-modules.proj" />
|
||||
|
||||
|
||||
<Import Project="$(PACKAGESROOT)\V8.Library\exports.props" />
|
||||
<Import Project="$(Pkgboost)\build\native\boost.targets" />
|
||||
<Import Project="$(Pkgboost_date_time_vc140)\build\native\boost_date_time-vc140.targets" />
|
||||
|
|
|
@ -34,7 +34,7 @@ void BroadcastTask::Execute() {
|
|||
// Compile the source code.
|
||||
auto compileResult = v8::Script::Compile(context, source, &sourceOrigin);
|
||||
if (compileResult.IsEmpty()) {
|
||||
LOG_ERROR("Broadcast", "Failed while compiling the provided source code");
|
||||
LOG_ERROR("Broadcast", "Failed while compiling the provided source code. %s", _source.c_str());
|
||||
_callback(NAPA_RESPONSE_BROADCAST_SCRIPT_ERROR);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -27,49 +27,49 @@ void ExecuteTask::Execute() {
|
|||
v8::HandleScope scope(isolate);
|
||||
auto context = isolate->GetCurrentContext();
|
||||
|
||||
v8::Local<v8::Function> function;
|
||||
v8::Local<v8::Function> dispatcher;
|
||||
std::vector<v8::Local<v8::Value>> args;
|
||||
|
||||
if (_module.empty()) {
|
||||
// Lookup function in global scope
|
||||
auto funcVal = context->Global()->Get(MakeExternalV8String(isolate, _func));
|
||||
if (!funcVal->IsFunction()) {
|
||||
std::string errorMessage = "Function '" + _func + "' not defined";
|
||||
LOG_ERROR("Execute", errorMessage.c_str());
|
||||
_callback({ NAPA_RESPONSE_EXECUTE_FUNC_ERROR, std::move(errorMessage), "", nullptr });
|
||||
auto dispatcherValue = context->Global()->Get(MakeExternalV8String(isolate, "__napa_function_dispatcher__"));
|
||||
NAPA_ASSERT(dispatcherValue->IsFunction(), "dispatcher function must exist in global scope");
|
||||
|
||||
dispatcher = v8::Local<v8::Function>::Cast(dispatcherValue);
|
||||
|
||||
auto funcValue = context->Global()->Get(MakeExternalV8String(isolate, _func));
|
||||
if (!funcValue->IsFunction()) {
|
||||
auto error = "Function '" + _func + "' does not exist in global scope";
|
||||
_callback({ NAPA_RESPONSE_EXECUTE_FUNC_ERROR, std::move(error), "", nullptr });
|
||||
return;
|
||||
}
|
||||
|
||||
function = v8::Local<v8::Function>::Cast(funcVal);
|
||||
|
||||
// Create the V8 args for the function call.
|
||||
args.reserve(_args.size());
|
||||
for (const auto& arg : _args) {
|
||||
args.emplace_back(MakeExternalV8String(isolate, arg));
|
||||
}
|
||||
args.reserve(3); // (func, args, contextHandle)
|
||||
args.emplace_back(v8::Local<v8::Function>::Cast(funcValue));
|
||||
} else {
|
||||
// Use the global dispatcher function
|
||||
auto dispatcherValue = context->Global()->Get(MakeExternalV8String(isolate, "__napa_execute_dispatcher__"));
|
||||
// Get the dispatcher function from global scope.
|
||||
auto dispatcherValue = context->Global()->Get(MakeExternalV8String(isolate, "__napa_module_dispatcher__"));
|
||||
NAPA_ASSERT(dispatcherValue->IsFunction(), "dispatcher function must exist in global scope");
|
||||
|
||||
// The dispatcher function.
|
||||
function = v8::Local<v8::Function>::Cast(dispatcherValue);
|
||||
dispatcher = v8::Local<v8::Function>::Cast(dispatcherValue);
|
||||
|
||||
// Prepare args
|
||||
args.reserve(3); // (module, func, args)
|
||||
args.reserve(4); // (moduleName, functionName, args, contextHandle)
|
||||
args.emplace_back(MakeExternalV8String(isolate, _module));
|
||||
args.emplace_back(MakeExternalV8String(isolate, _func));
|
||||
|
||||
auto arr = v8::Array::New(isolate, static_cast<int>(_args.size()));
|
||||
for (size_t i = 0; i < _args.size(); ++i) {
|
||||
(void)arr->CreateDataProperty(context, static_cast<uint32_t>(i), MakeExternalV8String(isolate, _args[i]));
|
||||
}
|
||||
args.emplace_back(arr);
|
||||
}
|
||||
|
||||
// Prepare function args
|
||||
auto arr = v8::Array::New(isolate, static_cast<int>(_args.size()));
|
||||
for (size_t i = 0; i < _args.size(); ++i) {
|
||||
(void)arr->CreateDataProperty(context, static_cast<uint32_t>(i), MakeExternalV8String(isolate, _args[i]));
|
||||
}
|
||||
args.emplace_back(arr);
|
||||
|
||||
// Transport context handle
|
||||
args.emplace_back(PtrToV8Uint32Array(isolate, _transportContext.get()));
|
||||
|
||||
// Execute the function.
|
||||
v8::TryCatch tryCatch(isolate);
|
||||
auto res = function->Call(context->Global(), static_cast<int>(args.size()), args.data());
|
||||
auto res = dispatcher->Call(context->Global(), static_cast<int>(args.size()), args.data());
|
||||
|
||||
// Terminating an isolate may occur from a different thread, i.e. from timeout service.
|
||||
// If the function call already finished successfully when the isolate is terminated it may lead
|
||||
|
@ -92,10 +92,13 @@ void ExecuteTask::Execute() {
|
|||
|
||||
if (tryCatch.HasCaught()) {
|
||||
auto exception = tryCatch.Exception();
|
||||
v8::String::Utf8Value errorMessage(exception);
|
||||
v8::String::Utf8Value exceptionStr(exception);
|
||||
auto stackTrace = tryCatch.StackTrace();
|
||||
v8::String::Utf8Value stackTraceStr(stackTrace);
|
||||
|
||||
LOG_ERROR("Execute", "Error occured while executing function '%s', error: %s", _func.c_str(), *errorMessage);
|
||||
_callback({ NAPA_RESPONSE_EXECUTE_FUNC_ERROR, *errorMessage, "", nullptr });
|
||||
LOG_ERROR("Execute", "JS exception thrown: %s - %s", *exceptionStr, *stackTraceStr);
|
||||
|
||||
_callback({ NAPA_RESPONSE_EXECUTE_FUNC_ERROR, *exceptionStr, "", nullptr });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <napa-log.h>
|
||||
|
||||
#include <boost/dll.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
@ -13,16 +14,16 @@
|
|||
using namespace napa;
|
||||
using namespace napa::scheduler;
|
||||
|
||||
// Forward declarations
|
||||
static void BroadcastFromFile(const std::string& file, Scheduler& scheduler, bool withOrigin);
|
||||
|
||||
// Static members initialization
|
||||
std::mutex ZoneImpl::_mutex;
|
||||
std::unordered_map<std::string, std::weak_ptr<ZoneImpl>> ZoneImpl::_zones;
|
||||
|
||||
static const char* _dispatchFunction =
|
||||
"function __napa_execute_dispatcher__(module, func, args) {\n"
|
||||
" var f = require(module)[func];\n"
|
||||
" if (!f) throw new Error(\"Cannot find function '\" + func + \"' in module '\" + module + \"'\");\n"
|
||||
" return f.apply(this, args);\n"
|
||||
"}";
|
||||
// The path to the file containing the execute dispatcher function
|
||||
static const std::string DISPATCHER_FILE = (boost::dll::this_line_location().parent_path() /
|
||||
"lib\\napa-dispatcher.js").string();
|
||||
|
||||
std::shared_ptr<ZoneImpl> ZoneImpl::Create(const ZoneSettings& settings) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
@ -38,12 +39,13 @@ std::shared_ptr<ZoneImpl> ZoneImpl::Create(const ZoneSettings& settings) {
|
|||
MakeSharedEnabler(const ZoneSettings& settings) : ZoneImpl(settings) {}
|
||||
};
|
||||
|
||||
std::shared_ptr<ZoneImpl> zone;
|
||||
std::shared_ptr<ZoneImpl> zone = std::make_shared<MakeSharedEnabler>(settings);
|
||||
_zones[settings.id] = zone;
|
||||
try {
|
||||
zone = std::make_shared<MakeSharedEnabler>(settings);
|
||||
_zones[settings.id] = zone;
|
||||
zone->Init();
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR("Zone", "Failed to create zone '%s': %s", settings.id.c_str(), ex.what());
|
||||
LOG_ERROR("Zone", "Failed to initialize zone '%s': %s", settings.id.c_str(), ex.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return zone;
|
||||
|
@ -69,36 +71,19 @@ std::shared_ptr<ZoneImpl> ZoneImpl::Get(const std::string& id) {
|
|||
}
|
||||
|
||||
ZoneImpl::ZoneImpl(const ZoneSettings& settings) : _settings(settings) {
|
||||
}
|
||||
|
||||
void ZoneImpl::Init() {
|
||||
// Create the zone's scheduler.
|
||||
_scheduler = std::make_unique<Scheduler>(_settings);
|
||||
|
||||
// Set the dispatcher function on all zone workers.
|
||||
_scheduler->ScheduleOnAllWorkers(std::make_shared<BroadcastTask>(_dispatchFunction));
|
||||
// Read dispatcher file content and broadcast it on all workers.
|
||||
// Do not set origin to avoid changing the global context path.
|
||||
BroadcastFromFile(DISPATCHER_FILE, *_scheduler, false);
|
||||
|
||||
// Read bootstrap file content and broadcast it on all workers.
|
||||
if (!_settings.bootstrapFile.empty()) {
|
||||
auto filePath = boost::filesystem::path(_settings.bootstrapFile);
|
||||
if (filePath.is_relative()) {
|
||||
filePath = (boost::filesystem::current_path() / filePath).normalize().make_preferred();
|
||||
}
|
||||
|
||||
auto filePathString = filePath.string();
|
||||
std::ifstream ifs;
|
||||
ifs.open(filePathString);
|
||||
|
||||
if (!ifs.is_open()) {
|
||||
throw std::runtime_error("Failed to open bootstrap file: " + filePathString);
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << ifs.rdbuf();
|
||||
|
||||
auto fileContent = buffer.str();
|
||||
if (fileContent.empty()) {
|
||||
throw std::runtime_error("Bootstrap file content was empty: " + filePathString);
|
||||
}
|
||||
|
||||
_scheduler->ScheduleOnAllWorkers(std::make_shared<BroadcastTask>(std::move(fileContent), filePath.string()));
|
||||
BroadcastFromFile(_settings.bootstrapFile, *_scheduler, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,4 +126,30 @@ const ZoneSettings& ZoneImpl::GetSettings() const {
|
|||
|
||||
Scheduler* ZoneImpl::GetScheduler() {
|
||||
return _scheduler.get();
|
||||
}
|
||||
|
||||
static void BroadcastFromFile(const std::string& file, Scheduler& scheduler, bool withOrigin) {
|
||||
auto filePath = boost::filesystem::path(file);
|
||||
if (filePath.is_relative()) {
|
||||
filePath = (boost::filesystem::current_path() / filePath).normalize().make_preferred();
|
||||
}
|
||||
|
||||
auto filePathString = filePath.string();
|
||||
std::ifstream ifs;
|
||||
ifs.open(filePathString);
|
||||
|
||||
if (!ifs.is_open()) {
|
||||
throw std::runtime_error("Failed to open file: " + filePathString);
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << ifs.rdbuf();
|
||||
|
||||
auto fileContent = buffer.str();
|
||||
if (fileContent.empty()) {
|
||||
throw std::runtime_error("File content was empty: " + filePathString);
|
||||
}
|
||||
|
||||
auto scriptOrigin = (withOrigin) ? filePathString : "";
|
||||
scheduler.ScheduleOnAllWorkers(std::make_shared<BroadcastTask>(std::move(fileContent), scriptOrigin));
|
||||
}
|
|
@ -39,6 +39,7 @@ namespace napa {
|
|||
|
||||
private:
|
||||
explicit ZoneImpl(const ZoneSettings& settings);
|
||||
void Init();
|
||||
|
||||
ZoneSettings _settings;
|
||||
std::unique_ptr<scheduler::Scheduler> _scheduler;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "catch.hpp"
|
||||
|
||||
#include "napa-initialization-guard.h"
|
||||
|
||||
#include <napa-memory.h>
|
||||
#include <napa/memory/allocator-debugger.h>
|
||||
#include <napa/stl/allocator.h>
|
||||
|
@ -9,6 +11,9 @@
|
|||
|
||||
using namespace napa::memory;
|
||||
|
||||
// Make sure Napa is initialized exactly once.
|
||||
static NapaInitializationGuard _guard;
|
||||
|
||||
namespace custom_allocator {
|
||||
char buffer[1024];
|
||||
size_t allocated = 0;
|
||||
|
@ -132,34 +137,34 @@ struct Foo {
|
|||
|
||||
TEST_CASE("Memory helpers", "[memory-helpers]") {
|
||||
NAPA_SET_DEFAULT_ALLOCATOR(custom_allocator::malloc, custom_allocator::free);
|
||||
|
||||
|
||||
SECTION("NAPA_MAKE_UNIQUE") {
|
||||
constexpr size_t count = 2;
|
||||
int elems[count] = {1, 2};
|
||||
{
|
||||
auto pointer = NAPA_MAKE_UNIQUE<Foo>("hello world", count, elems);
|
||||
REQUIRE(pointer != nullptr);
|
||||
{
|
||||
auto pointer = NAPA_MAKE_UNIQUE<Foo>("hello world", count, elems);
|
||||
REQUIRE(pointer != nullptr);
|
||||
|
||||
REQUIRE(pointer->_str == "hello world");
|
||||
REQUIRE(pointer->_vector.size() == 2);
|
||||
REQUIRE(pointer->_map.size() == 2);
|
||||
}
|
||||
REQUIRE(custom_allocator::allocated == 0);
|
||||
REQUIRE(pointer->_str == "hello world");
|
||||
REQUIRE(pointer->_vector.size() == 2);
|
||||
REQUIRE(pointer->_map.size() == 2);
|
||||
}
|
||||
REQUIRE(custom_allocator::allocated == 0);
|
||||
custom_allocator::reset();
|
||||
}
|
||||
|
||||
SECTION("NAPA_MAKE_SHARED") {
|
||||
constexpr size_t count = 2;
|
||||
int elems[count] = { 1, 2 };
|
||||
{
|
||||
auto pointer = NAPA_MAKE_SHARED<Foo>("hello world", count, elems);
|
||||
REQUIRE(pointer != nullptr);
|
||||
SECTION("NAPA_MAKE_SHARED") {
|
||||
constexpr size_t count = 2;
|
||||
int elems[count] = { 1, 2 };
|
||||
{
|
||||
auto pointer = NAPA_MAKE_SHARED<Foo>("hello world", count, elems);
|
||||
REQUIRE(pointer != nullptr);
|
||||
|
||||
REQUIRE(pointer->_str == "hello world");
|
||||
REQUIRE(pointer->_vector.size() == 2);
|
||||
REQUIRE(pointer->_map.size() == 2);
|
||||
}
|
||||
REQUIRE(custom_allocator::allocated == 0);
|
||||
REQUIRE(pointer->_str == "hello world");
|
||||
REQUIRE(pointer->_vector.size() == 2);
|
||||
REQUIRE(pointer->_map.size() == 2);
|
||||
}
|
||||
REQUIRE(custom_allocator::allocated == 0);
|
||||
custom_allocator::reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
<PropertyGroup>
|
||||
<V8BinPath>$(PACKAGESROOT)\V8.Library\$(COMPILER_VERSION)\bin\$(BuildType)\$(BuildArchitecture)</V8BinPath>
|
||||
<V8MiscPath>$(PACKAGESROOT)\V8.Library\misc\$(BuildType)\$(BuildArchitecture)</V8MiscPath>
|
||||
<TestDir>$(OutDir)api-tests</TestDir>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ItemDefinitionGroup>
|
||||
|
@ -35,34 +36,22 @@
|
|||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)\api-tests.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)\memory-tests.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)\zone-tests.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(NapaVanillaRoot)\src\napa.vcxproj" />
|
||||
</ItemGroup>
|
||||
<!-- Test artifacts binplace -->
|
||||
<ItemGroup>
|
||||
<Robocopy Include="$(NapaVanillaRoot)\src\$(IntermediateOutputPath)\napa.dll">
|
||||
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
|
||||
<Robocopy Include="$(OutputPath)\$(TargetFileName)">
|
||||
<DestinationFolder>$(IntermediateOutputPath)\api-tests\node_modules\napajs\bin</DestinationFolder>
|
||||
</Robocopy>
|
||||
<Robocopy Include="$(V8BinPath)\icui18n.dll">
|
||||
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
|
||||
<Robocopy Include="$(NapaVanillaRoot)\modules\napajs\package\$(IntermediateOutputPath)\node_modules\napajs">
|
||||
<DestinationFolder>$(IntermediateOutputPath)\api-tests\node_modules\napajs</DestinationFolder>
|
||||
</Robocopy>
|
||||
<Robocopy Include="$(V8BinPath)\icuuc.dll">
|
||||
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
|
||||
</Robocopy>
|
||||
<Robocopy Include="$(V8BinPath)\v8.dll">
|
||||
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
|
||||
</Robocopy>
|
||||
<Robocopy Include="$(V8MiscPath)\natives_blob.bin">
|
||||
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
|
||||
</Robocopy>
|
||||
<Robocopy Include="$(V8MiscPath)\snapshot_blob.bin">
|
||||
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
|
||||
</Robocopy>
|
||||
<Robocopy Include="$(MSBuildThisFileDirectory)\bootstrap.js">
|
||||
<DestinationFolder>$(IntermediateOutputPath)</DestinationFolder>
|
||||
<Robocopy Include="bootstrap.js">
|
||||
<DestinationFolder>$(IntermediateOutputPath)\api-tests\node_modules\napajs\bin</DestinationFolder>
|
||||
</Robocopy>
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <napa.h>
|
||||
|
||||
class NapaInitializationGuard {
|
||||
public:
|
||||
NapaInitializationGuard() {
|
||||
static NapaInitialization initialization;
|
||||
}
|
||||
|
||||
private:
|
||||
class NapaInitialization {
|
||||
public:
|
||||
NapaInitialization() {
|
||||
// Only call napa::Initialize once per process.
|
||||
napa::Initialize("--loggingProvider console");
|
||||
}
|
||||
|
||||
~NapaInitialization() {
|
||||
// Only call napa::Shutdown once per process.
|
||||
napa::Shutdown();
|
||||
}
|
||||
};
|
||||
};
|
|
@ -1,16 +1,15 @@
|
|||
#include "catch.hpp"
|
||||
|
||||
#include "napa.h"
|
||||
#include "napa-initialization-guard.h"
|
||||
|
||||
#include <napa.h>
|
||||
|
||||
#include <future>
|
||||
|
||||
std::once_flag flag;
|
||||
// Make sure Napa is initialized exactly once.
|
||||
static NapaInitializationGuard _guard;
|
||||
|
||||
TEST_CASE("zone apis", "[api]") {
|
||||
// Only call napa::Initialize once per process.
|
||||
std::call_once(flag, []() {
|
||||
REQUIRE(napa::Initialize("--loggingProvider console") == NAPA_RESPONSE_SUCCESS);
|
||||
});
|
||||
|
||||
SECTION("create zone with bootstrap file") {
|
||||
napa::ZoneProxy zone("zone1", "--bootstrapFile bootstrap.js");
|
||||
|
@ -20,7 +19,7 @@ TEST_CASE("zone apis", "[api]") {
|
|||
auto response = zone.ExecuteSync(request);
|
||||
|
||||
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
|
||||
REQUIRE(response.returnValue == "bootstrap");
|
||||
REQUIRE(response.returnValue == "\"bootstrap\"");
|
||||
}
|
||||
|
||||
SECTION("broadcast valid javascript") {
|
||||
|
@ -81,6 +80,9 @@ TEST_CASE("zone apis", "[api]") {
|
|||
std::promise<napa::ExecuteResponse> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
// Warmup to avoid loading napajs on first call
|
||||
zone.BroadcastSync("require('napajs');");
|
||||
|
||||
zone.Broadcast("function func(a, b) { return Number(a) + Number(b); }", [&promise, &zone](NapaResponseCode) {
|
||||
napa::ExecuteRequest request;
|
||||
request.function = NAPA_STRING_REF("func");
|
||||
|
@ -103,6 +105,9 @@ TEST_CASE("zone apis", "[api]") {
|
|||
std::promise<napa::ExecuteResponse> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
// Warmup to avoid loading napajs on first call
|
||||
zone.BroadcastSync("require('napajs');");
|
||||
|
||||
zone.Broadcast("function func() { while(true) {} }", [&promise, &zone](NapaResponseCode) {
|
||||
napa::ExecuteRequest request;
|
||||
request.function = NAPA_STRING_REF("func");
|
||||
|
@ -121,6 +126,9 @@ TEST_CASE("zone apis", "[api]") {
|
|||
SECTION("execute 2 javascript functions, one succeeds and one times out") {
|
||||
napa::ZoneProxy zone("zone1");
|
||||
|
||||
// Warmup to avoid loading napajs on first call
|
||||
zone.BroadcastSync("require('napajs');");
|
||||
|
||||
auto res = zone.BroadcastSync("function f1(a, b) { return Number(a) + Number(b); }");
|
||||
REQUIRE(res == NAPA_RESPONSE_SUCCESS);
|
||||
res = zone.BroadcastSync("function f2() { while(true) {} }");
|
||||
|
@ -169,7 +177,7 @@ TEST_CASE("zone apis", "[api]") {
|
|||
|
||||
auto response = zone.ExecuteSync(request);
|
||||
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
|
||||
REQUIRE(response.returnValue == ".txt");
|
||||
REQUIRE(response.returnValue == "\".txt\"");
|
||||
}
|
||||
|
||||
SECTION("execute function in a module") {
|
||||
|
@ -178,18 +186,10 @@ TEST_CASE("zone apis", "[api]") {
|
|||
napa::ExecuteRequest request;
|
||||
request.module = NAPA_STRING_REF("path");
|
||||
request.function = NAPA_STRING_REF("extname");
|
||||
request.arguments = { NAPA_STRING_REF("test.txt") };
|
||||
request.arguments = { NAPA_STRING_REF("\"test.txt\"") };
|
||||
|
||||
auto response = zone.ExecuteSync(request);
|
||||
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
|
||||
REQUIRE(response.returnValue == ".txt");
|
||||
}
|
||||
|
||||
/// <note>
|
||||
/// This must be the last test in this test suite, since napa initialize and shutdown
|
||||
/// can only run once per process.
|
||||
/// </note>
|
||||
SECTION("shutdown napa") {
|
||||
REQUIRE(napa::Shutdown() == NAPA_RESPONSE_SUCCESS);
|
||||
REQUIRE(response.returnValue == "\".txt\"");
|
||||
}
|
||||
}
|
|
@ -33,6 +33,9 @@ TEST_CASE("tasks", "[tasks]") {
|
|||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
v8::Context::Scope contextScope(context);
|
||||
|
||||
// Set a simple dispatcher function
|
||||
BroadcastTask("function __napa_function_dispatcher__(func, args) { return func.apply(this, args); }").Execute();
|
||||
|
||||
SECTION("load valid javascript") {
|
||||
NapaResponseCode loadResponseCode;
|
||||
BroadcastTask("var i = 3 + 5;", "", [&loadResponseCode](NapaResponseCode code) {
|
||||
|
@ -87,7 +90,6 @@ TEST_CASE("tasks", "[tasks]") {
|
|||
}).Execute();
|
||||
|
||||
REQUIRE(response.code == NAPA_RESPONSE_EXECUTE_FUNC_ERROR);
|
||||
REQUIRE(response.errorMessage == "Function 'bar' not defined");
|
||||
}
|
||||
|
||||
SECTION("execute fails when function throws exception") {
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
<Project ToolVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<QTestType>Console</QTestType>
|
||||
<QTestDirToDeploy>$(OutDir)</QTestDirToDeploy>
|
||||
<QTestDirToDeploy Condition="'$(TestDir)' == ''">$(OutDir)</QTestDirToDeploy>
|
||||
<QTestDirToDeploy Condition="'$(TestDir)' != ''">$(TestDir)</QTestDirToDeploy>
|
||||
<QTestAdditionalOptions>-r napa -o $(TargetName)$(TargetExt).xml</QTestAdditionalOptions>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
|
|
Загрузка…
Ссылка в новой задаче