{-# LANGUAGE LexicalNegation #-}
{-# LANGUAGE RecordWildCards #-}
module H2JVM.Internal.Convert.Method (convertMethod, convertMethodDescriptor) where
import Control.Monad (foldM)
import Data.List (nubBy, sortOn)
import Data.Word (Word16)
import Effectful
import Effectful.Error.Static (throwError)
import GHC.Stack (HasCallStack)
import Witch
import Data.List.NonEmpty qualified as NE
import Data.Vector qualified as V
import H2JVM.ClassFile.Method
import H2JVM.ConstantPool (ConstantPoolEntry (..))
import H2JVM.Internal.Convert.AccessFlag (accessFlagsToWord16)
import H2JVM.Internal.Convert.ConstantPool
import H2JVM.Internal.Convert.Instruction (CodeConverterEff, convertInstructions, fullyResolveAbs, fullyRunCodeConverter)
import H2JVM.Internal.Convert.Monad
import H2JVM.Internal.Convert.Type
import H2JVM.Internal.Raw.Types
import H2JVM.ClassFile.Method qualified as Abs
import H2JVM.Data.TypeMergingList qualified as TML
import H2JVM.Internal.Raw.ClassFile qualified as Raw
convertMethodAttribute :: ConvertEff r => HasCallStack => Abs.MethodAttribute -> Eff r Raw.AttributeInfo
convertMethodAttribute :: forall (r :: [Effect]).
(ConvertEff r, HasCallStack) =>
MethodAttribute -> Eff r AttributeInfo
convertMethodAttribute (Abs.Code (Abs.CodeAttributeData{[CodeAttribute]
[ExceptionTableEntry]
U2
NonEmpty Instruction
maxStack :: U2
maxLocals :: U2
code :: NonEmpty Instruction
exceptionTable :: [ExceptionTableEntry]
codeAttributes :: [CodeAttribute]
codeAttributes :: CodeAttributeData -> [CodeAttribute]
exceptionTable :: CodeAttributeData -> [ExceptionTableEntry]
code :: CodeAttributeData -> NonEmpty Instruction
maxLocals :: CodeAttributeData -> U2
maxStack :: CodeAttributeData -> U2
..})) = do
(code', attributes') <- Eff
(State ConvertState : r)
(NonEmpty Instruction, Vector AttributeInfo)
-> Eff r (NonEmpty Instruction, Vector AttributeInfo)
forall (r' :: [Effect]) a.
ConvertEff r' =>
Eff (State ConvertState : r') a -> Eff r' a
fullyRunCodeConverter (Eff
(State ConvertState : r)
(NonEmpty Instruction, Vector AttributeInfo)
-> Eff r (NonEmpty Instruction, Vector AttributeInfo))
-> Eff
(State ConvertState : r)
(NonEmpty Instruction, Vector AttributeInfo)
-> Eff r (NonEmpty Instruction, Vector AttributeInfo)
forall a b. (a -> b) -> a -> b
$ do
(NonEmpty Instruction
-> Vector AttributeInfo
-> (NonEmpty Instruction, Vector AttributeInfo))
-> Eff (State ConvertState : r) (NonEmpty Instruction)
-> Eff (State ConvertState : r) (Vector AttributeInfo)
-> Eff
(State ConvertState : r)
(NonEmpty Instruction, Vector AttributeInfo)
forall a b c.
(a -> b -> c)
-> Eff (State ConvertState : r) a
-> Eff (State ConvertState : r) b
-> Eff (State ConvertState : r) c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 (,) (NonEmpty Instruction
-> Eff (State ConvertState : r) (NonEmpty Instruction)
forall (r :: [Effect]).
(CodeConverterEff r, HasCallStack) =>
NonEmpty Instruction -> Eff r (NonEmpty Instruction)
convertInstructions NonEmpty Instruction
code) ([CodeAttribute]
-> Eff (State ConvertState : r) (Vector AttributeInfo)
forall (r :: [Effect]).
CodeConverterEff r =>
[CodeAttribute] -> Eff r (Vector AttributeInfo)
convertCodeAttributes [CodeAttribute]
codeAttributes)
exceptionTable' <- convertExceptionTable exceptionTable
nameIndex <- findIndexOf (CPUTF8Entry "Code")
pure $
Raw.AttributeInfo
(into nameIndex)
( Raw.CodeAttribute
maxStack
maxLocals
(V.fromList $ NE.toList code')
exceptionTable'
attributes'
)
where
convertExceptionTable :: ConvertEff r => [Abs.ExceptionTableEntry] -> Eff r (V.Vector Raw.ExceptionTableEntry)
convertExceptionTable :: forall (r :: [Effect]).
ConvertEff r =>
[ExceptionTableEntry] -> Eff r (Vector ExceptionTableEntry)
convertExceptionTable = ([ExceptionTableEntry] -> Vector ExceptionTableEntry)
-> Eff r [ExceptionTableEntry]
-> Eff r (Vector ExceptionTableEntry)
forall a b. (a -> b) -> Eff r a -> Eff r b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [ExceptionTableEntry] -> Vector ExceptionTableEntry
forall a. [a] -> Vector a
V.fromList (Eff r [ExceptionTableEntry] -> Eff r (Vector ExceptionTableEntry))
-> ([ExceptionTableEntry] -> Eff r [ExceptionTableEntry])
-> [ExceptionTableEntry]
-> Eff r (Vector ExceptionTableEntry)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ExceptionTableEntry -> Eff r ExceptionTableEntry)
-> [ExceptionTableEntry] -> Eff r [ExceptionTableEntry]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse ExceptionTableEntry -> Eff r ExceptionTableEntry
forall (r :: [Effect]).
ConvertEff r =>
ExceptionTableEntry -> Eff r ExceptionTableEntry
convertExceptionTableEntry
convertExceptionTableEntry :: ConvertEff r => Abs.ExceptionTableEntry -> Eff r Raw.ExceptionTableEntry
convertExceptionTableEntry :: forall (r :: [Effect]).
ConvertEff r =>
ExceptionTableEntry -> Eff r ExceptionTableEntry
convertExceptionTableEntry ExceptionTableEntry
_ = CodeConverterError -> Eff r ExceptionTableEntry
forall e (es :: [Effect]) a.
(HasCallStack, Error e :> es, Show e) =>
e -> Eff es a
throwError (CodeConverterError -> Eff r ExceptionTableEntry)
-> CodeConverterError -> Eff r ExceptionTableEntry
forall a b. (a -> b) -> a -> b
$ String -> CodeConverterError
UnsupportedAttribute String
"ExceptionTable"
convertCodeAttributes :: CodeConverterEff r => [Abs.CodeAttribute] -> Eff r (V.Vector Raw.AttributeInfo)
convertCodeAttributes :: forall (r :: [Effect]).
CodeConverterEff r =>
[CodeAttribute] -> Eff r (Vector AttributeInfo)
convertCodeAttributes = ([AttributeInfo] -> Vector AttributeInfo)
-> Eff r [AttributeInfo] -> Eff r (Vector AttributeInfo)
forall a b. (a -> b) -> Eff r a -> Eff r b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [AttributeInfo] -> Vector AttributeInfo
forall a. [a] -> Vector a
V.fromList (Eff r [AttributeInfo] -> Eff r (Vector AttributeInfo))
-> ([CodeAttribute] -> Eff r [AttributeInfo])
-> [CodeAttribute]
-> Eff r (Vector AttributeInfo)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CodeAttribute -> Eff r AttributeInfo)
-> [CodeAttribute] -> Eff r [AttributeInfo]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse CodeAttribute -> Eff r AttributeInfo
forall (r :: [Effect]).
CodeConverterEff r =>
CodeAttribute -> Eff r AttributeInfo
convertCodeAttribute'
convertCodeAttribute' :: CodeConverterEff r => Abs.CodeAttribute -> Eff r Raw.AttributeInfo
convertCodeAttribute' :: forall (r :: [Effect]).
CodeConverterEff r =>
CodeAttribute -> Eff r AttributeInfo
convertCodeAttribute' (LineNumberTable [LineNumberTableEntry]
lns) = do
lns' <- [LineNumberTableEntry] -> Eff r (Vector LineNumberTableEntry)
forall (r :: [Effect]).
[LineNumberTableEntry] -> Eff r (Vector LineNumberTableEntry)
convertLineNumberTable [LineNumberTableEntry]
lns
nameIndex <- findIndexOf (CPUTF8Entry "LineNumberTable")
pure $ Raw.AttributeInfo (into nameIndex) (Raw.LineNumberTableAttribute lns')
where
convertLineNumberTable :: [Abs.LineNumberTableEntry] -> Eff r (V.Vector Raw.LineNumberTableEntry)
convertLineNumberTable :: forall (r :: [Effect]).
[LineNumberTableEntry] -> Eff r (Vector LineNumberTableEntry)
convertLineNumberTable = ([LineNumberTableEntry] -> Vector LineNumberTableEntry)
-> Eff r [LineNumberTableEntry]
-> Eff r (Vector LineNumberTableEntry)
forall a b. (a -> b) -> Eff r a -> Eff r b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [LineNumberTableEntry] -> Vector LineNumberTableEntry
forall a. [a] -> Vector a
V.fromList (Eff r [LineNumberTableEntry]
-> Eff r (Vector LineNumberTableEntry))
-> ([LineNumberTableEntry] -> Eff r [LineNumberTableEntry])
-> [LineNumberTableEntry]
-> Eff r (Vector LineNumberTableEntry)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (LineNumberTableEntry -> Eff r LineNumberTableEntry)
-> [LineNumberTableEntry] -> Eff r [LineNumberTableEntry]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse LineNumberTableEntry -> Eff r LineNumberTableEntry
forall (r :: [Effect]).
LineNumberTableEntry -> Eff r LineNumberTableEntry
convertLineNumberTableEntry
convertLineNumberTableEntry :: Abs.LineNumberTableEntry -> Eff r Raw.LineNumberTableEntry
convertLineNumberTableEntry :: forall (r :: [Effect]).
LineNumberTableEntry -> Eff r LineNumberTableEntry
convertLineNumberTableEntry (Abs.LineNumberTableEntry U2
a U2
b) = LineNumberTableEntry -> Eff r LineNumberTableEntry
forall a. a -> Eff r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (LineNumberTableEntry -> Eff r LineNumberTableEntry)
-> LineNumberTableEntry -> Eff r LineNumberTableEntry
forall a b. (a -> b) -> a -> b
$ U2 -> U2 -> LineNumberTableEntry
Raw.LineNumberTableEntry U2
a U2
b
convertCodeAttribute' (StackMapTable [StackMapFrame]
frames) = do
frames' <- [StackMapFrame] -> Eff r (Vector StackMapFrame)
forall (r :: [Effect]).
CodeConverterEff r =>
[StackMapFrame] -> Eff r (Vector StackMapFrame)
convertStackMapTable [StackMapFrame]
frames
nameIndex <- findIndexOf (CPUTF8Entry "StackMapTable")
pure $ Raw.AttributeInfo (into nameIndex) (Raw.StackMapTableAttribute frames')
where
convertStackMapTable :: CodeConverterEff r => [Abs.StackMapFrame] -> Eff r (V.Vector Raw.StackMapFrame)
convertStackMapTable :: forall (r :: [Effect]).
CodeConverterEff r =>
[StackMapFrame] -> Eff r (Vector StackMapFrame)
convertStackMapTable [StackMapFrame]
fs = do
resolvedFrames <- (StackMapFrame -> Eff r (U2, StackMapFrame))
-> [StackMapFrame] -> Eff r [(U2, StackMapFrame)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse StackMapFrame -> Eff r (U2, StackMapFrame)
forall (r :: [Effect]).
CodeConverterEff r =>
StackMapFrame -> Eff r (U2, StackMapFrame)
resolveFrame [StackMapFrame]
fs
let uniqueFrames =
((U2, StackMapFrame) -> (U2, StackMapFrame) -> Bool)
-> [(U2, StackMapFrame)] -> [(U2, StackMapFrame)]
forall a. (a -> a -> Bool) -> [a] -> [a]
nubBy (\(U2
o1, StackMapFrame
_) (U2
o2, StackMapFrame
_) -> U2
o1 U2 -> U2 -> Bool
forall a. Eq a => a -> a -> Bool
== U2
o2) ([(U2, StackMapFrame)] -> [(U2, StackMapFrame)])
-> [(U2, StackMapFrame)] -> [(U2, StackMapFrame)]
forall a b. (a -> b) -> a -> b
$
((U2, StackMapFrame) -> U2)
-> [(U2, StackMapFrame)] -> [(U2, StackMapFrame)]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (U2, StackMapFrame) -> U2
forall a b. (a, b) -> a
fst [(U2, StackMapFrame)]
resolvedFrames
V.fromList . reverse . snd <$> foldM convertAndAccumulate (-1, []) uniqueFrames
resolveFrame :: CodeConverterEff r => Abs.StackMapFrame -> Eff r (Word16, Abs.StackMapFrame)
resolveFrame :: forall (r :: [Effect]).
CodeConverterEff r =>
StackMapFrame -> Eff r (U2, StackMapFrame)
resolveFrame StackMapFrame
f = do
off <- Label -> Eff r U2
forall (r :: [Effect]). CodeConverterEff r => Label -> Eff r U2
fullyResolveAbs (StackMapFrame -> Label
getFrameLabel StackMapFrame
f)
pure (off, f)
getFrameLabel :: StackMapFrame -> Label
getFrameLabel (Abs.SameFrame Label
l) = Label
l
getFrameLabel (Abs.ChopFrame U1
_ Label
l) = Label
l
getFrameLabel (Abs.SameLocals1StackItemFrame VerificationTypeInfo
_ Label
l) = Label
l
getFrameLabel (Abs.AppendFrame [VerificationTypeInfo]
_ Label
l) = Label
l
getFrameLabel (Abs.FullFrame [VerificationTypeInfo]
_ [VerificationTypeInfo]
_ Label
l) = Label
l
convertAndAccumulate :: CodeConverterEff r => (Int, [Raw.StackMapFrame]) -> (Word16, Abs.StackMapFrame) -> Eff r (Int, [Raw.StackMapFrame])
convertAndAccumulate :: forall (r :: [Effect]).
CodeConverterEff r =>
(Int, [StackMapFrame])
-> (U2, StackMapFrame) -> Eff r (Int, [StackMapFrame])
convertAndAccumulate (Int
prev, [StackMapFrame]
acc) (U2
absLabel16, StackMapFrame
frame) = do
let current :: Int
current = U2 -> Int
forall target source. From source target => source -> target
into U2
absLabel16
let delta :: U1
delta = forall target source.
(HasCallStack, TryFrom source target, Show source, Typeable source,
Typeable target) =>
source -> target
unsafeInto @U1 (Int
current Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
prev Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
rawFrame <- case StackMapFrame
frame of
Abs.SameFrame Label
_ ->
StackMapFrame -> Eff r StackMapFrame
forall a. a -> Eff r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (StackMapFrame -> Eff r StackMapFrame)
-> StackMapFrame -> Eff r StackMapFrame
forall a b. (a -> b) -> a -> b
$
if U1
delta U1 -> U1 -> Bool
forall a. Ord a => a -> a -> Bool
<= U1
63
then U1 -> StackMapFrame
Raw.SameFrame U1
delta
else U2 -> StackMapFrame
Raw.SameFrameExtended (U1 -> U2
forall target source. From source target => source -> target
into U1
delta)
Abs.ChopFrame U1
x Label
_ ->
StackMapFrame -> Eff r StackMapFrame
forall a. a -> Eff r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (StackMapFrame -> Eff r StackMapFrame)
-> StackMapFrame -> Eff r StackMapFrame
forall a b. (a -> b) -> a -> b
$ U1 -> U2 -> StackMapFrame
Raw.ChopFrame U1
x (U1 -> U2
forall target source. From source target => source -> target
into U1
delta)
Abs.SameLocals1StackItemFrame VerificationTypeInfo
x Label
_ -> do
x' <- VerificationTypeInfo -> Eff r VerificationTypeInfo
forall (r :: [Effect]).
CodeConverterEff r =>
VerificationTypeInfo -> Eff r VerificationTypeInfo
convertVerificationTypeInfo VerificationTypeInfo
x
pure $
if delta <= 63
then Raw.SameLocals1StackItemFrame x' delta
else Raw.SameLocals1StackItemFrameExtended x' (into delta)
Abs.AppendFrame [VerificationTypeInfo]
x Label
_ -> do
x' <- (VerificationTypeInfo -> Eff r VerificationTypeInfo)
-> [VerificationTypeInfo] -> Eff r [VerificationTypeInfo]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse VerificationTypeInfo -> Eff r VerificationTypeInfo
forall (r :: [Effect]).
CodeConverterEff r =>
VerificationTypeInfo -> Eff r VerificationTypeInfo
convertVerificationTypeInfo [VerificationTypeInfo]
x
pure $ Raw.AppendFrame (V.fromList x') (into delta)
Abs.FullFrame [VerificationTypeInfo]
x [VerificationTypeInfo]
y Label
_ -> do
x' <- (VerificationTypeInfo -> Eff r VerificationTypeInfo)
-> [VerificationTypeInfo] -> Eff r [VerificationTypeInfo]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse VerificationTypeInfo -> Eff r VerificationTypeInfo
forall (r :: [Effect]).
CodeConverterEff r =>
VerificationTypeInfo -> Eff r VerificationTypeInfo
convertVerificationTypeInfo [VerificationTypeInfo]
x
y' <- traverse convertVerificationTypeInfo y
pure $ Raw.FullFrame (V.fromList x') (V.fromList y') (into delta)
pure (current, rawFrame : acc)
convertVerificationTypeInfo :: CodeConverterEff r => Abs.VerificationTypeInfo -> Eff r Raw.VerificationTypeInfo
convertVerificationTypeInfo :: forall (r :: [Effect]).
CodeConverterEff r =>
VerificationTypeInfo -> Eff r VerificationTypeInfo
convertVerificationTypeInfo VerificationTypeInfo
Abs.TopVariableInfo = VerificationTypeInfo -> Eff r VerificationTypeInfo
forall a. a -> Eff r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure VerificationTypeInfo
Raw.TopVariableInfo
convertVerificationTypeInfo VerificationTypeInfo
Abs.IntegerVariableInfo = VerificationTypeInfo -> Eff r VerificationTypeInfo
forall a. a -> Eff r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure VerificationTypeInfo
Raw.IntegerVariableInfo
convertVerificationTypeInfo VerificationTypeInfo
Abs.FloatVariableInfo = VerificationTypeInfo -> Eff r VerificationTypeInfo
forall a. a -> Eff r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure VerificationTypeInfo
Raw.FloatVariableInfo
convertVerificationTypeInfo VerificationTypeInfo
Abs.LongVariableInfo = VerificationTypeInfo -> Eff r VerificationTypeInfo
forall a. a -> Eff r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure VerificationTypeInfo
Raw.LongVariableInfo
convertVerificationTypeInfo VerificationTypeInfo
Abs.DoubleVariableInfo = VerificationTypeInfo -> Eff r VerificationTypeInfo
forall a. a -> Eff r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure VerificationTypeInfo
Raw.DoubleVariableInfo
convertVerificationTypeInfo VerificationTypeInfo
Abs.NullVariableInfo = VerificationTypeInfo -> Eff r VerificationTypeInfo
forall a. a -> Eff r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure VerificationTypeInfo
Raw.NullVariableInfo
convertVerificationTypeInfo VerificationTypeInfo
Abs.UninitializedThisVariableInfo = VerificationTypeInfo -> Eff r VerificationTypeInfo
forall a. a -> Eff r a
forall (f :: * -> *) a. Applicative f => a -> f a
pure VerificationTypeInfo
Raw.UninitializedThisVariableInfo
convertVerificationTypeInfo (Abs.ObjectVariableInfo ClassInfoType
x) = do
cpIndex <- ConstantPoolEntry -> Eff r U2
forall (r :: [Effect]).
(HasCallStack, ConstantPoolEff r) =>
ConstantPoolEntry -> Eff r U2
findIndexOf (ClassInfoType -> ConstantPoolEntry
CPClassEntry ClassInfoType
x)
pure $ Raw.ObjectVariableInfo (into cpIndex)
convertVerificationTypeInfo (Abs.UninitializedVariableInfo Label
x) = do
label <- Label -> Eff r U2
forall (r :: [Effect]). CodeConverterEff r => Label -> Eff r U2
fullyResolveAbs Label
x
pure $ Raw.UninitializedVariableInfo (into label)
convertMethod :: ConvertEff r => Abs.ClassFileMethod -> Eff r Raw.MethodInfo
convertMethod :: forall (r :: [Effect]).
ConvertEff r =>
ClassFileMethod -> Eff r MethodInfo
convertMethod Abs.ClassFileMethod{[MethodAccessFlag]
Text
TypeMergingList MethodAttribute
MethodDescriptor
methodAccessFlags :: [MethodAccessFlag]
methodName :: Text
methodDescriptor :: MethodDescriptor
methodAttributes :: TypeMergingList MethodAttribute
methodAttributes :: ClassFileMethod -> TypeMergingList MethodAttribute
methodDescriptor :: ClassFileMethod -> MethodDescriptor
methodName :: ClassFileMethod -> Text
methodAccessFlags :: ClassFileMethod -> [MethodAccessFlag]
..} = do
let flags :: U2
flags = [MethodAccessFlag] -> U2
forall a. ConvertAccessFlag a => [a] -> U2
accessFlagsToWord16 [MethodAccessFlag]
methodAccessFlags
nameIndex <- ConstantPoolEntry -> Eff r U2
forall (r :: [Effect]).
(HasCallStack, ConstantPoolEff r) =>
ConstantPoolEntry -> Eff r U2
findIndexOf (Text -> ConstantPoolEntry
CPUTF8Entry Text
methodName)
descriptorIndex <- findIndexOf (CPUTF8Entry (convertMethodDescriptor methodDescriptor))
attributes <- traverse convertMethodAttribute (TML.toVector methodAttributes)
pure $ Raw.MethodInfo flags (into nameIndex) (into descriptorIndex) attributes