// Copyright 2017 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 "src/api.h" #include "src/codegen.h" #include "src/ffi/ffi-compiler.h" #include "src/objects-inl.h" #include "test/cctest/cctest.h" namespace v8 { namespace internal { namespace ffi { static void hello_world() { printf("hello world from native code\n"); } TEST(Run_FFI_Hello) { Isolate* isolate = CcTest::InitIsolateOnce(); HandleScope scope(isolate); Handle name = isolate->factory()->InternalizeUtf8String("hello_world"); Handle undefined = isolate->factory()->undefined_value(); AccountingAllocator allocator; Zone zone(&allocator, ZONE_NAME); FFISignature::Builder sig_builder(&zone, 0, 0); NativeFunction func = {sig_builder.Build(), reinterpret_cast(hello_world)}; Handle jsfunc = CompileJSToNativeWrapper(isolate, name, func); Handle result = Execution::Call(isolate, jsfunc, undefined, 0, nullptr).ToHandleChecked(); CHECK(result->IsUndefined(isolate)); } static int add2(int x, int y) { return x + y; } TEST(Run_FFI_add2) { Isolate* isolate = CcTest::InitIsolateOnce(); HandleScope scope(isolate); Handle name = isolate->factory()->InternalizeUtf8String("add2"); Handle undefined = isolate->factory()->undefined_value(); AccountingAllocator allocator; Zone zone(&allocator, ZONE_NAME); FFISignature::Builder sig_builder(&zone, 1, 2); sig_builder.AddReturn(FFIType::kInt32); sig_builder.AddParam(FFIType::kInt32); sig_builder.AddParam(FFIType::kInt32); NativeFunction func = {sig_builder.Build(), reinterpret_cast(add2)}; Handle jsfunc = CompileJSToNativeWrapper(isolate, name, func); // Simple math should work. { Handle args[] = {isolate->factory()->NewNumber(1.0), isolate->factory()->NewNumber(41.0)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(42.0, result->Number()); } // Truncate floating point to integer. { Handle args[] = {isolate->factory()->NewNumber(1.9), isolate->factory()->NewNumber(41.0)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(42.0, result->Number()); } // INT_MAX + 1 should wrap. { Handle args[] = {isolate->factory()->NewNumber(kMaxInt), isolate->factory()->NewNumber(1)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(kMinInt, result->Number()); } // INT_MIN + -1 should wrap. { Handle args[] = {isolate->factory()->NewNumber(kMinInt), isolate->factory()->NewNumber(-1)}; Handle 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 args[] = {isolate->factory()->NewNumber(1ull << 40), isolate->factory()->NewNumber(-1)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(-1, result->Number()); } // String '57' converts to 57. { Handle args[] = { isolate->factory()->NewStringFromAsciiChecked("57"), isolate->factory()->NewNumber(41.0)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(98.0, result->Number()); } // String 'foo' converts to 0. { Handle args[] = { isolate->factory()->NewStringFromAsciiChecked("foo"), isolate->factory()->NewNumber(41.0)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(41.0, result->Number()); } // String '58o' converts to 0. { Handle args[] = { isolate->factory()->NewStringFromAsciiChecked("58o"), isolate->factory()->NewNumber(41.0)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(41.0, result->Number()); } // NaN converts to 0. { Handle args[] = {isolate->factory()->nan_value(), isolate->factory()->NewNumber(41.0)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(41.0, result->Number()); } // null converts to 0. { Handle args[] = {isolate->factory()->null_value(), isolate->factory()->NewNumber(41.0)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(41.0, result->Number()); } } static int add6(int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f; } TEST(Run_FFI_add6) { Isolate* isolate = CcTest::InitIsolateOnce(); HandleScope scope(isolate); Handle name = isolate->factory()->InternalizeUtf8String("add6"); Handle undefined = isolate->factory()->undefined_value(); AccountingAllocator allocator; Zone zone(&allocator, ZONE_NAME); FFISignature::Builder sig_builder(&zone, 1, 7); sig_builder.AddReturn(FFIType::kInt32); for (int i = 0; i < 7; i++) { sig_builder.AddParam(FFIType::kInt32); } NativeFunction func = {sig_builder.Build(), reinterpret_cast(add6)}; Handle jsfunc = CompileJSToNativeWrapper(isolate, name, func); Handle args[] = { isolate->factory()->NewNumber(1), isolate->factory()->NewNumber(2), isolate->factory()->NewNumber(3), isolate->factory()->NewNumber(4), isolate->factory()->NewNumber(5), isolate->factory()->NewNumber(6)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(21.0, result->Number()); { // Ensure builtin frames are generated FLAG_allow_natives_syntax = true; v8::Local res = CompileRun( "var o = { valueOf: function() { %DebugTrace(); return 1; } }; o;"); Handle param(v8::Utils::OpenHandle(v8::Object::Cast(*res))); Handle args[] = {param, isolate->factory()->NewNumber(2), isolate->factory()->NewNumber(3), isolate->factory()->NewNumber(4), isolate->factory()->NewNumber(5), isolate->factory()->NewNumber(6), isolate->factory()->NewNumber(21)}; Handle result = Execution::Call(isolate, jsfunc, undefined, arraysize(args), args) .ToHandleChecked(); CHECK_EQ(21.0, result->Number()); CHECK_EQ( 1.0, res->NumberValue( reinterpret_cast(isolate)->GetCurrentContext()) .ToChecked()); } } } // namespace ffi } // namespace internal } // namespace v8