Skip to content

Commit 4714cee

Browse files
committed
Support for inner classes/interfaces
1 parent 06ec07c commit 4714cee

File tree

3 files changed

+148
-46
lines changed

3 files changed

+148
-46
lines changed

Language/Java/ClassFile.hs

Lines changed: 109 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
{-# LANGUAGE DoRec #-}
2-
module Language.Java.ClassFile (getClass, nameConstructor) where
2+
module Language.Java.ClassFile (
3+
ImportedDecl(..),
4+
-- * Classfile parsing
5+
getClass,
6+
-- * Magic identifiers
7+
nameConstructor
8+
) where
39

410
import Language.Java.Syntax
511
import Data.Binary.Get
@@ -15,6 +21,8 @@ import Control.Monad.Reader
1521
import Text.ParserCombinators.Parsec
1622
import Data.List
1723

24+
import Debug.Trace
25+
1826
parseFull :: Parser a -> Parser a
1927
-- language-java depends on Parsec 2, which is not Applicative...
2028
-- parseFull p = p <* eof
@@ -48,7 +56,7 @@ readType s = case parse (parseFull parseType) "" s of
4856

4957
parseClassType :: Parser ClassType
5058
parseClassType =
51-
do parts <- (many1 alphaNum) `sepBy` (char '/')
59+
do parts <- (many1 alphaNum) `sepBy` (char '/' <|> char '$')
5260
let ids = map (\ part -> (Ident part, [])) parts
5361
return $ ClassType ids
5462

@@ -132,32 +140,15 @@ getConstants = do
132140
classObject :: ClassType
133141
classObject = ClassType [(Ident "java", []), (Ident "lang", []), (Ident "Object", [])]
134142

143+
-- |Constructors are encoded in .class files as member functions of the name \"@\<init>@\".
135144
nameConstructor :: String
136145
nameConstructor = "<init>"
137146

138147
getClassType :: ClassImporter ClassType
139148
getClassType = constClass <$> getConstantRef
140149

141-
getClass :: Get TypeDecl
142-
getClass =
143-
do (_major, _minor) <- getHeader
144-
constTable <- getConstants
145-
flip runReaderT constTable $
146-
do (isInterface, modifiers) <- getToplevel
147-
148-
ClassType [(this, _)] <- getClassType
149-
superClass <- getClassType
150-
let super = if superClass == classObject then Nothing else Just (ClassRefType superClass)
151-
152-
ifaces <- getMany (ClassRefType <$> getClassType)
153-
fields <- getMany getField
154-
methods <- catMaybes <$> getMany (getMethod this)
155-
156-
_attributes <- getAttributes
157-
158-
return $ if isInterface
159-
then InterfaceTypeDecl $ InterfaceDecl modifiers this [] ifaces (InterfaceBody [])
160-
else ClassTypeDecl $ ClassDecl modifiers this [] super ifaces (ClassBody $ map MemberDecl $ fields ++ methods)
150+
-- getClassTypeMaybe :: ClassImporter (Maybe ClassType)
151+
-- getClassTypeMaybe = constClassMaybe <$>
161152

162153
getHeader =
163154
do signature <- replicateM 4 getWord8
@@ -167,20 +158,34 @@ getHeader =
167158
major <- getWord16be
168159
return (major, minor)
169160

170-
getConstantRef :: ClassImporter Constant
171-
getConstantRef =
161+
getConstantRefMaybe :: ClassImporter (Maybe Constant)
162+
getConstantRefMaybe =
172163
do idx <- lift getConstIdx
173-
asks (!idx)
164+
if idx == 0 then return Nothing else Just <$> asks (!idx)
165+
166+
getConstantRef :: ClassImporter Constant
167+
getConstantRef = fromJust <$> getConstantRefMaybe
174168

175-
getStringRef :: ClassImporter String
176-
getStringRef =
177-
do const <- getConstantRef
178-
case const of
179-
ConstantString s -> return s
169+
getStringRefMaybe ::ClassImporter (Maybe String)
170+
getStringRefMaybe =
171+
do const <- getConstantRefMaybe
172+
return $ case const of
173+
Just (ConstantString s) -> Just s
174+
_ -> Nothing
175+
176+
getStringRef :: ClassImporter String
177+
getStringRef = fromJust <$> getStringRefMaybe
178+
179+
data InnerClass = InnerClass { innerInner :: ClassType,
180+
innerOuter :: Maybe ClassType,
181+
innerName :: Maybe String,
182+
innerFlags :: Word16 }
183+
deriving Show
180184

181185
data Attribute = AttrConstantValue ConstantValue
182186
| AttrExceptions [ClassType]
183187
| AttrSynthetic
188+
| AttrInnerClasses [InnerClass]
184189
deriving Show
185190

186191
isAttrSynthetic AttrSynthetic = True
@@ -190,8 +195,19 @@ isAttrConstantValue (AttrConstantValue _) = True
190195
isAttrConstantValue _ = False
191196

192197
isAttrExceptions (AttrExceptions _) = True
193-
isAttrExceptions _ = False
194-
198+
isAttrExceptions _ = False
199+
200+
isAttrInnerClasses (AttrInnerClasses _) = True
201+
isAttrInnerClasses _ = False
202+
203+
getInnerClass :: ClassImporter InnerClass
204+
getInnerClass =
205+
do inner <- getClassType
206+
outer <- fmap constClass <$> getConstantRefMaybe
207+
name <- getStringRefMaybe
208+
flags <- lift getWord16be
209+
return $ InnerClass inner outer name flags
210+
195211
getAttribute :: ClassImporter (Maybe Attribute)
196212
getAttribute =
197213
do name <- getStringRef
@@ -206,13 +222,15 @@ getAttribute =
206222
ConstantValue val -> val
207223
"Exceptions" ->
208224
Just <$> AttrExceptions <$> getMany getClassType
225+
"InnerClasses" ->
226+
do Just <$> AttrInnerClasses <$> getMany getInnerClass
209227
_ ->
210228
do lift $ skip (fromIntegral len)
211229
return Nothing
212230

213231
getAttributes = catMaybes <$> getMany getAttribute
214232

215-
getField :: ClassImporter MemberDecl
233+
getField :: ClassImporter (Maybe MemberDecl)
216234
getField =
217235
do modifiers <- getModifiers
218236
name <- getStringRef
@@ -227,7 +245,8 @@ getField =
227245
ValueString s -> String s
228246
ValueFloat x -> Float $ realToFrac x
229247
ValueDouble x -> Double x
230-
return $ FieldDecl modifiers ty [VarDecl (VarId $ Ident name) init]
248+
return $ unlessSynthetic attributes $
249+
FieldDecl modifiers ty [VarDecl (VarId $ Ident name) init]
231250

232251
getMethod :: Ident -> ClassImporter (Maybe MemberDecl)
233252
getMethod cls =
@@ -240,12 +259,13 @@ getMethod cls =
240259
let exceptions = case find (isAttrExceptions) attributes of
241260
Nothing -> []
242261
Just (AttrExceptions tys) -> map ClassRefType tys
243-
return $
244-
if any isAttrSynthetic attributes then Nothing
245-
else Just $
246-
if isConstructor
247-
then ConstructorDecl modifiers [] cls formals exceptions (ConstructorBody Nothing [])
248-
else MethodDecl modifiers [] ret (Ident name) formals exceptions (MethodBody Nothing)
262+
return $ unlessSynthetic attributes $
263+
if isConstructor
264+
then ConstructorDecl modifiers [] cls formals exceptions (ConstructorBody Nothing [])
265+
else MethodDecl modifiers [] ret (Ident name) formals exceptions (MethodBody Nothing)
266+
267+
unlessSynthetic :: [Attribute] -> a -> Maybe a
268+
unlessSynthetic attributes x = if any isAttrSynthetic attributes then Nothing else Just x
249269

250270
parseModifiers :: Word16 -> [Modifier]
251271
parseModifiers flags =
@@ -272,3 +292,52 @@ getToplevel =
272292
let iface = 0x0200 .~. flags
273293
modifiers = parseModifiers flags
274294
return (iface, modifiers)
295+
296+
data ImportedDecl = ImportedTopLevel TypeDecl
297+
| ImportedInner TypeDecl
298+
299+
type DeclMap = [(ClassType, ImportedDecl)]
300+
301+
-- |Parse a binary .class file into a 'TypeDecl'
302+
getClass :: Maybe DeclMap -> Get (ClassType, ImportedDecl)
303+
getClass declMap =
304+
do (_major, _minor) <- getHeader
305+
constTable <- getConstants
306+
flip runReaderT constTable $
307+
do (isInterface, modifiers) <- getToplevel
308+
309+
classType@(ClassType parts) <- getClassType
310+
let (this, _) = last parts
311+
superClass <- getClassType
312+
let super = if superClass == classObject then Nothing else Just (ClassRefType superClass)
313+
314+
ifaces <- getMany (ClassRefType <$> getClassType)
315+
fields <- catMaybes <$> getMany getField
316+
methods <- catMaybes <$> getMany (getMethod this)
317+
318+
attributes <- getAttributes
319+
let innerClasses =
320+
case find isAttrInnerClasses attributes of
321+
Nothing -> []
322+
Just (AttrInnerClasses ics) -> ics
323+
innerDecls = mapMaybe (toInnerDecl classType) innerClasses
324+
isInner = any (\ ic -> innerInner ic == classType) innerClasses
325+
326+
let members = fields ++ methods ++ innerDecls
327+
decl = if isInterface
328+
then InterfaceTypeDecl $ InterfaceDecl modifiers this [] ifaces (InterfaceBody members)
329+
else ClassTypeDecl $ ClassDecl modifiers this [] super ifaces (ClassBody $ map MemberDecl members)
330+
331+
return (classType, (if isInner then ImportedInner else ImportedTopLevel) decl)
332+
333+
where toInnerDecl classType ic =
334+
do outer <- innerOuter ic
335+
guard $ outer == classType
336+
ImportedInner inner <- lookup (innerInner ic) =<< declMap
337+
let modifiers = parseModifiers $ innerFlags ic
338+
return $ case inner of
339+
ClassTypeDecl classDecl -> MemberClassDecl $ classDecl `withClassModifiers` modifiers
340+
InterfaceTypeDecl ifaceDecl -> MemberInterfaceDecl $ ifaceDecl `withInterfaceModifiers` modifiers
341+
342+
(ClassDecl _ id tys super ifaces body) `withClassModifiers` ms = ClassDecl ms id tys super ifaces body
343+
(InterfaceDecl _ id tys ifaces body) `withInterfaceModifiers` ms = InterfaceDecl ms id tys ifaces body

Main.hs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@ import Language.Java.ClassFile
77
import Data.Binary.Get
88
import qualified Data.ByteString.Lazy as BL
99
import Control.Applicative
10+
import Control.Monad (replicateM)
11+
import Data.Maybe (mapMaybe)
1012

1113
test =
1214
do
13-
file <- BL.readFile f
14-
return $ runGet getClass file
15+
streams <- mapM BL.readFile files
16+
17+
let decls = map (runGet (getClass $ Just decls)) streams
18+
return decls
1519
where
16-
f = "test/Foo.class"
17-
18-
main = print =<< pretty <$> test
20+
files = ["test/Foo.class", "test/Foo$Bar.class"]
21+
22+
main =
23+
do decls <- test
24+
let decls' = mapMaybe (filterTopLevel . snd) decls
25+
mapM_ print $ map pretty decls'
26+
where filterTopLevel (ImportedTopLevel decl) = Just decl
27+
filterTopLevel _ = Nothing

test/Foo.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,32 @@
22

33
public class Foo extends Object implements Comparable<Foo>
44
{
5-
class Bar
5+
public class Bar
66
{
7+
protected class Quux
8+
{
9+
}
10+
}
11+
12+
interface IBaz
13+
{
14+
class Blargh
15+
{
16+
};
17+
18+
int foo ();
19+
}
20+
21+
IBaz getBaz ()
22+
{
23+
return new IBaz() {
24+
public int foo () { return 1;};
25+
};
26+
}
27+
28+
Bar getBar ()
29+
{
30+
return null;
731
}
832

933
static final int x = 42;

0 commit comments

Comments
 (0)