Skip to content

Commit 44e59ef

Browse files
committed
Implement working version of scala port, yet without a cool interface/api.
1 parent 00ccc90 commit 44e59ef

File tree

9 files changed

+318
-53
lines changed

9 files changed

+318
-53
lines changed

source/ports/scala_port/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ target/
33
.vscode/
44
.bsp/
55
.metals/
6-
*.log
6+
*.log

source/ports/scala_port/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Metacall Scala Port
1+
# MetaCall Scala Port
22

33
## Setup
44

@@ -10,7 +10,6 @@ To run the tests, run `sbt test` in this README's directory.
1010

1111
Don't forget to set these environment variables:
1212
```
13-
LD_LIBRARY_PATH
1413
LOADER_SCRIPT_PATH
1514
LOADER_LIBRARY_PATH
1615
CONFIGURATION_PATH
@@ -19,4 +18,4 @@ DETOUR_LIBRARY_PATH
1918
PORT_LIBRARY_PATH
2019
```
2120

22-
> Note: You'll find the bindings and the code that runs on `sbt test` in `src/main/scala/Metacall.scala`.
21+
> Note: You'll find the bindings and the code that runs on `sbt test` in `src/main/scala/MetaCall.scala`.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* MetaCall Scala Port by Parra Studios
3+
* A complete infrastructure for supporting multiple language bindings in MetaCall.
4+
*
5+
* Copyright (C) 2016 - 2021 Vicente Eduardo Ferrer Garcia <[email protected]>
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
21+
package metacall
22+
23+
import com.sun.jna._
24+
import java.nio.file.Paths
25+
26+
class size_t(value: Long) extends IntegerType(Native.SIZE_T_SIZE, value) {
27+
def this() {
28+
this(0)
29+
}
30+
}
31+
32+
trait MetaCallBindings extends Library {
33+
// metacall.h
34+
def metacall_initialize(): Int
35+
def metacall_load_from_file(tag: String, paths: Array[String], size: size_t, handle: Pointer): Int
36+
def metacallv_s(name: String, args: Array[Pointer], size: size_t): Pointer
37+
def metacall_destroy(): Int
38+
39+
// metacall_value.h
40+
def metacall_value_create_int(i: Int): Pointer
41+
def metacall_value_create_string(str: String, length: size_t): Pointer
42+
43+
def metacall_value_to_int(v: Pointer): Int
44+
def metacall_value_to_long(v: Pointer): Long
45+
def metacall_value_to_string(v: Pointer): String
46+
47+
def metacall_value_from_int(v: Pointer, i: Int): Pointer
48+
def metacall_value_from_string(v: Pointer, str: String, length: size_t): Pointer
49+
50+
def metacall_value_size(v: Pointer): size_t
51+
def metacall_value_count(v: Pointer): size_t
52+
53+
def metacall_value_destroy(v: Pointer)
54+
55+
// TODO:
56+
/*
57+
enum metacall_value_id
58+
{
59+
METACALL_BOOL = 0,
60+
METACALL_CHAR = 1,
61+
METACALL_SHORT = 2,
62+
METACALL_INT = 3,
63+
METACALL_LONG = 4,
64+
METACALL_FLOAT = 5,
65+
METACALL_DOUBLE = 6,
66+
METACALL_STRING = 7,
67+
METACALL_BUFFER = 8,
68+
METACALL_ARRAY = 9,
69+
METACALL_MAP = 10,
70+
METACALL_PTR = 11,
71+
METACALL_FUTURE = 12,
72+
METACALL_FUNCTION = 13,
73+
METACALL_NULL = 14,
74+
METACALL_CLASS = 15,
75+
METACALL_OBJECT = 16,
76+
77+
METACALL_SIZE,
78+
METACALL_INVALID
79+
};
80+
*/
81+
82+
// TODO:
83+
def metacall_value_id(v: Pointer): Int /* enum metacall_value_id */
84+
}
85+
86+
object MetaCall {
87+
var metacall = Native.load("metacall", classOf[MetaCallBindings]);
88+
89+
// This is a experiment I have been doing in order to implement a high level
90+
// abstraction for Scala. The objective is to make it as trasparent as possible,
91+
// like if you were dealing with normal scala values. Generics can help for sure.
92+
93+
/*
94+
trait ValueLifetime {
95+
def finalize()
96+
}
97+
98+
def finalizer [T, V <: ValueLifetime] (v : V) (f : V => T) : T =
99+
try f(v)
100+
finally v.finalize()
101+
102+
abstract class ValuePtr[T](v: Pointer) extends ValueLifetime {
103+
def finalize() {
104+
metacall.metacall_value_destroy(v)
105+
}
106+
}
107+
108+
class Value[@specialized(Int) T](i: Int) extends ValuePtr[T](metacall.metacall_value_create_int(i)) {
109+
def to_value(): T = {
110+
metacall.metacall_value_to_int(v)
111+
}
112+
}
113+
114+
class Value[@specialized(String) T](str: String) extends ValuePtr(metacall.metacall_value_create_string(i)) {
115+
116+
}
117+
*/
118+
119+
if (metacall.metacall_initialize() != 0) {
120+
throw new RuntimeException("MetaCall could not initialize")
121+
}
122+
123+
var paths = Array(
124+
Paths.get("./src/test/scala/scripts/main.py").toAbsolutePath.toString()
125+
)
126+
127+
// Load the script list
128+
if (metacall.metacall_load_from_file("py", paths, new size_t(paths.length), null) != 0) {
129+
throw new RuntimeException("MetaCall failed to load the script")
130+
}
131+
132+
// Create array of parameters
133+
var args = Array(
134+
metacall.metacall_value_create_int(3),
135+
metacall.metacall_value_create_int(5),
136+
)
137+
138+
// Invoke the function
139+
var ret = metacall.metacallv_s("hello_sacala_from_python", args, new size_t(args.length))
140+
141+
// Note: Python uses longs, so it returns a long value
142+
println("Result:", metacall.metacall_value_to_long(ret))
143+
144+
// For avoiding conversion errors, it is possible to test against metacall_value_id,
145+
// or there is also a casting API for dealing with it.
146+
147+
// Clear parameters
148+
args.foreach { metacall.metacall_value_destroy }
149+
150+
// Clear return value
151+
metacall.metacall_value_destroy(ret)
152+
153+
if (metacall.metacall_destroy() != 0) {
154+
throw new RuntimeException("MetaCall did not finish successfully")
155+
}
156+
}
Lines changed: 144 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,156 @@
1+
/*
2+
* MetaCall Scala Port by Parra Studios
3+
* A complete infrastructure for supporting multiple language bindings in MetaCall.
4+
*
5+
* Copyright (C) 2016 - 2021 Vicente Eduardo Ferrer Garcia <[email protected]>
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
121
package metacall
222

323
import com.sun.jna._
24+
import java.nio.file.Paths
425

5-
trait MetacallBindings extends Library {
6-
def metacall_load_from_file(
7-
runtime: String,
8-
paths: Array[String],
9-
size: Int,
10-
handle: Array[Pointer]
11-
): Int
12-
13-
def metacallv_s(
14-
functionName: String,
15-
args: Array[Pointer],
16-
argsSize: Int
17-
): Pointer
26+
class size_t(value: Long) extends IntegerType(Native.SIZE_T_SIZE, value) {
27+
def this() {
28+
this(0)
29+
}
30+
}
1831

32+
trait MetaCallBindings extends Library {
33+
// metacall.h
1934
def metacall_initialize(): Int
35+
def metacall_load_from_file(tag: String, paths: Array[String], size: size_t, handle: Pointer): Int
36+
def metacallv_s(name: String, args: Array[Pointer], size: size_t): Pointer
37+
def metacall_destroy(): Int
2038

21-
def metacall_value_create_string(str: String, byteSize: Int): Pointer
22-
39+
// metacall_value.h
2340
def metacall_value_create_int(i: Int): Pointer
41+
def metacall_value_create_string(str: String, length: size_t): Pointer
42+
43+
def metacall_value_to_int(v: Pointer): Int
44+
def metacall_value_to_long(v: Pointer): Long
45+
def metacall_value_to_string(v: Pointer): String
46+
47+
def metacall_value_from_int(v: Pointer, i: Int): Pointer
48+
def metacall_value_from_string(v: Pointer, str: String, length: size_t): Pointer
49+
50+
def metacall_value_size(v: Pointer): size_t
51+
def metacall_value_count(v: Pointer): size_t
2452

25-
def metacall_destroy(value: Pointer): Int
53+
def metacall_value_destroy(v: Pointer)
54+
55+
// TODO:
56+
/*
57+
enum metacall_value_id
58+
{
59+
METACALL_BOOL = 0,
60+
METACALL_CHAR = 1,
61+
METACALL_SHORT = 2,
62+
METACALL_INT = 3,
63+
METACALL_LONG = 4,
64+
METACALL_FLOAT = 5,
65+
METACALL_DOUBLE = 6,
66+
METACALL_STRING = 7,
67+
METACALL_BUFFER = 8,
68+
METACALL_ARRAY = 9,
69+
METACALL_MAP = 10,
70+
METACALL_PTR = 11,
71+
METACALL_FUTURE = 12,
72+
METACALL_FUNCTION = 13,
73+
METACALL_NULL = 14,
74+
METACALL_CLASS = 15,
75+
METACALL_OBJECT = 16,
76+
77+
METACALL_SIZE,
78+
METACALL_INVALID
79+
};
80+
*/
81+
82+
// TODO:
83+
def metacall_value_id(v: Pointer): Int /* enum metacall_value_id */
2684
}
2785

28-
object Metacall {
29-
// CAUTION: Always check pointers passed to metacall (should not be Java null)
30-
31-
val mc = Native.load("metacall", classOf[MetacallBindings])
32-
import mc._
33-
34-
metacall_initialize()
35-
36-
println(
37-
metacall_load_from_file(
38-
"node",
39-
Array("./src/test/scala/main.js"),
40-
1,
41-
Array.empty
42-
)
43-
) // outputs 1
44-
45-
// Call hello
46-
val arg = metacall_value_create_string("Jack", "Jack".getBytes().length)
47-
println("ARG: " + arg.getString(0)) // works!
48-
val r = metacallv_s("hello", Array(arg), 1)
49-
println("R1: " + r) // does not work...
50-
metacall_destroy(arg) // works!
51-
52-
// Call increment
53-
val n = metacall_value_create_int(50)
54-
println("N: " + n.getInt(0)) // works!
55-
val r2 = metacallv_s("increment", Array(n), 1)
56-
println("R2: " + r2) // does not work...
86+
object MetaCall {
87+
var metacall = Native.load("metacall", classOf[MetaCallBindings]);
88+
89+
// This is a experiment I have been doing in order to implement a high level
90+
// abstraction for Scala. The objective is to make it as trasparent as possible,
91+
// like if you were dealing with normal scala values. Generics can help for sure.
92+
93+
/*
94+
trait ValueLifetime {
95+
def finalize()
96+
}
97+
98+
def finalizer [T, V <: ValueLifetime] (v : V) (f : V => T) : T =
99+
try f(v)
100+
finally v.finalize()
101+
102+
abstract class ValuePtr[T](v: Pointer) extends ValueLifetime {
103+
def finalize() {
104+
metacall.metacall_value_destroy(v)
105+
}
106+
}
107+
108+
class Value[@specialized(Int) T](i: Int) extends ValuePtr[T](metacall.metacall_value_create_int(i)) {
109+
def to_value(): T = {
110+
metacall.metacall_value_to_int(v)
111+
}
112+
}
113+
114+
class Value[@specialized(String) T](str: String) extends ValuePtr(metacall.metacall_value_create_string(i)) {
115+
116+
}
117+
*/
118+
119+
if (metacall.metacall_initialize() != 0) {
120+
throw new RuntimeException("MetaCall could not initialize")
121+
}
122+
123+
var paths = Array(
124+
Paths.get("./src/test/scala/scripts/main.py").toAbsolutePath.toString()
125+
)
126+
127+
// Load the script list
128+
if (metacall.metacall_load_from_file("py", paths, new size_t(paths.length), null) != 0) {
129+
throw new RuntimeException("MetaCall failed to load the script")
130+
}
131+
132+
// Create array of parameters
133+
var args = Array(
134+
metacall.metacall_value_create_int(3),
135+
metacall.metacall_value_create_int(5),
136+
)
137+
138+
// Invoke the function
139+
var ret = metacall.metacallv_s("hello_sacala_from_python", args, new size_t(args.length))
140+
141+
// Note: Python uses longs, so it returns a long value
142+
println("Result:", metacall.metacall_value_to_long(ret))
143+
144+
// For avoiding conversion errors, it is possible to test against metacall_value_id,
145+
// or there is also a casting API for dealing with it.
146+
147+
// Clear parameters
148+
args.foreach { metacall.metacall_value_destroy }
149+
150+
// Clear return value
151+
metacall.metacall_value_destroy(ret)
152+
153+
if (metacall.metacall_destroy() != 0) {
154+
throw new RuntimeException("MetaCall did not finish successfully")
155+
}
57156
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package metacall
2+
3+
import org.scalatest.flatspec.AnyFlatSpec
4+
5+
class MetaCallSpec extends AnyFlatSpec {
6+
"MetaCall" should "work" in {
7+
MetaCall
8+
}
9+
}

0 commit comments

Comments
 (0)