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

Side by Side 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 3 years, 12 months 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 unified diff | Download patch
« no previous file with comments | « test/cctest/cctest.gyp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <math.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "src/codegen.h"
11 #include "src/compiler/ffi-compiler.h"
12
13 #include "test/cctest/cctest.h"
14
15 using namespace v8::base;
16 using namespace v8::internal;
17 using namespace v8::internal::compiler;
18
19 static int foo() {
20 printf("hello world from native code\n");
21 return 42;
22 }
23
24 TEST(Run_FFI_Hello) {
25 Isolate* isolate = CcTest::InitIsolateOnce();
26 HandleScope scope(isolate);
27
28 Handle<String> name = isolate->factory()->InternalizeUtf8String("foo");
29 Handle<Object> undefined = isolate->factory()->undefined_value();
30
31 ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}};
32 static v8::internal::ffi::FFISignature sig_foo(1, 0, reps);
33 v8::internal::ffi::NativeFunction func = {&sig_foo,
34 reinterpret_cast<uint8_t*>(foo)};
35
36 Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func);
37
38 Handle<Object> result =
39 Execution::Call(isolate, jsfunc, undefined, 0, nullptr).ToHandleChecked();
40
41 CHECK_EQ(42.0, result->Number());
42 }
43
44 static int add2(int x, int y) { return x + y; }
45
46 TEST(Run_FFI_add2) {
47 Isolate* isolate = CcTest::InitIsolateOnce();
48 HandleScope scope(isolate);
49
50 Handle<String> name = isolate->factory()->InternalizeUtf8String("add2");
51 Handle<Object> undefined = isolate->factory()->undefined_value();
52
53 ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL},
54 {ffi::FFIType::kInt32, NULL},
55 {ffi::FFIType::kInt32, NULL}};
56 static v8::internal::ffi::FFISignature sig(1, 2, reps);
57 v8::internal::ffi::NativeFunction func = {&sig,
58 reinterpret_cast<uint8_t*>(add2)};
59
60 Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func);
61
62 // Simple math should work.
63 {
64 Handle<Object> args[] = {isolate->factory()->NewNumber(1.0),
65 isolate->factory()->NewNumber(41.0)};
66 Handle<Object> result =
67 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
68 .ToHandleChecked();
69 CHECK_EQ(42.0, result->Number());
70 }
71
72 // Truncate floating point to integer.
73 {
74 Handle<Object> args[] = {isolate->factory()->NewNumber(1.9),
75 isolate->factory()->NewNumber(41.0)};
76 Handle<Object> result =
77 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
78 .ToHandleChecked();
79 CHECK_EQ(42.0, result->Number());
80 }
81
82 // INT_MAX + 1 should wrap.
83 {
84 Handle<Object> args[] = {isolate->factory()->NewNumber(kMaxInt),
85 isolate->factory()->NewNumber(1)};
86 Handle<Object> result =
87 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
88 .ToHandleChecked();
89 CHECK_EQ(kMinInt, result->Number());
90 }
91
92 // INT_MIN + -1 should wrap.
93 {
94 Handle<Object> args[] = {isolate->factory()->NewNumber(kMinInt),
95 isolate->factory()->NewNumber(-1)};
96 Handle<Object> result =
97 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
98 .ToHandleChecked();
99 CHECK_EQ(kMaxInt, result->Number());
100 }
101
102 // Numbers get truncated to the 32 least significant bits.
103 {
104 Handle<Object> args[] = {isolate->factory()->NewNumber(1ull << 40),
105 isolate->factory()->NewNumber(-1)};
106 Handle<Object> result =
107 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
108 .ToHandleChecked();
109 CHECK_EQ(-1, result->Number());
110 }
111
112 // String '57' converts to 57.
113 {
114 Handle<Object> args[] = {
115 isolate->factory()->NewStringFromAsciiChecked("57"),
116 isolate->factory()->NewNumber(41.0)};
117 Handle<Object> result =
118 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
119 .ToHandleChecked();
120 CHECK_EQ(98.0, result->Number());
121 }
122
123 // String 'foo' converts to 0.
124 // TODO(ofrobots): should this throw instead?
125 {
126 Handle<Object> args[] = {
127 isolate->factory()->NewStringFromAsciiChecked("foo"),
128 isolate->factory()->NewNumber(41.0)};
129 Handle<Object> result =
130 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
131 .ToHandleChecked();
132 CHECK_EQ(41.0, result->Number());
133 }
134
135 // String '58o' converts to 0.
136 // TODO(ofrobots): should this throw instead?
137 {
138 Handle<Object> args[] = {
139 isolate->factory()->NewStringFromAsciiChecked("58o"),
140 isolate->factory()->NewNumber(41.0)};
141 Handle<Object> result =
142 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
143 .ToHandleChecked();
144 CHECK_EQ(41.0, result->Number());
145 }
146
147 // NaN converts to 0.
148 // TODO(ofrobots): should this throw instead?
149 {
150 Handle<Object> args[] = {isolate->factory()->nan_value(),
151 isolate->factory()->NewNumber(41.0)};
152 Handle<Object> result =
153 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
154 .ToHandleChecked();
155 CHECK_EQ(41.0, result->Number());
156 }
157
158 // null converts to 0.
159 // TODO(ofrobots): should this throw instead?
160 {
161 Handle<Object> args[] = {isolate->factory()->null_value(),
162 isolate->factory()->NewNumber(41.0)};
163 Handle<Object> result =
164 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
165 .ToHandleChecked();
166 CHECK_EQ(41.0, result->Number());
167 }
168 }
169
170 static void noop() {}
171
172 TEST(Run_FFI_empty_signature) {
173 Isolate* isolate = CcTest::InitIsolateOnce();
174 HandleScope scope(isolate);
175
176 Handle<String> name = isolate->factory()->InternalizeUtf8String("noop");
177 Handle<Object> undefined = isolate->factory()->undefined_value();
178
179 ffi::FFITypeElement reps[] = {};
180 static v8::internal::ffi::FFISignature sig(0, 0, reps);
181 v8::internal::ffi::NativeFunction func = {&sig,
182 reinterpret_cast<uint8_t*>(noop)};
183
184 Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func);
185
186 Execution::Call(isolate, jsfunc, undefined, 0, NULL).ToHandleChecked();
187 }
188
189 static int add7(int x1, int x2, int x3, int x4, int x5, int x6, int x7) {
190 return x1 + x2 + x3 + x4 + x5 + x6 + x7;
191 }
192
193 TEST(Run_FFI_add7) {
194 Isolate* isolate = CcTest::InitIsolateOnce();
195 HandleScope scope(isolate);
196
197 Handle<String> name = isolate->factory()->InternalizeUtf8String("add7");
198 Handle<Object> undefined = isolate->factory()->undefined_value();
199
200 ffi::FFITypeElement reps[] = {
201 {ffi::FFIType::kInt32, NULL}, {ffi::FFIType::kInt32, NULL},
202 {ffi::FFIType::kInt32, NULL}, {ffi::FFIType::kInt32, NULL},
203 {ffi::FFIType::kInt32, NULL}, {ffi::FFIType::kInt32, NULL},
204 {ffi::FFIType::kInt32, NULL}, {ffi::FFIType::kInt32, NULL}};
205 static v8::internal::ffi::FFISignature sig(1, 7, reps);
206 v8::internal::ffi::NativeFunction func = {&sig,
207 reinterpret_cast<uint8_t*>(add7)};
208
209 Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func);
210
211 Handle<Object> args[] = {
212 isolate->factory()->NewNumber(1.0), isolate->factory()->NewNumber(2.0),
213 isolate->factory()->NewNumber(3.0), isolate->factory()->NewNumber(4.0),
214 isolate->factory()->NewNumber(5.0), isolate->factory()->NewNumber(6.0),
215 isolate->factory()->NewNumber(7.0)};
216 Handle<Object> result =
217 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
218 .ToHandleChecked();
219
220 CHECK_EQ(28.0, result->Number());
221 }
222
223 static char* createString() {
224 int length = 12;
225 char* str = reinterpret_cast<char*>(malloc(sizeof(char) * length));
226 snprintf(str, length, "hello world");
227 return str;
228 }
229
230 TEST(Run_FFI_strings) {
231 Isolate* isolate = CcTest::InitIsolateOnce();
232 HandleScope scope(isolate);
233
234 Handle<String> name = isolate->factory()->InternalizeUtf8String("strlen");
235 Handle<Object> undefined = isolate->factory()->undefined_value();
236
237 ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL},
238 {ffi::FFIType::kCharPtr, NULL}};
239 static v8::internal::ffi::FFISignature sig(1, 1, reps);
240 v8::internal::ffi::NativeFunction func = {&sig,
241 reinterpret_cast<uint8_t*>(strlen)};
242
243 Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func);
244
245 Handle<Object> args[] = {
246 isolate->factory()->InternalizeUtf8String("strlenstrlen")};
247 Handle<Object> result =
248 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
249 .ToHandleChecked();
250
251 CHECK_EQ(12, result->Number());
252
253 Handle<String> name2 =
254 isolate->factory()->InternalizeUtf8String("createString");
255
256 ffi::FFITypeElement reps2[] = {{ffi::FFIType::kCharPtr, NULL}};
257 static v8::internal::ffi::FFISignature sig2(1, 0, reps2);
258 v8::internal::ffi::NativeFunction func2 = {
259 &sig2, reinterpret_cast<uint8_t*>(createString)};
260
261 Handle<JSFunction> jsfunc2 = CompileJSToNativeWrapper(isolate, name2, func2);
262
263 Handle<Object> result2 =
264 Execution::Call(isolate, jsfunc2, undefined, 0, nullptr)
265 .ToHandleChecked();
266
267 CHECK_EQ(strcmp(String::cast(*result2)->ToCString().get(), "hello world"), 0);
268 }
269
270 static uint8_t peek(uint8_t* data) { return data[0]; }
271
272 TEST(Run_FFI_typedarrays) {
273 Isolate* isolate = CcTest::InitIsolateOnce();
274 HandleScope scope(isolate);
275
276 Handle<String> name = isolate->factory()->InternalizeUtf8String("peek");
277 Handle<Object> undefined = isolate->factory()->undefined_value();
278
279 ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL},
280 {ffi::FFIType::kTypedArray, NULL}};
281 static v8::internal::ffi::FFISignature sig(1, 1, reps);
282 v8::internal::ffi::NativeFunction func = {&sig,
283 reinterpret_cast<uint8_t*>(peek)};
284
285 Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func);
286
287 uint8_t backing_store[1];
288 backing_store[0] = 42;
289 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
290 JSArrayBuffer::Setup(buffer, isolate, true, backing_store,
291 sizeof(backing_store));
292 Handle<JSTypedArray> array =
293 isolate->factory()->NewJSTypedArray(kExternalUint8Array, buffer, 0, 1);
294 Handle<Object> args[] = {array};
295 Handle<Object> result =
296 Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
297 .ToHandleChecked();
298
299 CHECK_EQ(42, result->Number());
300 }
301
302 static int takesCb(int (*cb)()) { return cb(); }
303
304 TEST(Run_FFI_FunctionArgs) {
305 Isolate* isolate = CcTest::InitIsolateOnce();
306 HandleScope scope(isolate);
307
308 Handle<String> name = isolate->factory()->InternalizeUtf8String("foo");
309 Handle<Object> undefined = isolate->factory()->undefined_value();
310
311 ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}};
312 static v8::internal::ffi::FFISignature sig_foo(1, 0, reps);
313 static v8::ffi::FFISignature sig_foo_external = {1, 0, reps};
314 v8::internal::ffi::NativeFunction func = {&sig_foo,
315 reinterpret_cast<uint8_t*>(foo)};
316
317 Handle<JSFunction> jsFoo = CompileJSToNativeWrapper(isolate, name, func);
318
319 v8::ffi::FFISupplementalInfo info = {&sig_foo_external};
320
321 ffi::FFITypeElement reps2[] = {{ffi::FFIType::kInt32, NULL},
322 {ffi::FFIType::kFunction, &info}};
323 static v8::internal::ffi::FFISignature sig_cb(1, 1, reps2);
324 v8::internal::ffi::NativeFunction func2 = {
325 &sig_cb, reinterpret_cast<uint8_t*>(takesCb)};
326
327 Handle<String> name2 = isolate->factory()->InternalizeUtf8String("takesCb");
328
329 Handle<JSFunction> jsCb = CompileJSToNativeWrapper(isolate, name2, func2);
330
331 Handle<Object> args[] = {jsFoo};
332
333 Handle<Object> result =
334 Execution::Call(isolate, jsCb, undefined, arraysize(args), args)
335 .ToHandleChecked();
336
337 printf("%f\n", result->Number());
338
339 CHECK_EQ(42.0, result->Number());
340 }
341
342 struct struct_type {
343 int i;
344 int64_t j;
345 };
346
347 static int takesStruct(struct_type* s) {
348 return static_cast<int>(s->i) - static_cast<int>(s->j);
349 }
350
351 TEST(Run_FFI_StructArgs) {
352 Isolate* isolate = CcTest::InitIsolateOnce();
353 HandleScope scope(isolate);
354
355 Handle<Object> undefined = isolate->factory()->undefined_value();
356
357 v8::ffi::FFITypeElement i_struct_type = {ffi::FFIType::kInt32, NULL};
358 v8::ffi::FFITypeElement j_struct_type = {ffi::FFIType::kInt64, NULL};
359 v8::ffi::FFIStructElement struct_elem[] = {{"i", &i_struct_type},
360 {"j", &j_struct_type}};
361 v8::ffi::FFIStructSignature struct_sig = {2, struct_elem};
362 v8::ffi::FFISupplementalInfo info = {.struct_elements = &struct_sig};
363 ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL},
364 {ffi::FFIType::kStructPtr, &info}};
365 static v8::internal::ffi::FFISignature sig(1, 1, reps);
366 v8::internal::ffi::NativeFunction func = {
367 &sig, reinterpret_cast<uint8_t*>(takesStruct)};
368
369 Handle<String> name =
370 isolate->factory()->InternalizeUtf8String("takesStruct");
371
372 Handle<JSFunction> jsFun = CompileJSToNativeWrapper(isolate, name, func);
373
374 Handle<JSObject> jsObj = isolate->factory()->NewJSObjectWithNullProto();
375 JSObject::AddProperty(jsObj, isolate->factory()->InternalizeUtf8String("i"),
376 isolate->factory()->NewNumber(42), NONE);
377 JSObject::AddProperty(jsObj, isolate->factory()->InternalizeUtf8String("j"),
378 isolate->factory()->NewNumber(17), NONE);
379
380 Handle<Object> args[] = {jsObj};
381
382 Handle<Object> result =
383 Execution::Call(isolate, jsFun, undefined, arraysize(args), args)
384 .ToHandleChecked();
385
386 CHECK_EQ(25.0, result->Number());
387 }
388
389 static int foreign = 1729;
390
391 static int* producesForeign() { return &foreign; }
392
393 static int consumesForeign(int* foreign) { return *foreign; }
394
395 TEST(Run_FFI_Foreigns) {
396 Isolate* isolate = CcTest::InitIsolateOnce();
397 HandleScope scope(isolate);
398
399 Handle<Object> undefined = isolate->factory()->undefined_value();
400
401 ffi::FFITypeElement reps[] = {{ffi::FFIType::kForeign, NULL}};
402 static v8::internal::ffi::FFISignature sig(1, 0, reps);
403 v8::internal::ffi::NativeFunction func = {
404 &sig, reinterpret_cast<uint8_t*>(producesForeign)};
405
406 Handle<String> name =
407 isolate->factory()->InternalizeUtf8String("producesForeign");
408
409 Handle<JSFunction> jsProducer = CompileJSToNativeWrapper(isolate, name, func);
410
411 ffi::FFITypeElement reps2[] = {{ffi::FFIType::kInt32, NULL},
412 {ffi::FFIType::kForeign, NULL}};
413 static v8::internal::ffi::FFISignature sig2(1, 1, reps2);
414 v8::internal::ffi::NativeFunction func2 = {
415 &sig2, reinterpret_cast<uint8_t*>(consumesForeign)};
416
417 Handle<String> name2 =
418 isolate->factory()->InternalizeUtf8String("consumesForeign");
419
420 Handle<JSFunction> jsConsumer =
421 CompileJSToNativeWrapper(isolate, name2, func2);
422
423 Handle<Object> intermediate =
424 Execution::Call(isolate, jsProducer, undefined, 0, {}).ToHandleChecked();
425
426 Handle<Object> args[] = {intermediate};
427
428 Handle<Object> result =
429 Execution::Call(isolate, jsConsumer, undefined, arraysize(args), args)
430 .ToHandleChecked();
431
432 CHECK_EQ(1729.0, result->Number());
433 }
434
435 static int constant() { return 1729; }
436
437 TEST(Run_FFI_BindNoArgs) {
438 Isolate* isolate = CcTest::InitIsolateOnce();
439 HandleScope scope(isolate);
440
441 ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL}};
442 static v8::internal::ffi::FFISignature sig(1, 0, reps);
443 v8::internal::ffi::NativeFunction func = {
444 &sig, reinterpret_cast<uint8_t*>(constant)};
445
446 Handle<JSArray> arr = isolate->factory()->NewJSArray(0);
447
448 int (*fn_ptr)() =
449 reinterpret_cast<int (*)()>(FFIFunctionBind(isolate, func, arr));
450
451 CHECK_EQ(1729, fn_ptr());
452 }
453
454 static void populate(void* data) { *(reinterpret_cast<int*>(data)) = 42; }
455
456 TEST(Run_FFI_BufferNoCopy) {
457 Isolate* isolate = CcTest::InitIsolateOnce();
458 HandleScope scope(isolate);
459
460 Handle<String> name = isolate->factory()->InternalizeUtf8String("populate");
461 Handle<Object> undefined = isolate->factory()->undefined_value();
462
463 ffi::FFITypeElement reps[] = {{ffi::FFIType::kBufferNoCopy, NULL}};
464 static v8::internal::ffi::FFISignature sig(0, 1, reps);
465 v8::internal::ffi::NativeFunction func = {
466 &sig, reinterpret_cast<uint8_t*>(populate)};
467
468 Handle<JSFunction> jsfunc = CompileJSToNativeWrapper(isolate, name, func);
469
470 uint8_t backing_store[1];
471 backing_store[0] = 17;
472 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
473 JSArrayBuffer::Setup(buffer, isolate, true, backing_store,
474 sizeof(backing_store));
475 Handle<JSTypedArray> array =
476 isolate->factory()->NewJSTypedArray(kExternalUint8Array, buffer, 0, 1);
477 Handle<Object> args[] = {array};
478 USE(Execution::Call(isolate, jsfunc, undefined, arraysize(args), args)
479 .ToHandleChecked());
480
481 CHECK_EQ(42, backing_store[0]);
482 }
483
484 static int add(int a, int b) { return a + b; }
485
486 TEST(Run_FFI_BindArgs) {
487 Isolate* isolate = CcTest::InitIsolateOnce();
488 HandleScope scope(isolate);
489
490 ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL},
491 {ffi::FFIType::kInt32, NULL},
492 {ffi::FFIType::kInt32, NULL}};
493 static v8::internal::ffi::FFISignature sig(1, 2, reps);
494 v8::internal::ffi::NativeFunction func = {&sig,
495 reinterpret_cast<uint8_t*>(add)};
496
497 Handle<JSArray> arr = isolate->factory()->NewJSArray(2);
498 USE(Object::SetElement(isolate, arr, 0, isolate->factory()->NewNumber(17),
499 SLOPPY));
500 USE(Object::SetElement(isolate, arr, 1, isolate->factory()->NewNumber(25),
501 SLOPPY));
502
503 int (*fn_ptr)() =
504 reinterpret_cast<int (*)()>(FFIFunctionBind(isolate, func, arr));
505
506 CHECK_EQ(42, fn_ptr());
507 }
508
509 TEST(Run_FFI_Serialization) {
510 Isolate* isolate = CcTest::InitIsolateOnce();
511 HandleScope scope(isolate);
512
513 ffi::FFITypeElement reps[] = {{ffi::FFIType::kInt32, NULL},
514 {ffi::FFIType::kInt32, NULL},
515 {ffi::FFIType::kInt32, NULL}};
516 static v8::internal::ffi::FFISignature sig(1, 2, reps);
517 v8::internal::ffi::NativeFunction func = {&sig,
518 reinterpret_cast<uint8_t*>(add)};
519
520 int* (*serializer)(int, int) =
521 reinterpret_cast<int* (*)(int, int)>(BuildFFISerializer(isolate, func));
522 int (*executor)(int*) = reinterpret_cast<int (*)(int*)>(
523 BuildFFIDeserializedExecutor(isolate, func));
524 int* serialized = serializer(17, 25);
525
526 CHECK_EQ(17, serialized[0]);
527 CHECK_EQ(25, serialized[1]);
528
529 int result = executor(serialized);
530 CHECK_EQ(42, result);
531 }
OLDNEW
« 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