/* Parser grammar for quick source code scan of Java(TM) language programs.
- Copyright (C) 1998 Free Software Foundation, Inc.
+ Copyright (C) 1998, 1999, 2000, 2002 Free Software Foundation, Inc.
Contributed by Alexandre Petit-Bianco (apbianco@cygnus.com)
This file is part of GNU CC.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "obstack.h"
+#include "toplev.h"
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
extern char *input_filename;
extern FILE *finput, *out;
static int absorber;
#define USE_ABSORBER absorber = 0
-/* Keep track of the current class name and package name. */
-static char *current_class;
-static char *package_name;
+/* Keep track of the current package name. */
+static const char *package_name;
/* Keep track of whether things have be listed before. */
static int previous_output;
/* Record modifier uses */
static int modifier_value;
+/* Record (almost) cyclomatic complexity. */
+static int complexity;
+
+/* Keeps track of number of bracket pairs after a variable declarator
+ id. */
+static int bracket_count;
+
+/* Numbers anonymous classes */
+static int anonymous_count;
+
+/* This is used to record the current class context. */
+struct class_context
+{
+ char *name;
+ struct class_context *next;
+};
+
+/* The global class context. */
+static struct class_context *current_class_context;
+
+/* A special constant used to represent an anonymous context. */
+static const char *anonymous_context = "ANONYMOUS";
+
+/* Count of method depth. */
+static int method_depth;
+
/* Record a method declaration */
struct method_declarator {
- char *method_name;
- char *args;
+ const char *method_name;
+ const char *args;
};
#define NEW_METHOD_DECLARATOR(D,N,A) \
{ \
}
/* Two actions for this grammar */
-static void report_class_declaration PROTO ((char *));
-static void report_main_declaration PROTO ((struct method_declarator *));
+static int make_class_name_recursive PARAMS ((struct obstack *stack,
+ struct class_context *ctx));
+static char *get_class_name PARAMS ((void));
+static void report_class_declaration PARAMS ((const char *));
+static void report_main_declaration PARAMS ((struct method_declarator *));
+static void push_class_context PARAMS ((const char *));
+static void pop_class_context PARAMS ((void));
+
+void report PARAMS ((void));
#include "lex.h"
#include "parse.h"
int value; /* For modifiers */
}
+%{
+extern int flag_assert;
+
+#include "lex.c"
+%}
+
%pure_parser
/* Things defined here have to match the order of what's in the
%token STATIC_TK FINAL_TK SYNCHRONIZED_TK
%token VOLATILE_TK TRANSIENT_TK NATIVE_TK
%token PAD_TK ABSTRACT_TK MODIFIER_TK
+%token STRICT_TK
/* Keep those two in order, too */
%token DECR_TK INCR_TK
%token SWITCH_TK CONST_TK TRY_TK
%token FOR_TK NEW_TK CONTINUE_TK
%token GOTO_TK PACKAGE_TK THIS_TK
+%token ASSERT_TK
%token BYTE_TK SHORT_TK INT_TK LONG_TK
%token CHAR_TK INTEGRAL_TK
;
array_type:
- primitive_type OSB_TK CSB_TK
-| name OSB_TK CSB_TK
+ primitive_type dims
{
- char *n = xmalloc (strlen ($1)+2);
- n [0] = '[';
- strcpy (n+1, $1);
- $$ = n;
+ while (bracket_count-- > 0)
+ $$ = concat ("[", $1, NULL);
}
-| array_type OSB_TK CSB_TK
- {
- char *n = xmalloc (strlen ($1)+2);
- n [0] = '[';
- strcpy (n+1, $1);
- $$ = n;
+| name dims
+ {
+ while (bracket_count-- > 0)
+ $$ = concat ("[", $1, NULL);
}
;
qualified_name:
name DOT_TK identifier
{
- char *n = xmalloc (strlen ($1)+strlen ($3)+2);
- sprintf (n, "%s.%s", $1, $3);
- $$ = n;
+ $$ = concat ($1, ".", $3, NULL);
}
;
type_declaration:
class_declaration
| interface_declaration
-| SC_TK
+| empty_statement
;
/* 19.7 Shortened from the original:
class_body:
OCB_TK CCB_TK
+ { pop_class_context (); }
| OCB_TK class_body_declarations CCB_TK
+ { pop_class_context (); }
;
class_body_declarations:
| method_declaration
| class_declaration /* Added, JDK1.1 inner classes */
| interface_declaration /* Added, JDK1.1 inner classes */
+| empty_statement
;
/* 19.8.2 Productions from 8.3: Field Declarations */
variable_declarator_id:
identifier
- { USE_ABSORBER; }
+ { bracket_count = 0; USE_ABSORBER; }
| variable_declarator_id OSB_TK CSB_TK
+ { ++bracket_count; }
;
variable_initializer:
/* 19.8.3 Productions from 8.4: Method Declarations */
method_declaration:
- method_header method_body
+ method_header
+ { ++method_depth; }
+ method_body
+ { --method_depth; }
;
method_header:
formal_parameter
| formal_parameter_list C_TK formal_parameter
{
- char *n = xmalloc (strlen ($1)+strlen($3)+2);
- sprintf (n, "%s,%s", $1, $3);
- $$ = n;
+ $$ = concat ($1, ",", $3, NULL);
}
;
type variable_declarator_id
{
USE_ABSORBER;
- $$ = $1;
+ if (bracket_count)
+ {
+ int i;
+ char *n = xmalloc (bracket_count + 1 + strlen ($$));
+ for (i = 0; i < bracket_count; ++i)
+ n[i] = '[';
+ strcpy (n + bracket_count, $$);
+ $$ = n;
+ }
+ else
+ $$ = $1;
}
| modifiers type variable_declarator_id /* Added, JDK1.1 final locals */
- { $$ = $2; }
+ {
+ if (bracket_count)
+ {
+ int i;
+ char *n = xmalloc (bracket_count + 1 + strlen ($$));
+ for (i = 0; i < bracket_count; ++i)
+ n[i] = '[';
+ strcpy (n + bracket_count, $$);
+ $$ = n;
+ }
+ else
+ $$ = $2;
+ }
;
throws:
method_body:
block
-| block SC_TK
| SC_TK
;
/* 19.8.4 Productions from 8.5: Static Initializers */
static_initializer:
static block
-| static block SC_TK /* Shouldn't be here. FIXME */
;
static: /* Test lval.sub_token here */
/* 19.9 Productions from 9: Interfaces */
/* 19.9.1 Productions from 9.1: Interfaces Declarations */
interface_declaration:
- INTERFACE_TK identifier interface_body
-| modifiers INTERFACE_TK identifier interface_body
- { modifier_value = 0; }
-| INTERFACE_TK identifier extends_interfaces interface_body
-| modifiers INTERFACE_TK identifier extends_interfaces interface_body
- { modifier_value = 0; }
+ INTERFACE_TK identifier
+ { report_class_declaration ($2); modifier_value = 0; }
+ interface_body
+| modifiers INTERFACE_TK identifier
+ { report_class_declaration ($3); modifier_value = 0; }
+ interface_body
+| INTERFACE_TK identifier extends_interfaces
+ { report_class_declaration ($2); modifier_value = 0; }
+ interface_body
+| modifiers INTERFACE_TK identifier extends_interfaces
+ { report_class_declaration ($3); modifier_value = 0; }
+ interface_body
;
extends_interfaces:
interface_body:
OCB_TK CCB_TK
+ { pop_class_context (); }
| OCB_TK interface_member_declarations CCB_TK
+ { pop_class_context (); }
;
interface_member_declarations:
| synchronized_statement
| throw_statement
| try_statement
+| assert_statement
;
empty_statement:
;
if_then_statement:
- IF_TK OP_TK expression CP_TK statement
+ IF_TK OP_TK expression CP_TK statement { ++complexity; }
;
if_then_else_statement:
IF_TK OP_TK expression CP_TK statement_nsi ELSE_TK statement
+ { ++complexity; }
;
if_then_else_statement_nsi:
IF_TK OP_TK expression CP_TK statement_nsi ELSE_TK statement_nsi
+ { ++complexity; }
;
switch_statement:
;
switch_block_statement_group:
- switch_labels block_statements
+ switch_labels block_statements { ++complexity; }
;
;
while_expression:
- WHILE_TK OP_TK expression CP_TK
+ WHILE_TK OP_TK expression CP_TK { ++complexity; }
;
while_statement:
do_statement:
do_statement_begin statement WHILE_TK OP_TK expression CP_TK SC_TK
+ { ++complexity; }
;
for_statement:
;
for_begin:
- for_header for_init
+ for_header for_init { ++complexity; }
;
for_init: /* Can be empty */
| statement_expression_list
| BREAK_TK identifier SC_TK
;
+/* `continue' with a label is considered for complexity but ordinary
+ continue is not. */
continue_statement:
CONTINUE_TK SC_TK
-| CONTINUE_TK identifier SC_TK
+ | CONTINUE_TK identifier SC_TK { ++complexity; }
;
return_statement:
;
throw_statement:
- THROW_TK expression SC_TK
+ THROW_TK expression SC_TK { ++complexity; }
;
+assert_statement:
+ ASSERT_TK expression REL_CL_TK expression SC_TK
+| ASSERT_TK expression SC_TK
+| ASSERT_TK error
+ {yyerror ("Missing term"); RECOVER;}
+| ASSERT_TK expression error
+ {yyerror ("';' expected"); RECOVER;}
+;
synchronized_statement:
synchronized OP_TK expression CP_TK block
| synchronized OP_TK expression CP_TK error
;
catch_clause:
- CATCH_TK OP_TK formal_parameter CP_TK block
+ CATCH_TK OP_TK formal_parameter CP_TK block { ++complexity; }
;
finally:
- FINALLY_TK block
+ FINALLY_TK block { ++complexity; }
;
/* 19.12 Production from 15: Expressions */
| field_access
| method_invocation
| array_access
- /* type DOT_TK CLASS_TK doens't work. So we split the rule
- 'type' into its components. Missing is something for array,
- which will complete the reference_type part. FIXME */
-| name DOT_TK CLASS_TK /* Added, JDK1.1 class literals */
- { USE_ABSORBER; }
-| primitive_type DOT_TK CLASS_TK /* Added, JDK1.1 class literals */
- { USE_ABSORBER; }
-| VOID_TK DOT_TK CLASS_TK /* Added, JDK1.1 class literals */
+| type_literals
/* Added, JDK1.1 inner classes. Documentation is wrong
refering to a 'ClassName' (class_name) rule that doesn't
exist. Used name instead. */
{ USE_ABSORBER; }
;
+type_literals:
+ name DOT_TK CLASS_TK
+ { USE_ABSORBER; }
+| array_type DOT_TK CLASS_TK
+ { USE_ABSORBER; }
+| primitive_type DOT_TK CLASS_TK
+ { USE_ABSORBER; }
+| VOID_TK DOT_TK CLASS_TK
+ { USE_ABSORBER; }
+;
+
class_instance_creation_expression:
NEW_TK class_type OP_TK argument_list CP_TK
| NEW_TK class_type OP_TK CP_TK
- /* Added, JDK1.1 inner classes but modified to use
- 'class_type' instead of 'TypeName' (type_name) mentionned
- in the documentation but doesn't exist. */
-| NEW_TK class_type OP_TK argument_list CP_TK class_body
-| NEW_TK class_type OP_TK CP_TK class_body
- /* Added, JDK1.1 inner classes, modified to use name or
- primary instead of primary solely which couldn't work in
- all situations. */
+| anonymous_class_creation
| something_dot_new identifier OP_TK CP_TK
| something_dot_new identifier OP_TK CP_TK class_body
| something_dot_new identifier OP_TK argument_list CP_TK
| something_dot_new identifier OP_TK argument_list CP_TK class_body
;
+anonymous_class_creation:
+ NEW_TK class_type OP_TK CP_TK
+ { report_class_declaration (anonymous_context); }
+ class_body
+| NEW_TK class_type OP_TK argument_list CP_TK
+ { report_class_declaration (anonymous_context); }
+ class_body
+;
+
something_dot_new: /* Added, not part of the specs. */
name DOT_TK NEW_TK
{ USE_ABSORBER; }
dims:
OSB_TK CSB_TK
+ { bracket_count = 1; }
| dims OSB_TK CSB_TK
+ { bracket_count++; }
;
field_access:
| SUPER_TK DOT_TK identifier
;
+/* We include method invocation in the complexity measure on the
+ theory that most method calls are virtual and therefore involve a
+ decision point. */
method_invocation:
name OP_TK CP_TK
- { USE_ABSORBER; }
+ { USE_ABSORBER; ++complexity; }
| name OP_TK argument_list CP_TK
- { USE_ABSORBER; }
-| primary DOT_TK identifier OP_TK CP_TK
-| primary DOT_TK identifier OP_TK argument_list CP_TK
-| SUPER_TK DOT_TK identifier OP_TK CP_TK
-| SUPER_TK DOT_TK identifier OP_TK argument_list CP_TK
+ { USE_ABSORBER; ++complexity; }
+| primary DOT_TK identifier OP_TK CP_TK { ++complexity; }
+| primary DOT_TK identifier OP_TK argument_list CP_TK { ++complexity; }
+| SUPER_TK DOT_TK identifier OP_TK CP_TK { ++complexity; }
+| SUPER_TK DOT_TK identifier OP_TK argument_list CP_TK { ++complexity; }
;
array_access:
conditional_and_expression:
inclusive_or_expression
| conditional_and_expression BOOL_AND_TK inclusive_or_expression
+ { ++complexity; }
;
conditional_or_expression:
conditional_and_expression
| conditional_or_expression BOOL_OR_TK conditional_and_expression
+ { ++complexity; }
;
conditional_expression: /* Error handling here is weak */
conditional_or_expression
| conditional_or_expression REL_QM_TK expression REL_CL_TK conditional_expression
+ { ++complexity; }
;
assignment_expression:
%%
\f
-#include "lex.c"
-
/* Create a new parser context */
void
java_push_parser_context ()
{
struct parser_ctxt *new =
- (struct parser_ctxt *)xmalloc(sizeof (struct parser_ctxt));
+ (struct parser_ctxt *) xcalloc (1, sizeof (struct parser_ctxt));
- bzero (new, sizeof (struct parser_ctxt));
new->next = ctxp;
ctxp = new;
}
+static void
+push_class_context (name)
+ const char *name;
+{
+ struct class_context *ctx;
+
+ ctx = (struct class_context *) xmalloc (sizeof (struct class_context));
+ ctx->name = (char *) name;
+ ctx->next = current_class_context;
+ current_class_context = ctx;
+}
+
+static void
+pop_class_context ()
+{
+ struct class_context *ctx;
+
+ if (current_class_context == NULL)
+ return;
+
+ ctx = current_class_context->next;
+ if (current_class_context->name != anonymous_context)
+ free (current_class_context->name);
+ free (current_class_context);
+
+ current_class_context = ctx;
+ if (current_class_context == NULL)
+ anonymous_count = 0;
+}
+
+/* Recursively construct the class name. This is just a helper
+ function for get_class_name(). */
+static int
+make_class_name_recursive (stack, ctx)
+ struct obstack *stack;
+ struct class_context *ctx;
+{
+ if (! ctx)
+ return 0;
+
+ make_class_name_recursive (stack, ctx->next);
+
+ /* Replace an anonymous context with the appropriate counter value. */
+ if (ctx->name == anonymous_context)
+ {
+ char buf[50];
+ ++anonymous_count;
+ sprintf (buf, "%d", anonymous_count);
+ ctx->name = xstrdup (buf);
+ }
+
+ obstack_grow (stack, ctx->name, strlen (ctx->name));
+ obstack_1grow (stack, '$');
+
+ return ISDIGIT (ctx->name[0]);
+}
+
+/* Return a newly allocated string holding the name of the class. */
+static char *
+get_class_name ()
+{
+ char *result;
+ int last_was_digit;
+ struct obstack name_stack;
+
+ obstack_init (&name_stack);
+
+ /* Duplicate the logic of parse.y:maybe_make_nested_class_name(). */
+ last_was_digit = make_class_name_recursive (&name_stack,
+ current_class_context->next);
+
+ if (! last_was_digit
+ && method_depth
+ && current_class_context->name != anonymous_context)
+ {
+ char buf[50];
+ ++anonymous_count;
+ sprintf (buf, "%d", anonymous_count);
+ obstack_grow (&name_stack, buf, strlen (buf));
+ obstack_1grow (&name_stack, '$');
+ }
+
+ if (current_class_context->name == anonymous_context)
+ {
+ char buf[50];
+ ++anonymous_count;
+ sprintf (buf, "%d", anonymous_count);
+ current_class_context->name = xstrdup (buf);
+ obstack_grow0 (&name_stack, buf, strlen (buf));
+ }
+ else
+ obstack_grow0 (&name_stack, current_class_context->name,
+ strlen (current_class_context->name));
+
+ result = xstrdup (obstack_finish (&name_stack));
+ obstack_free (&name_stack, NULL);
+
+ return result;
+}
+
/* Actions defined here */
static void
report_class_declaration (name)
- char * name;
+ const char * name;
{
extern int flag_dump_class, flag_list_filename;
+ push_class_context (name);
if (flag_dump_class)
{
+ char *name = get_class_name ();
+
if (!previous_output)
{
if (flag_list_filename)
fprintf (out, "%s: ", input_filename);
previous_output = 1;
}
-
+
if (package_name)
fprintf (out, "%s.%s ", package_name, name);
else
fprintf (out, "%s ", name);
+
+ free (name);
}
-
- current_class = name;
}
static void
&& declarator->args [0] == '['
&& (! strcmp (declarator->args+1, "String")
|| ! strcmp (declarator->args + 1, "java.lang.String"))
- && current_class)
+ && current_class_context)
{
if (!previous_output)
{
+ char *name = get_class_name ();
if (package_name)
- fprintf (out, "%s.%s ", package_name, current_class);
+ fprintf (out, "%s.%s ", package_name, name);
else
- fprintf (out, current_class);
+ fprintf (out, "%s", name);
+ free (name);
previous_output = 1;
}
}
}
+void
+report ()
+{
+ extern int flag_complexity;
+ if (flag_complexity)
+ fprintf (out, "%s %d\n", input_filename, complexity);
+}
+
/* Reset global status used by the report functions. */
void reset_report ()
{
previous_output = 0;
- current_class = package_name = NULL;
+ package_name = NULL;
+ current_class_context = NULL;
+ complexity = 0;
}
void
yyerror (msg)
- char *msg;
+ const char *msg ATTRIBUTE_UNUSED;
{
-}
-
-char *
-xstrdup (s)
- const char *s;
-{
- char *ret;
-
- ret = xmalloc (strlen (s) + 1);
- strcpy (ret, s);
- return ret;
+ fprintf (stderr, "%s: %d: %s\n", input_filename, lineno, msg);
+ exit (1);
}