Index: test/cctest/test-ffi.cc |
diff --git a/test/cctest/test-ffi.cc b/test/cctest/test-ffi.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b4358427c776e6383f6f00a1eb00e7288c8e6d06 |
--- /dev/null |
+++ b/test/cctest/test-ffi.cc |
@@ -0,0 +1,531 @@ |
+// Copyright 2016 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <math.h> |
+#include <stdint.h> |
+#include <stdlib.h> |
+#include <string.h> |
+ |
+#include "src/codegen.h" |
+#include "src/compiler/ffi-compiler.h" |
+ |
+#include "test/cctest/cctest.h" |
+ |
+using namespace v8::base; |
+using namespace v8::internal; |
+using namespace v8::internal::compiler; |
+ |
+static int foo() { |
+ printf("hello world from native code\n"); |
+ return 42; |
+} |
+ |
+TEST(Run_FFI_Hello) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ Handle<String> name = isolate->factory()->InternalizeUtf8String("foo"); |
+ Handle<Object> undefined = isolate->factory()->undefined_value(); |
+ |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}}; |
+ static v8::internal::ffi::FFISignature sig_foo(1, 0, reps); |
+ v8::internal::ffi::NativeFunction func = {&sig_foo, |
+ reinterpret_cast<uint8_t*>(foo)}; |
+ |
+ Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func); |
+ |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, 0, nullptr).ToHandleChecked(); |
+ |
+ CHECK_EQ(42.0, result->Number()); |
+} |
+ |
+static int add2(int x, int y) { return x + y; } |
+ |
+TEST(Run_FFI_add2) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ Handle<String> name = isolate->factory()->InternalizeUtf8String("add2"); |
+ Handle<Object> undefined = isolate->factory()->undefined_value(); |
+ |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kInt32, NULL}}; |
+ static v8::internal::ffi::FFISignature sig(1, 2, reps); |
+ v8::internal::ffi::NativeFunction func = {&sig, |
+ reinterpret_cast<uint8_t*>(add2)}; |
+ |
+ Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func); |
+ |
+ // Simple math should work. |
+ { |
+ Handle<Object> args[] = {isolate->factory()->NewNumber(1.0), |
+ isolate->factory()->NewNumber(41.0)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ CHECK_EQ(42.0, result->Number()); |
+ } |
+ |
+ // Truncate floating point to integer. |
+ { |
+ Handle<Object> args[] = {isolate->factory()->NewNumber(1.9), |
+ isolate->factory()->NewNumber(41.0)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ CHECK_EQ(42.0, result->Number()); |
+ } |
+ |
+ // INT_MAX + 1 should wrap. |
+ { |
+ Handle<Object> args[] = {isolate->factory()->NewNumber(kMaxInt), |
+ isolate->factory()->NewNumber(1)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ CHECK_EQ(kMinInt, result->Number()); |
+ } |
+ |
+ // INT_MIN + -1 should wrap. |
+ { |
+ Handle<Object> args[] = {isolate->factory()->NewNumber(kMinInt), |
+ isolate->factory()->NewNumber(-1)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ CHECK_EQ(kMaxInt, result->Number()); |
+ } |
+ |
+ // Numbers get truncated to the 32 least significant bits. |
+ { |
+ Handle<Object> args[] = {isolate->factory()->NewNumber(1ull << 40), |
+ isolate->factory()->NewNumber(-1)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ CHECK_EQ(-1, result->Number()); |
+ } |
+ |
+ // String '57' converts to 57. |
+ { |
+ Handle<Object> args[] = { |
+ isolate->factory()->NewStringFromAsciiChecked("57"), |
+ isolate->factory()->NewNumber(41.0)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ CHECK_EQ(98.0, result->Number()); |
+ } |
+ |
+ // String 'foo' converts to 0. |
+ // TODO(ofrobots): should this throw instead? |
+ { |
+ Handle<Object> args[] = { |
+ isolate->factory()->NewStringFromAsciiChecked("foo"), |
+ isolate->factory()->NewNumber(41.0)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ CHECK_EQ(41.0, result->Number()); |
+ } |
+ |
+ // String '58o' converts to 0. |
+ // TODO(ofrobots): should this throw instead? |
+ { |
+ Handle<Object> args[] = { |
+ isolate->factory()->NewStringFromAsciiChecked("58o"), |
+ isolate->factory()->NewNumber(41.0)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ CHECK_EQ(41.0, result->Number()); |
+ } |
+ |
+ // NaN converts to 0. |
+ // TODO(ofrobots): should this throw instead? |
+ { |
+ Handle<Object> args[] = {isolate->factory()->nan_value(), |
+ isolate->factory()->NewNumber(41.0)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ CHECK_EQ(41.0, result->Number()); |
+ } |
+ |
+ // null converts to 0. |
+ // TODO(ofrobots): should this throw instead? |
+ { |
+ Handle<Object> args[] = {isolate->factory()->null_value(), |
+ isolate->factory()->NewNumber(41.0)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ CHECK_EQ(41.0, result->Number()); |
+ } |
+} |
+ |
+static void noop() {} |
+ |
+TEST(Run_FFI_empty_signature) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ Handle<String> name = isolate->factory()->InternalizeUtf8String("noop"); |
+ Handle<Object> undefined = isolate->factory()->undefined_value(); |
+ |
+ ffi::FFITypeElement reps[] = {}; |
+ static v8::internal::ffi::FFISignature sig(0, 0, reps); |
+ v8::internal::ffi::NativeFunction func = {&sig, |
+ reinterpret_cast<uint8_t*>(noop)}; |
+ |
+ Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func); |
+ |
+ Execution::Call(isolate, jsfunc, undefined, 0, NULL).ToHandleChecked(); |
+} |
+ |
+static int add7(int x1, int x2, int x3, int x4, int x5, int x6, int x7) { |
+ return x1 + x2 + x3 + x4 + x5 + x6 + x7; |
+} |
+ |
+TEST(Run_FFI_add7) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ Handle<String> name = isolate->factory()->InternalizeUtf8String("add7"); |
+ Handle<Object> undefined = isolate->factory()->undefined_value(); |
+ |
+ ffi::FFITypeElement reps[] = { |
+ {ffi::FFIType::kInt32, NULL}, {ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kInt32, NULL}, {ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kInt32, NULL}, {ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kInt32, NULL}, {ffi::FFIType::kInt32, NULL}}; |
+ static v8::internal::ffi::FFISignature sig(1, 7, reps); |
+ v8::internal::ffi::NativeFunction func = {&sig, |
+ reinterpret_cast<uint8_t*>(add7)}; |
+ |
+ Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func); |
+ |
+ Handle<Object> args[] = { |
+ isolate->factory()->NewNumber(1.0), isolate->factory()->NewNumber(2.0), |
+ isolate->factory()->NewNumber(3.0), isolate->factory()->NewNumber(4.0), |
+ isolate->factory()->NewNumber(5.0), isolate->factory()->NewNumber(6.0), |
+ isolate->factory()->NewNumber(7.0)}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ |
+ CHECK_EQ(28.0, result->Number()); |
+} |
+ |
+static char* createString() { |
+ int length = 12; |
+ char* str = reinterpret_cast<char*>(malloc(sizeof(char) * length)); |
+ snprintf(str, length, "hello world"); |
+ return str; |
+} |
+ |
+TEST(Run_FFI_strings) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ Handle<String> name = isolate->factory()->InternalizeUtf8String("strlen"); |
+ Handle<Object> undefined = isolate->factory()->undefined_value(); |
+ |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kCharPtr, NULL}}; |
+ static v8::internal::ffi::FFISignature sig(1, 1, reps); |
+ v8::internal::ffi::NativeFunction func = {&sig, |
+ reinterpret_cast<uint8_t*>(strlen)}; |
+ |
+ Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func); |
+ |
+ Handle<Object> args[] = { |
+ isolate->factory()->InternalizeUtf8String("strlenstrlen")}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ |
+ CHECK_EQ(12, result->Number()); |
+ |
+ Handle<String> name2 = |
+ isolate->factory()->InternalizeUtf8String("createString"); |
+ |
+ ffi::FFITypeElement reps2[] = {{ffi::FFIType::kCharPtr, NULL}}; |
+ static v8::internal::ffi::FFISignature sig2(1, 0, reps2); |
+ v8::internal::ffi::NativeFunction func2 = { |
+ &sig2, reinterpret_cast<uint8_t*>(createString)}; |
+ |
+ Handle<JSFunction> jsfunc2 = CompileJSToNativeWrapper(isolate, name2, func2); |
+ |
+ Handle<Object> result2 = |
+ Execution::Call(isolate, jsfunc2, undefined, 0, nullptr) |
+ .ToHandleChecked(); |
+ |
+ CHECK_EQ(strcmp(String::cast(*result2)->ToCString().get(), "hello world"), 0); |
+} |
+ |
+static uint8_t peek(uint8_t* data) { return data[0]; } |
+ |
+TEST(Run_FFI_typedarrays) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ Handle<String> name = isolate->factory()->InternalizeUtf8String("peek"); |
+ Handle<Object> undefined = isolate->factory()->undefined_value(); |
+ |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kTypedArray, NULL}}; |
+ static v8::internal::ffi::FFISignature sig(1, 1, reps); |
+ v8::internal::ffi::NativeFunction func = {&sig, |
+ reinterpret_cast<uint8_t*>(peek)}; |
+ |
+ Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func); |
+ |
+ uint8_t backing_store[1]; |
+ backing_store[0] = 42; |
+ Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); |
+ JSArrayBuffer::Setup(buffer, isolate, true, backing_store, |
+ sizeof(backing_store)); |
+ Handle<JSTypedArray> array = |
+ isolate->factory()->NewJSTypedArray(kExternalUint8Array, buffer, 0, 1); |
+ Handle<Object> args[] = {array}; |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ |
+ CHECK_EQ(42, result->Number()); |
+} |
+ |
+static int takesCb(int (*cb)()) { return cb(); } |
+ |
+TEST(Run_FFI_FunctionArgs) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ Handle<String> name = isolate->factory()->InternalizeUtf8String("foo"); |
+ Handle<Object> undefined = isolate->factory()->undefined_value(); |
+ |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}}; |
+ static v8::internal::ffi::FFISignature sig_foo(1, 0, reps); |
+ static v8::ffi::FFISignature sig_foo_external = {1, 0, reps}; |
+ v8::internal::ffi::NativeFunction func = {&sig_foo, |
+ reinterpret_cast<uint8_t*>(foo)}; |
+ |
+ Handle<JSFunction> jsFoo = CompileJSToNativeWrapper(isolate, name, func); |
+ |
+ v8::ffi::FFISupplementalInfo info = {&sig_foo_external}; |
+ |
+ ffi::FFITypeElement reps2[] = {{ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kFunction, &info}}; |
+ static v8::internal::ffi::FFISignature sig_cb(1, 1, reps2); |
+ v8::internal::ffi::NativeFunction func2 = { |
+ &sig_cb, reinterpret_cast<uint8_t*>(takesCb)}; |
+ |
+ Handle<String> name2 = isolate->factory()->InternalizeUtf8String("takesCb"); |
+ |
+ Handle<JSFunction> jsCb = CompileJSToNativeWrapper(isolate, name2, func2); |
+ |
+ Handle<Object> args[] = {jsFoo}; |
+ |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsCb, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ |
+ printf("%f\n", result->Number()); |
+ |
+ CHECK_EQ(42.0, result->Number()); |
+} |
+ |
+struct struct_type { |
+ int i; |
+ int64_t j; |
+}; |
+ |
+static int takesStruct(struct_type* s) { |
+ return static_cast<int>(s->i) - static_cast<int>(s->j); |
+} |
+ |
+TEST(Run_FFI_StructArgs) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ Handle<Object> undefined = isolate->factory()->undefined_value(); |
+ |
+ v8::ffi::FFITypeElement i_struct_type = {ffi::FFIType::kInt32, NULL}; |
+ v8::ffi::FFITypeElement j_struct_type = {ffi::FFIType::kInt64, NULL}; |
+ v8::ffi::FFIStructElement struct_elem[] = {{"i", &i_struct_type}, |
+ {"j", &j_struct_type}}; |
+ v8::ffi::FFIStructSignature struct_sig = {2, struct_elem}; |
+ v8::ffi::FFISupplementalInfo info = {.struct_elements = &struct_sig}; |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kStructPtr, &info}}; |
+ static v8::internal::ffi::FFISignature sig(1, 1, reps); |
+ v8::internal::ffi::NativeFunction func = { |
+ &sig, reinterpret_cast<uint8_t*>(takesStruct)}; |
+ |
+ Handle<String> name = |
+ isolate->factory()->InternalizeUtf8String("takesStruct"); |
+ |
+ Handle<JSFunction> jsFun = CompileJSToNativeWrapper(isolate, name, func); |
+ |
+ Handle<JSObject> jsObj = isolate->factory()->NewJSObjectWithNullProto(); |
+ JSObject::AddProperty(jsObj, isolate->factory()->InternalizeUtf8String("i"), |
+ isolate->factory()->NewNumber(42), NONE); |
+ JSObject::AddProperty(jsObj, isolate->factory()->InternalizeUtf8String("j"), |
+ isolate->factory()->NewNumber(17), NONE); |
+ |
+ Handle<Object> args[] = {jsObj}; |
+ |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsFun, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ |
+ CHECK_EQ(25.0, result->Number()); |
+} |
+ |
+static int foreign = 1729; |
+ |
+static int* producesForeign() { return &foreign; } |
+ |
+static int consumesForeign(int* foreign) { return *foreign; } |
+ |
+TEST(Run_FFI_Foreigns) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ Handle<Object> undefined = isolate->factory()->undefined_value(); |
+ |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kForeign, NULL}}; |
+ static v8::internal::ffi::FFISignature sig(1, 0, reps); |
+ v8::internal::ffi::NativeFunction func = { |
+ &sig, reinterpret_cast<uint8_t*>(producesForeign)}; |
+ |
+ Handle<String> name = |
+ isolate->factory()->InternalizeUtf8String("producesForeign"); |
+ |
+ Handle<JSFunction> jsProducer = CompileJSToNativeWrapper(isolate, name, func); |
+ |
+ ffi::FFITypeElement reps2[] = {{ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kForeign, NULL}}; |
+ static v8::internal::ffi::FFISignature sig2(1, 1, reps2); |
+ v8::internal::ffi::NativeFunction func2 = { |
+ &sig2, reinterpret_cast<uint8_t*>(consumesForeign)}; |
+ |
+ Handle<String> name2 = |
+ isolate->factory()->InternalizeUtf8String("consumesForeign"); |
+ |
+ Handle<JSFunction> jsConsumer = |
+ CompileJSToNativeWrapper(isolate, name2, func2); |
+ |
+ Handle<Object> intermediate = |
+ Execution::Call(isolate, jsProducer, undefined, 0, {}).ToHandleChecked(); |
+ |
+ Handle<Object> args[] = {intermediate}; |
+ |
+ Handle<Object> result = |
+ Execution::Call(isolate, jsConsumer, undefined, arraysize(args), args) |
+ .ToHandleChecked(); |
+ |
+ CHECK_EQ(1729.0, result->Number()); |
+} |
+ |
+static int constant() { return 1729; } |
+ |
+TEST(Run_FFI_BindNoArgs) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}}; |
+ static v8::internal::ffi::FFISignature sig(1, 0, reps); |
+ v8::internal::ffi::NativeFunction func = { |
+ &sig, reinterpret_cast<uint8_t*>(constant)}; |
+ |
+ Handle<JSArray> arr = isolate->factory()->NewJSArray(0); |
+ |
+ int (*fn_ptr)() = |
+ reinterpret_cast<int (*)()>(FFIFunctionBind(isolate, func, arr)); |
+ |
+ CHECK_EQ(1729, fn_ptr()); |
+} |
+ |
+static void populate(void* data) { *(reinterpret_cast<int*>(data)) = 42; } |
+ |
+TEST(Run_FFI_BufferNoCopy) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ Handle<String> name = isolate->factory()->InternalizeUtf8String("populate"); |
+ Handle<Object> undefined = isolate->factory()->undefined_value(); |
+ |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kBufferNoCopy, NULL}}; |
+ static v8::internal::ffi::FFISignature sig(0, 1, reps); |
+ v8::internal::ffi::NativeFunction func = { |
+ &sig, reinterpret_cast<uint8_t*>(populate)}; |
+ |
+ Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func); |
+ |
+ uint8_t backing_store[1]; |
+ backing_store[0] = 17; |
+ Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); |
+ JSArrayBuffer::Setup(buffer, isolate, true, backing_store, |
+ sizeof(backing_store)); |
+ Handle<JSTypedArray> array = |
+ isolate->factory()->NewJSTypedArray(kExternalUint8Array, buffer, 0, 1); |
+ Handle<Object> args[] = {array}; |
+ USE(Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) |
+ .ToHandleChecked()); |
+ |
+ CHECK_EQ(42, backing_store[0]); |
+} |
+ |
+static int add(int a, int b) { return a + b; } |
+ |
+TEST(Run_FFI_BindArgs) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kInt32, NULL}}; |
+ static v8::internal::ffi::FFISignature sig(1, 2, reps); |
+ v8::internal::ffi::NativeFunction func = {&sig, |
+ reinterpret_cast<uint8_t*>(add)}; |
+ |
+ Handle<JSArray> arr = isolate->factory()->NewJSArray(2); |
+ USE(Object::SetElement(isolate, arr, 0, isolate->factory()->NewNumber(17), |
+ SLOPPY)); |
+ USE(Object::SetElement(isolate, arr, 1, isolate->factory()->NewNumber(25), |
+ SLOPPY)); |
+ |
+ int (*fn_ptr)() = |
+ reinterpret_cast<int (*)()>(FFIFunctionBind(isolate, func, arr)); |
+ |
+ CHECK_EQ(42, fn_ptr()); |
+} |
+ |
+TEST(Run_FFI_Serialization) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ |
+ ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kInt32, NULL}, |
+ {ffi::FFIType::kInt32, NULL}}; |
+ static v8::internal::ffi::FFISignature sig(1, 2, reps); |
+ v8::internal::ffi::NativeFunction func = {&sig, |
+ reinterpret_cast<uint8_t*>(add)}; |
+ |
+ int* (*serializer)(int, int) = |
+ reinterpret_cast<int* (*)(int, int)>(BuildFFISerializer(isolate, func)); |
+ int (*executor)(int*) = reinterpret_cast<int (*)(int*)>( |
+ BuildFFIDeserializedExecutor(isolate, func)); |
+ int* serialized = serializer(17, 25); |
+ |
+ CHECK_EQ(17, serialized[0]); |
+ CHECK_EQ(25, serialized[1]); |
+ |
+ int result = executor(serialized); |
+ CHECK_EQ(42, result); |
+} |