ffi wrapper

This commit is contained in:
ekibun
2020-09-20 15:57:08 +08:00
parent 50c7ff12db
commit 5f9fdac9f4
7 changed files with 1104 additions and 65 deletions

263
cxx/ffi.cpp Normal file
View File

@@ -0,0 +1,263 @@
/*
* @Description:
* @Author: ekibun
* @Date: 2020-09-06 18:32:45
* @LastEditors: ekibun
* @LastEditTime: 2020-09-20 15:50:41
*/
#include "quickjs/quickjs.h"
#include <functional>
#ifdef _MSC_VER
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT __attribute__((visibility("default")))
#endif
extern "C"
{
typedef JSValue *JSChannel(JSContext *ctx, const char *method, JSValueConst *argv);
DLLEXPORT JSValue *jsEXCEPTION()
{
return new JSValue{JS_EXCEPTION};
}
DLLEXPORT JSValue *jsUNDEFINED()
{
return new JSValue{JS_UNDEFINED};
}
DLLEXPORT JSValue *jsNULL()
{
return new JSValue{JS_NULL};
}
JSModuleDef *js_module_loader(
JSContext *ctx,
const char *module_name, void *opaque)
{
JSRuntime *rt = JS_GetRuntime(ctx);
JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt);
JSValue val = *channel(ctx, "__load_module__", new JSValue{JS_NewString(ctx, module_name)});
const char *str = JS_ToCString(ctx, val);
JSValue func_val = JS_Eval(ctx, str, strlen(str), module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
JS_FreeCString(ctx, str);
JS_FreeValue(ctx, val);
if (JS_IsException(func_val))
return NULL;
/* the module is already referenced, so we must free it */
JSModuleDef *m = (JSModuleDef *)JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val);
return m;
}
JSValue js_channel(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
JSRuntime *rt = JS_GetRuntime(ctx);
JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt);
const char *str = JS_ToCString(ctx, argv[0]);
JS_DupValue(ctx, *(argv + 1));
JSValue ret = *channel(ctx, str, argv + 1);
JS_FreeValue(ctx, *(argv + 1));
JS_FreeCString(ctx, str);
return ret;
}
DLLEXPORT JSRuntime *jsNewRuntime(JSChannel channel)
{
JSRuntime *rt = JS_NewRuntime();
JS_SetRuntimeOpaque(rt, channel);
JS_SetModuleLoaderFunc(rt, nullptr, js_module_loader, nullptr);
return rt;
}
DLLEXPORT void jsFreeRuntime(JSRuntime *rt)
{
JS_SetRuntimeOpaque(rt, nullptr);
JS_FreeRuntime(rt);
}
DLLEXPORT JSContext *jsNewContext(JSRuntime *rt)
{
JSContext *ctx = JS_NewContext(rt);
JSAtom atom = JS_NewAtom(ctx, "channel");
JSValue globalObject = JS_GetGlobalObject(ctx);
JS_SetProperty(ctx, globalObject, atom, JS_NewCFunction(ctx, js_channel, "channel", 2));
JS_FreeValue(ctx, globalObject);
JS_FreeAtom(ctx, atom);
return ctx;
}
DLLEXPORT void jsFreeContext(JSContext *ctx)
{
JS_FreeContext(ctx);
}
DLLEXPORT JSRuntime *jsGetRuntime(JSContext *ctx)
{
return JS_GetRuntime(ctx);
}
DLLEXPORT JSValue *jsEval(JSContext *ctx, const char *input, size_t input_len, const char *filename, int eval_flags)
{
return new JSValue{JS_Eval(ctx, input, input_len, filename, eval_flags)};
}
DLLEXPORT int32_t jsValueGetTag(JSValue *val)
{
return JS_VALUE_GET_TAG(*val);
}
DLLEXPORT void *jsValueGetPtr(JSValue *val)
{
return JS_VALUE_GET_PTR(*val);
}
DLLEXPORT int32_t jsTagIsFloat64(int32_t tag)
{
return JS_TAG_IS_FLOAT64(tag);
}
DLLEXPORT JSValue *jsNewBool(JSContext *ctx, int val)
{
return new JSValue{JS_NewBool(ctx, val)};
}
DLLEXPORT JSValue *jsNewInt64(JSContext *ctx, int64_t val)
{
return new JSValue{JS_NewInt64(ctx, val)};
}
DLLEXPORT JSValue *jsNewFloat64(JSContext *ctx, double val)
{
return new JSValue{JS_NewFloat64(ctx, val)};
}
DLLEXPORT JSValue *jsNewString(JSContext *ctx, const char *str)
{
return new JSValue{JS_NewString(ctx, str)};
}
DLLEXPORT JSValue *jsNewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)
{
return new JSValue{JS_NewArrayBufferCopy(ctx, buf, len)};
}
DLLEXPORT JSValue *jsNewArray(JSContext *ctx)
{
return new JSValue{JS_NewArray(ctx)};
}
DLLEXPORT JSValue *jsNewObject(JSContext *ctx)
{
return new JSValue{JS_NewObject(ctx)};
}
DLLEXPORT void jsFreeValue(JSContext *ctx, JSValue *v)
{
JS_FreeValue(ctx, *v);
}
DLLEXPORT void jsFreeValueRT(JSRuntime *rt, JSValue *v)
{
JS_FreeValueRT(rt, *v);
}
DLLEXPORT JSValue *jsDupValue(JSContext *ctx, JSValueConst *v)
{
return new JSValue{JS_DupValue(ctx, *v)};
}
DLLEXPORT JSValue *jsDupValueRT(JSRuntime *rt, JSValue *v)
{
return new JSValue{JS_DupValueRT(rt, *v)};
}
DLLEXPORT int32_t jsToBool(JSContext *ctx, JSValueConst *val)
{
return JS_ToBool(ctx, *val);
}
DLLEXPORT int64_t jsToInt64(JSContext *ctx, JSValueConst *val)
{
int64_t p;
JS_ToInt64(ctx, &p, *val);
return p;
}
DLLEXPORT double jsToFloat64(JSContext *ctx, JSValueConst *val)
{
double p;
JS_ToFloat64(ctx, &p, *val);
return p;
}
DLLEXPORT const char *jsToCString(JSContext *ctx, JSValueConst *val)
{
return JS_ToCString(ctx, *val);
}
DLLEXPORT void jsFreeCString(JSContext *ctx, const char *ptr)
{
return JS_FreeCString(ctx, ptr);
}
DLLEXPORT uint8_t *jsGetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst *obj)
{
return JS_GetArrayBuffer(ctx, psize, *obj);
}
DLLEXPORT int32_t jsIsFunction(JSContext *ctx, JSValueConst *val)
{
return JS_IsFunction(ctx, *val);
}
DLLEXPORT int32_t jsIsArray(JSContext *ctx, JSValueConst *val)
{
return JS_IsArray(ctx, *val);
}
DLLEXPORT void deleteJSValue(JSValueConst *val)
{
delete val;
}
DLLEXPORT JSValue *jsGetProperty(JSContext *ctx, JSValueConst *this_obj,
JSAtom prop)
{
return new JSValue{JS_GetProperty(ctx, *this_obj, prop)};
}
DLLEXPORT int jsDefinePropertyValue(JSContext *ctx, JSValueConst *this_obj,
JSAtom prop, JSValue *val, int flags)
{
return JS_DefinePropertyValue(ctx, *this_obj, prop, *val, flags);
}
DLLEXPORT void jsFreeAtom(JSContext *ctx, JSAtom v)
{
JS_FreeAtom(ctx, v);
}
DLLEXPORT JSAtom jsValueToAtom(JSContext *ctx, JSValueConst *val)
{
return JS_ValueToAtom(ctx, *val);
}
DLLEXPORT JSValue *jsAtomToValue(JSContext *ctx, JSAtom val)
{
return new JSValue{JS_AtomToValue(ctx, val)};
}
DLLEXPORT int jsGetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
uint32_t *plen, JSValueConst *obj, int flags)
{
return JS_GetOwnPropertyNames(ctx, ptab, plen, *obj, flags);
}
DLLEXPORT JSAtom jsPropertyEnumGetAtom(JSPropertyEnum *ptab, int i)
{
return ptab[i].atom;
}
}

594
lib/ffi.dart Normal file
View File

@@ -0,0 +1,594 @@
/*
* @Description:
* @Author: ekibun
* @Date: 2020-09-19 10:29:04
* @LastEditors: ekibun
* @LastEditTime: 2020-09-20 15:41:02
*/
import 'dart:ffi';
import 'package:ffi/ffi.dart';
abstract class JSRef {
void release();
}
/// JS_Eval() flags
class JSEvalType {
static const GLOBAL = 0 << 0;
static const MODULE = 1 << 0;
}
class JSProp {
static const CONFIGURABLE = (1 << 0);
static const WRITABLE = (1 << 1);
static const ENUMERABLE = (1 << 2);
static const C_W_E = (CONFIGURABLE | WRITABLE | ENUMERABLE);
}
class JSTag {
static const FIRST = -11; /* first negative tag */
static const BIG_DECIMAL = -11;
static const BIG_INT = -10;
static const BIG_FLOAT = -9;
static const SYMBOL = -8;
static const STRING = -7;
static const MODULE = -3; /* used internally */
static const FUNCTION_BYTECODE = -2; /* used internally */
static const OBJECT = -1;
static const INT = 0;
static const BOOL = 1;
static const NULL = 2;
static const UNDEFINED = 3;
static const UNINITIALIZED = 4;
static const CATCH_OFFSET = 5;
static const EXCEPTION = 6;
static const FLOAT64 = 7;
}
final DynamicLibrary qjsLib = DynamicLibrary.open("test/lib/build/Debug/ffi_library.dll");
/// JSValue *jsEXCEPTION()
final Pointer Function() jsEXCEPTION =
qjsLib.lookup<NativeFunction<Pointer Function()>>("jsEXCEPTION").asFunction();
/// JSValue *jsUNDEFINED()
final Pointer Function() jsUNDEFINED =
qjsLib.lookup<NativeFunction<Pointer Function()>>("jsUNDEFINED").asFunction();
/// JSRuntime *jsNewRuntime(JSChannel channel)
final Pointer Function(
Pointer<NativeFunction<Pointer Function(Pointer ctx, Pointer method, Pointer argv)>>,
) _jsNewRuntime = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
)>>("jsNewRuntime")
.asFunction();
typedef JSChannel = Pointer Function(Pointer ctx, String method, Pointer argv);
class RuntimeOpaque {
JSChannel channel;
List<JSRef> ref = List();
}
final Map<Pointer, RuntimeOpaque> runtimeOpaques = Map();
Pointer channelDispacher(Pointer ctx, Pointer<Utf8> method, Pointer argv) {
return runtimeOpaques[jsGetRuntime(ctx)].channel(ctx, Utf8.fromUtf8(method), argv);
}
Pointer jsNewRuntime(
JSChannel callback,
) {
var rt = _jsNewRuntime(Pointer.fromFunction(channelDispacher));
runtimeOpaques[rt] = RuntimeOpaque()..channel = callback;
return rt;
}
/// void jsFreeRuntime(JSRuntime *rt)
final void Function(
Pointer,
) _jsFreeRuntime = qjsLib
.lookup<
NativeFunction<
Void Function(
Pointer,
)>>("jsFreeRuntime")
.asFunction();
void jsFreeRuntime(
Pointer rt,
) {
runtimeOpaques[rt]?.ref?.forEach((val) {
val.release();
});
runtimeOpaques.remove(rt);
_jsFreeRuntime(rt);
}
/// JSContext *jsNewContext(JSRuntime *rt)
final Pointer Function(
Pointer rt,
) jsNewContext = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
)>>("jsNewContext")
.asFunction();
/// void jsFreeContext(JSContext *ctx)
final void Function(
Pointer,
) jsFreeContext = qjsLib
.lookup<
NativeFunction<
Void Function(
Pointer,
)>>("jsFreeContext")
.asFunction();
/// JSRuntime *jsGetRuntime(JSContext *ctx)
final Pointer Function(
Pointer,
) jsGetRuntime = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
)>>("jsGetRuntime")
.asFunction();
/// JSValue *jsEval(JSContext *ctx, const char *input, size_t input_len, const char *filename, int eval_flags)
final Pointer Function(
Pointer ctx,
Pointer<Utf8> input,
int inputLen,
Pointer<Utf8> filename,
int evalFlags,
) _jsEval = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Pointer<Utf8>,
Int64,
Pointer<Utf8>,
Int32,
)>>("jsEval")
.asFunction();
Pointer jsEval(
Pointer ctx,
String input,
String filename,
int evalFlags,
) {
var utf8input = Utf8.toUtf8(input);
var utf8filename = Utf8.toUtf8(filename);
var val = _jsEval(ctx, utf8input, Utf8.strlen(utf8input), utf8filename, evalFlags);
free(utf8input);
free(utf8filename);
return val;
}
/// DLLEXPORT int32_t jsValueGetTag(JSValue *val)
final int Function(
Pointer val,
) jsValueGetTag = qjsLib
.lookup<
NativeFunction<
Int32 Function(
Pointer,
)>>("jsValueGetTag")
.asFunction();
/// void *jsValueGetPtr(JSValue *val)
final Pointer Function(
Pointer val,
) jsValueGetPtr = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
)>>("jsValueGetPtr")
.asFunction();
/// DLLEXPORT bool jsTagIsFloat64(int32_t tag)
final int Function(
int val,
) jsTagIsFloat64 = qjsLib
.lookup<
NativeFunction<
Int32 Function(
Int32,
)>>("jsTagIsFloat64")
.asFunction();
/// JSValue *jsNewBool(JSContext *ctx, int val)
final Pointer Function(
Pointer ctx,
int val,
) jsNewBool = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Int32,
)>>("jsNewBool")
.asFunction();
/// JSValue *jsNewInt64(JSContext *ctx, int64_t val)
final Pointer Function(
Pointer ctx,
int val,
) jsNewInt64 = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Int64,
)>>("jsNewInt64")
.asFunction();
/// JSValue *jsNewFloat64(JSContext *ctx, double val)
final Pointer Function(
Pointer ctx,
double val,
) jsNewFloat64 = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Double,
)>>("jsNewFloat64")
.asFunction();
/// JSValue *jsNewString(JSContext *ctx, const char *str)
final Pointer Function(
Pointer ctx,
Pointer<Utf8> str,
) _jsNewString = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Pointer<Utf8>,
)>>("jsNewString")
.asFunction();
Pointer jsNewString(
Pointer ctx,
String str,
) {
var utf8str = Utf8.toUtf8(str);
return _jsNewString(ctx, utf8str);
}
/// JSValue *jsNewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)
final Pointer Function(
Pointer ctx,
Pointer<Uint8> buf,
int len,
) jsNewArrayBufferCopy = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Pointer<Uint8>,
Uint64,
)>>("jsNewArrayBufferCopy")
.asFunction();
/// JSValue *jsNewArray(JSContext *ctx)
final Pointer Function(
Pointer ctx,
) jsNewArray = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
)>>("jsNewArray")
.asFunction();
/// JSValue *jsNewObject(JSContext *ctx)
final Pointer Function(
Pointer ctx,
) jsNewObject = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
)>>("jsNewObject")
.asFunction();
/// void jsFreeValue(JSContext *ctx, JSValue *val)
final void Function(
Pointer ctx,
Pointer val,
) jsFreeValue = qjsLib
.lookup<
NativeFunction<
Void Function(
Pointer,
Pointer,
)>>("jsFreeValue")
.asFunction();
/// void jsFreeValueRT(JSRuntime *rt, JSValue *v)
final void Function(
Pointer rt,
Pointer val,
) jsFreeValueRT = qjsLib
.lookup<
NativeFunction<
Void Function(
Pointer,
Pointer,
)>>("jsFreeValueRT")
.asFunction();
/// JSValue *jsDupValue(JSContext *ctx, JSValueConst *v)
final Pointer Function(
Pointer ctx,
Pointer val,
) jsDupValue = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Pointer,
)>>("jsDupValue")
.asFunction();
/// JSValue *jsDupValueRT(JSRuntime *rt, JSValue *v)
final Pointer Function(
Pointer rt,
Pointer val,
) jsDupValueRT = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Pointer,
)>>("jsDupValueRT")
.asFunction();
/// int32_t jsToBool(JSContext *ctx, JSValueConst *val)
final int Function(
Pointer ctx,
Pointer val,
) jsToBool = qjsLib
.lookup<
NativeFunction<
Int32 Function(
Pointer,
Pointer,
)>>("jsToBool")
.asFunction();
/// int64_t jsToFloat64(JSContext *ctx, JSValueConst *val)
final int Function(
Pointer ctx,
Pointer val,
) jsToInt64 = qjsLib
.lookup<
NativeFunction<
Int64 Function(
Pointer,
Pointer,
)>>("jsToInt64")
.asFunction();
/// double jsToFloat64(JSContext *ctx, JSValueConst *val)
final double Function(
Pointer ctx,
Pointer val,
) jsToFloat64 = qjsLib
.lookup<
NativeFunction<
Double Function(
Pointer,
Pointer,
)>>("jsToFloat64")
.asFunction();
/// const char *jsToCString(JSContext *ctx, JSValue *val)
final Pointer<Utf8> Function(
Pointer ctx,
Pointer val,
) _jsToCString = qjsLib
.lookup<
NativeFunction<
Pointer<Utf8> Function(
Pointer,
Pointer,
)>>("jsToCString")
.asFunction();
/// void jsFreeCString(JSContext *ctx, const char *ptr)
final void Function(
Pointer ctx,
Pointer<Utf8> val,
) jsFreeCString = qjsLib
.lookup<
NativeFunction<
Void Function(
Pointer,
Pointer<Utf8>,
)>>("jsFreeCString")
.asFunction();
String jsToCString(
Pointer ctx,
Pointer val,
) {
var ptr = _jsToCString(ctx, val);
var str = Utf8.fromUtf8(ptr);
jsFreeCString(ctx, ptr);
return str;
}
/// uint8_t *jsGetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst *obj)
final Pointer<Uint8> Function(
Pointer ctx,
Pointer<Int64> psize,
Pointer val,
) jsGetArrayBuffer = qjsLib
.lookup<
NativeFunction<
Pointer<Uint8> Function(
Pointer,
Pointer<Int64>,
Pointer,
)>>("jsGetArrayBuffer")
.asFunction();
/// int32_t jsIsFunction(JSContext *ctx, JSValueConst *val)
final int Function(
Pointer ctx,
Pointer val,
) jsIsFunction = qjsLib
.lookup<
NativeFunction<
Int32 Function(
Pointer,
Pointer,
)>>("jsIsFunction")
.asFunction();
/// int32_t jsIsArray(JSContext *ctx, JSValueConst *val)
final int Function(
Pointer ctx,
Pointer val,
) jsIsArray = qjsLib
.lookup<
NativeFunction<
Int32 Function(
Pointer,
Pointer,
)>>("jsIsArray")
.asFunction();
/// void deleteJSValue(JSValueConst *val)
final void Function(
Pointer val,
) deleteJSValue = qjsLib
.lookup<
NativeFunction<
Void Function(
Pointer,
)>>("deleteJSValue")
.asFunction();
/// JSValue *jsGetProperty(JSContext *ctx, JSValueConst *this_obj,
/// JSAtom prop)
final Pointer Function(
Pointer ctx,
Pointer thisObj,
int prop,
) jsGetProperty = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Pointer,
Uint32,
)>>("jsGetProperty")
.asFunction();
/// int jsDefinePropertyValue(JSContext *ctx, JSValueConst *this_obj,
/// JSAtom prop, JSValue *val, int flags)
final int Function(
Pointer ctx,
Pointer thisObj,
int prop,
Pointer val,
int flag
) jsDefinePropertyValue = qjsLib
.lookup<
NativeFunction<
Int32 Function(
Pointer,
Pointer,
Uint32,
Pointer,
Int32,
)>>("jsDefinePropertyValue")
.asFunction();
/// void jsFreeAtom(JSContext *ctx, JSAtom v)
final Pointer Function(
Pointer ctx,
int v,
) jsFreeAtom = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Uint32,
)>>("jsFreeAtom")
.asFunction();
/// JSAtom jsValueToAtom(JSContext *ctx, JSValueConst *val)
final int Function(
Pointer ctx,
Pointer val,
) jsValueToAtom = qjsLib
.lookup<
NativeFunction<
Uint32 Function(
Pointer,
Pointer,
)>>("jsValueToAtom")
.asFunction();
/// JSValue *jsAtomToValue(JSContext *ctx, JSAtom val)
final Pointer Function(
Pointer ctx,
int val,
) jsAtomToValue = qjsLib
.lookup<
NativeFunction<
Pointer Function(
Pointer,
Uint32,
)>>("jsAtomToValue")
.asFunction();
/// int jsGetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
/// uint32_t *plen, JSValueConst *obj, int flags)
final int Function(
Pointer ctx,
Pointer<Pointer> ptab,
Pointer<Uint32> plen,
Pointer obj,
int flags,
) jsGetOwnPropertyNames = qjsLib
.lookup<
NativeFunction<
Int32 Function(
Pointer,
Pointer<Pointer>,
Pointer<Uint32>,
Pointer,
Int32,
)>>("jsGetOwnPropertyNames")
.asFunction();
/// JSAtom jsPropertyEnumGetAtom(JSPropertyEnum *ptab, int i)
final int Function(
Pointer ptab,
int i,
) jsPropertyEnumGetAtom = qjsLib
.lookup<
NativeFunction<
Uint32 Function(
Pointer,
Int32,
)>>("jsPropertyEnumGetAtom")
.asFunction();

177
lib/wrapper.dart Normal file
View File

@@ -0,0 +1,177 @@
/*
* @Description:
* @Author: ekibun
* @Date: 2020-09-19 22:07:47
* @LastEditors: ekibun
* @LastEditTime: 2020-09-20 15:41:16
*/
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'ffi.dart';
class JSFunction extends JSRef {
Pointer val;
Pointer ctx;
JSFunction(this.ctx, Pointer val) {
Pointer rt = jsGetRuntime(ctx);
this.val = jsDupValue(ctx, val);
runtimeOpaques[rt]?.ref?.add(this);
}
@override
void release() {
if (val != null) {
jsFreeValue(ctx, val);
deleteJSValue(val);
val = null;
}
}
@override
noSuchMethod(Invocation invocation) {
return super.noSuchMethod(invocation);
}
}
Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
if (cache == null) cache = Map();
if (val is bool) return jsNewBool(ctx, val ? 1 : 0);
if (val is int) return jsNewInt64(ctx, val);
if (val is double) return jsNewFloat64(ctx, val);
if (val is String) return jsNewString(ctx, val);
if (val is Uint8List) {
var ptr = allocate<Uint8>(count: val.length);
var byteList = ptr.asTypedList(val.length);
byteList.setAll(0, val);
var ret = jsNewArrayBufferCopy(ctx, ptr, val.length);
free(ptr);
return ret;
}
if (cache.containsKey(val)) {
return cache[val];
}
if (val is JSFunction) {
return jsDupValue(ctx, val.val);
}
if (val is List) {
Pointer ret = jsNewArray(ctx);
cache[val] = ret;
for (int i = 0; i < val.length; ++i) {
var jsAtomVal = jsNewInt64(ctx, i);
var jsAtom = jsValueToAtom(ctx, jsAtomVal);
jsDefinePropertyValue(
ctx,
ret,
jsAtom,
dartToJs(ctx, val[i], cache: cache),
JSProp.C_W_E,
);
jsFreeAtom(ctx, jsAtom);
jsFreeValue(ctx, jsAtomVal);
deleteJSValue(jsAtomVal);
}
return ret;
}
if (val is Map) {
Pointer ret = jsNewObject(ctx);
cache[val] = ret;
for (MapEntry<dynamic, dynamic> entry in val.entries){
var jsAtomVal = dartToJs(ctx, entry.key, cache: cache);
var jsAtom = jsValueToAtom(ctx, jsAtomVal);
jsDefinePropertyValue(
ctx,
ret,
jsAtom,
dartToJs(ctx, entry.value, cache: cache),
JSProp.C_W_E,
);
jsFreeAtom(ctx, jsAtom);
jsFreeValue(ctx, jsAtomVal);
deleteJSValue(jsAtomVal);
}
return ret;
}
return jsUNDEFINED();
}
dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
if (cache == null) cache = Map();
int tag = jsValueGetTag(val);
if (jsTagIsFloat64(tag) != 0) {
return jsToFloat64(ctx, val);
}
switch (tag) {
case JSTag.BOOL:
return jsToBool(ctx, val) != 0;
case JSTag.INT:
return jsToInt64(ctx, val);
case JSTag.STRING:
return jsToCString(ctx, val);
case JSTag.OBJECT:
Pointer<Int64> psize = allocate<Int64>();
Pointer<Uint8> buf = jsGetArrayBuffer(ctx, psize, val);
int size = psize.value;
free(psize);
if (buf.address != 0) {
return buf.asTypedList(size);
}
int valptr = jsValueGetPtr(val).address;
if (cache.containsKey(valptr)) {
return cache[valptr];
}
if (jsIsFunction(ctx, val) != 0) {
return JSFunction(ctx, val);
} else if (jsIsArray(ctx, val) != 0) {
var jsAtomVal = jsNewString(ctx, "length");
var jsAtom = jsValueToAtom(ctx, jsAtomVal);
var jslength = jsGetProperty(ctx, val, jsAtom);
jsFreeAtom(ctx, jsAtom);
jsFreeValue(ctx, jsAtomVal);
deleteJSValue(jsAtomVal);
int length = jsToInt64(ctx, jslength);
deleteJSValue(jslength);
List<dynamic> ret = List();
cache[valptr] = ret;
for (int i = 0; i < length; ++i) {
var jsAtomVal = jsNewInt64(ctx, i);
var jsAtom = jsValueToAtom(ctx, jsAtomVal);
var jsProp = jsGetProperty(ctx, val, jsAtom);
jsFreeAtom(ctx, jsAtom);
jsFreeValue(ctx, jsAtomVal);
deleteJSValue(jsAtomVal);
ret.add(jsToDart(ctx, jsProp, cache: cache));
jsFreeValue(ctx, jsProp);
deleteJSValue(jsProp);
}
return ret;
} else {
Pointer<Pointer> ptab = allocate<Pointer>();
Pointer<Uint32> plen = allocate<Uint32>();
if (jsGetOwnPropertyNames(ctx, ptab, plen, val, -1) != 0) return null;
int len = plen.value;
free(plen);
Map<dynamic, dynamic> ret = Map();
cache[valptr] = ret;
for (int i = 0; i < len; ++i) {
var jsAtom = jsPropertyEnumGetAtom(ptab.value, i);
var jsAtomValue = jsAtomToValue(ctx, jsAtom);
var jsProp = jsGetProperty(ctx, val, jsAtom);
ret[jsToDart(ctx, jsAtomValue, cache: cache)] = jsToDart(ctx, jsProp, cache: cache);
jsFreeValue(ctx, jsAtomValue);
deleteJSValue(jsAtomValue);
jsFreeValue(ctx, jsProp);
deleteJSValue(jsProp);
jsFreeAtom(ctx, jsAtom);
}
free(ptab);
return ret;
}
break;
default:
}
return null;
}

View File

@@ -3,31 +3,75 @@
* @Author: ekibun
* @Date: 2020-09-06 13:02:46
* @LastEditors: ekibun
* @LastEditTime: 2020-09-13 22:59:06
* @LastEditTime: 2020-09-20 15:55:50
*/
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'dart:convert';
import 'dart:io';
void main() {
final DynamicLibrary qjsLib = DynamicLibrary.open("test/lib/build/Debug/ffi_library.dll");
print(qjsLib);
// JSRuntime *js_NewRuntime(void);
final Pointer Function() jsNewRuntime =
qjsLib.lookup<NativeFunction<Pointer Function()>>("jsNewRuntime").asFunction();
final rt = jsNewRuntime();
print(rt);
// JSContext *js_NewContext(JSRuntime *rt);
final Pointer Function(Pointer rt) jsNewContext =
qjsLib.lookup<NativeFunction<Pointer Function(Pointer)>>("jsNewContext").asFunction();
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_qjs/ffi.dart';
import 'package:flutter_qjs/wrapper.dart';
void main() async {
test('make', () async {
final utf8Encoding = Encoding.getByName('utf-8');
final cmakePath =
"C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin/cmake.exe";
final buildDir = "./build";
var result = Process.runSync(
cmakePath,
['-S', './', '-B', buildDir],
workingDirectory: 'test/lib',
stdoutEncoding: utf8Encoding,
stderrEncoding: utf8Encoding,
);
stdout.write(result.stdout);
stderr.write(result.stderr);
expect(result.exitCode, 0);
result = Process.runSync(
cmakePath,
['--build', buildDir, '--verbose'],
workingDirectory: 'test/lib',
stdoutEncoding: utf8Encoding,
stderrEncoding: utf8Encoding,
);
stdout.write(result.stdout);
stderr.write(result.stderr);
expect(result.exitCode, 0);
});
test('jsToDart', () async {
final rt = jsNewRuntime((ctx, method, argv) {
var argvs = jsToDart(ctx, argv);
print([method, argvs]);
return dartToJs(ctx, [
argvs,
{
[233, 2]: {}
}
]);
});
final ctx = jsNewContext(rt);
print(ctx);
// JSValue *js_Eval(JSContext *ctx, const char *input, const char *filename, int eval_flags)
final Pointer Function(Pointer rt, Pointer<Utf8> input, Pointer<Utf8> filename, int evalFlags) jsEval =
qjsLib.lookup<NativeFunction<Pointer Function(Pointer,Pointer<Utf8>,Pointer<Utf8>, Int32)>>("jsEval").asFunction();
final jsval = jsEval(ctx, Utf8.toUtf8("`hello \${'world'}!`"), Utf8.toUtf8("<eval>"), 0);
// const char *js_ToCString(JSContext *ctx, JSValue *val)
final Pointer<Utf8> Function(Pointer rt, Pointer val) jsToCString =
qjsLib.lookup<NativeFunction<Pointer<Utf8> Function(Pointer,Pointer)>>("jsToCString").asFunction();
final str = Utf8.fromUtf8(jsToCString(ctx, jsval));
print(str);
final jsval = jsEval(
ctx,
"""
const a = {};
a.a = a;
channel('channel', [
0.1, true, false, 1, "world",
new ArrayBuffer(2),
()=>'hello',
a
]);
""",
"<eval>",
JSEvalType.GLOBAL,
);
print(jsToDart(ctx, jsval));
jsFreeValue(ctx, jsval);
deleteJSValue(jsval);
jsFreeContext(ctx);
jsFreeRuntime(rt);
});
}

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
project(ffi_library LANGUAGES CXX)
add_library(ffi_library SHARED ffi.cpp)
add_library(ffi_library SHARED ${CMAKE_CURRENT_SOURCE_DIR}/../../cxx/ffi.cpp)
# quickjs
set(QUICK_JS_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../cxx/quickjs)

View File

@@ -1,39 +0,0 @@
/*
* @Description:
* @Author: ekibun
* @Date: 2020-09-06 18:32:45
* @LastEditors: ekibun
* @LastEditTime: 2020-09-13 17:26:29
*/
#include "../../cxx/quickjs/quickjs.h"
#include <cstring>
#ifdef _MSC_VER
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT __attribute__((visibility("default")))
#endif
extern "C"
{
DLLEXPORT JSRuntime *jsNewRuntime()
{
return JS_NewRuntime();
}
DLLEXPORT JSContext *jsNewContext(JSRuntime *rt)
{
return JS_NewContext(rt);
}
DLLEXPORT JSValue *jsEval(JSContext *ctx, const char *input, const char *filename, int eval_flags)
{
return new JSValue{JS_Eval(ctx, input, strlen(input), filename, eval_flags)};
}
DLLEXPORT const char *jsToCString(JSContext *ctx, JSValue *val)
{
return JS_ToCString(ctx, *val);
}
}