Frege code generator for Java classes
This project aims to reduce the manual effort for defining native bindings for Java classes in Frege.
Given a Java class and it's purity (whether it is pure or mutable(ST
) or doing IO(IO
)), this will generate corresponding Frege code for that class. The generated code may still not compile due to other unknown types or you might want to wrap the return type in Maybe
if the method is known to return a possible null
. So this is just an utility so that we don't have to look at every Java method signatures and write down the corresponding Frege type signatures which is both time consuming and more error-prone. The tool supports generating Frege code for all the classes in an entire Java package and its sub packages recursively. It also figures out the dependencies from method signatures and generates import statements in the Frege modules.
Here are some examples on how this works:
Example 1: java.awt.Point
in ST
- All public members are resolved including public fields.
- Overloaded constructors and methods are grouped and their types are separated with
|
. - If the class implements
Serializable
,derive Serializable
will be generated. In the same way, if the class is notSerializable
but implementsCloneable
,derive Cloneable
will be generated and for subclasses ofThrowable
,derive Exceptional
will be generated.
data Point = native java.awt.Point where
native x ".x" :: Mutable s Point -> ST s Int
native y ".y" :: Mutable s Point -> ST s Int
native new :: Int -> Int -> STMutable s Point
| Mutable s Point -> STMutable s Point
| () -> STMutable s Point
native equals :: Mutable s Point -> Object -> ST s Bool
native getLocation :: Mutable s Point -> STMutable s Point
native getX :: Mutable s Point -> ST s Double
native getY :: Mutable s Point -> ST s Double
native move :: Mutable s Point -> Int -> Int -> ST s ()
native setLocation :: Mutable s Point -> Mutable s Point -> ST s ()
| Mutable s Point -> Int -> Int -> ST s ()
| Mutable s Point -> Double -> Double -> ST s ()
native toString :: Mutable s Point -> ST s String
native translate :: Mutable s Point -> Int -> Int -> ST s ()
derive Serializable Point
Example 2: java.util.HashMap
in ST
Type parameters for generic classes and methods are resolved, so are nested classes (java.util.Map.Entry
). The naming convention for nested classes in the generated code is to prepend the class name with the parent class name and an underscore but it can be overridden to have a different name (See types.properties
below).
data HashMap k v = native java.util.HashMap where
native new :: Mutable s (Map k v) -> STMutable s (HashMap k v)
| () -> STMutable s (HashMap k v)
| Int -> STMutable s (HashMap k v)
| Int -> Float -> STMutable s (HashMap k v)
native clear :: Mutable s (HashMap k v) -> ST s ()
native clone :: Mutable s (HashMap k v) -> ST s Object
native containsKey :: Mutable s (HashMap k v) -> Object -> ST s Bool
native containsValue :: Mutable s (HashMap k v) -> Object -> ST s Bool
native entrySet :: Mutable s (HashMap k v) -> STMutable s (Set (MapEntry k v))
native get :: Mutable s (HashMap k v) -> Object -> ST s v
native isEmpty :: Mutable s (HashMap k v) -> ST s Bool
native keySet :: Mutable s (HashMap k v) -> STMutable s (Set k)
native put :: Mutable s (HashMap k v) -> k -> v -> ST s v
native putAll :: Mutable s (HashMap k v) -> Mutable s (Map k v) -> ST s ()
native remove :: Mutable s (HashMap k v) -> Object -> ST s v
native size :: Mutable s (HashMap k v) -> ST s Int
native values :: Mutable s (HashMap k v) -> STMutable s (Collection v)
derive Serializable (HashMap k v)
Example 3: java.util.Locale
as pure
- Static constant fields are resolved and generated with all lowercase characters.
- Though the class itself is pure, if any of the parameters for a method is impure such as arrays or other impure types or if the method is returning
void
or doesn't take any parameters or throws any checkedException
, the method will be considered as impure (here for example,getAvailableLocales
,getExtensionKeys
). - Due to grouping of overloaded methods, if any of the overloaded methods is pure but others are impure, the pure method will be considered as impure. For example, here,
native getDefault java.util.Locale.getDefault :: LocaleCategory -> ST s Locale
is supposed to be pure because bothLocaleCategory
andLocale
are pure but since the other overloaded version,() -> ST s Locale
is impure, the first version is also modified to be impure (inST
).
data Locale = pure native java.util.Locale where
pure native english java.util.Locale.ENGLISH :: Locale
pure native french java.util.Locale.FRENCH :: Locale
pure native german java.util.Locale.GERMAN :: Locale
pure native italian java.util.Locale.ITALIAN :: Locale
pure native japanese java.util.Locale.JAPANESE :: Locale
pure native korean java.util.Locale.KOREAN :: Locale
pure native chinese java.util.Locale.CHINESE :: Locale
pure native simplified_chinese java.util.Locale.SIMPLIFIED_CHINESE :: Locale
pure native traditional_chinese java.util.Locale.TRADITIONAL_CHINESE :: Locale
pure native france java.util.Locale.FRANCE :: Locale
pure native germany java.util.Locale.GERMANY :: Locale
pure native italy java.util.Locale.ITALY :: Locale
pure native japan java.util.Locale.JAPAN :: Locale
pure native korea java.util.Locale.KOREA :: Locale
pure native china java.util.Locale.CHINA :: Locale
pure native prc java.util.Locale.PRC :: Locale
pure native taiwan java.util.Locale.TAIWAN :: Locale
pure native uk java.util.Locale.UK :: Locale
pure native us java.util.Locale.US :: Locale
pure native canada java.util.Locale.CANADA :: Locale
pure native canada_french java.util.Locale.CANADA_FRENCH :: Locale
pure native root java.util.Locale.ROOT :: Locale
pure native private_use_extension java.util.Locale.PRIVATE_USE_EXTENSION :: Char
pure native unicode_locale_extension java.util.Locale.UNICODE_LOCALE_EXTENSION :: Char
native new :: String -> ST s Locale
| String -> String -> ST s Locale
| String -> String -> String -> ST s Locale
pure native clone :: Locale -> Object
pure native equals :: Locale -> Object -> Bool
pure native forLanguageTag java.util.Locale.forLanguageTag :: String -> Locale
native getAvailableLocales java.util.Locale.getAvailableLocales :: () -> STMutable s (JArray Locale)
pure native getCountry :: Locale -> String
native getDefault java.util.Locale.getDefault :: LocaleCategory -> ST s Locale
| () -> ST s Locale
pure native getDisplayCountry :: Locale -> String
| Locale -> Locale -> String
pure native getDisplayLanguage :: Locale -> String
| Locale -> Locale -> String
pure native getDisplayName :: Locale -> Locale -> String
| Locale -> String
pure native getDisplayScript :: Locale -> String
| Locale -> Locale -> String
pure native getDisplayVariant :: Locale -> Locale -> String
| Locale -> String
pure native getExtension :: Locale -> Char -> String
native getExtensionKeys :: Locale -> STMutable s (Set Character)
pure native getISO3Country :: Locale -> String
pure native getISO3Language :: Locale -> String
native getISOCountries java.util.Locale.getISOCountries :: () -> STMutable s (JArray String)
native getISOLanguages java.util.Locale.getISOLanguages :: () -> STMutable s (JArray String)
pure native getLanguage :: Locale -> String
pure native getScript :: Locale -> String
native getUnicodeLocaleAttributes :: Locale -> STMutable s (Set String)
native getUnicodeLocaleKeys :: Locale -> STMutable s (Set String)
pure native getUnicodeLocaleType :: Locale -> String -> String
pure native getVariant :: Locale -> String
pure native hashCode :: Locale -> Int
native setDefault java.util.Locale.setDefault :: Locale -> ST s ()
| LocaleCategory -> Locale -> ST s ()
pure native toLanguageTag :: Locale -> String
pure native toString :: Locale -> String
derive Serializable Locale
Example 4: java.io.FileInputStream
in IO
Checked exceptions are identified and a throws
is appended to the function type with all the checked exceptions.
data FileInputStream = native java.io.FileInputStream where
native new :: MutableIO File -> IOMutable FileInputStream throws FileNotFoundException
| String -> IOMutable FileInputStream throws FileNotFoundException
| FileDescriptor -> IOMutable FileInputStream
native available :: MutableIO FileInputStream -> IO Int throws IOException
native close :: MutableIO FileInputStream -> IO () throws IOException
native getChannel :: MutableIO FileInputStream -> IOMutable FileChannel
native getFD :: MutableIO FileInputStream -> IO FileDescriptor throws IOException
native read :: MutableIO FileInputStream -> IO Int throws IOException
| MutableIO FileInputStream -> MutableIO (JArray Byte) -> IO Int throws IOException
| MutableIO FileInputStream -> MutableIO (JArray Byte) -> Int -> Int -> IO Int throws IOException
native skip :: MutableIO FileInputStream -> Long -> IO Long throws IOException
types.properties
- All the examples above use a properties file which indicates all the Java classes, their purity and their optional new names in Frege code. The name used here as the key is the class name returned by
java.lang.Class.getName()
for that class. Nested classes can also be mentioned with their name as returned byjava.lang.Class.getName()
. - If a class is missing in this file but being used in the class we are generating, the missing class is assumed to be
ST
and the unqualified name of the class will be used in the generated code.
boolean=pure,Bool
byte=pure,Byte
char=pure,Char
double=pure,Double
float=pure,Float
int=pure,Int
java.awt.Point=st
java.io.File=io
java.io.FileInputStream=io
java.io.InputStream=io
java.io.OutputStream=io
java.io.PrintStream=io
java.io.PrintWriter=io
java.io.Reader=io
java.io.Writer=io
java.lang.Appendable=st
java.lang.Comparable=pure
java.lang.Iterable=st
java.lang.Readable=st
java.lang.String=pure
java.lang.StringBuffer=st
java.lang.StringBuilder=st
java.math.BigDecimal=pure
java.math.BigInteger=pure,Integer
java.nio.ByteBuffer=st
java.nio.ByteOrder=pure
java.nio.CharBuffer=st
java.nio.DoubleBuffer=st
java.nio.FloatBuffer=st
java.nio.IntBuffer=st
java.nio.LongBuffer=st
java.nio.ShortBuffer=st
java.nio.channels.FileChannel=io
java.nio.channels.ReadableByteChannel=st
java.nio.file.Path=pure
java.nio.file.WatchService=st
java.security.Permission=pure
java.security.PermissionCollection=st
java.util.AbstractCollection=st
java.util.AbstractList=st
java.util.AbstractMap$SimpleEntry=st,AbstractMapSimpleEntry
java.util.AbstractMap$SimpleImmutableEntry=pure,AbstractMapSimpleImmutableEntry
java.util.AbstractMap=st
java.util.AbstractQueue=st
java.util.AbstractSequentialList=st
java.util.AbstractSet=st
java.util.ArrayDeque=st
java.util.ArrayList=st
java.util.Arrays=pure
java.util.BitSet=st
java.util.Calendar=st
java.util.Collection=st
java.util.Collections=pure
java.util.Comparator=pure
java.util.Currency=pure
java.util.Date=st
java.util.Deque=st
java.util.Dictionary=st
java.util.EnumMap=st
java.util.EnumSet=st
java.util.Enumeration=st
java.util.EventListener=st
java.util.EventListenerProxy=st
java.util.EventObject=st
java.util.Formattable=st
java.util.FormattableFlags=pure
java.util.Formatter$BigDecimalLayoutForm=pure,FormatterBigDecimalLayoutForm
java.util.Formatter=st
java.util.GregorianCalendar=st
java.util.HashMap=st
java.util.HashSet=st
java.util.Hashtable=st
java.util.IdentityHashMap=st
java.util.Iterator=st
java.util.LinkedHashMap=st
java.util.LinkedHashSet=st
java.util.LinkedList=st
java.util.List=st
java.util.ListIterator=st
java.util.Locale$Builder=st,LocaleBuilder
java.util.Locale$Category=pure,LocaleCategory
java.util.Locale=pure
java.util.Map$Entry=st,MapEntry
java.util.Map=st
java.util.NavigableMap=st
java.util.NavigableSet=st
java.util.Objects=pure
java.util.Observable=st
java.util.Observer=st
java.util.PriorityQueue=st
java.util.Properties=st
java.util.PropertyPermission=pure
java.util.Queue=st
java.util.Random=st
java.util.RandomAccess=st
java.util.Scanner=st
java.util.Set=st
java.util.SortedMap=st
java.util.SortedSet=st
java.util.Stack=st
java.util.TimeZone=pure
java.util.TreeMap=st
java.util.TreeSet=st
java.util.Vector=st
java.util.WeakHashMap=st
java.util.regex.MatchResult=pure
java.util.regex.Matcher=st,RegexMatcher
java.util.regex.Pattern=pure,RegexPattern
long=pure,Long
short=pure,Short
void=pure,()
How to run
-
Download and extract
frege-native-gen-<version>.zip
from releases. Thetypes.properties
is included in the zip. -
Mention your class name along with it's purity in types.properties.
For example,
java.util.HashSet=st
or if you want to call it a different name in Frege, addjava.util.HashSet=st,JHashSet
-
Run
java -cp "lib/*:$JAVA_HOME/jre/lib/*" frege.nativegen.Main -p java.util
This would convert all the classes in
java.util
package including sub packages and create corresponding Frege modules (one Frege module per package). The generated sources will be ingenerated-sources
folder.- Run
java -cp "lib/*:$JAVA_HOME/jre/lib/*" frege.nativegen.Main -c java.util.List,java.util.Set
for a specific set of classes. - To generate for classes other than those in JDK, add the respective library in the classpath:
java -cp "/path/to/guava.jar:lib/*" frege.nativegen.Main -p com.google.common.collect
- Run
java -cp "lib/*:$JAVA_HOME/jre/lib/*" frege.nativegen.Main -h
for help.
- Run
How to run with Gradle
From project source root,
- For all the classes in a package:
./gradlew run -Poptions="-p java.util"
- For a specific set of classes:
./gradlew run -Poptions="-c java.util.List,java.util.Set"
- To run with different types.properties:
./gradlew run -Poptions="-c java.util.List -t /path/to/types.properties"
- For help:
./gradlew run -Poptions="-h"
Continuous Integration
The Travis CI build of this repository is at https://travis-ci.org/Frege/frege-native-gen.
The snapshot builds are automatically deploy to Sonatype at https://oss.sonatype.org/content/groups/public/org/frege-lang/frege-native-gen/.