Skip to content

Commit

Permalink
Add j19 implementation, improve reliability.
Browse files Browse the repository at this point in the history
  • Loading branch information
markehammons committed Nov 25, 2022
1 parent 1396cba commit 108f707
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 121 deletions.
18 changes: 7 additions & 11 deletions core/src/fr/hammons/slinc/LibraryI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ object LibraryI:
def getLocalLookup(name: String): Lookup
def getLibraryPathLookup(name: String): Lookup
def getStandardLibLookup: Lookup
def getResourceLibLookup(location: String): Lookup

def checkMethodIsCompatible(using q: Quotes)(s: q.reflect.Symbol): Unit =
import quotes.reflect.*
Expand Down Expand Up @@ -237,20 +238,15 @@ object LibraryI:
platformSpecificExpr: Expr[PlatformSpecific]
)(using Quotes, Type[L]) =
import quotes.reflect.*
val name: Option[LibraryLocation] = LibraryName.libraryName[L]
val name: LibraryLocation = LibraryName.libraryName[L]
name match
case None => '{ $platformSpecificExpr.getStandardLibLookup }
case Some(LibraryLocation.Local(s)) =>
case LibraryLocation.Standardard => '{ $platformSpecificExpr.getStandardLibLookup }
case LibraryLocation.Local(s) =>
'{ $platformSpecificExpr.getLocalLookup(${ Expr(s) }) }
case Some(LibraryLocation.Path(s)) =>
case LibraryLocation.Path(s) =>
'{ $platformSpecificExpr.getLibraryPathLookup(${ Expr(s) }) }
case Some(LibraryLocation.Resource(s)) =>
val sExpr = Expr(s)
val clPath = '{
Tools.sendResourceToCache($sExpr)
Tools.compileCachedResourceIfNeeded($sExpr).toAbsolutePath().nn.toString()
}
'{ $platformSpecificExpr.getLocalLookup($clPath ) }
case LibraryLocation.Resource(s) =>
'{ $platformSpecificExpr.getResourceLibLookup(${Expr(s)} ) }

inline def getMethodAddress[L](l: Lookup) = ${
getMethodAddressImpl[L]('l)
Expand Down
3 changes: 2 additions & 1 deletion core/src/fr/hammons/slinc/LibraryName.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum LibraryLocation:
case Local(s: String)
case Resource(s: String)
case Path(s: String)
case Standardard

object LibraryName:
def libraryName[L](using Quotes, Type[L]) =
Expand All @@ -24,4 +25,4 @@ object LibraryName:
case s"@$path" => LibraryLocation.Resource(path)
case s"#$path" => LibraryLocation.Path(path)
case s => LibraryLocation.Local(s)
}.headOption
}.headOption.getOrElse(LibraryLocation.Standardard)
15 changes: 14 additions & 1 deletion core/src/fr/hammons/slinc/Lookup.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
package fr.hammons.slinc

private[slinc] trait Lookup:
import java.nio.file.Path
import java.nio.file.Paths

trait Lookup(libraryLocation: LibraryLocation):
def lookup(name: String): Object

def lookupError(name: String): Error = libraryLocation match
case LibraryLocation.Standardard => Error(s"Failed to load symbol $name from the standard library.")
case LibraryLocation.Resource(location) =>
Error(s"Failed to load symbol $name from resource $location. This could be caused by resource collision. Is the resource name unique enough?")
case LibraryLocation.Local(location) =>
val absPath = Paths.get(location).nn.toAbsolutePath.nn
Error(s"Failed to load symbol $name from local path $absPath")
case LibraryLocation.Path(libName) =>
Error(s"Failed to load symbol $name from library $libName")
65 changes: 39 additions & 26 deletions core/src/fr/hammons/slinc/Tools.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,55 @@ import java.nio.channels.ByteChannel
import java.nio.channels.Channels
import scala.sys.process.*


object Tools {
object Tools:
private val appDataStore =
Paths.get(os match
case OS.Windows =>
s"${System.getenv("APPDATA").nn}\\local\\slinc\\libstore\\"
case OS.Linux => s"${System.getProperty("user.home").nn}/.cache/slinc/"
case OS.Darwin =>
s"${System.getProperty("user.home").nn}/Library/Application Support/"
).nn

private val sharedLibSuffix =
Paths
.get(os match
case OS.Windows =>
s"${System.getenv("APPDATA").nn}\\local\\slinc\\libstore\\"
case OS.Linux => s"${System.getProperty("user.home").nn}/.cache/slinc/"
case OS.Darwin =>
s"${System.getProperty("user.home").nn}/Library/Application Support/"
)
.nn

private val sharedLibSuffix =
os match
case OS.Linux | OS.Darwin => ".so"
case OS.Windows => ".dll"
case OS.Unknown => throw Error("Cannot do lib compilation on this platform, it's unknown.")
case OS.Windows => ".dll"
case OS.Unknown =>
throw Error("Cannot do lib compilation on this platform, it's unknown.")

def sendResourceToCache(name: String) =
println(name)
def sendResourceToCache(name: String): Unit =
Files.createDirectories(appDataStore)
val cacheLocation = appDataStore.resolve(s"$name.c")
if !Files.exists(appDataStore.resolve(s"$name.c")) then
Files.copy(getClass().getResourceAsStream(s"/native/$name.c"), cacheLocation)
println(cacheLocation)
if !Files.exists(appDataStore.resolve(s"$name.c")) then
val stream = getClass().getResourceAsStream(s"/native/$name.c")
if stream != null then Files.copy(stream, cacheLocation)
else throw Error(s"Could not find resource /native/$name.c")

def compileCachedResourceIfNeeded(name: String): Path =
def compileCachedResourceIfNeeded(name: String): Unit =
val cacheLocation = appDataStore.resolve(s"$name$sharedLibSuffix")
val headerLocation = appDataStore.resolve(s"$name.c")

println(headerLocation)

if !Files.exists(cacheLocation) then
val cmd = Seq("clang", "-shared", "-Os", "-o", cacheLocation.nn.toAbsolutePath().nn.toString(), headerLocation.nn.toAbsolutePath().nn.toString())
if cmd.! != 0 then throw Error(s"failed to compile $headerLocation")

cacheLocation.nn
val cmd = Seq(
"clang",
"-shared",
"-fvisibility=default",
"-Os",
"-o",
cacheLocation.nn.toAbsolutePath().nn.toString(),
headerLocation.nn.toAbsolutePath().nn.toString()
)
if cmd.! != 0 then throw Error(s"failed to compile $headerLocation: ${cmd.mkString(" ")}")

def loadCachedLibrary(name: String) =
val cacheLocation = appDataStore.resolve(s"$name.$sharedLibSuffix")
System.loadLibrary(cacheLocation.nn.toAbsolutePath().nn.toString())
def loadCachedLibrary(name: String) =
val cacheLocation = appDataStore.resolve(s"$name$sharedLibSuffix")
println(cacheLocation)
System.load(cacheLocation.nn.toAbsolutePath().nn.toString())

}
end Tools
13 changes: 13 additions & 0 deletions core/test/resources/native/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifdef _WIN32
# ifdef WIN_EXPORT
# define EXPORTED __declspec( dllexport )
# else
# define EXPORTED __declspec( dllimport )
# endif
#else
# define EXPORTED
#endif

EXPORTED int identity_int(int i) {
return i;
}
1 change: 1 addition & 0 deletions core/test/src/fr/hammons/slinc/BindingsSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ trait BindingsSpec(val slinc: Slinc) extends ScalaCheckSuite:
test("int_identity") {
assertEquals(Test.identity_int(5), 5)
}
Test.identity_int(5)

object BindingsSpec:
case class div_t(quot: Int, rem: Int)
71 changes: 16 additions & 55 deletions j17/src/fr/hammons/slinc/Library17.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,67 +46,28 @@ class Library17(layoutI: LayoutI, linker: CLinker)

linker.downcallHandle(address.asInstanceOf[Addressable], md, fd).nn

private val standardLibLookup = new Lookup:
val lookupImpl = CLinker.systemLookup.nn
def lookup(name: String) = lookupImpl
.lookup(name)
.nn
.orElseThrow(() =>
throw Error(s"Failed to load $name from the standard library")
)
.nn

class J17Lookup(s: SymbolLookup, libraryLocation: LibraryLocation) extends Lookup(libraryLocation):
def lookup(name: String): Object = s.lookup(name).nn.orElseThrow(() => throw this.lookupError(name)).nn
private val standardLibLookup = J17Lookup(CLinker.systemLookup().nn,LibraryLocation.Standardard)
override def getStandardLibLookup: Lookup =
Tools.hashCode()

standardLibLookup

override def getLibraryPathLookup(libName: String): Lookup =
new Lookup:
System.loadLibrary(libName)
val lookupImpl = SymbolLookup.loaderLookup().nn
def lookup(symbolName: String) = lookupImpl
.lookup(symbolName)
.nn
.orElseThrow(() =>
throw Error(s"Failed to load ${symbolName} from $libName")
)
.nn
System.loadLibrary(libName)
J17Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Path(libName))


override def getResourceLibLookup(location: String): Lookup =
Tools.sendResourceToCache(location)
Tools.compileCachedResourceIfNeeded(location)
Tools.loadCachedLibrary(location)

J17Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Resource(location))

override def getLocalLookup(libPath: String): Lookup =
new Lookup:
System.load(libPath)
System.load(libPath)

val lookupImpl = SymbolLookup.loaderLookup().nn
def lookup(symbolName: String) = lookupImpl
.lookup(symbolName)
.nn
.orElseThrow(() =>
throw Error(s"Failed to load $symbolName from $libPath")
)
.nn
J17Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Local(libPath))

def getLookup(name: Option[String]): Lookup =
import scala.jdk.OptionConverters.*
name match
case Some(n) =>
new Lookup:
if Files.exists(Paths.get(n)) then
System.load(Paths.get(n).nn.toRealPath().nn.toString())
else System.loadLibrary(n)
val l = SymbolLookup.loaderLookup().nn
def lookup(name: String) = l
.lookup(name)
.nn
.toScala
.getOrElse(throw Error(s"Failed to load $name from $n"))
case None =>
new Lookup:
val l = CLinker.systemLookup().nn
def lookup(name: String) = l
.lookup(name)
.nn
.toScala
.getOrElse(
throw Error(s"Failed to load $name from standard library")
)
end Library17
3 changes: 0 additions & 3 deletions j17/test/resources/native/test.c

This file was deleted.

44 changes: 20 additions & 24 deletions j19/src/fr/hammons/slinc/Library19.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,24 @@ class Library19(layoutI: LayoutI, linker: Linker)

linker.downcallHandle(address.asInstanceOf[Addressable], fd).nn

override def getLookup(name: Option[String]): Lookup =
import scala.jdk.OptionConverters.*
class J19Lookup(s: SymbolLookup, l: LibraryLocation) extends Lookup(l):
def lookup(name: String): Object = s.lookup(name).nn.orElseThrow(() => this.lookupError(name)).nn
override def getLibraryPathLookup(name: String): Lookup =
System.loadLibrary(name)

name match
case Some(n) =>
new Lookup:
if Files.exists(Paths.get(n)) then
System.load(Paths.get(n).nn.toRealPath().nn.toString())
else System.loadLibrary(n)
val l = SymbolLookup.loaderLookup().nn
def lookup(name: String) =
l.lookup(name)
.nn
.toScala
.getOrElse(throw Error(s"Lookup of $name in $n failed"))
case None =>
new Lookup:
val l = linker.defaultLookup().nn
def lookup(name: String) = l
.lookup(name)
.nn
.toScala
.getOrElse(
throw Error(s"Lookup of $name in standard library failed")
)
J19Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Path(name))

override def getLocalLookup(name: String): Lookup =
System.load(name)

J19Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Local(name))

override def getResourceLibLookup(location: String): Lookup =
Tools.sendResourceToCache(location)
Tools.compileCachedResourceIfNeeded(location)
Tools.loadCachedLibrary(location)

J19Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Resource(location))

override def getStandardLibLookup: Lookup = J19Lookup(linker.defaultLookup().nn, LibraryLocation.Standardard)
end Library19

0 comments on commit 108f707

Please sign in to comment.