In gcc/: 2010-10-06 Nicola Pero <nicola.pero@meta-innovation.com>

In gcc/:
2010-10-06  Nicola Pero  <nicola.pero@meta-innovation.com>

        Implemented fast enumeration for Objective-C.
        * c-parser.c (objc_could_be_foreach_context): New.
        (c_lex_one_token): Recognize RID_IN keyword in a potential
        Objective-C foreach context.
        (c_parser_declaration_or_fndef): Added parameter.  Accept
        Objective-C RID_IN keyword as terminating a declaration; in that
        case, return the declaration in the new parameter.
        (c_parser_extenral_declaration): Updated calls to
        c_parser_declaration_or_fndef.
        (c_parser_declaration_or_fndef): Same change.
        (c_parser_compound_statement_nostart): Same change.
        (c_parser_label): Same change.
        (c_parser_objc_methodprotolist): Same change.
        (c_parser_omp_for_loop): Same change.
        (c_parser_for_statement): Detect and parse Objective-C foreach
        statements.
        (c_parser_omp_for_loop): Updated call to check_for_loop_decls().
        * c-decl.c (check_for_loop_decls): Added parameter to allow ObjC
        fast enumeration parsing code to turn off the c99 error but still
        perform checks on the loop declarations.
        * c-tree.h (check_for_loop_decls): Updated declaration.
        * doc/objc.texi: Document fast enumeration.

In gcc/c-family/:
2010-10-06  Nicola Pero  <nicola.pero@meta-innovation.com>

        Implemented fast enumeration for Objective-C.
        * c-common.h (objc_finish_foreach_loop): New.
        * stub-objc.c (objc_finish_foreach_loop): New.

In gcc/objc/:
2010-10-06  Nicola Pero  <nicola.pero@meta-innovation.com>

        Implemented fast enumeration for Objective-C.
        * objc-act.c (build_fast_enumeration_state_template): New.
        (TAG_ENUMERATION_MUTATION): New.
        (TAG_FAST_ENUMERATION_STATE): New.
        (synth_module_prologue): Call build_fast_enumeration_state_template() and set up
        objc_enumeration_mutation_decl.
        (objc_create_temporary_var): Allow providing a name to temporary
        variables.
        (objc_build_exc_ptr): Updated calls to
        objc_create_temporary_var().
        (next_sjlj_build_try_catch_finally): Same change.
        (objc_finish_foreach_loop): New.
        * objc-act.h: Added OCTI_FAST_ENUM_STATE_TEMP,
        OCTI_ENUM_MUTATION_DECL, objc_fast_enumeration_state_template,
        objc_enumeration_mutation_decl.

        Merge from 'apple/trunk' branch on FSF servers.

        2006-04-12 Fariborz Jahanian <fjahanian@apple.com>

        Radar 4507230
        * objc-act.c (objc_type_valid_for_messaging): New routine to check
        for valid objc object types.
        (objc_finish_foreach_loop): Check for invalid objc objects in
        foreach header.

In gcc/testsuite/:
2010-10-05  Nicola Pero  <nicola.pero@meta-innovation.com>

        Implemented fast enumeration for Objective-C.
        * objc.dg/foreach-1.m: New.
        * objc.dg/foreach-2.m: New.
        * objc.dg/foreach-3.m: New.
        * objc.dg/foreach-4.m: New.
        * objc.dg/foreach-5.m: New.
        * objc.dg/foreach-6.m: New.
        * objc.dg/foreach-7.m: New.

        Merge from 'apple/trunk' branch on FSF servers:
        2006-04-13 Fariborz Jahanian <fjahanian@apple.com>

        Radar 4502236
        * objc.dg/objc-foreach-5.m: New.

        2006-04-12 Fariborz Jahanian <fjahanian@apple.com>

        Radar 4507230
        * objc.dg/objc-foreach-4.m: New.

        2006-03-13  Fariborz Jahanian <fjahanian@apple.com>

        Radar 4472881
        * objc.dg/objc-foreach-3.m: New.

        2005-03-07 Fariborz Jahanian <fjahanian@apple.com>

        Radar 4468498
        * objc.dg/objc-foreach-2.m: New.

        2006-02-15   Fariborz Jahanian <fjahanian@apple.com>

        Radar 4294910
        * objc.dg/objc-foreach-1.m: New

In libobjc/:
2010-10-06  Nicola Pero  <nicola.pero@meta-innovation.com>

        Implemented fast enumeration for Objective-C.
        * Makefile.in (C_SOURCE_FILES): Added objc-foreach.c.
        (OBJC_H): Added runtime.h
        * objc-foreach.c: New file.
        * objc/runtime.h: New file.

From-SVN: r165019
This commit is contained in:
Nicola Pero 2010-10-06 10:10:14 +00:00 committed by Nicola Pero
parent b938bc48d8
commit f05b9d93e9
28 changed files with 2547 additions and 47 deletions

View File

@ -1,3 +1,28 @@
2010-10-06 Nicola Pero <nicola.pero@meta-innovation.com>
Implemented fast enumeration for Objective-C.
* c-parser.c (objc_could_be_foreach_context): New.
(c_lex_one_token): Recognize RID_IN keyword in a potential
Objective-C foreach context.
(c_parser_declaration_or_fndef): Added parameter. Accept
Objective-C RID_IN keyword as terminating a declaration; in that
case, return the declaration in the new parameter.
(c_parser_extenral_declaration): Updated calls to
c_parser_declaration_or_fndef.
(c_parser_declaration_or_fndef): Same change.
(c_parser_compound_statement_nostart): Same change.
(c_parser_label): Same change.
(c_parser_objc_methodprotolist): Same change.
(c_parser_omp_for_loop): Same change.
(c_parser_for_statement): Detect and parse Objective-C foreach
statements.
(c_parser_omp_for_loop): Updated call to check_for_loop_decls().
* c-decl.c (check_for_loop_decls): Added parameter to allow ObjC
fast enumeration parsing code to turn off the c99 error but still
perform checks on the loop declarations.
* c-tree.h (check_for_loop_decls): Updated declaration.
* doc/objc.texi: Document fast enumeration.
2010-10-06 Nick Clifton <nickc@redhat.com> 2010-10-06 Nick Clifton <nickc@redhat.com>
* config/mn10300/mn10300.h (FIRST_PSEUDO_REGISTER): Increment by * config/mn10300/mn10300.h (FIRST_PSEUDO_REGISTER): Increment by

View File

@ -8310,16 +8310,23 @@ finish_function (void)
/* Check the declarations given in a for-loop for satisfying the C99 /* Check the declarations given in a for-loop for satisfying the C99
constraints. If exactly one such decl is found, return it. LOC is constraints. If exactly one such decl is found, return it. LOC is
the location of the opening parenthesis of the for loop. */ the location of the opening parenthesis of the for loop. The last
parameter allows you to control the "for loop initial declarations
are only allowed in C99 mode". Normally, you should pass
flag_isoc99 as that parameter. But in some cases (Objective-C
foreach loop, for example) we want to run the checks in this
function even if not in C99 mode, so we allow the caller to turn
off the error about not being in C99 mode.
*/
tree tree
check_for_loop_decls (location_t loc) check_for_loop_decls (location_t loc, bool turn_off_iso_c99_error)
{ {
struct c_binding *b; struct c_binding *b;
tree one_decl = NULL_TREE; tree one_decl = NULL_TREE;
int n_decls = 0; int n_decls = 0;
if (!flag_isoc99) if (!turn_off_iso_c99_error)
{ {
static bool hint = true; static bool hint = true;
/* If we get here, declarations have been used in a for loop without /* If we get here, declarations have been used in a for loop without

View File

@ -1,3 +1,9 @@
2010-10-06 Nicola Pero <nicola.pero@meta-innovation.com>
Implemented fast enumeration for Objective-C.
* c-common.h (objc_finish_foreach_loop): New.
* stub-objc.c (objc_finish_foreach_loop): New.
2010-10-05 Joseph Myers <joseph@codesourcery.com> 2010-10-05 Joseph Myers <joseph@codesourcery.com>
* c-common.h (struct diagnostic_context): Don't declare here. * c-common.h (struct diagnostic_context): Don't declare here.

View File

@ -1007,6 +1007,7 @@ extern int objc_static_init_needed_p (void);
extern tree objc_generate_static_init_call (tree); extern tree objc_generate_static_init_call (tree);
extern tree objc_generate_write_barrier (tree, enum tree_code, tree); extern tree objc_generate_write_barrier (tree, enum tree_code, tree);
extern void objc_set_method_opt (bool); extern void objc_set_method_opt (bool);
extern void objc_finish_foreach_loop (location_t, tree, tree, tree, tree, tree);
/* The following are provided by the C and C++ front-ends, and called by /* The following are provided by the C and C++ front-ends, and called by
ObjC/ObjC++. */ ObjC/ObjC++. */

View File

@ -355,3 +355,11 @@ objc_generate_write_barrier (tree ARG_UNUSED (lhs),
{ {
return 0; return 0;
} }
void
objc_finish_foreach_loop (location_t ARG_UNUSED (location), tree ARG_UNUSED (object_expression),
tree ARG_UNUSED (collection_expression), tree ARG_UNUSED (for_body),
tree ARG_UNUSED (break_label), tree ARG_UNUSED (continue_label))
{
return;
}

View File

@ -185,6 +185,11 @@ typedef struct GTY(()) c_parser {
/* True if we are in a context where the Objective-C "PQ" keywords /* True if we are in a context where the Objective-C "PQ" keywords
are considered keywords. */ are considered keywords. */
BOOL_BITFIELD objc_pq_context : 1; BOOL_BITFIELD objc_pq_context : 1;
/* True if we are parsing a (potential) Objective-C foreach
statement. This is set to true after we parsed 'for (' and while
we wait for 'in' or ';' to decide if it's a standard C for loop or an
Objective-C foreach loop. */
BOOL_BITFIELD objc_could_be_foreach_context : 1;
/* The following flag is needed to contextualize Objective-C lexical /* The following flag is needed to contextualize Objective-C lexical
analysis. In some cases (e.g., 'int NSObject;'), it is analysis. In some cases (e.g., 'int NSObject;'), it is
undesirable to bind an identifier to an Objective-C class, even undesirable to bind an identifier to an Objective-C class, even
@ -253,6 +258,26 @@ c_lex_one_token (c_parser *parser, c_token *token)
token->keyword = rid_code; token->keyword = rid_code;
break; break;
} }
else if (parser->objc_could_be_foreach_context
&& rid_code == RID_IN)
{
/* We are in Objective-C, inside a (potential)
foreach context (which means after having
parsed 'for (', but before having parsed ';'),
and we found 'in'. We consider it the keyword
which terminates the declaration at the
beginning of a foreach-statement. Note that
this means you can't use 'in' for anything else
in that context; in particular, in Objective-C
you can't use 'in' as the name of the running
variable in a C for loop. We could potentially
try to add code here to disambiguate, but it
seems a reasonable limitation.
*/
token->type = CPP_KEYWORD;
token->keyword = rid_code;
break;
}
/* Else, "pq" keywords outside of the "pq" context are /* Else, "pq" keywords outside of the "pq" context are
not keywords, and we fall through to the code for not keywords, and we fall through to the code for
normal tokens. normal tokens.
@ -947,7 +972,7 @@ typedef enum c_dtr_syn {
static void c_parser_external_declaration (c_parser *); static void c_parser_external_declaration (c_parser *);
static void c_parser_asm_definition (c_parser *); static void c_parser_asm_definition (c_parser *);
static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool,
bool, bool); bool, bool, tree *);
static void c_parser_static_assert_declaration_no_semi (c_parser *); static void c_parser_static_assert_declaration_no_semi (c_parser *);
static void c_parser_static_assert_declaration (c_parser *); static void c_parser_static_assert_declaration (c_parser *);
static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool, static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool,
@ -1170,7 +1195,7 @@ c_parser_external_declaration (c_parser *parser)
an @interface or @protocol with prefix attributes). We can an @interface or @protocol with prefix attributes). We can
only tell which after parsing the declaration specifiers, if only tell which after parsing the declaration specifiers, if
any, and the first declarator. */ any, and the first declarator. */
c_parser_declaration_or_fndef (parser, true, true, true, false, true); c_parser_declaration_or_fndef (parser, true, true, true, false, true, NULL);
break; break;
} }
} }
@ -1189,6 +1214,8 @@ c_parser_external_declaration (c_parser *parser)
(old-style parameter declarations) they are diagnosed. If (old-style parameter declarations) they are diagnosed. If
START_ATTR_OK is true, the declaration specifiers may start with START_ATTR_OK is true, the declaration specifiers may start with
attributes; otherwise they may not. attributes; otherwise they may not.
OBJC_FOREACH_OBJECT_DECLARATION can be used to get back the parsed
declaration when parsing an Objective-C foreach statement.
declaration: declaration:
declaration-specifiers init-declarator-list[opt] ; declaration-specifiers init-declarator-list[opt] ;
@ -1235,6 +1262,10 @@ c_parser_external_declaration (c_parser *parser)
specifiers, but only at top level (elsewhere they conflict with specifiers, but only at top level (elsewhere they conflict with
other syntax). other syntax).
In Objective-C, declarations of the looping variable in a foreach
statement are exceptionally terminated by 'in' (for example, 'for
(NSObject *object in array) { ... }').
OpenMP: OpenMP:
declaration: declaration:
@ -1243,7 +1274,8 @@ c_parser_external_declaration (c_parser *parser)
static void static void
c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
bool static_assert_ok, bool empty_ok, bool static_assert_ok, bool empty_ok,
bool nested, bool start_attr_ok) bool nested, bool start_attr_ok,
tree *objc_foreach_object_declaration)
{ {
struct c_declspecs *specs; struct c_declspecs *specs;
tree prefix_attrs; tree prefix_attrs;
@ -1375,7 +1407,8 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
|| c_parser_next_token_is (parser, CPP_COMMA) || c_parser_next_token_is (parser, CPP_COMMA)
|| c_parser_next_token_is (parser, CPP_SEMICOLON) || c_parser_next_token_is (parser, CPP_SEMICOLON)
|| c_parser_next_token_is_keyword (parser, RID_ASM) || c_parser_next_token_is_keyword (parser, RID_ASM)
|| c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) || c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)
|| c_parser_next_token_is_keyword (parser, RID_IN))
{ {
tree asm_name = NULL_TREE; tree asm_name = NULL_TREE;
tree postfix_attrs = NULL_TREE; tree postfix_attrs = NULL_TREE;
@ -1421,7 +1454,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
all_prefix_attrs)); all_prefix_attrs));
if (d) if (d)
finish_decl (d, UNKNOWN_LOCATION, NULL_TREE, finish_decl (d, UNKNOWN_LOCATION, NULL_TREE,
NULL_TREE, asm_name); NULL_TREE, asm_name);
if (c_parser_next_token_is_keyword (parser, RID_IN))
{
if (d)
*objc_foreach_object_declaration = d;
else
*objc_foreach_object_declaration = error_mark_node;
}
} }
if (c_parser_next_token_is (parser, CPP_COMMA)) if (c_parser_next_token_is (parser, CPP_COMMA))
{ {
@ -1438,6 +1479,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
c_parser_consume_token (parser); c_parser_consume_token (parser);
return; return;
} }
else if (c_parser_next_token_is_keyword (parser, RID_IN))
{
/* This can only happen in Objective-C: we found the
'in' that terminates the declaration inside an
Objective-C foreach statement. Do not consume the
token, so that the caller can use it to determine
that this indeed is a foreach context. */
return;
}
else else
{ {
c_parser_error (parser, "expected %<,%> or %<;%>"); c_parser_error (parser, "expected %<,%> or %<;%>");
@ -1484,7 +1534,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
while (c_parser_next_token_is_not (parser, CPP_EOF) while (c_parser_next_token_is_not (parser, CPP_EOF)
&& c_parser_next_token_is_not (parser, CPP_OPEN_BRACE)) && c_parser_next_token_is_not (parser, CPP_OPEN_BRACE))
c_parser_declaration_or_fndef (parser, false, false, false, c_parser_declaration_or_fndef (parser, false, false, false,
true, false); true, false, NULL);
store_parm_decls (); store_parm_decls ();
DECL_STRUCT_FUNCTION (current_function_decl)->function_start_locus DECL_STRUCT_FUNCTION (current_function_decl)->function_start_locus
= c_parser_peek_token (parser)->location; = c_parser_peek_token (parser)->location;
@ -3743,7 +3793,7 @@ c_parser_compound_statement_nostart (c_parser *parser)
{ {
last_label = false; last_label = false;
mark_valid_location_for_stdc_pragma (false); mark_valid_location_for_stdc_pragma (false);
c_parser_declaration_or_fndef (parser, true, true, true, true, true); c_parser_declaration_or_fndef (parser, true, true, true, true, true, NULL);
if (last_stmt) if (last_stmt)
pedwarn_c90 (loc, pedwarn_c90 (loc,
(pedantic && !flag_isoc99) (pedantic && !flag_isoc99)
@ -3771,7 +3821,7 @@ c_parser_compound_statement_nostart (c_parser *parser)
last_label = false; last_label = false;
mark_valid_location_for_stdc_pragma (false); mark_valid_location_for_stdc_pragma (false);
c_parser_declaration_or_fndef (parser, true, true, true, true, c_parser_declaration_or_fndef (parser, true, true, true, true,
true); true, NULL);
/* Following the old parser, __extension__ does not /* Following the old parser, __extension__ does not
disable this diagnostic. */ disable this diagnostic. */
restore_extension_diagnostics (ext); restore_extension_diagnostics (ext);
@ -3911,7 +3961,7 @@ c_parser_label (c_parser *parser)
c_parser_declaration_or_fndef (parser, /*fndef_ok*/ false, c_parser_declaration_or_fndef (parser, /*fndef_ok*/ false,
/*static_assert_ok*/ true, /*static_assert_ok*/ true,
/*nested*/ true, /*empty_ok*/ false, /*nested*/ true, /*empty_ok*/ false,
/*start_attr_ok*/ true); /*start_attr_ok*/ true, NULL);
} }
} }
} }
@ -4427,20 +4477,68 @@ c_parser_do_statement (c_parser *parser)
Note in particular that the nested function does not include a Note in particular that the nested function does not include a
trailing ';', whereas the "declaration" production includes one. trailing ';', whereas the "declaration" production includes one.
Also, can we reject bad declarations earlier and cheaper than Also, can we reject bad declarations earlier and cheaper than
check_for_loop_decls? */ check_for_loop_decls?
In Objective-C, there are two additional variants:
foreach-statement:
for ( expression in expresssion ) statement
for ( declaration in expression ) statement
This is inconsistent with C, because the second variant is allowed
even if c99 is not enabled.
The rest of the comment documents these Objective-C foreach-statement.
Here is the canonical example of the first variant:
for (object in array) { do something with object }
we call the first expression ("object") the "object_expression" and
the second expression ("array") the "collection_expression".
object_expression must be an lvalue of type "id" (a generic Objective-C
object) because the loop works by assigning to object_expression the
various objects from the collection_expression. collection_expression
must evaluate to something of type "id" which responds to the method
countByEnumeratingWithState:objects:count:.
The canonical example of the second variant is:
for (id object in array) { do something with object }
which is completely equivalent to
{
id object;
for (object in array) { do something with object }
}
Note that initizializing 'object' in some way (eg, "for ((object =
xxx) in array) { do something with object }") is possibly
technically valid, but completely pointless as 'object' will be
assigned to something else as soon as the loop starts. We should
most likely reject it (TODO).
The beginning of the Objective-C foreach-statement looks exactly
like the beginning of the for-statement, and we can tell it is a
foreach-statement only because the initial declaration or
expression is terminated by 'in' instead of ';'.
*/
static void static void
c_parser_for_statement (c_parser *parser) c_parser_for_statement (c_parser *parser)
{ {
tree block, cond, incr, save_break, save_cont, body; tree block, cond, incr, save_break, save_cont, body;
/* The following are only used when parsing an ObjC foreach statement. */
tree object_expression, collection_expression;
location_t loc = c_parser_peek_token (parser)->location; location_t loc = c_parser_peek_token (parser)->location;
location_t for_loc = c_parser_peek_token (parser)->location; location_t for_loc = c_parser_peek_token (parser)->location;
bool is_foreach_statement = false;
gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR)); gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR));
c_parser_consume_token (parser); c_parser_consume_token (parser);
block = c_begin_compound_stmt (flag_isoc99); /* Open a compound statement in Objective-C as well, just in case this is
as foreach expression. */
block = c_begin_compound_stmt (flag_isoc99 || c_dialect_objc ());
if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
{ {
/* Parse the initialization declaration or expression. */ /* Parse the initialization declaration or expression. */
cond = error_mark_node;
object_expression = error_mark_node;
if (c_parser_next_token_is (parser, CPP_SEMICOLON)) if (c_parser_next_token_is (parser, CPP_SEMICOLON))
{ {
c_parser_consume_token (parser); c_parser_consume_token (parser);
@ -4448,8 +4546,20 @@ c_parser_for_statement (c_parser *parser)
} }
else if (c_parser_next_token_starts_declaration (parser)) else if (c_parser_next_token_starts_declaration (parser))
{ {
c_parser_declaration_or_fndef (parser, true, true, true, true, true); parser->objc_could_be_foreach_context = true;
check_for_loop_decls (for_loc); c_parser_declaration_or_fndef (parser, true, true, true, true, true,
&object_expression);
parser->objc_could_be_foreach_context = false;
if (c_parser_next_token_is_keyword (parser, RID_IN))
{
c_parser_consume_token (parser);
is_foreach_statement = true;
if (check_for_loop_decls (for_loc, true) == NULL_TREE)
c_parser_error (parser, "multiple iterating variables in fast enumeration");
}
else
check_for_loop_decls (for_loc, flag_isoc99);
} }
else if (c_parser_next_token_is_keyword (parser, RID_EXTENSION)) else if (c_parser_next_token_is_keyword (parser, RID_EXTENSION))
{ {
@ -4466,10 +4576,21 @@ c_parser_for_statement (c_parser *parser)
int ext; int ext;
ext = disable_extension_diagnostics (); ext = disable_extension_diagnostics ();
c_parser_consume_token (parser); c_parser_consume_token (parser);
parser->objc_could_be_foreach_context = true;
c_parser_declaration_or_fndef (parser, true, true, true, true, c_parser_declaration_or_fndef (parser, true, true, true, true,
true); true, &object_expression);
parser->objc_could_be_foreach_context = false;
restore_extension_diagnostics (ext); restore_extension_diagnostics (ext);
check_for_loop_decls (for_loc); if (c_parser_next_token_is_keyword (parser, RID_IN))
{
c_parser_consume_token (parser);
is_foreach_statement = true;
if (check_for_loop_decls (for_loc, true) == NULL_TREE)
c_parser_error (parser, "multiple iterating variables in fast enumeration");
}
else
check_for_loop_decls (for_loc, flag_isoc99);
} }
else else
goto init_expr; goto init_expr;
@ -4477,25 +4598,62 @@ c_parser_for_statement (c_parser *parser)
else else
{ {
init_expr: init_expr:
c_finish_expr_stmt (loc, c_parser_expression (parser).value); {
c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); tree init_expression;
parser->objc_could_be_foreach_context = true;
init_expression = c_parser_expression (parser).value;
parser->objc_could_be_foreach_context = false;
if (c_parser_next_token_is_keyword (parser, RID_IN))
{
c_parser_consume_token (parser);
is_foreach_statement = true;
if (! lvalue_p (init_expression))
c_parser_error (parser, "invalid iterating variable in fast enumeration");
object_expression = c_process_expr_stmt (loc, init_expression);
}
else
{
c_finish_expr_stmt (loc, init_expression);
c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
}
}
} }
/* Parse the loop condition. */ /* Parse the loop condition. In the case of a foreach
if (c_parser_next_token_is (parser, CPP_SEMICOLON)) statement, there is no loop condition. */
if (!is_foreach_statement)
{ {
c_parser_consume_token (parser); if (c_parser_next_token_is (parser, CPP_SEMICOLON))
cond = NULL_TREE; {
c_parser_consume_token (parser);
cond = NULL_TREE;
}
else
{
cond = c_parser_condition (parser);
c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
}
} }
else /* Parse the increment expression (the third expression in a
{ for-statement). In the case of a foreach-statement, this is
cond = c_parser_condition (parser); the expression that follows the 'in'. */
c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
}
/* Parse the increment expression. */
if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
incr = c_process_expr_stmt (loc, NULL_TREE); {
if (is_foreach_statement)
{
c_parser_error (parser, "missing collection in fast enumeration");
collection_expression = error_mark_node;
}
else
incr = c_process_expr_stmt (loc, NULL_TREE);
}
else else
incr = c_process_expr_stmt (loc, c_parser_expression (parser).value); {
if (is_foreach_statement)
collection_expression = c_process_expr_stmt (loc, c_parser_expression (parser).value);
else
incr = c_process_expr_stmt (loc, c_parser_expression (parser).value);
}
c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
} }
else else
@ -4508,8 +4666,11 @@ c_parser_for_statement (c_parser *parser)
save_cont = c_cont_label; save_cont = c_cont_label;
c_cont_label = NULL_TREE; c_cont_label = NULL_TREE;
body = c_parser_c99_block_statement (parser); body = c_parser_c99_block_statement (parser);
c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true); if (is_foreach_statement)
add_stmt (c_end_compound_stmt (loc, block, flag_isoc99)); objc_finish_foreach_loop (loc, object_expression, collection_expression, body, c_break_label, c_cont_label);
else
c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true);
add_stmt (c_end_compound_stmt (loc, block, flag_isoc99 || c_dialect_objc ()));
c_break_label = save_break; c_break_label = save_break;
c_cont_label = save_cont; c_cont_label = save_cont;
} }
@ -6790,7 +6951,7 @@ c_parser_objc_methodprotolist (c_parser *parser)
} }
else else
c_parser_declaration_or_fndef (parser, false, false, true, c_parser_declaration_or_fndef (parser, false, false, true,
false, true); false, true, NULL);
break; break;
} }
} }
@ -8439,8 +8600,8 @@ c_parser_omp_for_loop (location_t loc,
{ {
if (i > 0) if (i > 0)
VEC_safe_push (tree, gc, for_block, c_begin_compound_stmt (true)); VEC_safe_push (tree, gc, for_block, c_begin_compound_stmt (true));
c_parser_declaration_or_fndef (parser, true, true, true, true, true); c_parser_declaration_or_fndef (parser, true, true, true, true, true, NULL);
decl = check_for_loop_decls (for_loc); decl = check_for_loop_decls (for_loc, flag_isoc99);
if (decl == NULL) if (decl == NULL)
goto error_init; goto error_init;
if (DECL_INITIAL (decl) == error_mark_node) if (DECL_INITIAL (decl) == error_mark_node)

View File

@ -429,7 +429,7 @@ extern struct c_declarator *build_array_declarator (location_t, tree,
bool, bool); bool, bool);
extern tree build_enumerator (location_t, location_t, struct c_enum_contents *, extern tree build_enumerator (location_t, location_t, struct c_enum_contents *,
tree, tree); tree, tree);
extern tree check_for_loop_decls (location_t); extern tree check_for_loop_decls (location_t, bool);
extern void mark_forward_parm_decls (void); extern void mark_forward_parm_decls (void);
extern void declare_parm_level (void); extern void declare_parm_level (void);
extern void undeclared_variable (location_t, tree); extern void undeclared_variable (location_t, tree);

View File

@ -20,6 +20,7 @@ several resources on the Internet that present the language.
* compatibility_alias:: * compatibility_alias::
* Exceptions:: * Exceptions::
* Synchronization:: * Synchronization::
* Fast enumeration::
@end menu @end menu
@node Executing code before main @node Executing code before main
@ -739,3 +740,218 @@ Because of the interactions between synchronization and exception
handling, you can only use @code{@@synchronized} when compiling with handling, you can only use @code{@@synchronized} when compiling with
exceptions enabled, that is with the command line option exceptions enabled, that is with the command line option
@option{-fobjc-exceptions}. @option{-fobjc-exceptions}.
@c =========================================================================
@node Fast enumeration
@section Fast enumeration
@menu
* Using fast enumeration::
* c99-like fast enumeration syntax::
* Fast enumeration details::
* Fast enumeration protocol::
@end menu
@c ================================
@node Using fast enumeration
@subsection Using fast enumeration
GNU Objective-C provides support for the fast enumeration syntax:
@smallexample
id array = @dots{};
id object;
for (object in array)
@{
/* Do something with 'object' */
@}
@end smallexample
@code{array} needs to be an Objective-C object (usually a collection
object, for example an array, a dictionary or a set) which implements
the ``Fast Enumeration Protocol'' (see below). If you are using a
Foundation library such as GNUstep Base or Apple Cocoa Foundation, all
collection objects in the library implement this protocol and can be
used in this way.
The code above would iterate over all objects in @code{array}. For
each of them, it assigns it to @code{object}, then executes the
@code{Do something with 'object'} statements.
Here is a fully worked-out example using a Foundation library (which
provides the implementation of @code{NSArray}, @code{NSString} and
@code{NSLog}):
@smallexample
NSArray *array = [NSArray arrayWithObjects: @@"1", @@"2", @@"3", nil];
NSString *object;
for (object in array)
NSLog (@@"Iterating over %@@", object);
@end smallexample
@c ================================
@node c99-like fast enumeration syntax
@subsection c99-like fast enumeration syntax
A c99-like declaration syntax is also allowed:
@smallexample
id array = @dots{};
for (id object in array)
@{
/* Do something with 'object' */
@}
@end smallexample
this is completely equivalent to:
@smallexample
id array = @dots{};
@{
id object;
for (object in array)
@{
/* Do something with 'object' */
@}
@}
@end smallexample
but can save some typing.
Note that the option @option{-std=c99} is not required to allow this
syntax in Objective-C.
@c ================================
@node Fast enumeration details
@subsection Fast enumeration details
Here is a more technical description with the gory details. Consider the code
@smallexample
for (@var{object expression} in @var{collection expression})
@{
@var{statements}
@}
@end smallexample
here is what happens when you run it:
@itemize @bullet
@item
@code{@var{collection expression}} is evaluated exactly once and the
result is used as the collection object to iterate over. This means
it is safe to write code such as @code{for (object in [NSDictionary
keyEnumerator]) @dots{}}.
@item
the iteration is implemented by the compiler by repeatedly getting
batches of objects from the collection object using the fast
enumeration protocol (see below), then iterating over all objects in
the batch. This is faster than a normal enumeration where objects are
retrieved one by one (hence the name ``fast enumeration'').
@item
if there are no objects in the collection, then
@code{@var{object expression}} is set to @code{nil} and the loop
immediately terminates.
@item
if there are objects in the collection, then for each object in the
collection (in the order they are returned) @code{@var{object expression}}
is set to the object, then @code{@var{statements}} are executed.
@item
@code{@var{statements}} can contain @code{break} and @code{continue}
commands, which will abort the iteration or skip to the next loop
iteration as expected.
@item
when the iteration ends because there are no more objects to iterate
over, @code{@var{object expression}} is set to @code{nil}. This allows
you to determine whether the iteration finished because a @code{break}
command was used (in which case @code{@var{object expression}} will remain
set to the last object that was iterated over) or because it iterated
over all the objects (in which case @code{@var{object expression}} will be
set to @code{nil}).
@item
@code{@var{statements}} must not make any changes to the collection
object; if they do, it is a hard error and the fast enumeration
terminates by invoking @code{objc_enumerationMutation}, a runtime
function that normally aborts the program but which can be customized
by Foundation libraries via @code{objc_set_mutation_handler} to do
something different, such as raising an exception.
@end itemize
@c ================================
@node Fast enumeration protocol
@subsection Fast enumeration protocol
If you want your own collection object to be usable with fast
enumeration, you need to have it implement the method
@smallexample
- (unsigned long) countByEnumeratingWithState: (NSFastEnumerationState *)state
objects: (id *)objects
count: (unsigneld long)len;
@end smallexample
where @code{NSFastEnumerationState} must be defined in your code as follows:
@smallexample
typdef struct
@{
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
@} NSFastEnumerationState;
@end smallexample
If no @code{NSFastEnumerationState} is defined in your code, the
compiler will automatically replace @code{NSFastEnumerationState *}
with @code{struct __objcFastEnumerationState *}, where that type is
silently defined by the compiler in an identical way. This can be
confusing and we recommend that you define
@code{NSFastEnumerationState} (as shown above) instead.
The method is called repeatedly during a fast enumeration to retrieve
batches of objects. Each invocation of the method should retrieve the
next batch of objects.
The return value of the method is the number of objects in the current
batch; this should not exceed @code{len}, which is the maximum size of
a batch as requested by the caller. The batch itself is returned in
the @code{itemsPtr} field of the @code{NSFastEnumerationState} struct.
To help with returning the objects, the @code{objects} array is a C
array preallocated by the caller (on the stack) of size @code{len}.
In many cases you can put the objects you want to return in that
@code{objects} array, then do @code{itemsPtr = objects}. But you
don't have to; if your collection already has the objects to return in
some form of C array, it could return them from there instead.
The @code{state} and @code{extra} fields of the
@code{NSFastEnumerationState} structure allows your collection object
to keep track of the state of the enumeration. In a simple array
implementation, @code{state} may keep track of the index of the last
object that was returned, and @code{extra} may be unused.
The @code{mutationsPtr} field of the @code{NSFastEnumerationState} is
used to keep track of mutations. It should point to a number; before
working on each object, the fast enumeration loop will check that this
number has not changed. If it has, a mutation has happened and the
fast enumeration will abort. So, @code{mutationsPtr} could be set to
point to some sort of version number of your collection, which is
increased by one every time there is a change (for example when an
object is added or removed). Or, if you are content with less strict
mutation checks, it could point to the number of objects in your
collection or some other value that can be checked to perform an
approximate check that the collection has not been mutated.

View File

@ -1,3 +1,31 @@
2010-10-06 Nicola Pero <nicola.pero@meta-innovation.com>
Implemented fast enumeration for Objective-C.
* objc-act.c (build_fast_enumeration_state_template): New.
(TAG_ENUMERATION_MUTATION): New.
(TAG_FAST_ENUMERATION_STATE): New.
(synth_module_prologue): Call build_fast_enumeration_state_template() and set up
objc_enumeration_mutation_decl.
(objc_create_temporary_var): Allow providing a name to temporary
variables.
(objc_build_exc_ptr): Updated calls to
objc_create_temporary_var().
(next_sjlj_build_try_catch_finally): Same change.
(objc_finish_foreach_loop): New.
* objc-act.h: Added OCTI_FAST_ENUM_STATE_TEMP,
OCTI_ENUM_MUTATION_DECL, objc_fast_enumeration_state_template,
objc_enumeration_mutation_decl.
Merge from 'apple/trunk' branch on FSF servers.
2006-04-12 Fariborz Jahanian <fjahanian@apple.com>
Radar 4507230
* objc-act.c (objc_type_valid_for_messaging): New routine to check
for valid objc object types.
(objc_finish_foreach_loop): Check for invalid objc objects in
foreach header.
2010-10-05 Nicola Pero <nicola.pero@meta-innovation.com> 2010-10-05 Nicola Pero <nicola.pero@meta-innovation.com>
Merge from 'apple/trunk' branch on FSF servers. Merge from 'apple/trunk' branch on FSF servers.
@ -5,8 +33,8 @@
2005-10-17 Fariborz Jahanian <fjahanian@apple.com> 2005-10-17 Fariborz Jahanian <fjahanian@apple.com>
Radar 4290840 Radar 4290840
* objc-act.c (objc_start_method_definition): Check for error_mark_node for * objc-act.c (objc_start_method_definition): Check for
the selector name and make a quick exit. error_mark_node for the selector name and make a quick exit.
2010-10-04 Andi Kleen <ak@linux.intel.com> 2010-10-04 Andi Kleen <ak@linux.intel.com>

View File

@ -171,6 +171,8 @@ static tree get_class_ivars (tree, bool);
static tree generate_protocol_list (tree); static tree generate_protocol_list (tree);
static void build_protocol_reference (tree); static void build_protocol_reference (tree);
static void build_fast_enumeration_state_template (void);
#ifdef OBJCPLUS #ifdef OBJCPLUS
static void objc_generate_cxx_cdtors (void); static void objc_generate_cxx_cdtors (void);
#endif #endif
@ -240,6 +242,7 @@ static void generate_struct_by_value_array (void)
ATTRIBUTE_NORETURN; ATTRIBUTE_NORETURN;
static void mark_referenced_methods (void); static void mark_referenced_methods (void);
static void generate_objc_image_info (void); static void generate_objc_image_info (void);
static bool objc_type_valid_for_messaging (tree typ);
/*** Private Interface (data) ***/ /*** Private Interface (data) ***/
@ -274,6 +277,9 @@ static void generate_objc_image_info (void);
#define PROTOCOL_OBJECT_CLASS_NAME "Protocol" #define PROTOCOL_OBJECT_CLASS_NAME "Protocol"
#define TAG_ENUMERATION_MUTATION "objc_enumerationMutation"
#define TAG_FAST_ENUMERATION_STATE "__objcFastEnumerationState"
static const char *TAG_GETCLASS; static const char *TAG_GETCLASS;
static const char *TAG_GETMETACLASS; static const char *TAG_GETMETACLASS;
static const char *TAG_MSGSEND; static const char *TAG_MSGSEND;
@ -1952,6 +1958,17 @@ synth_module_prologue (void)
self_id = get_identifier ("self"); self_id = get_identifier ("self");
ucmd_id = get_identifier ("_cmd"); ucmd_id = get_identifier ("_cmd");
/* Declare struct _objc_fast_enumeration_state { ... }; */
build_fast_enumeration_state_template ();
/* void objc_enumeration_mutation (id) */
type = build_function_type (void_type_node,
tree_cons (NULL_TREE, objc_object_type, NULL_TREE));
objc_enumeration_mutation_decl
= add_builtin_function (TAG_ENUMERATION_MUTATION, type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (objc_enumeration_mutation_decl) = 0;
#ifdef OBJCPLUS #ifdef OBJCPLUS
pop_lang_context (); pop_lang_context ();
#endif #endif
@ -3596,13 +3613,25 @@ get_class_ivars (tree interface, bool inherited)
return ivar_chain; return ivar_chain;
} }
/* Create a temporary variable of type 'type'. If 'name' is set, uses
the specified name, else use no name. Returns the declaration of
the type. The 'name' is mostly useful for debugging.
*/
static tree static tree
objc_create_temporary_var (tree type) objc_create_temporary_var (tree type, const char *name)
{ {
tree decl; tree decl;
decl = build_decl (input_location, if (name != NULL)
VAR_DECL, NULL_TREE, type); {
decl = build_decl (input_location,
VAR_DECL, get_identifier (name), type);
}
else
{
decl = build_decl (input_location,
VAR_DECL, NULL_TREE, type);
}
TREE_USED (decl) = 1; TREE_USED (decl) = 1;
DECL_ARTIFICIAL (decl) = 1; DECL_ARTIFICIAL (decl) = 1;
DECL_IGNORED_P (decl) = 1; DECL_IGNORED_P (decl) = 1;
@ -3687,7 +3716,7 @@ objc_build_exc_ptr (void)
tree var = cur_try_context->caught_decl; tree var = cur_try_context->caught_decl;
if (!var) if (!var)
{ {
var = objc_create_temporary_var (objc_object_type); var = objc_create_temporary_var (objc_object_type, NULL);
cur_try_context->caught_decl = var; cur_try_context->caught_decl = var;
} }
return var; return var;
@ -3888,10 +3917,10 @@ next_sjlj_build_try_catch_finally (void)
/* Create the declarations involved. */ /* Create the declarations involved. */
t = xref_tag (RECORD_TYPE, get_identifier (UTAG_EXCDATA)); t = xref_tag (RECORD_TYPE, get_identifier (UTAG_EXCDATA));
stack_decl = objc_create_temporary_var (t); stack_decl = objc_create_temporary_var (t, NULL);
cur_try_context->stack_decl = stack_decl; cur_try_context->stack_decl = stack_decl;
rethrow_decl = objc_create_temporary_var (objc_object_type); rethrow_decl = objc_create_temporary_var (objc_object_type, NULL);
cur_try_context->rethrow_decl = rethrow_decl; cur_try_context->rethrow_decl = rethrow_decl;
TREE_CHAIN (rethrow_decl) = stack_decl; TREE_CHAIN (rethrow_decl) = stack_decl;
@ -9980,4 +10009,548 @@ objc_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
#endif #endif
} }
/* This routine returns true if TYP is a valid objc object type,
suitable for messaging; false otherwise.
*/
static bool
objc_type_valid_for_messaging (tree typ)
{
if (!POINTER_TYPE_P (typ))
return false;
do
typ = TREE_TYPE (typ); /* Remove indirections. */
while (POINTER_TYPE_P (typ));
if (TREE_CODE (typ) != RECORD_TYPE)
return false;
return objc_is_object_id (typ) || TYPE_HAS_OBJC_INFO (typ);
}
/* Begin code generation for fast enumeration (foreach) ... */
/* Defines
struct __objcFastEnumerationState
{
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
Confusingly enough, NSFastEnumeration is then defined by libraries
to be the same structure.
*/
static void
build_fast_enumeration_state_template (void)
{
tree decls, *chain = NULL;
/* { */
objc_fast_enumeration_state_template = objc_start_struct (get_identifier
(TAG_FAST_ENUMERATION_STATE));
/* unsigned long state; */
decls = add_field_decl (long_unsigned_type_node, "state", &chain);
/* id *itemsPtr; */
add_field_decl (build_pointer_type (objc_object_type),
"itemsPtr", &chain);
/* unsigned long *mutationsPtr; */
add_field_decl (build_pointer_type (long_unsigned_type_node),
"mutationsPtr", &chain);
/* unsigned long extra[5]; */
add_field_decl (build_sized_array_type (long_unsigned_type_node, 5),
"extra", &chain);
/* } */
objc_finish_struct (objc_fast_enumeration_state_template, decls);
}
/*
'objc_finish_foreach_loop()' generates the code for an Objective-C
foreach loop. The 'location' argument is the location of the 'for'
that starts the loop. The 'object_expression' is the expression of
the 'object' that iterates; the 'collection_expression' is the
expression of the collection that we iterate over (we need to make
sure we evaluate this only once); the 'for_body' is the set of
statements to be executed in each iteration; 'break_label' and
'continue_label' are the break and continue labels which we need to
emit since the <statements> may be jumping to 'break_label' (if they
contain 'break') or to 'continue_label' (if they contain
'continue').
The syntax is
for (<object expression> in <collection expression>)
<statements>
which is compiled into the following blurb:
{
id __objc_foreach_collection;
__objc_fast_enumeration_state __objc_foreach_enum_state;
unsigned long __objc_foreach_batchsize;
id __objc_foreach_items[16];
__objc_foreach_collection = <collection expression>;
__objc_foreach_enum_state = { 0 };
__objc_foreach_batchsize = [__objc_foreach_collection countByEnumeratingWithState: &__objc_foreach_enum_state objects: __objc_foreach_items count: 16];
if (__objc_foreach_batchsize == 0)
<object expression> = nil;
else
{
unsigned long __objc_foreach_mutations_pointer = *__objc_foreach_enum_state.mutationsPtr;
next_batch:
{
unsigned long __objc_foreach_index;
__objc_foreach_index = 0;
next_object:
if (__objc_foreach_mutation_pointer != *__objc_foreach_enum_state.mutationsPtr) objc_enumeration_mutation (<collection expression>);
<object expression> = enumState.itemsPtr[__objc_foreach_index];
<statements> [PS: inside <statments>, 'break' jumps to break_label and 'continue' jumps to continue_label]
continue_label:
__objc_foreach_index++;
if (__objc_foreach_index < __objc_foreach_batchsize) goto next_object;
__objc_foreach_batchsize = [__objc_foreach_collection countByEnumeratingWithState: &__objc_foreach_enum_state objects: __objc_foreach_items count: 16];
}
if (__objc_foreach_batchsize != 0) goto next_batch;
<object expression> = nil;
break_label:
}
}
'statements' may contain a 'continue' or 'break' instruction, which
the user expects to 'continue' or 'break' the entire foreach loop.
We are provided the labels that 'break' and 'continue' jump to, so
we place them where we want them to jump to when they pick them.
Optimization TODO: we could cache the IMP of
countByEnumeratingWithState:objects:count:.
*/
/* If you need to debug objc_finish_foreach_loop(), uncomment the following line. */
/* #define DEBUG_OBJC_FINISH_FOREACH_LOOP 1 */
#ifdef DEBUG_OBJC_FINISH_FOREACH_LOOP
#include "tree-pretty-print.h"
#endif
void
objc_finish_foreach_loop (location_t location, tree object_expression, tree collection_expression, tree for_body,
tree break_label, tree continue_label)
{
/* A tree representing the __objcFastEnumerationState struct type,
or NSFastEnumerationState struct, whatever we are using. */
tree objc_fast_enumeration_state_type;
/* The trees representing the declarations of each of the local variables. */
tree objc_foreach_collection_decl;
tree objc_foreach_enum_state_decl;
tree objc_foreach_items_decl;
tree objc_foreach_batchsize_decl;
tree objc_foreach_mutations_pointer_decl;
tree objc_foreach_index_decl;
/* A tree representing the selector countByEnumeratingWithState:objects:count:. */
tree selector_name;
/* A tree representing the local bind. */
tree bind;
/* A tree representing the external 'if (__objc_foreach_batchsize)' */
tree first_if;
/* A tree representing the 'else' part of 'first_if' */
tree first_else;
/* A tree representing the 'next_batch' label. */
tree next_batch_label_decl;
/* A tree representing the binding after the 'next_batch' label. */
tree next_batch_bind;
/* A tree representing the 'next_object' label. */
tree next_object_label_decl;
/* Temporary variables. */
tree t;
int i;
if (object_expression == error_mark_node)
return;
if (collection_expression == error_mark_node)
return;
if (!objc_type_valid_for_messaging (TREE_TYPE (object_expression)))
{
error ("iterating variable in fast enumeration is not an object");
return;
}
if (!objc_type_valid_for_messaging (TREE_TYPE (collection_expression)))
{
error ("collection in fast enumeration is not an object");
return;
}
/* TODO: Check that object_expression is either a variable
declaration, or an lvalue. */
/* This kludge is an idea from apple. We use the
__objcFastEnumerationState struct implicitly defined by the
compiler, unless a NSFastEnumerationState struct has been defined
(by a Foundation library such as GNUstep Base) in which case, we
use that one.
*/
objc_fast_enumeration_state_type = objc_fast_enumeration_state_template;
{
tree objc_NSFastEnumeration_type = lookup_name (get_identifier ("NSFastEnumerationState"));
if (objc_NSFastEnumeration_type)
{
/* TODO: We really need to check that
objc_NSFastEnumeration_type is the same as ours! */
if (TREE_CODE (objc_NSFastEnumeration_type) == TYPE_DECL)
{
/* If it's a typedef, use the original type. */
if (DECL_ORIGINAL_TYPE (objc_NSFastEnumeration_type))
objc_fast_enumeration_state_type = DECL_ORIGINAL_TYPE (objc_NSFastEnumeration_type);
else
objc_fast_enumeration_state_type = TREE_TYPE (objc_NSFastEnumeration_type);
}
}
}
/* { */
/* Done by c-parser.c. */
/* type object; */
/* Done by c-parser.c. */
/* id __objc_foreach_collection */
objc_foreach_collection_decl = objc_create_temporary_var (objc_object_type, "__objc_foreach_collection");
/* __objcFastEnumerationState __objc_foreach_enum_state; */
objc_foreach_enum_state_decl = objc_create_temporary_var (objc_fast_enumeration_state_type, "__objc_foreach_enum_state");
TREE_CHAIN (objc_foreach_enum_state_decl) = objc_foreach_collection_decl;
/* id __objc_foreach_items[16]; */
objc_foreach_items_decl = objc_create_temporary_var (build_sized_array_type (objc_object_type, 16), "__objc_foreach_items");
TREE_CHAIN (objc_foreach_items_decl) = objc_foreach_enum_state_decl;
/* unsigned long __objc_foreach_batchsize; */
objc_foreach_batchsize_decl = objc_create_temporary_var (long_unsigned_type_node, "__objc_foreach_batchsize");
TREE_CHAIN (objc_foreach_batchsize_decl) = objc_foreach_items_decl;
/* Generate the local variable binding. */
bind = build3 (BIND_EXPR, void_type_node, objc_foreach_batchsize_decl, NULL, NULL);
SET_EXPR_LOCATION (bind, location);
TREE_SIDE_EFFECTS (bind) = 1;
/* __objc_foreach_collection = <collection expression>; */
t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_collection_decl, collection_expression);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (bind));
/* __objc_foreach_enum_state.state = 0; */
t = build2 (MODIFY_EXPR, void_type_node, objc_build_component_ref (objc_foreach_enum_state_decl,
get_identifier ("state")),
build_int_cst (long_unsigned_type_node, 0));
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (bind));
/* __objc_foreach_enum_state.itemsPtr = NULL; */
t = build2 (MODIFY_EXPR, void_type_node, objc_build_component_ref (objc_foreach_enum_state_decl,
get_identifier ("itemsPtr")),
null_pointer_node);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (bind));
/* __objc_foreach_enum_state.mutationsPtr = NULL; */
t = build2 (MODIFY_EXPR, void_type_node, objc_build_component_ref (objc_foreach_enum_state_decl,
get_identifier ("mutationsPtr")),
null_pointer_node);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (bind));
/* __objc_foreach_enum_state.extra[0] = 0; */
/* __objc_foreach_enum_state.extra[1] = 0; */
/* __objc_foreach_enum_state.extra[2] = 0; */
/* __objc_foreach_enum_state.extra[3] = 0; */
/* __objc_foreach_enum_state.extra[4] = 0; */
for (i = 0; i < 5 ; i++)
{
t = build2 (MODIFY_EXPR, void_type_node,
build_array_ref (location, objc_build_component_ref (objc_foreach_enum_state_decl,
get_identifier ("extra")),
build_int_cst (NULL_TREE, i)),
build_int_cst (long_unsigned_type_node, 0));
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (bind));
}
/* __objc_foreach_batchsize = [__objc_foreach_collection countByEnumeratingWithState: &__objc_foreach_enum_state objects: __objc_foreach_items count: 16]; */
selector_name = get_identifier ("countByEnumeratingWithState:objects:count:");
#ifdef OBJCPLUS
t = objc_finish_message_expr (objc_foreach_collection_decl, selector_name,
/* Parameters. */
tree_cons /* &__objc_foreach_enum_state */
(NULL_TREE, build_fold_addr_expr_loc (location, objc_foreach_enum_state_decl),
tree_cons /* __objc_foreach_items */
(NULL_TREE, objc_foreach_items_decl,
tree_cons /* 16 */
(NULL_TREE, build_int_cst (NULL_TREE, 16), NULL_TREE))));
#else
/* In C, we need to decay the __objc_foreach_items array that we are passing. */
{
struct c_expr array;
array.value = objc_foreach_items_decl;
t = objc_finish_message_expr (objc_foreach_collection_decl, selector_name,
/* Parameters. */
tree_cons /* &__objc_foreach_enum_state */
(NULL_TREE, build_fold_addr_expr_loc (location, objc_foreach_enum_state_decl),
tree_cons /* __objc_foreach_items */
(NULL_TREE, default_function_array_conversion (location, array).value,
tree_cons /* 16 */
(NULL_TREE, build_int_cst (NULL_TREE, 16), NULL_TREE))));
}
#endif
t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_batchsize_decl, t);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (bind));
/* if (__objc_foreach_batchsize == 0) */
first_if = build3 (COND_EXPR, void_type_node,
/* Condition. */
c_fully_fold
(c_common_truthvalue_conversion
(location,
build_binary_op (location,
EQ_EXPR,
objc_foreach_batchsize_decl,
build_int_cst (long_unsigned_type_node, 0), 1)),
false, NULL),
/* Then block (we fill it in later). */
NULL_TREE,
/* Else block (we fill it in later). */
NULL_TREE);
SET_EXPR_LOCATION (first_if, location);
append_to_statement_list (first_if, &BIND_EXPR_BODY (bind));
/* then <object expression> = nil; */
t = build2 (MODIFY_EXPR, void_type_node, object_expression, convert (objc_object_type, null_pointer_node));
SET_EXPR_LOCATION (t, location);
COND_EXPR_THEN (first_if) = t;
/* Now we build the 'else' part of the if; once we finish building
it, we attach it to first_if as the 'else' part. */
/* else */
/* { */
/* unsigned long __objc_foreach_mutations_pointer; */
objc_foreach_mutations_pointer_decl = objc_create_temporary_var (long_unsigned_type_node, "__objc_foreach_mutations_pointer");
/* Generate the local variable binding. */
first_else = build3 (BIND_EXPR, void_type_node, objc_foreach_mutations_pointer_decl, NULL, NULL);
SET_EXPR_LOCATION (first_else, location);
TREE_SIDE_EFFECTS (first_else) = 1;
/* __objc_foreach_mutations_pointer = *__objc_foreach_enum_state.mutationsPtr; */
t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_mutations_pointer_decl,
build_indirect_ref (location, objc_build_component_ref (objc_foreach_enum_state_decl,
get_identifier ("mutationsPtr")),
RO_UNARY_STAR));
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (first_else));
/* next_batch: */
next_batch_label_decl = create_artificial_label (location);
t = build1 (LABEL_EXPR, void_type_node, next_batch_label_decl);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (first_else));
/* { */
/* unsigned long __objc_foreach_index; */
objc_foreach_index_decl = objc_create_temporary_var (long_unsigned_type_node, "__objc_foreach_index");
/* Generate the local variable binding. */
next_batch_bind = build3 (BIND_EXPR, void_type_node, objc_foreach_index_decl, NULL, NULL);
SET_EXPR_LOCATION (next_batch_bind, location);
TREE_SIDE_EFFECTS (next_batch_bind) = 1;
append_to_statement_list (next_batch_bind, &BIND_EXPR_BODY (first_else));
/* __objc_foreach_index = 0; */
t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_index_decl,
build_int_cst (long_unsigned_type_node, 0));
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind));
/* next_object: */
next_object_label_decl = create_artificial_label (location);
t = build1 (LABEL_EXPR, void_type_node, next_object_label_decl);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind));
/* if (__objc_foreach_mutation_pointer != *__objc_foreach_enum_state.mutationsPtr) objc_enumeration_mutation (<collection expression>); */
t = build3 (COND_EXPR, void_type_node,
/* Condition. */
c_fully_fold
(c_common_truthvalue_conversion
(location,
build_binary_op
(location,
NE_EXPR,
objc_foreach_mutations_pointer_decl,
build_indirect_ref (location,
objc_build_component_ref (objc_foreach_enum_state_decl,
get_identifier ("mutationsPtr")),
RO_UNARY_STAR), 1)),
false, NULL),
/* Then block. */
build_function_call (input_location,
objc_enumeration_mutation_decl,
tree_cons (NULL, collection_expression, NULL)),
/* Else block. */
NULL_TREE);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind));
/* <object expression> = enumState.itemsPtr[__objc_foreach_index]; */
t = build2 (MODIFY_EXPR, void_type_node, object_expression,
build_array_ref (location, objc_build_component_ref (objc_foreach_enum_state_decl,
get_identifier ("itemsPtr")),
objc_foreach_index_decl));
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind));
/* <statements> [PS: in <statments>, 'break' jumps to break_label and 'continue' jumps to continue_label] */
append_to_statement_list (for_body, &BIND_EXPR_BODY (next_batch_bind));
/* continue_label: */
if (continue_label)
{
t = build1 (LABEL_EXPR, void_type_node, continue_label);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind));
}
/* __objc_foreach_index++; */
t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_index_decl,
build_binary_op (location,
PLUS_EXPR,
objc_foreach_index_decl,
build_int_cst (long_unsigned_type_node, 1), 1));
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind));
/* if (__objc_foreach_index < __objc_foreach_batchsize) goto next_object; */
t = build3 (COND_EXPR, void_type_node,
/* Condition. */
c_fully_fold
(c_common_truthvalue_conversion
(location,
build_binary_op (location,
LT_EXPR,
objc_foreach_index_decl,
objc_foreach_batchsize_decl, 1)),
false, NULL),
/* Then block. */
build1 (GOTO_EXPR, void_type_node, next_object_label_decl),
/* Else block. */
NULL_TREE);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind));
/* __objc_foreach_batchsize = [__objc_foreach_collection countByEnumeratingWithState: &__objc_foreach_enum_state objects: __objc_foreach_items count: 16]; */
#ifdef OBJCPLUS
t = objc_finish_message_expr (objc_foreach_collection_decl, selector_name,
/* Parameters. */
tree_cons /* &__objc_foreach_enum_state */
(NULL_TREE, build_fold_addr_expr_loc (location, objc_foreach_enum_state_decl),
tree_cons /* __objc_foreach_items */
(NULL_TREE, objc_foreach_items_decl,
tree_cons /* 16 */
(NULL_TREE, build_int_cst (NULL_TREE, 16), NULL_TREE))));
#else
/* In C, we need to decay the __objc_foreach_items array that we are passing. */
{
struct c_expr array;
array.value = objc_foreach_items_decl;
t = objc_finish_message_expr (objc_foreach_collection_decl, selector_name,
/* Parameters. */
tree_cons /* &__objc_foreach_enum_state */
(NULL_TREE, build_fold_addr_expr_loc (location, objc_foreach_enum_state_decl),
tree_cons /* __objc_foreach_items */
(NULL_TREE, default_function_array_conversion (location, array).value,
tree_cons /* 16 */
(NULL_TREE, build_int_cst (NULL_TREE, 16), NULL_TREE))));
}
#endif
t = build2 (MODIFY_EXPR, void_type_node, objc_foreach_batchsize_decl, t);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (next_batch_bind));
/* } */
/* if (__objc_foreach_batchsize != 0) goto next_batch; */
t = build3 (COND_EXPR, void_type_node,
/* Condition. */
c_fully_fold
(c_common_truthvalue_conversion
(location,
build_binary_op (location,
NE_EXPR,
objc_foreach_batchsize_decl,
build_int_cst (long_unsigned_type_node, 0), 1)),
false, NULL),
/* Then block. */
build1 (GOTO_EXPR, void_type_node, next_batch_label_decl),
/* Else block. */
NULL_TREE);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (first_else));
/* <object expression> = nil; */
t = build2 (MODIFY_EXPR, void_type_node, object_expression, convert (objc_object_type, null_pointer_node));
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (first_else));
/* break_label: */
if (break_label)
{
t = build1 (LABEL_EXPR, void_type_node, break_label);
SET_EXPR_LOCATION (t, location);
append_to_statement_list (t, &BIND_EXPR_BODY (first_else));
}
/* } */
COND_EXPR_ELSE (first_if) = first_else;
/* Do the whole thing. */
add_stmt (bind);
#ifdef DEBUG_OBJC_FINISH_FOREACH_LOOP
/* This will print to stderr the whole blurb generated by the
compiler while compiling (assuming the compiler doesn't crash
before getting here).
*/
debug_generic_stmt (bind);
#endif
/* } */
/* Done by c-parser.c */
}
#include "gt-objc-objc-act.h" #include "gt-objc-objc-act.h"

View File

@ -269,6 +269,9 @@ enum objc_tree_index
OCTI_ASSIGN_GLOBAL_DECL, OCTI_ASSIGN_GLOBAL_DECL,
OCTI_ASSIGN_STRONGCAST_DECL, OCTI_ASSIGN_STRONGCAST_DECL,
OCTI_FAST_ENUM_STATE_TEMP,
OCTI_ENUM_MUTATION_DECL,
OCTI_MAX OCTI_MAX
}; };
@ -433,5 +436,9 @@ extern GTY(()) tree objc_global_trees[OCTI_MAX];
#define string_class_decl objc_global_trees[OCTI_STRING_CLASS_DECL] #define string_class_decl objc_global_trees[OCTI_STRING_CLASS_DECL]
#define internal_const_str_type objc_global_trees[OCTI_INTERNAL_CNST_STR_TYPE] #define internal_const_str_type objc_global_trees[OCTI_INTERNAL_CNST_STR_TYPE]
#define UOBJC_SUPER_decl objc_global_trees[OCTI_SUPER_DECL] #define UOBJC_SUPER_decl objc_global_trees[OCTI_SUPER_DECL]
#define objc_fast_enumeration_state_template \
objc_global_trees[OCTI_FAST_ENUM_STATE_TEMP]
#define objc_enumeration_mutation_decl \
objc_global_trees[OCTI_ENUM_MUTATION_DECL]
#endif /* GCC_OBJC_ACT_H */ #endif /* GCC_OBJC_ACT_H */

View File

@ -1,8 +1,45 @@
2010-10-05 Nicola Pero <nicola.pero@meta-innovation.com>
Implemented fast enumeration for Objective-C.
* objc.dg/foreach-1.m: New.
* objc.dg/foreach-2.m: New.
* objc.dg/foreach-3.m: New.
* objc.dg/foreach-4.m: New.
* objc.dg/foreach-5.m: New.
* objc.dg/foreach-6.m: New.
* objc.dg/foreach-7.m: New.
Merge from 'apple/trunk' branch on FSF servers:
2006-04-13 Fariborz Jahanian <fjahanian@apple.com>
Radar 4502236
* objc.dg/objc-foreach-5.m: New.
2006-04-12 Fariborz Jahanian <fjahanian@apple.com>
Radar 4507230
* objc.dg/objc-foreach-4.m: New.
2006-03-13 Fariborz Jahanian <fjahanian@apple.com>
Radar 4472881
* objc.dg/objc-foreach-3.m: New.
2005-03-07 Fariborz Jahanian <fjahanian@apple.com>
Radar 4468498
* objc.dg/objc-foreach-2.m: New.
2006-02-15 Fariborz Jahanian <fjahanian@apple.com>
Radar 4294910
* objc.dg/objc-foreach-1.m: New
2010-10-06 Hariharan Sandanagobalane <hariharan@picochip.com> 2010-10-06 Hariharan Sandanagobalane <hariharan@picochip.com>
* gcc.c-torture/execute/cmpsi-2.c : Unsigned comparisons should use * gcc.c-torture/execute/cmpsi-2.c : Unsigned comparisons should use
unsigned values. unsigned values.
2010-10-05 Nicola Pero <nicola.pero@meta-innovation.com> 2010-10-05 Nicola Pero <nicola.pero@meta-innovation.com>
PR objc++/28050 PR objc++/28050

View File

@ -0,0 +1,81 @@
/* Test basic Objective-C foreach syntax. This tests iterations that
do nothing.
*/
/* FIXME: Run this test with the NeXT runtime as well. */
/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */
/* { dg-do run } */
#include <objc/objc.h>
#include <objc/Object.h>
extern void abort (void);
/*
struct __objcFastEnumerationState
{
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
*/
@interface Object (NSFastEnumeration)
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state
objects:(id *)stackbuf
count:(unsigned int)len;
@end
int main (void)
{
int test_variable = 0;
int counter = 0;
id array = nil;
id object = nil;
/* Test that 'for (object in array)' is recognized and that nothing
happens if array is nil. */
for (object in array)
test_variable = 8;
if (test_variable == 8)
abort ();
if (object != nil)
abort ();
/* Test that if nothing is done, object is set to nil. */
object = [Object new];
for (object in array)
;
if (object != nil)
abort ();
/* Test that you can reference 'object' inside the body. */
for (object in array)
object = nil;
if (object != nil)
abort ();
/* Test that 'for (id element in array) is recognized (and works). */
for (id element in array)
test_variable = 8;
if (test_variable == 8)
abort ();
/* Test that you can reference 'object' inside the body. */
for (id element in array)
element = nil;
/* Test that C for loops still work. */
test_variable = 0;
for (counter = 0; counter < 4; counter++)
test_variable++;
if (test_variable != 4)
abort ();
return 0;
}

View File

@ -0,0 +1,279 @@
/* Test basic Objective-C foreach syntax. This tests iterations, with
the basic syntax 'for (object in array) statements'
*/
/* FIXME: Run this test with the NeXT runtime as well. */
/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */
/* { dg-do run } */
#include <objc/objc.h>
#include <objc/Object.h>
#include <objc/NXConstStr.h>
#include <stdlib.h>
extern void abort (void);
/*
struct __objcFastEnumerationState
{
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
*/
/* A mini-array implementation that can be used to test fast
enumeration. You create the array with some objects; you can
mutate the array, and you can fast-enumerate it.
*/
@interface MyArray : Object
{
unsigned int length;
id *objects;
unsigned long mutated;
}
- (id) initWithLength: (unsigned int)l objects: (id *)o;
- (void) mutate;
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state
objects:(id *)stackbuf
count:(unsigned long)len;
@end
@implementation MyArray : Object
- (id) initWithLength: (unsigned int)l
objects: (id *)o
{
length = l;
objects = o;
mutated = 0;
}
- (void) mutate
{
mutated = 1;
}
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState*)state
objects: (id*)stackbuf
count: (unsigned long)len
{
unsigned long i, batch_size;
/* We keep how many objects we served in the state->state counter. So the next batch
will contain up to length - state->state objects. */
batch_size = length - state->state;
/* Make obvious adjustments. */
if (batch_size < 0)
batch_size = 0;
if (batch_size > len)
batch_size = len;
/* Copy the objects. */
for (i = 0; i < batch_size; i++)
stackbuf[i] = objects[i];
state->state += batch_size;
state->itemsPtr = stackbuf;
state->mutationsPtr = &mutated;
return batch_size;
}
@end
int main (void)
{
MyArray *array;
Object *object;
int test_variable, counter, i;
id *objects;
array = [[MyArray alloc] initWithLength: 0
objects: NULL];
/* Test that an empty array does nothing. */
for (object in array)
abort ();
if (object != nil)
abort ();
/* Test iterating over 1 object. */
objects = malloc (sizeof (id) * 1);
objects[0] = @"One Object";
array = [[MyArray alloc] initWithLength: 1
objects: objects];
for (object in array)
printf ("%p\n", object);
/* Test iterating over 20 objects. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
for (object in array)
printf ("%p\n", object);
/* Test iterating over 200 objects. */
objects = malloc (sizeof (id) * 200);
for (i = 0; i < 200; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 200
objects: objects];
counter = 0;
for (object in array)
{
if (object != nil)
counter++;
}
if (counter != 200)
abort ();
printf ("Counter was %d (should be 200)\n", counter);
/* Test iterating again over the same array. */
counter = 0;
for (object in array)
{
if (object != nil)
counter++;
}
if (counter != 200)
abort ();
printf ("Counter was %d (should be 200)\n", counter);
/* Test nested iterations. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (object in array)
{
id another_object;
for (another_object in array)
if (another_object != nil)
counter++;
}
printf ("Counter was %d (should be 400)\n", counter);
if (counter != 400)
abort ();
/* Test 'continue'. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (object in array)
{
if (counter == 15)
continue;
counter++;
}
printf ("Counter was %d (should be 15)\n", counter);
if (counter != 15)
abort ();
/* Test 'break'. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (object in array)
{
counter++;
if (counter == 15)
break;
}
printf ("Counter was %d (should be 15)\n", counter);
if (counter != 15)
abort ();
/* Test 'break' and 'continue' in nested iterations. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (object in array)
{
int local_counter = 0;
id another_object;
/* Each internal loop should increase counter by 24. */
for (another_object in array)
{
local_counter++;
if (local_counter == 10)
{
counter = counter + 20;
break;
}
if (local_counter >= 5)
continue;
counter++;
}
/* Exit after 4 iterations. */
if (counter == 96)
break;
}
printf ("Counter was %d (should be 96)\n", counter);
if (counter != 96)
abort ();
/* Test that if we 'break', the object is set to the last one, while
if we run out of objects, it is set to 'nil'. */
for (object in array)
;
if (object != nil)
abort ();
for (object in array)
break;
if (object == nil)
abort ();
/* Test that C for loops still work. */
test_variable = 0;
for (counter = 0; counter < 4; counter++)
test_variable++;
if (test_variable != 4)
abort ();
return 0;
}

View File

@ -0,0 +1,114 @@
/* Test basic Objective-C foreach syntax. This tests the mutation.
*/
/* FIXME: Run this test with the NeXT runtime as well. */
/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */
/* FIXME: This test should be run, and it succeeds if the program
aborts at the right time (when the mutation happens). It currently
works, but how do we tell the testsuite to test for it ?
*/
/* { dg-do compile } */
#include <objc/objc.h>
#include <objc/Object.h>
#include <objc/NXConstStr.h>
#include <stdlib.h>
extern void abort (void);
/*
struct __objcFastEnumerationState
{
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
*/
/* A mini-array implementation that can be used to test fast
enumeration. You create the array with some objects; you can
mutate the array, and you can fast-enumerate it.
*/
@interface MyArray : Object
{
unsigned int length;
id *objects;
unsigned long mutated;
}
- (id) initWithLength: (unsigned int)l objects: (id *)o;
- (void) mutate;
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state
objects:(id *)stackbuf
count:(unsigned long)len;
@end
@implementation MyArray : Object
- (id) initWithLength: (unsigned int)l
objects: (id *)o
{
length = l;
objects = o;
mutated = 0;
}
- (void) mutate
{
mutated = 1;
}
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState*)state
objects: (id*)stackbuf
count: (unsigned long)len
{
unsigned long i, batch_size;
/* Change the mutationsPtr if 'mutate' is called. */
state->mutationsPtr = &mutated;
/* We keep how many objects we served in the state->state counter. So the next batch
will contain up to length - state->state objects. */
batch_size = length - state->state;
/* Make obvious adjustments. */
if (batch_size < 0)
batch_size = 0;
if (batch_size > len)
batch_size = len;
/* Copy the objects. */
for (i = 0; i < batch_size; i++)
stackbuf[i] = objects[i];
state->state += batch_size;
state->itemsPtr = stackbuf;
return batch_size;
}
@end
int main (void)
{
MyArray *array;
Object *object;
int counter, i;
id *objects;
/* Test iterating over 20 objects, mutating after 15. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (object in array)
{
counter++;
printf ("%d\n", counter);
if (counter == 14)
{
printf ("Mutating (should abort at next iteration)\n");
[array mutate];
}
}
return 0;
}

View File

@ -0,0 +1,259 @@
/* Test basic Objective-C foreach syntax. This tests iterations, with
the declaration syntax 'for (id object in array) statements'
*/
/* FIXME: Run this test with the NeXT runtime as well. */
/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */
/* { dg-do run } */
#include <objc/objc.h>
#include <objc/Object.h>
#include <objc/NXConstStr.h>
#include <stdlib.h>
extern void abort (void);
/*
struct __objcFastEnumerationState
{
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
*/
/* A mini-array implementation that can be used to test fast
enumeration. You create the array with some objects; you can
mutate the array, and you can fast-enumerate it.
*/
@interface MyArray : Object
{
unsigned int length;
id *objects;
unsigned long mutated;
}
- (id) initWithLength: (unsigned int)l objects: (id *)o;
- (void) mutate;
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state
objects:(id *)stackbuf
count:(unsigned long)len;
@end
@implementation MyArray : Object
- (id) initWithLength: (unsigned int)l
objects: (id *)o
{
length = l;
objects = o;
mutated = 0;
}
- (void) mutate
{
mutated = 1;
}
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState*)state
objects: (id*)stackbuf
count: (unsigned long)len
{
unsigned long i, batch_size;
/* We keep how many objects we served in the state->state counter. So the next batch
will contain up to length - state->state objects. */
batch_size = length - state->state;
/* Make obvious adjustments. */
if (batch_size < 0)
batch_size = 0;
if (batch_size > len)
batch_size = len;
/* Copy the objects. */
for (i = 0; i < batch_size; i++)
stackbuf[i] = objects[i];
state->state += batch_size;
state->itemsPtr = stackbuf;
state->mutationsPtr = &mutated;
return batch_size;
}
@end
int main (void)
{
MyArray *array;
int test_variable, counter, i;
id *objects;
array = [[MyArray alloc] initWithLength: 0
objects: NULL];
/* Test that an empty array does nothing. */
for (id object in array)
abort ();
/* Test iterating over 1 object. */
objects = malloc (sizeof (id) * 1);
objects[0] = @"One Object";
array = [[MyArray alloc] initWithLength: 1
objects: objects];
for (id object in array)
printf ("%p\n", object);
/* Test iterating over 20 objects. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
for (id object in array)
printf ("%p\n", object);
/* Test iterating over 200 objects. */
objects = malloc (sizeof (id) * 200);
for (i = 0; i < 200; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 200
objects: objects];
counter = 0;
for (id object in array)
{
if (object != nil)
counter++;
}
if (counter != 200)
abort ();
printf ("Counter was %d (should be 200)\n", counter);
/* Test iterating again over the same array. */
counter = 0;
for (id object in array)
{
if (object != nil)
counter++;
}
if (counter != 200)
abort ();
printf ("Counter was %d (should be 200)\n", counter);
/* Test nested iterations. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (id object in array)
{
for (id another_object in array)
if (another_object != nil)
counter++;
}
printf ("Counter was %d (should be 400)\n", counter);
if (counter != 400)
abort ();
/* Test 'continue'. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (id object in array)
{
if (counter == 15)
continue;
counter++;
}
printf ("Counter was %d (should be 15)\n", counter);
if (counter != 15)
abort ();
/* Test 'break'. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (id object in array)
{
counter++;
if (counter == 15)
break;
}
printf ("Counter was %d (should be 15)\n", counter);
if (counter != 15)
abort ();
/* Test 'break' and 'continue' in nested iterations. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (id object in array)
{
int local_counter = 0;
/* Each internal loop should increase counter by 24. */
for (id another_object in array)
{
local_counter++;
if (local_counter == 10)
{
counter = counter + 20;
break;
}
if (local_counter >= 5)
continue;
counter++;
}
/* Exit after 4 iterations. */
if (counter == 96)
break;
}
printf ("Counter was %d (should be 96)\n", counter);
if (counter != 96)
abort ();
/* Test that C for loops still work. */
test_variable = 0;
for (counter = 0; counter < 4; counter++)
test_variable++;
if (test_variable != 4)
abort ();
return 0;
}

View File

@ -0,0 +1,258 @@
/* Test basic Objective-C foreach syntax. This tests that if you
define your own NSFastEnumeration struct, the compiler picks it up.
*/
/* FIXME: Run this test with the NeXT runtime as well. */
/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */
/* { dg-do run } */
#include <objc/objc.h>
#include <objc/Object.h>
#include <objc/NXConstStr.h>
#include <stdlib.h>
extern void abort (void);
typedef struct
{
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
} NSFastEnumerationState;
/* A mini-array implementation that can be used to test fast
enumeration. You create the array with some objects; you can
mutate the array, and you can fast-enumerate it.
*/
@interface MyArray : Object
{
unsigned int length;
id *objects;
unsigned long mutated;
}
- (id) initWithLength: (unsigned int)l objects: (id *)o;
- (void) mutate;
- (unsigned long)countByEnumeratingWithState: (NSFastEnumerationState *)state
objects:(id *)stackbuf
count:(unsigned long)len;
@end
@implementation MyArray : Object
- (id) initWithLength: (unsigned int)l
objects: (id *)o
{
length = l;
objects = o;
mutated = 0;
}
- (void) mutate
{
mutated = 1;
}
- (unsigned long)countByEnumeratingWithState: (NSFastEnumerationState*)state
objects: (id*)stackbuf
count: (unsigned long)len
{
unsigned long i, batch_size;
/* We keep how many objects we served in the state->state counter. So the next batch
will contain up to length - state->state objects. */
batch_size = length - state->state;
/* Make obvious adjustments. */
if (batch_size < 0)
batch_size = 0;
if (batch_size > len)
batch_size = len;
/* Copy the objects. */
for (i = 0; i < batch_size; i++)
stackbuf[i] = objects[i];
state->state += batch_size;
state->itemsPtr = stackbuf;
state->mutationsPtr = &mutated;
return batch_size;
}
@end
int main (void)
{
MyArray *array;
int test_variable, counter, i;
id *objects;
array = [[MyArray alloc] initWithLength: 0
objects: NULL];
/* Test that an empty array does nothing. */
for (id object in array)
abort ();
/* Test iterating over 1 object. */
objects = malloc (sizeof (id) * 1);
objects[0] = @"One Object";
array = [[MyArray alloc] initWithLength: 1
objects: objects];
for (id object in array)
printf ("%p\n", object);
/* Test iterating over 20 objects. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
for (id object in array)
printf ("%p\n", object);
/* Test iterating over 200 objects. */
objects = malloc (sizeof (id) * 200);
for (i = 0; i < 200; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 200
objects: objects];
counter = 0;
for (id object in array)
{
if (object != nil)
counter++;
}
if (counter != 200)
abort ();
printf ("Counter was %d (should be 200)\n", counter);
/* Test iterating again over the same array. */
counter = 0;
for (id object in array)
{
if (object != nil)
counter++;
}
if (counter != 200)
abort ();
printf ("Counter was %d (should be 200)\n", counter);
/* Test nested iterations. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (id object in array)
{
for (id another_object in array)
if (another_object != nil)
counter++;
}
printf ("Counter was %d (should be 400)\n", counter);
if (counter != 400)
abort ();
/* Test 'continue'. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (id object in array)
{
if (counter == 15)
continue;
counter++;
}
printf ("Counter was %d (should be 15)\n", counter);
if (counter != 15)
abort ();
/* Test 'break'. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (id object in array)
{
counter++;
if (counter == 15)
break;
}
printf ("Counter was %d (should be 15)\n", counter);
if (counter != 15)
abort ();
/* Test 'break' and 'continue' in nested iterations. */
objects = malloc (sizeof (id) * 20);
for (i = 0; i < 20; i++)
objects[i] = @"object";
array = [[MyArray alloc] initWithLength: 20
objects: objects];
counter = 0;
for (id object in array)
{
int local_counter = 0;
/* Each internal loop should increase counter by 24. */
for (id another_object in array)
{
local_counter++;
if (local_counter == 10)
{
counter = counter + 20;
break;
}
if (local_counter >= 5)
continue;
counter++;
}
/* Exit after 4 iterations. */
if (counter == 96)
break;
}
printf ("Counter was %d (should be 96)\n", counter);
if (counter != 96)
abort ();
/* Test that C for loops still work. */
test_variable = 0;
for (counter = 0; counter < 4; counter++)
test_variable++;
if (test_variable != 4)
abort ();
return 0;
}

View File

@ -0,0 +1,52 @@
/* Test basic Objective-C foreach syntax. This tests warnings and errors. */
/* FIXME: Run this test with the NeXT runtime as well. */
/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */
/* { dg-do compile } */
#include <objc/objc.h>
#include <objc/Object.h>
extern void abort (void);
/*
struct __objcFastEnumerationState
{
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
*/
@interface Object (NSFastEnumeration)
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state
objects:(id *)stackbuf
count:(unsigned int)len;
- (id) enumerator;
@end
int main (void)
{
id array = nil;
id object = nil;
for (object in array) /* Ok */
;
for (object in nil) /* Ok */
;
for (object in) /* { dg-error "missing collection in fast enumeration" } */
;
for (object = nil in array) /* { dg-error "invalid iterating variable in fast enumeration" } */
;
for (object in [object enumerator]) /* Ok */
;
for (12 in array) /* { dg-error "invalid iterating variable in fast enumeration" } */
; /* { dg-error "iterating variable in fast enumeration is not an object" } */
for (object in 12)
; /* { dg-error "collection in fast enumeration is not an object" } */
return 0;
}

View File

@ -0,0 +1,60 @@
/* Test basic Objective-C foreach syntax. This tests warnings and errors. */
/* FIXME: Run this test with the NeXT runtime as well. */
/* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */
/* { dg-do compile } */
#include <objc/objc.h>
#include <objc/Object.h>
extern void abort (void);
/*
struct __objcFastEnumerationState
{
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
*/
@interface Object (NSFastEnumeration)
- (unsigned long)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state
objects:(id *)stackbuf
count:(unsigned int)len;
- (id) enumerator;
@end
void function (void)
{
return;
}
id object_function (void)
{
return nil;
}
int main (void)
{
id array = nil;
id object = nil;
for (typedef int my_typedef in array) /* { dg-error "declaration of non-variable" } */
; /* { dg-error "iterating variable in fast enumeration is not an object" } */
for (function () in nil) /* { dg-error "invalid iterating variable in fast enumeration" } */
; /* { dg-error "iterating variable in fast enumeration is not an object" } */
for (object_function () in nil) /* { dg-error "invalid iterating variable in fast enumeration" } */
;
for ([object enumerator] in array) /* { dg-error "invalid iterating variable in fast enumeration" } */
;
for (object = nil in array) /* { dg-error "invalid iterating variable in fast enumeration" } */
;
for (id key, value in array) /* { dg-error "multiple iterating variables in fast enumeration" } */
;
return 0;
}

View File

@ -0,0 +1,41 @@
/* Syntax check for the new foreach statement. */
/* { dg-do compile } */
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
@interface MyList
@end
@implementation MyList
- (unsigned int)countByEnumeratingWithState:(struct __objcFastEnumerationState *)state objects:(id *)items count:(unsigned int)stackcount
{
return 0;
}
- (void)addObject:object {
}
@end
@interface MyList (BasicTest)
- (void)compilerTestAgainst;
@end
void BEGIN();
void INFORLOOP();
void END();
@implementation MyList (BasicTest)
- (void)compilerTestAgainst {
BEGIN();
for (id elem in (self))
if (elem)
INFORLOOP();
END();
}
@end

View File

@ -0,0 +1,41 @@
/* Syntax check for the new foreach statement. */
/* { dg-do compile } */
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
@interface MyList
@end
@implementation MyList
- (unsigned int)countByEnumeratingWithState:(struct __objcFastEnumerationState *)state objects:(id *)items count:(unsigned int)stackcount
{
return 0;
}
- (void)addObject:object {
}
@end
@interface MyList (BasicTest)
- (void)compilerTestAgainst;
@end
void BEGIN();
void INFORLOOP();
void END();
@implementation MyList (BasicTest)
- (void)compilerTestAgainst {
id elem;
BEGIN();
for (elem in (self))
if (elem)
INFORLOOP();
END();
}
@end

View File

@ -0,0 +1,42 @@
/* Syntax check for the new foreach statement.
Use of declaration in loop-header without requiring c99 mode. */
/* { dg-do compile } */
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
@interface MyList
@end
@implementation MyList
- (unsigned int)countByEnumeratingWithState:(struct __objcFastEnumerationState *)state objects:(id *)items count:(unsigned int)stackcount
{
return 0;
}
- (void)addObject:object {
}
@end
@interface MyList (BasicTest)
- (void)compilerTestAgainst;
@end
void BEGIN();
void INFORLOOP();
void END();
@implementation MyList (BasicTest)
- (void)compilerTestAgainst {
BEGIN();
for (id elem in (self))
if (elem)
INFORLOOP();
END();
}
@end

View File

@ -0,0 +1,17 @@
/* Test for valid objc objects used in a for-each statement. */
/* FIXME: Run this test with the GNU runtime as well. */
/* { dg-skip-if "" { *-*-* } { "-fgnu-runtime" } { "" } } */
/* { dg-do compile { target *-*-darwin* } } */
#include <Foundation/Foundation.h>
// gcc -o foo foo.m -framework Foundation
int main (int argc, char const* argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray * arr = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];
for (NSString * foo in arr) {
NSLog(@"foo is %@", foo);
}
[pool release];
return 0;
}

View File

@ -0,0 +1,37 @@
/* FIXME: Run this test with the GNU runtime as well. */
/* { dg-skip-if "" { *-*-* } { "-fgnu-runtime" } { "" } } */
/* { dg-do compile { target *-*-darwin* } } */
#import <Foundation/Foundation.h>
NSArray * createTestVictim(unsigned capacity) {
NSMutableArray * arr = [[NSMutableArray alloc] initWithCapacity:capacity];
int x = 0;
for(x = 0; x < capacity; x++) {
NSNumber * num = [NSNumber numberWithInteger:x];
[arr addObject:num];
}
NSArray * immutableCopy = [arr copy];
[arr release];
return immutableCopy;
}
void addStuffUp(NSArray * values) {
NSInteger accumulator = 0;
// for (id item in values) {
id item;
for (item in values) {
accumulator += [item integerValue];
}
}
int main (int argc, char const* argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray * target = createTestVictim(10);
addStuffUp(target);
[pool release];
return 0;
}
/* { dg-final { scan-assembler "_addStuffUp:" } } */

View File

@ -1,3 +1,11 @@
2010-10-06 Nicola Pero <nicola.pero@meta-innovation.com>
Implemented fast enumeration for Objective-C.
* Makefile.in (C_SOURCE_FILES): Added objc-foreach.c.
(OBJC_H): Added runtime.h
* objc-foreach.c: New file.
* objc/runtime.h: New file.
2010-09-30 Kai Tietz <kai.tietz@onevision.com> 2010-09-30 Kai Tietz <kai.tietz@onevision.com>
* objc/deprecated/struct_objc_class.h: Add padding * objc/deprecated/struct_objc_class.h: Add padding

View File

@ -120,6 +120,7 @@ OBJC_H = \
message.h \ message.h \
objc-api.h \ objc-api.h \
objc-decls.h \ objc-decls.h \
runtime.h \
thr.h \ thr.h \
\ \
hash.h \ hash.h \
@ -163,6 +164,7 @@ C_SOURCE_FILES = \
init.c \ init.c \
memory.c \ memory.c \
nil_method.c \ nil_method.c \
objc-foreach.c \
objc-sync.c \ objc-sync.c \
objects.c \ objects.c \
sarray.c \ sarray.c \

52
libobjc/objc-foreach.c Normal file
View File

@ -0,0 +1,52 @@
/* GNU Objective C Runtime 'fast enumeration' implementation
Copyright (C) 2010 Free Software Foundation, Inc.
Contributed by Nicola Pero <nicola.pero@meta-innovation.com>
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GCC 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 for more
details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
/*
This file implements objc_enumeration_mutation() and
objc_set_enumeration_mutation_handler(), the two functions required
to handle mutations during a fast enumeration.
*/
#include "objc/runtime.h" /* For objc_enumerationMutation() and objc_set_enumeration_mutation_handler() */
#include "objc-private/error.h" /* For _objc_abort() */
/* The enumeration mutation handler currently in use. */
static void (*__objc_enumeration_mutation_handler)(id) = NULL;
void
objc_set_enumeration_mutation_handler (void (*handler)(id))
{
__objc_enumeration_mutation_handler = handler;
}
void
objc_enumerationMutation (id collection)
{
if (__objc_enumeration_mutation_handler != NULL)
(*__objc_enumeration_mutation_handler) (collection);
/* We always abort if we get here; there is no point in going on as
the next iteration in the fast enumeration would probably go
deeply wrong. */
_objc_abort ("Collection %p mutated during fast enumeration", collection);
}

88
libobjc/objc/runtime.h Normal file
View File

@ -0,0 +1,88 @@
/* GNU Objective-C Runtime API.
Copyright (C) 2010 Free Software Foundation, Inc.
Contributed by Nicola Pero <nicola.pero@meta-innovation.com>
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
GCC 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 for more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
#ifndef __objc_runtime_INCLUDE_GNU
#define __objc_runtime_INCLUDE_GNU
#include "objc.h"
/* The following is temporary, until all code from objc-api.h has been
moved into this file and objc-api.h will include runtime.h. */
#include "objc-api.h"
/* 'objc_enumerationMutation()' is called when a collection is
mutated while being "fast enumerated". That is a hard error, and
objc_enumerationMutation is called to deal with it. 'collection'
is the collection object that was mutated during an enumeration.
objc_enumerationMutation() will invoke the mutation handler if any
is set. Then, it will abort the program.
Compatibility note: the Apple runtime will not abort the program
after calling the mutation handler.
*/
objc_EXPORT void objc_enumerationMutation (id collection);
/* 'objc_set_enumeration_mutation_handler' can be used to set a
function that will be called (instead of aborting) when a fast
enumeration is mutated during enumeration. The handler will be
called with the 'collection' being mutated as the only argument and
it should not return; it should either exit the program, or could
throw an exception. The recommended implementation is to throw an
exception - the user can then use exception handlers to deal with
it.
This function is not thread safe (other threads may be trying to
invoke the enumeration mutation handler while you are changing it!)
and should be called during during the program initialization
before threads are started. It is mostly reserved for "Foundation"
libraries; in the case of GNUstep, GNUstep Base may be using this
function to improve the standard enumeration mutation handling.
You probably shouldn't use this function unless you are writing
your own Foundation library.
*/
objc_EXPORT void objc_set_enumeration_mutation_handler (void (*handler)(id));
/* This structure (used during fast enumeration) is automatically
defined by the compiler (it is as if this definition was always
included in all Objective-C files). Note that it is usually
defined again with the name of NSFastEnumeration by "Foundation"
libraries such as GNUstep Base. And if NSFastEnumeration is
defined, the compiler will use it instead of
__objcFastEnumerationState when doing fast enumeration.
*/
/*
struct __objcFastEnumerationState
{
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
*/
/* For compatibility with the Apple/NeXT runtime. */
#define objc_setEnumerationMutationHandler objc_set_enumeration_mutation_handler
#endif