mirror of
https://github.com/wgh136/flutter_qjs.git
synced 2025-09-27 05:27:23 +00:00
add function channel
This commit is contained in:
13
.vscode/launch.json
vendored
13
.vscode/launch.json
vendored
@@ -4,9 +4,20 @@
|
|||||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "(Windows) 启动",
|
||||||
|
"type": "cppvsdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "example/build/windows/runner/Debug/flutter_qjs_example.exe",
|
||||||
|
"args": [],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Flutter",
|
"name": "Flutter",
|
||||||
"program": "lib/main.dart",
|
"program": "example/lib/main.dart",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart"
|
"type": "dart"
|
||||||
}
|
}
|
||||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -98,7 +98,8 @@
|
|||||||
"numeric": "cpp",
|
"numeric": "cpp",
|
||||||
"cfenv": "cpp",
|
"cfenv": "cpp",
|
||||||
"cinttypes": "cpp",
|
"cinttypes": "cpp",
|
||||||
"typeindex": "cpp"
|
"typeindex": "cpp",
|
||||||
|
"__functional_03": "cpp"
|
||||||
},
|
},
|
||||||
"java.configuration.updateBuildConfiguration": "interactive"
|
"java.configuration.updateBuildConfiguration": "interactive"
|
||||||
}
|
}
|
22
LICENSE
22
LICENSE
@@ -1 +1,21 @@
|
|||||||
TODO: Add your license here.
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 https://github.com/czy0729
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@@ -1,6 +1,6 @@
|
|||||||
# flutter_qjs
|
# flutter_qjs
|
||||||
|
|
||||||
A new flutter plugin project.
|
A quickjs engine for flutter.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
@@ -3,16 +3,17 @@
|
|||||||
* @Author: ekibun
|
* @Author: ekibun
|
||||||
* @Date: 2020-08-07 13:55:52
|
* @Date: 2020-08-07 13:55:52
|
||||||
* @LastEditors: ekibun
|
* @LastEditors: ekibun
|
||||||
* @LastEditTime: 2020-08-13 13:47:16
|
* @LastEditTime: 2020-08-15 14:45:00
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "quickjspp/quickjspp.hpp"
|
#include "quickjspp/quickjspp.hpp"
|
||||||
#include "quickjspp/quickjs/list.h"
|
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace qjs
|
namespace qjs
|
||||||
{
|
{
|
||||||
|
#include "quickjspp/quickjs/list.h"
|
||||||
|
|
||||||
static JSClassID js_dart_promise_class_id;
|
static JSClassID js_dart_promise_class_id;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -22,11 +23,12 @@ namespace qjs
|
|||||||
} JSOSFutureArgv;
|
} JSOSFutureArgv;
|
||||||
|
|
||||||
using JSFutureReturn = std::function<JSOSFutureArgv(JSContext *)>;
|
using JSFutureReturn = std::function<JSOSFutureArgv(JSContext *)>;
|
||||||
using DartChannel = std::function<std::future<JSFutureReturn>(std::string, std::string)>;
|
using DartChannel = std::function<std::promise<JSFutureReturn> *(std::string, Value)>;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
struct list_head link;
|
struct list_head link;
|
||||||
|
std::promise<JSFutureReturn> *promise;
|
||||||
std::shared_future<JSFutureReturn> future;
|
std::shared_future<JSFutureReturn> future;
|
||||||
JSValue resolve;
|
JSValue resolve;
|
||||||
JSValue reject;
|
JSValue reject;
|
||||||
@@ -35,10 +37,10 @@ namespace qjs
|
|||||||
typedef struct JSThreadState
|
typedef struct JSThreadState
|
||||||
{
|
{
|
||||||
struct list_head os_future; /* list of JSOSFuture.link */
|
struct list_head os_future; /* list of JSOSFuture.link */
|
||||||
std::function<std::future<JSFutureReturn>(std::string, std::string)> channel;
|
DartChannel channel;
|
||||||
} JSThreadState;
|
} JSThreadState;
|
||||||
|
|
||||||
static JSValue js_add_future(Value resolve, Value reject, std::shared_future<JSFutureReturn> future)
|
static JSValue js_add_future(Value resolve, Value reject, std::promise<JSFutureReturn> *promise)
|
||||||
{
|
{
|
||||||
JSRuntime *rt = JS_GetRuntime(resolve.ctx);
|
JSRuntime *rt = JS_GetRuntime(resolve.ctx);
|
||||||
JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt);
|
JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt);
|
||||||
@@ -61,7 +63,8 @@ namespace qjs
|
|||||||
JS_FreeValue(resolve.ctx, obj);
|
JS_FreeValue(resolve.ctx, obj);
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
}
|
}
|
||||||
th->future = future;
|
th->promise = promise;
|
||||||
|
th->future = promise->get_future();
|
||||||
th->resolve = JS_DupValue(resolve.ctx, jsResolve);
|
th->resolve = JS_DupValue(resolve.ctx, jsResolve);
|
||||||
th->reject = JS_DupValue(reject.ctx, jsReject);
|
th->reject = JS_DupValue(reject.ctx, jsReject);
|
||||||
list_add_tail(&th->link, &ts->os_future);
|
list_add_tail(&th->link, &ts->os_future);
|
||||||
@@ -69,7 +72,7 @@ namespace qjs
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue js_dart_future(Value resolve, Value reject, std::string name, std::string args)
|
JSValue js_dart_future(Value resolve, Value reject, std::string name, Value args)
|
||||||
{
|
{
|
||||||
JSRuntime *rt = JS_GetRuntime(resolve.ctx);
|
JSRuntime *rt = JS_GetRuntime(resolve.ctx);
|
||||||
JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt);
|
JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt);
|
||||||
@@ -116,6 +119,7 @@ namespace qjs
|
|||||||
{
|
{
|
||||||
JSOSFuture *th = list_entry(el, JSOSFuture, link);
|
JSOSFuture *th = list_entry(el, JSOSFuture, link);
|
||||||
th->future.get();
|
th->future.get();
|
||||||
|
delete th->promise;
|
||||||
unlink_future(rt, th);
|
unlink_future(rt, th);
|
||||||
free_future(rt, th);
|
free_future(rt, th);
|
||||||
}
|
}
|
||||||
@@ -159,7 +163,6 @@ namespace qjs
|
|||||||
{
|
{
|
||||||
JSOSFutureArgv argv = th->future.get()(ctx);
|
JSOSFutureArgv argv = th->future.get()(ctx);
|
||||||
JSValue resolve, reject;
|
JSValue resolve, reject;
|
||||||
int64_t delay;
|
|
||||||
/* the timer expired */
|
/* the timer expired */
|
||||||
resolve = th->resolve;
|
resolve = th->resolve;
|
||||||
th->resolve = JS_UNDEFINED;
|
th->resolve = JS_UNDEFINED;
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
* @Author: ekibun
|
* @Author: ekibun
|
||||||
* @Date: 2020-08-08 10:30:59
|
* @Date: 2020-08-08 10:30:59
|
||||||
* @LastEditors: ekibun
|
* @LastEditors: ekibun
|
||||||
* @LastEditTime: 2020-08-13 13:47:28
|
* @LastEditTime: 2020-08-15 13:13:43
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -21,8 +21,7 @@ namespace qjs
|
|||||||
{
|
{
|
||||||
struct EngineTask
|
struct EngineTask
|
||||||
{
|
{
|
||||||
std::string command;
|
std::function<Value(Context&)> invoke;
|
||||||
std::string name;
|
|
||||||
std::function<void(std::string)> resolve;
|
std::function<void(std::string)> resolve;
|
||||||
std::function<void(std::string)> reject;
|
std::function<void(std::string)> reject;
|
||||||
};
|
};
|
||||||
@@ -72,10 +71,26 @@ namespace qjs
|
|||||||
R"xxx(
|
R"xxx(
|
||||||
import * as __DartImpl from "__DartImpl";
|
import * as __DartImpl from "__DartImpl";
|
||||||
globalThis.dart = (method, ...args) => new Promise((res, rej) =>
|
globalThis.dart = (method, ...args) => new Promise((res, rej) =>
|
||||||
__DartImpl.__invoke((v) => res(JSON.parse(v)), rej, method, JSON.stringify(args)));
|
__DartImpl.__invoke(res, rej, method, args));
|
||||||
)xxx",
|
)xxx",
|
||||||
"<dart>", JS_EVAL_TYPE_MODULE);
|
"<dart>", JS_EVAL_TYPE_MODULE);
|
||||||
std::vector<EngineTaskResolver> unresolvedTask;
|
std::vector<EngineTaskResolver> unresolvedTask;
|
||||||
|
Value promiseWrapper = ctx.eval(
|
||||||
|
R"xxx(
|
||||||
|
(value) => {
|
||||||
|
const __ret = Promise.resolve(value)
|
||||||
|
.then(v => {
|
||||||
|
__ret.__value = v;
|
||||||
|
__ret.__resolved = true;
|
||||||
|
}).catch(e => {
|
||||||
|
__ret.__error = e;
|
||||||
|
__ret.__rejected = true;
|
||||||
|
});
|
||||||
|
return __ret;
|
||||||
|
}
|
||||||
|
)xxx",
|
||||||
|
"<PromiseWrapper>", JS_EVAL_TYPE_GLOBAL);
|
||||||
|
|
||||||
// 循环
|
// 循环
|
||||||
while (!this->stoped)
|
while (!this->stoped)
|
||||||
{
|
{
|
||||||
@@ -93,25 +108,11 @@ namespace qjs
|
|||||||
if (task.resolve)
|
if (task.resolve)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ctx.global()["__evalstr"] = JS_NewString(ctx.ctx, task.command.c_str());
|
Value val = task.invoke(ctx);
|
||||||
Value ret = ctx.eval(
|
Value ret = Value{ctx.ctx, JS_Call(ctx.ctx, promiseWrapper.v, ctx.global().v, 1, &(val.v))};
|
||||||
R"xxx(
|
|
||||||
(() => {
|
|
||||||
const __ret = Promise.resolve(eval(__evalstr))
|
|
||||||
.then(v => {
|
|
||||||
__ret.__value = v;
|
|
||||||
__ret.__resolved = true;
|
|
||||||
}).catch(e => {
|
|
||||||
__ret.__error = e;
|
|
||||||
__ret.__rejected = true;
|
|
||||||
});
|
|
||||||
return __ret;
|
|
||||||
})()
|
|
||||||
)xxx",
|
|
||||||
task.name.c_str());
|
|
||||||
unresolvedTask.emplace_back(EngineTaskResolver{ret, std::move(task.resolve), std::move(task.reject)});
|
unresolvedTask.emplace_back(EngineTaskResolver{ret, std::move(task.resolve), std::move(task.reject)});
|
||||||
}
|
}
|
||||||
catch (exception e)
|
catch (exception)
|
||||||
{
|
{
|
||||||
task.reject(getStackTrack(ctx.getException()));
|
task.reject(getStackTrack(ctx.getException()));
|
||||||
}
|
}
|
||||||
@@ -152,16 +153,16 @@ namespace qjs
|
|||||||
{
|
{
|
||||||
idle = js_dart_poll(ctx.ctx);
|
idle = js_dart_poll(ctx.ctx);
|
||||||
}
|
}
|
||||||
catch (exception e)
|
catch (exception)
|
||||||
{
|
{
|
||||||
handleException(ctx.getException());
|
handleException(ctx.getException());
|
||||||
}
|
}
|
||||||
// 空闲时reject所有task
|
// 空闲时reject所有task
|
||||||
if (idle && !JS_IsJobPending(rt.rt) && !unresolvedTask.empty())
|
if (idle && !JS_IsJobPending(rt.rt) && !unresolvedTask.empty())
|
||||||
{
|
{
|
||||||
for (EngineTaskResolver &task : unresolvedTask)
|
for (EngineTaskResolver &_task : unresolvedTask)
|
||||||
{
|
{
|
||||||
task.reject("Promise cannot resolve");
|
_task.reject("Promise cannot resolve");
|
||||||
}
|
}
|
||||||
unresolvedTask.clear();
|
unresolvedTask.clear();
|
||||||
}
|
}
|
||||||
|
Submodule cxx/quickjspp updated: b98fbc128a...0768b665d5
@@ -3,7 +3,7 @@
|
|||||||
* @Author: ekibun
|
* @Author: ekibun
|
||||||
* @Date: 2020-07-18 23:28:55
|
* @Date: 2020-07-18 23:28:55
|
||||||
* @LastEditors: ekibun
|
* @LastEditors: ekibun
|
||||||
* @LastEditTime: 2020-08-08 17:38:48
|
* @LastEditTime: 2020-08-15 14:01:09
|
||||||
*/
|
*/
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -48,6 +48,8 @@ class _TestPageState extends State<TestPage> {
|
|||||||
case "http":
|
case "http":
|
||||||
Response response = await Dio().get(arg[0]);
|
Response response = await Dio().get(arg[0]);
|
||||||
return response.data;
|
return response.data;
|
||||||
|
case "hello":
|
||||||
|
return await arg[0](["hello: "]);
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -32,7 +32,7 @@ add_definitions(-DUNICODE -D_UNICODE)
|
|||||||
# Compilation settings that should be applied to most targets.
|
# Compilation settings that should be applied to most targets.
|
||||||
function(APPLY_STANDARD_SETTINGS TARGET)
|
function(APPLY_STANDARD_SETTINGS TARGET)
|
||||||
target_compile_features(${TARGET} PUBLIC cxx_std_17)
|
target_compile_features(${TARGET} PUBLIC cxx_std_17)
|
||||||
target_compile_options(${TARGET} PRIVATE /W4 /WX- /wd"4100")
|
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
|
||||||
target_compile_options(${TARGET} PRIVATE /EHsc)
|
target_compile_options(${TARGET} PRIVATE /EHsc)
|
||||||
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
|
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
|
||||||
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
|
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
|
||||||
|
@@ -3,10 +3,9 @@
|
|||||||
* @Author: ekibun
|
* @Author: ekibun
|
||||||
* @Date: 2020-08-08 08:29:09
|
* @Date: 2020-08-08 08:29:09
|
||||||
* @LastEditors: ekibun
|
* @LastEditors: ekibun
|
||||||
* @LastEditTime: 2020-08-08 17:40:35
|
* @LastEditTime: 2020-08-15 13:58:11
|
||||||
*/
|
*/
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
@@ -19,12 +18,30 @@ class FlutterJs {
|
|||||||
final int engineId = await _channel.invokeMethod("initEngine");
|
final int engineId = await _channel.invokeMethod("initEngine");
|
||||||
_channel.setMethodCallHandler((call) async {
|
_channel.setMethodCallHandler((call) async {
|
||||||
if (methodHandler == null) return call.noSuchMethod(null);
|
if (methodHandler == null) return call.noSuchMethod(null);
|
||||||
List args = jsonDecode(call.arguments);
|
return await methodHandler(call.method, _wrapFunctionArguments(call.arguments));
|
||||||
return jsonEncode(await methodHandler(call.method, args));
|
|
||||||
});
|
});
|
||||||
return engineId;
|
return engineId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static dynamic _wrapFunctionArguments(dynamic val) {
|
||||||
|
if (val is List) {
|
||||||
|
for (var i = 0; i < val.length; ++i) {
|
||||||
|
val[i] = _wrapFunctionArguments(val[i]);
|
||||||
|
}
|
||||||
|
} else if (val is Map) {
|
||||||
|
if (val["__js_function__"] != 0) {
|
||||||
|
var functionId = val["__js_function__"];
|
||||||
|
return (List<dynamic> args) async {
|
||||||
|
var arguments = {"function": functionId, "arguments": args};
|
||||||
|
return await _channel.invokeMethod("call", arguments);
|
||||||
|
};
|
||||||
|
}else for(var key in val.keys) {
|
||||||
|
val[key] = _wrapFunctionArguments(val[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
static Future<String> evaluate(String command, String name) async {
|
static Future<String> evaluate(String command, String name) async {
|
||||||
var arguments = {"script": command, "name": command};
|
var arguments = {"script": command, "name": command};
|
||||||
final String jsResult = await _channel.invokeMethod("evaluate", arguments);
|
final String jsResult = await _channel.invokeMethod("evaluate", arguments);
|
||||||
|
176
windows/dart_js_wrapper.hpp
Normal file
176
windows/dart_js_wrapper.hpp
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* @Description:
|
||||||
|
* @Author: ekibun
|
||||||
|
* @Date: 2020-08-14 21:45:02
|
||||||
|
* @LastEditors: ekibun
|
||||||
|
* @LastEditTime: 2020-08-15 14:08:04
|
||||||
|
*/
|
||||||
|
#include "../cxx/js_engine.hpp"
|
||||||
|
#include <flutter/standard_method_codec.h>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct hash<qjs::Value>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const qjs::Value &key) const
|
||||||
|
{
|
||||||
|
return std::hash<std::string>()((std::string)key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<flutter::EncodableValue>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const flutter::EncodableValue &key) const
|
||||||
|
{
|
||||||
|
return key.index();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
namespace qjs
|
||||||
|
{
|
||||||
|
JSValue dartToJsAtom(JSContext *ctx, flutter::EncodableValue val)
|
||||||
|
{
|
||||||
|
if (std::holds_alternative<bool>(val))
|
||||||
|
return JS_NewBool(ctx, std::get<bool>(val));
|
||||||
|
if (std::holds_alternative<int32_t>(val))
|
||||||
|
return JS_NewInt32(ctx, std::get<int32_t>(val));
|
||||||
|
if (std::holds_alternative<int64_t>(val))
|
||||||
|
return JS_NewInt64(ctx, std::get<int64_t>(val));
|
||||||
|
if (std::holds_alternative<double>(val))
|
||||||
|
return JS_NewFloat64(ctx, std::get<double>(val));
|
||||||
|
if (std::holds_alternative<std::string>(val))
|
||||||
|
return JS_NewString(ctx, std::get<std::string>(val).c_str());
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue dartToJs(JSContext *ctx, flutter::EncodableValue val, std::unordered_map<flutter::EncodableValue, JSValue> cache)
|
||||||
|
{
|
||||||
|
if (val.IsNull())
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
if (cache.find(val) != cache.end())
|
||||||
|
return cache[val];
|
||||||
|
{
|
||||||
|
JSValue atomValue = dartToJsAtom(ctx, val);
|
||||||
|
if (!JS_IsUndefined(atomValue))
|
||||||
|
return atomValue;
|
||||||
|
}
|
||||||
|
if (std::holds_alternative<std::vector<uint8_t>>(val))
|
||||||
|
{
|
||||||
|
auto buf = std::get<std::vector<uint8_t>>(val);
|
||||||
|
return JS_NewArrayBufferCopy(ctx, buf.data(), buf.size());
|
||||||
|
}
|
||||||
|
if (std::holds_alternative<std::vector<int32_t>>(val))
|
||||||
|
{
|
||||||
|
auto buf = std::get<std::vector<int32_t>>(val);
|
||||||
|
return JS_NewArrayBufferCopy(ctx, (uint8_t *)buf.data(), buf.size() * 4);
|
||||||
|
}
|
||||||
|
if (std::holds_alternative<std::vector<int64_t>>(val))
|
||||||
|
{
|
||||||
|
auto buf = std::get<std::vector<int64_t>>(val);
|
||||||
|
return JS_NewArrayBufferCopy(ctx, (uint8_t *)buf.data(), buf.size() * 8);
|
||||||
|
}
|
||||||
|
if (std::holds_alternative<std::vector<double>>(val))
|
||||||
|
{
|
||||||
|
auto buf = std::get<std::vector<double>>(val);
|
||||||
|
JSValue array = JS_NewArray(ctx);
|
||||||
|
cache[val] = array;
|
||||||
|
auto size = (uint32_t)buf.size();
|
||||||
|
for (uint32_t i = 0; i < size; i++)
|
||||||
|
JS_DefinePropertyValue(
|
||||||
|
ctx, array, JS_NewAtomUInt32(ctx, i), JS_NewFloat64(ctx, buf[i]),
|
||||||
|
JS_PROP_C_W_E);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
if (std::holds_alternative<flutter::EncodableList>(val))
|
||||||
|
{
|
||||||
|
auto list = std::get<flutter::EncodableList>(val);
|
||||||
|
JSValue array = JS_NewArray(ctx);
|
||||||
|
cache[val] = array;
|
||||||
|
auto size = (uint32_t)list.size();
|
||||||
|
for (uint32_t i = 0; i < size; i++)
|
||||||
|
JS_DefinePropertyValue(
|
||||||
|
ctx, array, JS_NewAtomUInt32(ctx, i), dartToJs(ctx, list[i], cache),
|
||||||
|
JS_PROP_C_W_E);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
if (std::holds_alternative<flutter::EncodableMap>(val))
|
||||||
|
{
|
||||||
|
auto map = std::get<flutter::EncodableMap>(val);
|
||||||
|
JSValue obj = JS_NewObject(ctx);
|
||||||
|
cache[val] = obj;
|
||||||
|
for (auto iter = map.begin(); iter != map.end(); ++iter)
|
||||||
|
JS_DefinePropertyValue(
|
||||||
|
ctx, obj, JS_ValueToAtom(ctx, dartToJs(ctx, iter->first, cache)), dartToJs(ctx, iter->second, cache),
|
||||||
|
JS_PROP_C_W_E);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter::EncodableValue jsToDart(Value val, std::unordered_map<Value, flutter::EncodableValue> cache)
|
||||||
|
{
|
||||||
|
if (cache.find(val) != cache.end())
|
||||||
|
return cache[val];
|
||||||
|
if (JS_IsBool(val.v))
|
||||||
|
return (bool)val;
|
||||||
|
if (JS_IsNumber(val.v))
|
||||||
|
return (double)val;
|
||||||
|
if (JS_IsString(val.v))
|
||||||
|
return (std::string)val;
|
||||||
|
{ // ArrayBuffer
|
||||||
|
size_t size;
|
||||||
|
uint8_t *buf = JS_GetArrayBuffer(val.ctx, &size, val.v);
|
||||||
|
if (buf)
|
||||||
|
return (std::vector<uint8_t>(buf, buf + size));
|
||||||
|
}
|
||||||
|
flutter::EncodableValue ret;
|
||||||
|
if (JS_IsUndefined(val.v) || JS_IsNull(val.v) || JS_IsUninitialized(val.v))
|
||||||
|
goto exception;
|
||||||
|
if (JS_IsObject(val.v))
|
||||||
|
{
|
||||||
|
if (JS_IsFunction(val.ctx, val.v))
|
||||||
|
{
|
||||||
|
flutter::EncodableMap retMap;
|
||||||
|
retMap[std::string("__js_function__")] = (int64_t) new JSValue { JS_DupValue(val.ctx, val.v) };
|
||||||
|
ret = retMap;
|
||||||
|
}
|
||||||
|
else if (JS_IsArray(val.ctx, val.v) > 0)
|
||||||
|
{
|
||||||
|
flutter::EncodableList retList;
|
||||||
|
cache[val] = retList;
|
||||||
|
uint32_t arrlen = (uint32_t)val["length"];
|
||||||
|
for (uint32_t i = 0; i < arrlen; i++)
|
||||||
|
{
|
||||||
|
retList.push_back(jsToDart(val[i], cache));
|
||||||
|
}
|
||||||
|
ret = retList;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qjs::JSPropertyEnum *ptab;
|
||||||
|
uint32_t plen;
|
||||||
|
if (JS_GetOwnPropertyNames(val.ctx, &ptab, &plen, val.v, -1))
|
||||||
|
goto exception;
|
||||||
|
flutter::EncodableMap retMap;
|
||||||
|
cache[val] = retMap;
|
||||||
|
for (uint32_t i = 0; i < plen; i++)
|
||||||
|
{
|
||||||
|
retMap[jsToDart({val.ctx, JS_AtomToValue(val.ctx, ptab[i].atom)}, cache)] =
|
||||||
|
jsToDart({val.ctx, JS_GetProperty(val.ctx, val.v, ptab[i].atom)}, cache);
|
||||||
|
JS_FreeAtom(val.ctx, ptab[i].atom);
|
||||||
|
}
|
||||||
|
js_free(val.ctx, ptab);
|
||||||
|
ret = retMap;
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
exception:
|
||||||
|
ret = flutter::EncodableValue();
|
||||||
|
done:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} // namespace qjs
|
@@ -8,7 +8,7 @@
|
|||||||
#include <flutter/standard_method_codec.h>
|
#include <flutter/standard_method_codec.h>
|
||||||
#include <flutter/method_result_functions.h>
|
#include <flutter/method_result_functions.h>
|
||||||
|
|
||||||
#include "../cxx/js_engine.hpp"
|
#include "dart_js_wrapper.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -30,17 +30,17 @@ namespace
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<flutter::MethodChannel<flutter::EncodableValue>> channel;
|
std::shared_ptr<flutter::MethodChannel<flutter::EncodableValue>> channel;
|
||||||
std::future<qjs::JSFutureReturn> invokeChannelMethod(std::string name, std::string args)
|
std::promise<qjs::JSFutureReturn> *invokeChannelMethod(std::string name, qjs::Value args)
|
||||||
{
|
{
|
||||||
auto promise = new std::promise<qjs::JSFutureReturn>();
|
auto promise = new std::promise<qjs::JSFutureReturn>();
|
||||||
channel->InvokeMethod(
|
channel->InvokeMethod(
|
||||||
name,
|
name,
|
||||||
std::make_unique<flutter::EncodableValue>(args),
|
std::make_unique<flutter::EncodableValue>(qjs::jsToDart(args, std::unordered_map<qjs::Value, flutter::EncodableValue>())),
|
||||||
std::make_unique<flutter::MethodResultFunctions<flutter::EncodableValue>>(
|
std::make_unique<flutter::MethodResultFunctions<flutter::EncodableValue>>(
|
||||||
(flutter::ResultHandlerSuccess<flutter::EncodableValue>)[promise](
|
(flutter::ResultHandlerSuccess<flutter::EncodableValue>)[promise](
|
||||||
const flutter::EncodableValue *result) {
|
const flutter::EncodableValue *result) {
|
||||||
promise->set_value((qjs::JSFutureReturn)[rep = std::get<std::string>(*result)](qjs::JSContext * ctx) {
|
promise->set_value((qjs::JSFutureReturn)[result = result ? *result : flutter::EncodableValue()](qjs::JSContext * ctx) {
|
||||||
qjs::JSValue *ret = new qjs::JSValue{JS_NewString(ctx, rep.c_str())};
|
qjs::JSValue *ret = new qjs::JSValue{qjs::dartToJs(ctx, result, std::unordered_map<flutter::EncodableValue, qjs::JSValue>())};
|
||||||
return qjs::JSOSFutureArgv{1, ret};
|
return qjs::JSOSFutureArgv{1, ret};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -59,7 +59,7 @@ namespace
|
|||||||
return qjs::JSOSFutureArgv{-1, ret};
|
return qjs::JSOSFutureArgv{-1, ret};
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
return promise->get_future();
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@@ -111,7 +111,7 @@ namespace
|
|||||||
if (method_call.method_name().compare("initEngine") == 0)
|
if (method_call.method_name().compare("initEngine") == 0)
|
||||||
{
|
{
|
||||||
engine = new qjs::Engine((qjs::DartChannel)invokeChannelMethod);
|
engine = new qjs::Engine((qjs::DartChannel)invokeChannelMethod);
|
||||||
flutter::EncodableValue response((long)engine);
|
flutter::EncodableValue response = (int64_t)engine;
|
||||||
result->Success(&response);
|
result->Success(&response);
|
||||||
}
|
}
|
||||||
else if (method_call.method_name().compare("evaluate") == 0)
|
else if (method_call.method_name().compare("evaluate") == 0)
|
||||||
@@ -121,9 +121,39 @@ namespace
|
|||||||
std::string name = std::get<std::string>(ValueOrNull(args, "name"));
|
std::string name = std::get<std::string>(ValueOrNull(args, "name"));
|
||||||
auto presult = result.release();
|
auto presult = result.release();
|
||||||
engine->commit(qjs::EngineTask{
|
engine->commit(qjs::EngineTask{
|
||||||
script, name,
|
[script, name](qjs::Context &ctx) {
|
||||||
|
return ctx.eval(script, name.c_str(), JS_EVAL_TYPE_GLOBAL);
|
||||||
|
},
|
||||||
[presult](std::string resolve) {
|
[presult](std::string resolve) {
|
||||||
flutter::EncodableValue response(resolve);
|
flutter::EncodableValue response = resolve;
|
||||||
|
presult->Success(&response);
|
||||||
|
},
|
||||||
|
[presult](std::string reject) {
|
||||||
|
presult->Error("FlutterJSException", reject);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
else if (method_call.method_name().compare("call") == 0)
|
||||||
|
{
|
||||||
|
flutter::EncodableMap args = *std::get_if<flutter::EncodableMap>(method_call.arguments());
|
||||||
|
qjs::JSValue *function = (qjs::JSValue *)std::get<int64_t>(ValueOrNull(args, "function"));
|
||||||
|
flutter::EncodableList arguments = std::get<flutter::EncodableList>(ValueOrNull(args, "arguments"));
|
||||||
|
auto presult = result.release();
|
||||||
|
engine->commit(qjs::EngineTask{
|
||||||
|
[function, arguments](qjs::Context &ctx) {
|
||||||
|
size_t argscount = arguments.size();
|
||||||
|
qjs::JSValue *callargs = new qjs::JSValue[argscount];
|
||||||
|
for (size_t i = 0; i < argscount; i++)
|
||||||
|
{
|
||||||
|
callargs[i] = qjs::dartToJs(ctx.ctx, arguments[i], std::unordered_map<flutter::EncodableValue, qjs::JSValue>());
|
||||||
|
}
|
||||||
|
qjs::JSValue ret = JS_Call(ctx.ctx, *function, qjs::JSValue{qjs::JSValueUnion{0}, qjs::JS_TAG_UNDEFINED}, (int)argscount, callargs);
|
||||||
|
qjs::JS_FreeValue(ctx.ctx, *function);
|
||||||
|
if (qjs::JS_IsException(ret))
|
||||||
|
throw qjs::exception{};
|
||||||
|
return qjs::Value{ctx.ctx, ret};
|
||||||
|
},
|
||||||
|
[presult](std::string resolve) {
|
||||||
|
flutter::EncodableValue response = resolve;
|
||||||
presult->Success(&response);
|
presult->Success(&response);
|
||||||
},
|
},
|
||||||
[presult](std::string reject) {
|
[presult](std::string reject) {
|
||||||
|
Reference in New Issue
Block a user