import re
from collections import namedtuple

# From: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html

##########################################################################
# 4.3. Descriptors and Signatures
##########################################################################

# A descriptor is a string representing the type of a field or method.
# Descriptors are represented in the class file format using modified UTF-8
# strings (§4.4.7) and thus may be drawn, where not further constrained, from
# the entire Unicode codespace.

# A signature is a string representing the generic type of a field or method, or
# generic type information for a class declaration.

# ------------------------------------------------------------------------
# 4.3.1. Grammar Notation
# ------------------------------------------------------------------------

# Descriptors and signatures are specified using a grammar. This grammar is a
# set of productions that describe how sequences of characters can form
# syntactically correct descriptors of various types. Terminal symbols of the
# grammar are shown in bold fixed-width font. Nonterminal symbols are shown in
# italic type. The definition of a nonterminal is introduced by the name of the
# nonterminal being defined, followed by a colon. One or more alternative right-
# hand sides for the nonterminal then follow on succeeding lines. For example,
# the production:


# FieldType:
#     BaseType
#     ObjectType
#     ArrayType

# states that a FieldType may represent either a BaseType, an ObjectType or an
# ArrayType.

# A nonterminal symbol on the right-hand side of a production that is followed
# by an asterisk (*) represents zero or more possibly different values produced
# from that nonterminal, appended without any intervening space. Similarly, a
# nonterminal symbol on the right-hand side of a production that is followed by
# an plus sign (+) represents one or more possibly different values produced
# from that nonterminal, appended without any intervening space. The production:


# MethodDescriptor:
#     ( ParameterDescriptor* ) ReturnDescriptor

# states that a MethodDescriptor represents a left parenthesis, followed by zero
# or more ParameterDescriptor values, followed by a right parenthesis, followed
# by a ReturnDescriptor.

MethodDescriptor = namedtuple('MethodDescriptor', ['parameters', 'return_type'])

# ------------------------------------------------------------------------
# 4.3.2. Field Descriptors
# ------------------------------------------------------------------------

# A field descriptor represents the type of a class, instance, or local
# variable. It is a series of characters generated by the grammar:


# FieldDescriptor:
#     FieldType

# FieldType:
#     BaseType
#     ObjectType
#     ArrayType

# BaseType:
#     B
#     C
#     D
#     F
#     I
#     J
#     S
#     Z

# ObjectType:
#     L ClassName ;

# ArrayType:
#     [ ComponentType

# ComponentType:
#     FieldType

# The characters of BaseType, the L and ; of ObjectType, and the [ of ArrayType
# are all ASCII characters.

# The ClassName represents a binary class or interface name encoded in internal
# form (§4.2.1).

# The interpretation of field descriptors as types is as shown in Table 4.2.

# A field descriptor representing an array type is valid only if it represents a
# type with 255 or fewer dimensions.

# Table 4.2. Interpretation of FieldType characters

# BaseType Character  Type    Interpretation
# B   byte    signed byte
# C   char    Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16
# D   double  double-precision floating-point value
# F   float   single-precision floating-point value
# I   int integer
# J   long    long integer
# L ClassName ;   reference   an instance of class ClassName
# S   short   signed short
# Z   boolean true or false
# [   reference   one array dimension
# The field descriptor of an instance variable of type int is simply I.

# The field descriptor of an instance variable of type Object is
# Ljava/lang/Object;. Note that the internal form of the binary name for class
# Object is used.

# The field descriptor of an instance variable that is a multidimensional double
# array, double d[][][], is [[[D.

FIELD_DESCRIPTOR_RE = re.compile("\[*L[^;]+;|\[*[ZBCSIFDJV]")

# # ------------------------------------------------------------------------
# # 4.3.3. Method Descriptors
# # ------------------------------------------------------------------------

# A method descriptor represents the parameters that the method takes and the
# value that it returns:


# MethodDescriptor:
#     ( ParameterDescriptor* ) ReturnDescriptor

# A parameter descriptor represents a parameter passed to a method:


# ParameterDescriptor:
#     FieldType

# A return descriptor represents the type of the value returned from a method.
# It is a series of characters generated by the grammar:

# ReturnDescriptor:
#     FieldType
#     VoidDescriptor

# VoidDescriptor:
#     V
# The character V indicates that the method returns no value (its return type is void).

# A method descriptor is valid only if it represents method parameters with a
# total length of 255 or less, where that length includes the contribution for
# this in the case of instance or interface method invocations. The total length
# is calculated by summing the contributions of the individual parameters, where
# a parameter of type long or double contributes two units to the length and a
# parameter of any other type contributes one unit.

# The method descriptor for the method:

# Object m(int i, double d, Thread t) {..}

# is (IDLjava/lang/Thread;)Ljava/lang/Object;. Note that the internal forms of
# the binary names of Thread and Object are used.

# The method descriptor for m is the same whether m is a class method or an
# instance method. Although an instance method is passed this, a reference to
# the current class instance, in addition to its intended parameters, that fact
# is not reflected in the method descriptor. The reference to this is passed
# implicitly by the method invocation instructions of the Java Virtual Machine
# that invoke instance methods (§2.6.1). A reference to this is not passed to a
# class method.

def method_descriptor(descriptor):
    matches = FIELD_DESCRIPTOR_RE.findall(descriptor)
    return MethodDescriptor(parameters=matches[:-1], return_type=matches[-1])

# ------------------------------------------------------------------------
# 4.3.4. Signatures
# ------------------------------------------------------------------------

# Signatures are used to encode Java programming language type information that
# is not part of the Java Virtual Machine type system, such as generic type and
# method declarations and parameterized types. See The Java Language
# Specification, Java SE 7 Edition for details about such types.

# This kind of type information is needed to support reflection and debugging,
# and by a Java compiler.

# In the following, the terminal symbol Identifier is used to denote the name of
# a type, field, local variable, parameter, method, or type variable, as
# generated by a Java compiler. Such a name must not contain any of the ASCII
# characters . ; [ / < > : (that is, the characters forbidden in method names
# (§4.2.2) and also colon) but may contain characters that must not appear in an
# identifier in the Java programming language (JLS §3.8).

# A class signature, defined by the production ClassSignature, is used to encode
# type information about a class declaration. It describes any formal type
# parameters the class might have, and lists its (possibly parameterized) direct
# superclass and direct superinterfaces, if any.


# ClassSignature:
#     FormalTypeParametersopt SuperclassSignature SuperinterfaceSignature*

# A formal type parameter is described by its name, followed by its class and
# interface bounds. If the class bound does not specify a type, it is taken to
# be Object.


# FormalTypeParameters:
#     < FormalTypeParameter+ >

# FormalTypeParameter:
#     Identifier ClassBound InterfaceBound*

# ClassBound:
#     : FieldTypeSignatureopt

# InterfaceBound:
#     : FieldTypeSignature

# SuperclassSignature:
#     ClassTypeSignature

# SuperinterfaceSignature:
#     ClassTypeSignature

# A field type signature, defined by the production FieldTypeSignature, encodes
# the (possibly parameterized) type for a field, parameter or local variable.


# FieldTypeSignature:
#     ClassTypeSignature
#     ArrayTypeSignature
#     TypeVariableSignature

# A class type signature gives complete type information for a class or
# interface type. The class type signature must be formulated such that it can
# be reliably mapped to the binary name of the class it denotes by erasing any
# type arguments and converting each . character in the signature to a $
# character.


# ClassTypeSignature:
#     L PackageSpecifieropt SimpleClassTypeSignature ClassTypeSignatureSuffix* ;

# PackageSpecifier:
#     Identifier / PackageSpecifier*

# SimpleClassTypeSignature:
#     Identifier TypeArgumentsopt

# ClassTypeSignatureSuffix:
#     . SimpleClassTypeSignature

# TypeVariableSignature:
#     T Identifier ;

# TypeArguments:
#     < TypeArgument+ >

# TypeArgument:
#     WildcardIndicatoropt FieldTypeSignature
#     *

# WildcardIndicator:
#     +
#     -

# ArrayTypeSignature:
#     [ TypeSignature

# TypeSignature:
#     FieldTypeSignature
#     BaseType

# A method signature, defined by the production MethodTypeSignature, encodes the
# (possibly parameterized) types of the method's formal arguments and of the
# exceptions it has declared in its throws clause, its (possibly parameterized)
# return type, and any formal type parameters in the method declaration.


# MethodTypeSignature:
#     FormalTypeParametersopt (TypeSignature*) ReturnType ThrowsSignature*

# ReturnType:
#     TypeSignature
#     VoidDescriptor

# ThrowsSignature:
#     ^ ClassTypeSignature
#     ^ TypeVariableSignature

# If the throws clause of a method or constructor does not involve type
# variables, the ThowsSignature may be elided from the MethodTypeSignature.

# A Java compiler must output generic signature information for any class,
# interface, constructor or member whose generic signature in the Java
# programming language would include references to type variables or
# parameterized types.

# The signature and descriptor (§4.3.3) of a given method or constructor may not
# correspond exactly, due to compiler-generated artifacts. In particular, the
# number of TypeSignatures that encode formal arguments in MethodTypeSignature
# may be less than the number of ParameterDescriptors in MethodDescriptor.

# Oracle's Java Virtual Machine implementation does not check the well-
# formedness of the signatures described in this subsection during loading or
# linking. Instead, these checks are deferred until the signatures are used by
# reflective methods, as specified in the API of Class and members of
# java.lang.reflect. Future versions of a Java Virtual Machine implementation
# may be required to perform some or all of these checks during loading or
# linking.
