Index: src/runtime/runtime-ffi.cc |
diff --git a/src/runtime/runtime-ffi.cc b/src/runtime/runtime-ffi.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8d047f472f1e3434dea334c1c8a6c678220dbeef |
--- /dev/null |
+++ b/src/runtime/runtime-ffi.cc |
@@ -0,0 +1,267 @@ |
+// 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 "src/runtime/runtime-utils.h" |
+ |
+#include "src/arguments.h" |
+#include "src/assembler.h" |
+#include "src/code-factory.h" |
+#include "src/compilation-info.h" |
+#include "src/compiler/common-operator.h" |
+#include "src/compiler/ffi-compiler.h" |
+#include "src/compiler/graph.h" |
+#include "src/compiler/js-graph.h" |
+#include "src/compiler/linkage.h" |
+#include "src/compiler/machine-operator.h" |
+#include "src/compiler/pipeline.h" |
+ |
+namespace v8 { |
+namespace internal { |
+ |
+using namespace v8::internal::compiler; |
+ |
+#define WORD_SIZE 8 |
+ |
+// TODO(mattloring): need pool per function, not global |
+List<char *> mem_pool(10); |
+std::map<int, void *> callback_compilations; |
+ |
+static int StructSize(ffi::FFIStructSignature *sig) { |
+ int total_size = 0; |
+ int curr_alignment = 0; |
+ for (size_t i = 0; i < sig->elem_count; i++) { |
+ int elem_size = TypeSize(sig->elems[i].type->type); |
+ // Account for padding |
+ if (elem_size + curr_alignment > WORD_SIZE) { |
+ total_size += WORD_SIZE - curr_alignment; |
+ curr_alignment = 0; |
+ } |
+ curr_alignment += elem_size; |
+ total_size += elem_size; |
+ } |
+ return total_size; |
+} |
+ |
+RUNTIME_FUNCTION(Runtime_JSStringToCharPtr) { |
+ HandleScope scope(isolate); |
+ DCHECK_EQ(1, args.length()); |
+ Handle<String> string; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, string, Object::ToString(isolate, args.at<Object>(0))); |
+ int length = string->length() + 1; |
+ char *str = reinterpret_cast<char *>(malloc(length)); |
+ snprintf(str, length, "%s", ((*string)->ToCString()).release()); |
+ mem_pool.Add(str); |
+ // TODO(mattloring): should this be a foreign somehow? this cast is not |
+ // correct |
+ return reinterpret_cast<Object *>(str); |
+} |
+ |
+RUNTIME_FUNCTION(Runtime_TypedArrayToUint8Ptr) { |
+ HandleScope scope(isolate); |
+ DCHECK_EQ(1, args.length()); |
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, input_obj, 0); |
+ Handle<JSTypedArray> array(JSTypedArray::cast(*input_obj)); |
+ uint8_t *contents = |
+ reinterpret_cast<uint8_t *>(array->GetBuffer()->backing_store()); |
+ size_t offset = NumberToSize(array->byte_offset()); |
+ size_t length = NumberToSize(array->byte_length()); |
+ char *result = reinterpret_cast<char *>(malloc(length)); |
+ memcpy(result, contents + offset, length); |
+ mem_pool.Add(result); |
+ // TODO(mattloring): should this be a foreign somehow? this cast is not |
+ // correct |
+ return reinterpret_cast<Object *>(result); |
+} |
+ |
+RUNTIME_FUNCTION(Runtime_BufferToPtrNoCopy) { |
+ HandleScope scope(isolate); |
+ DCHECK_EQ(1, args.length()); |
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, input_obj, 0); |
+ Handle<JSTypedArray> array(JSTypedArray::cast(*input_obj)); |
+ uint8_t *contents = |
+ reinterpret_cast<uint8_t *>(array->GetBuffer()->backing_store()); |
+ size_t offset = NumberToSize(array->byte_offset()); |
+ // TODO(mattloring): should this be a foreign somehow? this cast is not |
+ // correct |
+ return reinterpret_cast<Object *>(contents + offset); |
+} |
+ |
+RUNTIME_FUNCTION(Runtime_CharPtrToJSString) { |
+ HandleScope scope(isolate); |
+ DCHECK_EQ(1, args.length()); |
+ char *str = reinterpret_cast<char *>(args[0]); |
+ Handle<String> name = isolate->factory()->InternalizeUtf8String(str); |
+ free(str); |
+ return *name; |
+} |
+ |
+static void CallNoReturn(Isolate *isolate, Object *callable, Object *receiver, |
+ int argc, Object *argv[]) { |
+ HandleScope scope(isolate); |
+ Handle<Object> call_handle(callable, isolate); |
+ Handle<Object> rec_handle(receiver, isolate); |
+ Handle<Object> *argv_handles = new Handle<Object>[ argc ]; |
+ for (int i = 0; i < argc; i++) { |
+ Handle<Object> arg_handle(argv[i], isolate); |
+ argv_handles[i] = arg_handle; |
+ } |
+ // TODO(mattloring): retrieve return value instead of USE |
+ USE(i::Execution::Call(isolate, call_handle, rec_handle, argc, argv_handles)); |
+} |
+ |
+RUNTIME_FUNCTION(Runtime_JSFunctionToFnPtr) { |
+ HandleScope scope(isolate); |
+ DCHECK_EQ(2, args.length()); |
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, input_obj, 0); |
+ Handle<JSFunction> func(JSFunction::cast(*input_obj)); |
+ |
+ if (callback_compilations[func->shared()->start_position()]) { |
+ // Use cached result |
+ // TODO(mattloring): cache on better property than start position |
+ return reinterpret_cast<Object *>( |
+ callback_compilations[func->shared()->start_position()]); |
+ } |
+ |
+ ffi::FFISignature *sig = reinterpret_cast<ffi::FFISignature *>(args[1]); |
+ Zone zone(isolate->allocator(), ZONE_NAME); |
+ Graph graph(&zone); |
+ CommonOperatorBuilder common(&zone); |
+ MachineOperatorBuilder machine(&zone); |
+ JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine); |
+ |
+ int param_count = static_cast<int>(sig->parameter_count()); |
+ |
+ MachineType invoke_reps[] = {MachineType::Pointer(), MachineType::Pointer(), |
+ MachineType::Pointer(), MachineType::Int64(), |
+ MachineType::Pointer()}; |
+ MachineSignature invoke_sig(0, 5, invoke_reps); |
+ CallDescriptor *invoke_desc = |
+ Linkage::GetSimplifiedCDescriptor(&zone, &invoke_sig); |
+ |
+ // Unmarshall arguments coming from C going to JS |
+ // Marshall value returned from JS back to C |
+ Node *start = |
+ graph.NewNode(common.Start(3 + param_count)); // 4 + param count |
+ |
+ FFIGraphBuilder builder(&zone, &jsgraph); |
+ builder.set_control_ptr(&start); |
+ builder.set_effect_ptr(&start); |
+ |
+ graph.SetStart(start); |
+ |
+ Node *context = jsgraph.HeapConstant(Handle<Context>(func->context())); |
+ Object **call_args = new Object *[param_count]; |
+ Node *call_args_node = graph.NewNode(common.ExternalConstant( |
+ ExternalReference(reinterpret_cast<Address>(call_args), isolate))); |
+ Node *prev_arg_translation = start; |
+ if (param_count > 0) { |
+ for (int i = 0; i < param_count; i++) { // Arguments |
+ Node *translated_param = builder.ToJS( |
+ graph.NewNode(common.Parameter(i), start), context, sig->GetParam(i)); |
+ prev_arg_translation = graph.NewNode( |
+ machine.Store(StoreRepresentation( |
+ MachineRepresentation::kTaggedPointer, kNoWriteBarrier)), |
+ call_args_node, jsgraph.Int32Constant(i), translated_param, |
+ prev_arg_translation, start); |
+ } |
+ } |
+ |
+ int index = 0; |
+ Node **arguments = zone.NewArray<Node *>(8); |
+ arguments[index++] = graph.NewNode( |
+ common.Int64Constant(reinterpret_cast<int64_t>(&CallNoReturn))); |
+ arguments[index++] = graph.NewNode(common.ExternalConstant( |
+ ExternalReference(reinterpret_cast<Address>(isolate), isolate))); |
+ arguments[index++] = jsgraph.HeapConstant(func); |
+ arguments[index++] = jsgraph.UndefinedConstant(); |
+ arguments[index++] = jsgraph.Int64Constant(param_count); |
+ arguments[index++] = call_args_node; |
+ arguments[index++] = prev_arg_translation; |
+ arguments[index++] = start; |
+ Node *call = graph.NewNode(common.Call(invoke_desc), index, arguments); |
+ Node *call_control = graph.NewNode(common.IfSuccess(), call); |
+ // TODO(mattloring): retval = call; |
+ Node *retval = jsgraph.Constant(42); |
+ if (sig->return_count() != 0) { |
+ retval = builder.FromJS(retval, context, sig->GetReturn()); |
+ } |
+ Node *ret = graph.NewNode(common.Return(), jsgraph.Int32Constant(0), retval, |
+ call, call_control); |
+ graph.SetEnd(graph.NewNode(common.End(1), ret)); |
+ |
+ MachineType *reps = builder.GetMachineTypes(sig); |
+ MachineSignature mach_sig(sig->return_count(), param_count, reps); |
+ |
+ CallDescriptor *desc = Linkage::GetSimplifiedCDescriptor(&zone, &mach_sig); |
+ |
+ Code::Flags flags = Code::ComputeFlags(Code::STUB); |
+ |
+ CompilationInfo info(ArrayVector("js-to-native-cb"), jsgraph.isolate(), &zone, |
+ flags); |
+ Handle<Code> code = Pipeline::GenerateCodeForTesting(&info, desc, &graph); |
+ callback_compilations[func->shared()->start_position()] = code->entry(); |
+ // TODO(mattloring): should this be a foreign somehow? this cast is not |
+ // correct |
+ return reinterpret_cast<Object *>(code->entry()); |
+} |
+ |
+RUNTIME_FUNCTION(Runtime_JSObjectToStructPtr) { |
+ HandleScope scope(isolate); |
+ DCHECK_EQ(2, args.length()); |
+ Handle<JSObject> obj(JSObject::cast(args[0])); |
+ ffi::FFIStructSignature *sig = |
+ reinterpret_cast<ffi::FFIStructSignature *>(args[1]); |
+ char *result = reinterpret_cast<char *>(malloc(StructSize(sig))); |
+ char *write_pos = result; |
+ int alignment = 0; |
+ for (size_t i = 0; i < sig->elem_count; i++) { |
+ v8::ffi::FFIStructElement elem = sig->elems[i]; |
+ int elem_size = TypeSize(elem.type->type); |
+ Handle<String> name = isolate->factory()->InternalizeUtf8String(elem.name); |
+ LookupIterator it(obj, name); |
+ Handle<Object> value = Object::GetProperty(&it).ToHandleChecked(); |
+ if (elem_size + alignment > WORD_SIZE) { |
+ memset(write_pos, 0, WORD_SIZE - alignment); |
+ write_pos += (WORD_SIZE - alignment); |
+ alignment = 0; |
+ } |
+ WriteObject(value, elem.type->type, write_pos); |
+ write_pos += elem_size; |
+ alignment += elem_size; |
+ } |
+ mem_pool.Add(result); |
+ // TODO(mattloring): should this be a foreign somehow? this cast is not |
+ // correct |
+ return reinterpret_cast<Object *>(result); |
+} |
+ |
+RUNTIME_FUNCTION(Runtime_PointerToForeign) { |
+ HandleScope scope(isolate); |
+ DCHECK_EQ(1, args.length()); |
+ intptr_t *ptr = reinterpret_cast<intptr_t *>(args[0]); |
+ Handle<Foreign> foreign = |
+ isolate->factory()->NewForeign(reinterpret_cast<Address>(ptr)); |
+ return *foreign; |
+} |
+ |
+RUNTIME_FUNCTION(Runtime_ForeignToPointer) { |
+ HandleScope scope(isolate); |
+ DCHECK_EQ(1, args.length()); |
+ Handle<Foreign> foreign(Foreign::cast(args[0])); |
+ // TODO(mattloring): should this be a foreign somehow? this cast is not |
+ // correct |
+ return reinterpret_cast<Object *>(foreign->foreign_address()); |
+} |
+ |
+RUNTIME_FUNCTION(Runtime_ReleaseFFIMemPool) { |
+ HandleScope scope(isolate); |
+ for (char *addr : mem_pool) { |
+ free(addr); |
+ } |
+ mem_pool.Clear(); |
+ return isolate->heap()->ToBoolean(true); |
+} |
+} // namespace internal |
+} // namespace v8 |