blob: b9b0070fcb45ece20144b7b2234a979f898a41a9 [file] [log] [blame]
#!/usr/bin/env runhaskell
{-
/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-}
{-
The simplest way to get Haskell is through MacPorts: sudo port install ghc
Otherwise, see http://www.haskell.org/ghc/
-}
import Data.List
import Data.Maybe
import Data.Char
data Width = W32 | W64
deriving (Show, Eq, Bounded, Enum)
data NType = NBOOL | Nschar | Nuchar | Nsshort | Nushort | Nsint | Nuint
| Nslong | Nulong | Nslonglong | Nulonglong | Nfloat | Ndouble
deriving (Show, Eq, Bounded, Enum)
data JPrim = Jboolean | Jbyte | Jchar | Jshort | Jint | Jlong | Jfloat | Jdouble
deriving (Show, Eq, Bounded, Enum)
data JClass = JBoolean | JByte | JCharacter | JShort | JInteger | JLong
| JFloat | JDouble
deriving (Show, Eq, Bounded, Enum)
data FFIType = SINT8 | UINT8 | SINT16 | UINT16 | SINT32 | UINT32
| SINT64 | UINT64 | FLOAT | DOUBLE
deriving (Show, Eq, Bounded, Enum)
widths = [minBound..maxBound] :: [Width]
ntypes = [minBound..maxBound] :: [NType]
jprims = [minBound..maxBound] :: [JPrim]
jclasses = [minBound..maxBound] :: [JClass]
ffitypes = [minBound..maxBound] :: [FFIType]
-- What's the FFIType for a given Width and NType? For example: W32 NBOOL -> SINT8
ffitype :: Width -> NType -> FFIType
ffitype _ NBOOL = SINT8
ffitype _ Nschar = SINT8
ffitype _ Nuchar = UINT8
ffitype _ Nsshort = SINT16
ffitype _ Nushort = UINT16
ffitype _ Nsint = SINT32
ffitype _ Nuint = UINT32
ffitype W32 Nslong = SINT32
ffitype W64 Nslong = SINT64
ffitype W32 Nulong = UINT32
ffitype W64 Nulong = UINT64
ffitype _ Nslonglong = SINT64
ffitype _ Nulonglong = UINT64
ffitype _ Nfloat = FLOAT
ffitype _ Ndouble = DOUBLE
sizeof :: FFIType -> Int
sizeof SINT8 = 1
sizeof UINT8 = 1
sizeof SINT16 = 2
sizeof UINT16 = 2
sizeof SINT32 = 4
sizeof UINT32 = 4
sizeof SINT64 = 8
sizeof UINT64 = 8
sizeof FLOAT = 4
sizeof DOUBLE = 8
-- What's the Obj-C encoding for a given NType? For example: unsigned char -> 'C'
encoding nt = fromJust $ lookup nt $
[(NBOOL, 'B'), (Nschar, 'c'), (Nuchar, 'C'), (Nsshort, 's'),
(Nushort, 'S'), (Nsint, 'i'), (Nuint, 'I'), (Nslong, 'l'),
(Nulong, 'L'), (Nslonglong, 'q'), (Nulonglong, 'Q'),
(Nfloat, 'f'), (Ndouble, 'd')]
-- What's the JPrim for a given NType? For example: native signed long long -> java long
ntype2jprim nt = fromJust $ lookup nt $
[(NBOOL, Jboolean), (Nschar, Jbyte), (Nuchar, Jbyte),
(Nsshort, Jshort), (Nushort, Jshort), (Nsint, Jint), (Nuint, Jint),
(Nslong, Jlong), (Nulong, Jlong),
(Nslonglong, Jlong), (Nulonglong, Jlong),
(Nfloat, Jfloat), (Ndouble, Jdouble)]
-- What's the JClass for a given JPrim? For example: int -> Integer
jprim2jclass jp = fromJust $ lookup jp $
[(Jboolean, JBoolean), (Jbyte, JByte), (Jchar, JCharacter),
(Jshort, JShort), (Jint, JInteger), (Jlong, JLong),
(Jfloat, JFloat), (Jdouble, JDouble)]
-- Convert a type to something suitable for Java code. For example: Jboolean -> boolean
ntype2js nt = tail $ show nt
jclass2js t = tail $ show t
jprim2js p = tail $ show p
ffitype2js f = "FFI_" ++ (show f)
-- Capitalize the first letter of a String
capitalize [] = []
capitalize s = [toUpper $ head s] ++ tail s
-- Given an Width and NType, return the Java code for reading said NType from memory.
popAddr :: Width -> NType -> String
popAddr _ NBOOL = "rt.unsafe.getByte(addr) != 0"
popAddr _ Nschar = "rt.unsafe.getByte(addr)"
popAddr _ Nuchar = "rt.unsafe.getByte(addr)"
popAddr W32 Nslong = "rt.unsafe.getInt(addr)"
popAddr W32 Nulong = "rt.unsafe.getInt(addr)"
popAddr _ ntype = "rt.unsafe.get" ++ (capitalize.jprim2js.ntype2jprim $ ntype) ++ "(addr)"
-- Given an Width and NType, return the Java code for writing said NType to memory.
pushAddr :: Width -> NType -> String
pushAddr _ NBOOL = "rt.unsafe.putByte(addr, (byte) (x ? 1 : 0));"
pushAddr _ Nschar = "rt.unsafe.putByte(addr, x);"
pushAddr _ Nuchar = "rt.unsafe.putByte(addr, x);"
pushAddr W32 Nslong = "rt.unsafe.putInt(addr, (int) x);"
pushAddr W32 Nulong = "rt.unsafe.putInt(addr, (int) x);"
pushAddr _ ntype = "rt.unsafe.put" ++ (capitalize jprimS) ++ "(addr, (" ++ jprimS ++ ") x);"
where jprimS = jprim2js.ntype2jprim $ ntype
-- Helpers for generating Java ternarnies and conditionals.
archExpr x32 x64 = if x32 /= x64 then retdiff else x32
where retdiff = "(JObjCRuntime.IS64 ? (" ++ x64 ++ ") : (" ++ x32 ++ "))"
archStmt x32 x64 = if x32 /= x64 then retdiff else x32
where retdiff = "if(JObjCRuntime.IS64){ " ++ x64 ++ " }else{ " ++ x32 ++ " }"
-- Get a Java expression for the correct FFIType at runtime. For example: (JObjCRuntime.IS64 ? FFI_SINT64 : FFI_SINT32)
ffitypeVal nt = archExpr (ffitype2js $ ffitype W32 nt)
(ffitype2js $ ffitype W64 nt)
-- Similar to ffiTypeVal. Get the correct pop expression and push statement.
popAddrVal nt = archExpr (popAddr W32 nt) (popAddr W64 nt)
pushAddrVal nt = archStmt (pushAddr W32 nt) (pushAddr W64 nt)
-- What's the Coder class name we're using for a given NType?
coderName nt = aux nt ++ "Coder"
where
aux NBOOL = "Bool"
aux Nschar = "SChar"
aux Nuchar = "UChar"
aux Nsshort = "SShort"
aux Nushort = "UShort"
aux Nsint = "SInt"
aux Nuint = "UInt"
aux Nslong = "SLong"
aux Nulong = "ULong"
aux Nslonglong = "SLongLong"
aux Nulonglong = "ULongLong"
aux Nfloat = "Float"
aux Ndouble = "Double"
-- Operation for converting between primitives. Usually it just casts, but booleans are special.
jconvertPrims sym Jboolean Jboolean = sym
jconvertPrims sym Jboolean b = "((" ++ jprim2js b ++ ")(" ++ sym ++ " ? 1 : 0))"
jconvertPrims sym a Jboolean = "(" ++ sym ++ " != 0)"
jconvertPrims sym a b = if a == b then sym else "((" ++ jprim2js b ++ ")" ++ sym ++ ")"
sizeofRet nt =
let ffitypes = map (\w -> ffitype w nt) widths
sizes = map sizeof ffitypes in
if (length $ nub sizes) == 1
then "\t\treturn " ++ (show.head $ sizes) ++ ";"
else unlines [
"\t\tswitch(w){",
(unlines $ map casestmt widths),
"\t\tdefault: return -1;",
"\t\t}"]
where
casestmt w = "\t\t\tcase " ++ (show w) ++ ": return " ++
(show.sizeof $ ffitype w nt) ++ ";"
-- Generate a coder class for a given NType.
c2java ntype =
unlines [
"// native " ++ ntypeS ++ " -> java " ++ jprimS,
"public static final class " ++ className ++ " extends PrimitiveCoder<" ++ jclassS ++ ">{",
"\tpublic static final " ++ className ++ " INST = new " ++ className ++ "();",
"\tpublic " ++ className ++ "(){ super("++ffitypeVal ntype++", \"" ++ [encoding ntype] ++ "\", "++jclassS++".class, "++jprimS++".class); }",
"\t// compile time",
"\t@Override public void push(JObjCRuntime rt, long addr, " ++ jprimS ++ " x){",
"\t\t" ++ pushAddrVal ntype,
"\t}",
"\t@Override public " ++ jprimS ++ " pop" ++ capitalize jprimS ++ "(JObjCRuntime rt, long addr){",
"\t\treturn " ++ popAddrVal ntype ++ ";",
"\t}",
"\t// for runtime coding",
"\t@Override public int sizeof(Width w){",
sizeofRet ntype,
"\t}",
"\t@Override public void push(JObjCRuntime rt, long addr, " ++ jclassS ++ " x){ " ++
"push(rt, addr, (" ++ jprimS ++ ") x); }",
"\t@Override public " ++ jclassS ++ " pop(JObjCRuntime rt, long addr){ " ++
"return pop" ++ capitalize jprimS ++ "(rt, addr); }",
"\t// proxies for mixed encoding",
makeProxyMethods ntype,
"}"
]
where
jprim = ntype2jprim ntype
jclass = jprim2jclass jprim
jprimS = jprim2js jprim
jclassS = jclass2js jclass
ntypeS = ntype2js ntype
className = coderName ntype
-- Generate push and pop methods that convert and proxy to actual implementation.
makeProxyMethods nt = unlines $ map aux jprims
where
targetJPrim = ntype2jprim nt
targetJPrimS = jprim2js targetJPrim
aux jprim = if targetJPrim == jprim then "" else unlines [
"\t@Override public void push(JObjCRuntime rt, long addr, " ++ jprimS ++ " x){ " ++
"push(rt, addr, " ++ pushConversion "x" ++ "); }",
"\t@Override public " ++ jprimS ++ " pop" ++ capitalize jprimS ++ "(JObjCRuntime rt, long addr){ " ++
"return " ++ (popConversion ("pop" ++ capitalize targetJPrimS ++ "(rt, addr)")) ++ "; }"
]
where
jprimS = jprim2js jprim
pushConversion sym = jconvertPrims sym jprim targetJPrim
popConversion sym = jconvertPrims sym targetJPrim jprim
main = do
putStrLn "package com.apple.jobjc;"
putStrLn "import com.apple.jobjc.JObjCRuntime.Width;"
putStrLn "// Auto generated by PrimitiveCoder.hs"
putStrLn "// Do not edit by hand."
putStrLn "public abstract class PrimitiveCoder<T> extends Coder<T>{"
putStrLn "\tpublic PrimitiveCoder(int ffiTypeCode, String objCEncoding, Class jclass, Class jprim){"
putStrLn "\t\tsuper(ffiTypeCode, objCEncoding, jclass, jprim);"
putStrLn "\t}"
mapM_ (\p -> putStrLn $ unlines [makePopI p, makePushI p]) jprims
mapM_ (putStrLn . c2java) ntypes
putStrLn "}"
where
makePopI jprim = unlines ["\tpublic final " ++ jprim2js jprim ++ " pop" ++ (capitalize.jprim2js $ jprim)
++ "(NativeArgumentBuffer args){\n"
++ "\t\treturn pop" ++ (capitalize.jprim2js $ jprim) ++ "(args.runtime, args.retValPtr);\n"
++ "\t}",
"\tpublic abstract " ++ jprim2js jprim ++ " pop" ++ (capitalize.jprim2js $ jprim) ++ "(JObjCRuntime runtime, long addr);"]
makePushI jprim = unlines ["\tpublic final void push"
++ "(NativeArgumentBuffer args, " ++ jprim2js jprim ++ " x){\n"
++ "\t\tpush(args.runtime, args.argValuesPtr, x);\n"
++ "\t\targs.didPutArgValue(sizeof());\n"
++ "\t}",
"\tpublic abstract void push(JObjCRuntime runtime, long addr, " ++ jprim2js jprim ++ " x);"]