Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Unified Diff: test/cctest/test-ffi.cc

Issue 2250863002: WIP: prototype ffi support (from 2084663004) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Rebase Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « test/cctest/cctest.gyp ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
+}
« no previous file with comments | « test/cctest/cctest.gyp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698