From fb6b872842ea8d5c53459ce4ce859a66e02559cd Mon Sep 17 00:00:00 2001 From: Vicente Ferrer Date: Wed, 27 Jan 2021 22:46:57 +0100 Subject: [PATCH 1/4] Solving some Scala port bugs and trying to implement support for multiple arguments. --- .../scala_port/src/main/scala/Bindings.scala | 5 +-- .../ports/scala_port/src/main/scala/Ptr.scala | 16 ++----- .../scala_port/src/main/scala/Value.scala | 2 +- .../scala_port/src/main/scala/instances.scala | 38 ++++++++-------- .../scala_port/src/main/scala/util.scala | 3 +- .../src/test/scala/MetaCallSpec.scala | 45 +++++++++++-------- 6 files changed, 54 insertions(+), 55 deletions(-) diff --git a/source/ports/scala_port/src/main/scala/Bindings.scala b/source/ports/scala_port/src/main/scala/Bindings.scala index 9328747458..0cbf060077 100644 --- a/source/ports/scala_port/src/main/scala/Bindings.scala +++ b/source/ports/scala_port/src/main/scala/Bindings.scala @@ -21,7 +21,7 @@ package metacall import com.sun.jna._ -import com.sun.jna.ptr._ +import com.sun.jna.ptr.PointerByReference import util._ /** Interface mirroring the MetaCall library using JNA. See: @@ -41,6 +41,7 @@ protected[metacall] trait Bindings extends Library { ): Int def metacallv_s(name: String, args: Array[Pointer], size: SizeT): Pointer + def metacallfv_s(func: Pointer, args: Array[Pointer], size: SizeT): Pointer def metacall_registerv( name: String, @@ -53,8 +54,6 @@ protected[metacall] trait Bindings extends Library { def metacall_function(name: String): Pointer - def metacallfv(fn: Pointer, args: Array[Pointer]): Pointer - def metacall_function_size(func: Pointer): SizeT def metacall_destroy(): Int diff --git a/source/ports/scala_port/src/main/scala/Ptr.scala b/source/ports/scala_port/src/main/scala/Ptr.scala index 0f7872af33..1fb7157d05 100644 --- a/source/ports/scala_port/src/main/scala/Ptr.scala +++ b/source/ports/scala_port/src/main/scala/Ptr.scala @@ -1,7 +1,7 @@ package metacall import metacall.util._ -import com.sun.jna._, ptr.PointerByReference +import com.sun.jna._ import cats._, cats.implicits._, cats.effect._ /** Create a [[Ptr]] to MetaCall value of type [[A]] */ @@ -88,11 +88,10 @@ object Ptr { case FunctionValue(fn) => Create[FunctionPointer].create { new FunctionPointer { - def callback(argc: SizeT, arg: PointerByReference, data: Pointer): Pointer = { - val argValue = - Ptr.toValue(Ptr.fromPrimitiveUnsafe(arg.getValue())) + def callback(argc: SizeT, args: Pointer, data: Pointer): Pointer = { + val argsList = args.getPointerArray(0).map(ptr => Ptr.toValue(Ptr.fromPrimitiveUnsafe(ptr))).toList - Ptr.fromValueUnsafe(fn(argValue)).ptr + Ptr.fromValueUnsafe(fn(argsList)).ptr } } } @@ -273,13 +272,6 @@ object MapPtrType extends PtrType { private[metacall] final class FunctionPtr(val ptr: Pointer) extends Ptr[FunctionPointer] { val ptrType: PtrType = FunctionPtrType - - /** This reference is here just to keep the function ref from being garbage collected */ - private var ref: PointerByReference = null - - /** Don't forget to use this method when creating a new instance. */ - private[metacall] def setRef(ref: PointerByReference): Unit = - if (this.ref == null) this.ref = ref } object FunctionPtrType extends PtrType { val id = 13 diff --git a/source/ports/scala_port/src/main/scala/Value.scala b/source/ports/scala_port/src/main/scala/Value.scala index 6205ad25ae..64d48fb3ec 100644 --- a/source/ports/scala_port/src/main/scala/Value.scala +++ b/source/ports/scala_port/src/main/scala/Value.scala @@ -21,6 +21,6 @@ final case class DoubleValue(value: Double) extends NumericValue[Double] final case class BooleanValue(value: Boolean) extends Value final case class ArrayValue(value: Vector[Value]) extends Value final case class MapValue(value: Map[Value, Value]) extends Value -final case class FunctionValue(value: Value => Value) extends Value +final case class FunctionValue(value: List[Value] => Value) extends Value final case object NullValue extends Value final case object InvalidValue extends Value diff --git a/source/ports/scala_port/src/main/scala/instances.scala b/source/ports/scala_port/src/main/scala/instances.scala index be52ead5d0..0c3f4fe57c 100644 --- a/source/ports/scala_port/src/main/scala/instances.scala +++ b/source/ports/scala_port/src/main/scala/instances.scala @@ -209,21 +209,20 @@ object instances { def create(value: FunctionPointer): Ptr[FunctionPointer] = { val ref = new PointerByReference() - Bindings.instance.metacall_registerv( + if (Bindings.instance.metacall_registerv( null, value, ref, InvalidPtrType.id, - SizeT(1), - Array(InvalidPtrType.id) - ) - - val jnaPointer = Bindings.instance.metacall_value_create_function(ref.getValue()) - val ptr = new FunctionPtr(jnaPointer) - - ptr.setRef(ref) + SizeT(0), + Array() + ) != 0) { + throw new Exception( + "Invalid function value creation." + ) + } - ptr + new FunctionPtr(Bindings.instance.metacall_value_create_function(ref.getValue())) } } @@ -232,30 +231,34 @@ object instances { new FunctionPointer { def callback( argc: util.SizeT, - args: PointerByReference, + args: Pointer, data: Pointer ): Pointer = { val fnPointer = Bindings.instance.metacall_value_to_function(ptr.ptr) - Bindings.instance.metacallfv( + Bindings.instance.metacallfv_s( fnPointer, - args.getValue().getPointerArray(0) + args.getPointerArray(0), + argc ) } } } def value(ptr: Ptr[FunctionPointer]): Value = { - val valueFn = (arg: Value) => { - val argPtr = Ptr.fromValueUnsafe(arg) + val valueFn = (args: List[Value]) => { + val argPtrArray = args.map(arg => Ptr.fromValueUnsafe(arg).ptr).toArray val fnPointer = Bindings.instance.metacall_value_to_function(ptr.ptr) val callbackRet = - Bindings.instance.metacallfv(fnPointer, Array(argPtr.ptr)) + Bindings.instance.metacallfv_s(fnPointer, argPtrArray, SizeT(argPtrArray.size.asInstanceOf[Long])) val retPtr = Ptr.fromPrimitiveUnsafe(callbackRet) val retValue = Ptr.toValue(retPtr) Bindings.instance.metacall_value_destroy(callbackRet) - Bindings.instance.metacall_value_destroy(argPtr.ptr) + + for (argPtr <- argPtrArray) { + Bindings.instance.metacall_value_destroy(argPtr) + } retValue } @@ -267,5 +270,4 @@ object instances { implicit val invalidCreate = new Create[Unit] { def create(value: Unit): Ptr[Unit] = InvalidPtr } - } diff --git a/source/ports/scala_port/src/main/scala/util.scala b/source/ports/scala_port/src/main/scala/util.scala index ce7668fbce..7ae7ffbed9 100644 --- a/source/ports/scala_port/src/main/scala/util.scala +++ b/source/ports/scala_port/src/main/scala/util.scala @@ -1,7 +1,6 @@ package metacall import com.sun.jna._ -import com.sun.jna.ptr.PointerByReference object util { private[metacall] class SizeT(value: Long) @@ -13,7 +12,7 @@ object util { } private[metacall] trait FunctionPointer extends Callback { - def callback(argc: SizeT, args: PointerByReference, data: Pointer): Pointer + def callback(argc: SizeT, args: Pointer, data: Pointer): Pointer } sealed class MetaCallException(message: String, val cause: Option[String]) diff --git a/source/ports/scala_port/src/test/scala/MetaCallSpec.scala b/source/ports/scala_port/src/test/scala/MetaCallSpec.scala index 2ad6e170b1..af6d3410db 100644 --- a/source/ports/scala_port/src/test/scala/MetaCallSpec.scala +++ b/source/ports/scala_port/src/test/scala/MetaCallSpec.scala @@ -216,28 +216,28 @@ class MetaCallSpec extends AnyFlatSpec { } "`FunctionPointer`s" should "be created/retrieved correctly" in { - // TODO: args should be Array[Pointer], or converted to it at least - // in the body of the callback. args.getValue() returns the first element - // of the array, but we should not use this to handle the args (or we can, - // but we should do pointer arithmetic manually to access it) val cb = new FunctionPointer { override def callback( argc: SizeT, - args: PointerByReference, + args: Pointer, data: Pointer - ): Pointer = metacall.metacall_value_copy(args.getValue()) + ): Pointer = { + val argsPtrArray = args.getPointerArray(0) + + metacall.metacall_value_copy(argsPtrArray.head) + } } val fnRef = new PointerByReference() - metacall.metacall_registerv( + assert(metacall.metacall_registerv( null, cb, fnRef, StringPtrType.id, SizeT(1), Array(StringPtrType.id) - ) + ) == 0) val f = metacall.metacall_value_create_function(fnRef.getValue()) @@ -260,25 +260,28 @@ class MetaCallSpec extends AnyFlatSpec { val fnCallback = new FunctionPointer { final override def callback( argc: SizeT, - args: PointerByReference, + args: Pointer, data: Pointer - ): Pointer = - Ptr.toValue(Ptr.fromPrimitiveUnsafe(args.getValue())) match { - case LongValue(l) => Ptr.fromValueUnsafe(LongValue(l + 1)).ptr + ): Pointer = { + val argsPtrArray = args.getPointerArray(0) + + Ptr.toValue(Ptr.fromPrimitiveUnsafe(argsPtrArray.head)) match { + case LongValue(l) => Ptr.fromValueUnsafe(LongValue(l + 3L)).ptr case _ => Ptr.fromValueUnsafe(NullValue).ptr } + } } val fnRef = new PointerByReference() - metacall.metacall_registerv( + assert(metacall.metacall_registerv( null, fnCallback, fnRef, - IntPtrType.id, + LongPtrType.id, SizeT(1), - Array(IntPtrType.id) - ) + Array(LongPtrType.id) + ) == 0) val fnPtr = fnRef.getValue() @@ -290,7 +293,7 @@ class MetaCallSpec extends AnyFlatSpec { val res = metacall.metacall_value_to_long(ret) - assert(res == 2) + assert(res == 4L) metacall.metacall_value_destroy(ret) } @@ -304,22 +307,26 @@ class MetaCallSpec extends AnyFlatSpec { SizeT(1) ) - assert(metacall.metacall_value_to_long(ret) == 1) + assert(metacall.metacall_value_to_long(ret) == 1L) metacall.metacall_value_destroy(ret) metacall.metacall_value_destroy(v) } + // TODO: + /* "FunctionValues" should "be constructed and passed to foreign functions" in { val fnVal = FunctionValue { - case LongValue(l) => LongValue(l + 1) + case LongValue(l) :: Nil => LongValue(l + 1L) case _ => NullValue } + // TODO: This test causes segmentation fault val ret = Caller.call[IO]("apply_fn_to_one", Vector(fnVal)).unsafeRunSync() assert(ret == LongValue(2L)) } + */ "MetaCall" should "be destroyed successfully" in { require( From f210b8585c80a7116de67f6e15884685f1c6d60a Mon Sep 17 00:00:00 2001 From: Vicente Ferrer Date: Thu, 28 Jan 2021 13:12:30 +0100 Subject: [PATCH 2/4] Trying to debug Scala Port with valgrind. --- source/ports/scala_port/README.md | 22 +++++++++ source/ports/scala_port/build.sbt | 48 ++++++++++--------- .../src/test/scala/MetaCallSpec.scala | 1 - .../src/test/scala/MetaCallSpecMain.scala | 5 ++ source/ports/scala_port/valgrind.supp | 6 +++ 5 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 source/ports/scala_port/src/test/scala/MetaCallSpecMain.scala create mode 100644 source/ports/scala_port/valgrind.supp diff --git a/source/ports/scala_port/README.md b/source/ports/scala_port/README.md index 00caf202ba..e365dc65fe 100644 --- a/source/ports/scala_port/README.md +++ b/source/ports/scala_port/README.md @@ -19,3 +19,25 @@ PORT_LIBRARY_PATH ``` > Note: You'll find the bindings and the code that runs on `sbt test` in `src/main/scala/MetaCall.scala`. + +## Debugging + +Uncomment this line in `build.sbt`: +``` +"-Djava.compiler=NONE", +``` + +Build the project: +``` +sbt compile +``` + +For runing valgrind with the correct classpath, run: +``` +sbt "export test:fullClasspath" +``` + +Then copy the classpath into the valgrind command: +``` +valgrind --tool=memcheck --trace-children=yes --error-limit=no scala -Djava.compiler=NONE -cp src/test/scala/MetaCallSpecMain.scala +``` diff --git a/source/ports/scala_port/build.sbt b/source/ports/scala_port/build.sbt index 4cb26ace66..84c796301d 100644 --- a/source/ports/scala_port/build.sbt +++ b/source/ports/scala_port/build.sbt @@ -1,28 +1,32 @@ -ThisBuild / scalaVersion := "2.13.4" -ThisBuild / version := "0.1.0-SNAPSHOT" -ThisBuild / organization := "com.metacall" -ThisBuild / organizationName := "metacall" +lazy val commonSettings = Seq( + scalaVersion := "2.13.4", + version := "0.1.0-SNAPSHOT", + organization := "com.metacall", + organizationName := "metacall", + scalacOptions ++= Seq( + // Only for debugging purposes + // "-Djava.compiler=NONE", + "-feature", + "-deprecation", + "-Wunused:imports,patvars,privates,locals,explicits,implicits,params", + "-Xlint", + "-explaintypes", + "-Wdead-code", + "-Wextra-implicit", + "-Wnumeric-widen", + "-Wconf:cat=lint-byname-implicit:silent" + ), + scalacOptions in (Compile, console) := Seq.empty, + libraryDependencies ++= Seq( + "net.java.dev.jna" % "jna" % "5.6.0", + "org.typelevel" %% "cats-core" % "2.3.1", + "org.scalatest" %% "scalatest" % "3.2.2" % Test, + ) +) lazy val root = (project in file(".")) + .settings(commonSettings: _*) .settings( name := "metacall", - scalacOptions ++= Seq( - "-feature", - "-deprecation", - "-Wunused:imports,patvars,privates,locals,explicits,implicits,params", - "-Xlint", - "-explaintypes", - "-Wdead-code", - "-Wextra-implicit", - "-Wnumeric-widen", - "-Wconf:cat=lint-byname-implicit:silent" - ), - scalacOptions in (Compile, console) := Seq.empty, - libraryDependencies ++= Seq( - "net.java.dev.jna" % "jna" % "5.6.0", - "org.typelevel" %% "cats-core" % "2.3.1", - "org.scalatest" %% "scalatest" % "3.2.2" % Test, - "com.lihaoyi" %% "pprint" % "0.5.6" % Test - ), fork in (Test / run) := true ) diff --git a/source/ports/scala_port/src/test/scala/MetaCallSpec.scala b/source/ports/scala_port/src/test/scala/MetaCallSpec.scala index ad9b1fdca4..b087606e3e 100644 --- a/source/ports/scala_port/src/test/scala/MetaCallSpec.scala +++ b/source/ports/scala_port/src/test/scala/MetaCallSpec.scala @@ -414,5 +414,4 @@ class MetaCallSpec extends AnyFlatSpec { "MetaCall was not successfully destroyed" ) } - } diff --git a/source/ports/scala_port/src/test/scala/MetaCallSpecMain.scala b/source/ports/scala_port/src/test/scala/MetaCallSpecMain.scala new file mode 100644 index 0000000000..9aaaa64a36 --- /dev/null +++ b/source/ports/scala_port/src/test/scala/MetaCallSpecMain.scala @@ -0,0 +1,5 @@ +import metacall._ + +object Main extends App { + (new MetaCallSpec()).execute() +} diff --git a/source/ports/scala_port/valgrind.supp b/source/ports/scala_port/valgrind.supp new file mode 100644 index 0000000000..3f3f1b7d89 --- /dev/null +++ b/source/ports/scala_port/valgrind.supp @@ -0,0 +1,6 @@ +{ + ignore_jvm + Memcheck:Leak + ... + obj:*/libjvm.so* +} From 9addeb40399df6f9e0d0903d2f44d58834f636a7 Mon Sep 17 00:00:00 2001 From: viferga Date: Fri, 29 Jan 2021 00:43:13 -0900 Subject: [PATCH 3/4] Corrected segmentation fault in python when an invalid annotation is being used in some function, add two new APIs to introspect a function signature. --- .../loaders/py_loader/source/py_loader_impl.c | 29 +++- source/metacall/include/metacall/metacall.h | 36 ++++- source/metacall/source/metacall.c | 37 +++++ source/tests/CMakeLists.txt | 1 + .../metacall_python_fail_test/CMakeLists.txt | 147 ++++++++++++++++++ .../metacall_python_fail_test/source/main.cpp | 28 ++++ .../source/metacall_python_fail_test.cpp | 77 +++++++++ 7 files changed, 348 insertions(+), 7 deletions(-) create mode 100644 source/tests/metacall_python_fail_test/CMakeLists.txt create mode 100644 source/tests/metacall_python_fail_test/source/main.cpp create mode 100644 source/tests/metacall_python_fail_test/source/metacall_python_fail_test.cpp diff --git a/source/loaders/py_loader/source/py_loader_impl.c b/source/loaders/py_loader/source/py_loader_impl.c index 965e3184be..1c8e4db496 100644 --- a/source/loaders/py_loader/source/py_loader_impl.c +++ b/source/loaders/py_loader/source/py_loader_impl.c @@ -1969,14 +1969,29 @@ int py_loader_impl_clear(loader_impl impl, loader_handle handle) return 1; } -type py_loader_impl_discover_type(loader_impl impl, PyObject *annotation) +type py_loader_impl_discover_type(loader_impl impl, PyObject *annotation, const char * func_name, const char * parameter_name) { type t = NULL; if (annotation != NULL) { - PyObject *annotation_qualname = PyObject_GetAttrString(annotation, "__qualname__"); + static const char qualname[] = "__qualname__"; + if (PyObject_HasAttrString(annotation, qualname) == 0) + { + if (parameter_name != NULL) + { + log_write("metacall", LOG_LEVEL_WARNING, "Invalid annotation type in the parameter '%s' of the function %s", parameter_name, func_name); + } + else + { + log_write("metacall", LOG_LEVEL_WARNING, "Invalid annotation type in the return type of the function %s", func_name); + } + + return NULL; + } + + PyObject *annotation_qualname = PyObject_GetAttrString(annotation, qualname); const char *annotation_name = PyUnicode_AsUTF8(annotation_qualname); if (strcmp(annotation_name, "_empty") != 0) @@ -1984,9 +1999,9 @@ type py_loader_impl_discover_type(loader_impl impl, PyObject *annotation) t = loader_impl_type(impl, annotation_name); log_write("metacall", LOG_LEVEL_DEBUG, "Discover type (%p) (%p): %s", (void *)annotation, (void *)type_derived(t), annotation_name); - - Py_DECREF(annotation_qualname); } + + Py_DECREF(annotation_qualname); } return t; @@ -2074,6 +2089,8 @@ int py_loader_impl_discover_func(loader_impl impl, PyObject *func, function f) { signature s = function_signature(f); + const char * func_name = function_name(f); + PyObject *parameters = PyObject_GetAttrString(result, "parameters"); PyObject *return_annotation = PyObject_GetAttrString(result, "return_annotation"); @@ -2111,7 +2128,7 @@ int py_loader_impl_discover_func(loader_impl impl, PyObject *func, function f) PyObject *annotation = PyObject_GetAttrString(parameter, "annotation"); - type t = py_loader_impl_discover_type(impl, annotation); + type t = py_loader_impl_discover_type(impl, annotation, func_name, parameter_name); signature_set(s, iterator, parameter_name, t); } @@ -2119,7 +2136,7 @@ int py_loader_impl_discover_func(loader_impl impl, PyObject *func, function f) } } - signature_set_return(s, py_loader_impl_discover_type(impl, return_annotation)); + signature_set_return(s, py_loader_impl_discover_type(impl, return_annotation, func_name, NULL)); return 0; } diff --git a/source/metacall/include/metacall/metacall.h b/source/metacall/include/metacall/metacall.h index 2138824672..61865d73d5 100644 --- a/source/metacall/include/metacall/metacall.h +++ b/source/metacall/include/metacall/metacall.h @@ -371,7 +371,41 @@ METACALL_API void * metacallt_s(const char * name, const enum metacall_value_id * @return * Function reference, null if the function does not exist */ -METACALL_API void * metacall_function(const char * name); +METACALL_API void * metacall_function(const char * name); + +/** +* @brief +* Get the function parameter type id +* +* @param[in] func +* The pointer to the function obtained from metacall_function +* +* @param[in] parameter +* The index of the parameter to be retrieved +* +* @param[out] id +* The parameter type id that will be returned +* +* @return +* Return 0 if the @parameter index exists and @func is valid, 1 otherwhise +*/ +METACALL_API int metacall_function_parameter_type(void * func, size_t parameter, enum metacall_value_id * id); + +/** +* @brief +* Get the function return type id +* +* @param[in] func +* The pointer to the function obtained from metacall_function +* +* +* @param[out] id +* The value id of the return type of the function @func +* +* @return +* Return 0 if the @func is valid, 1 otherwhise +*/ +METACALL_API int metacall_function_return_type(void * func, enum metacall_value_id * id); /** * @brief diff --git a/source/metacall/source/metacall.c b/source/metacall/source/metacall.c index b00af43abc..27de41f17e 100644 --- a/source/metacall/source/metacall.c +++ b/source/metacall/source/metacall.c @@ -610,6 +610,43 @@ void * metacall_function(const char * name) } return f; +} + +int metacall_function_parameter_type(void * func, size_t parameter, enum metacall_value_id * id) +{ + if (func != NULL) + { + function f = (function)func; + signature s = function_signature(f); + + if (parameter < signature_count(s)) + { + *id = type_index(signature_get_type(s, parameter)); + + return 0; + } + } + + *id = METACALL_INVALID; + + return 1; +} + +int metacall_function_return_type(void * func, enum metacall_value_id * id) +{ + if (func != NULL) + { + function f = (function)func; + signature s = function_signature(f); + + *id = type_index(signature_get_return(s)); + + return 0; + } + + *id = METACALL_INVALID; + + return 1; } size_t metacall_function_size(void * func) diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt index d74a7119fc..a4b440b4d4 100644 --- a/source/tests/CMakeLists.txt +++ b/source/tests/CMakeLists.txt @@ -156,6 +156,7 @@ add_subdirectory(metacall_python_varargs_test) add_subdirectory(metacall_python_port_test) add_subdirectory(metacall_python_port_https_test) add_subdirectory(metacall_python_callback_test) +add_subdirectory(metacall_python_fail_test) add_subdirectory(metacall_map_test) add_subdirectory(metacall_map_await_test) add_subdirectory(metacall_initialize_test) diff --git a/source/tests/metacall_python_fail_test/CMakeLists.txt b/source/tests/metacall_python_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..1991afbdac --- /dev/null +++ b/source/tests/metacall_python_fail_test/CMakeLists.txt @@ -0,0 +1,147 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall_distributable +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_fail_test/source/main.cpp b/source/tests/metacall_python_fail_test/source/main.cpp new file mode 100644 index 0000000000..2276015601 --- /dev/null +++ b/source/tests/metacall_python_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2021 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char * argv[]) +{ + ::testing::InitGoogleMock(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_fail_test/source/metacall_python_fail_test.cpp b/source/tests/metacall_python_fail_test/source/metacall_python_fail_test.cpp new file mode 100644 index 0000000000..6521a6b663 --- /dev/null +++ b/source/tests/metacall_python_fail_test/source/metacall_python_fail_test.cpp @@ -0,0 +1,77 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2021 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int) 0, (int) metacall_initialize()); + + /* Python */ + #if defined(OPTION_BUILD_LOADERS_PY) + { + const char buffer[] = + "def sumList(list: [int]):\n" + " return sum(list)\n"; + + EXPECT_EQ((int) 0, (int) metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + enum metacall_value_id id; + + EXPECT_EQ((int) 0, (int) metacall_function_parameter_type(metacall_function("sumList"), 0, &id)); + + // The type of list must be invalid once it loads + EXPECT_EQ((enum metacall_value_id) METACALL_INVALID, (enum metacall_value_id) id); + } + #endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char * inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *) NULL, (char *) inspect_str); + + EXPECT_GT((size_t) size, (size_t) 0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + EXPECT_EQ((int) 0, (int) metacall_destroy()); +} From ded829b787dd07ee922e71ba7259bfcf98969ed5 Mon Sep 17 00:00:00 2001 From: viferga Date: Fri, 29 Jan 2021 01:01:02 -0900 Subject: [PATCH 4/4] Solve minor bug in node port related to the import of node modules. --- source/ports/node_port/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/ports/node_port/index.js b/source/ports/node_port/index.js index 28598873b6..81438decfb 100644 --- a/source/ports/node_port/index.js +++ b/source/ports/node_port/index.js @@ -200,6 +200,14 @@ mod.prototype.require = function (name) { }; /* Try to load it with NodeJS first */ + try { + return node_require.apply(this, [ name ]); + } catch (e) { + if (e.code !== 'MODULE_NOT_FOUND') { + throw e; + } + } + try { return node_require.apply(this, [ require.resolve(name) ]); } catch (e) {