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:
parent
b938bc48d8
commit
f05b9d93e9
@ -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>
|
||||
|
||||
* config/mn10300/mn10300.h (FIRST_PSEUDO_REGISTER): Increment by
|
||||
|
13
gcc/c-decl.c
13
gcc/c-decl.c
@ -8310,16 +8310,23 @@ finish_function (void)
|
||||
|
||||
/* Check the declarations given in a for-loop for satisfying the C99
|
||||
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
|
||||
check_for_loop_decls (location_t loc)
|
||||
check_for_loop_decls (location_t loc, bool turn_off_iso_c99_error)
|
||||
{
|
||||
struct c_binding *b;
|
||||
tree one_decl = NULL_TREE;
|
||||
int n_decls = 0;
|
||||
|
||||
if (!flag_isoc99)
|
||||
if (!turn_off_iso_c99_error)
|
||||
{
|
||||
static bool hint = true;
|
||||
/* If we get here, declarations have been used in a for loop without
|
||||
|
@ -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>
|
||||
|
||||
* c-common.h (struct diagnostic_context): Don't declare here.
|
||||
|
@ -1007,6 +1007,7 @@ extern int objc_static_init_needed_p (void);
|
||||
extern tree objc_generate_static_init_call (tree);
|
||||
extern tree objc_generate_write_barrier (tree, enum tree_code, tree);
|
||||
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
|
||||
ObjC/ObjC++. */
|
||||
|
@ -355,3 +355,11 @@ objc_generate_write_barrier (tree ARG_UNUSED (lhs),
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
229
gcc/c-parser.c
229
gcc/c-parser.c
@ -185,6 +185,11 @@ typedef struct GTY(()) c_parser {
|
||||
/* True if we are in a context where the Objective-C "PQ" keywords
|
||||
are considered keywords. */
|
||||
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
|
||||
analysis. In some cases (e.g., 'int NSObject;'), it is
|
||||
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;
|
||||
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
|
||||
not keywords, and we fall through to the code for
|
||||
normal tokens.
|
||||
@ -947,7 +972,7 @@ typedef enum c_dtr_syn {
|
||||
static void c_parser_external_declaration (c_parser *);
|
||||
static void c_parser_asm_definition (c_parser *);
|
||||
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 (c_parser *);
|
||||
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
|
||||
only tell which after parsing the declaration specifiers, if
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1189,6 +1214,8 @@ c_parser_external_declaration (c_parser *parser)
|
||||
(old-style parameter declarations) they are diagnosed. If
|
||||
START_ATTR_OK is true, the declaration specifiers may start with
|
||||
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-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
|
||||
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:
|
||||
|
||||
declaration:
|
||||
@ -1243,7 +1274,8 @@ c_parser_external_declaration (c_parser *parser)
|
||||
static void
|
||||
c_parser_declaration_or_fndef (c_parser *parser, bool fndef_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;
|
||||
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_SEMICOLON)
|
||||
|| 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 postfix_attrs = NULL_TREE;
|
||||
@ -1421,7 +1454,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
|
||||
all_prefix_attrs));
|
||||
if (d)
|
||||
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))
|
||||
{
|
||||
@ -1438,6 +1479,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
|
||||
c_parser_consume_token (parser);
|
||||
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
|
||||
{
|
||||
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)
|
||||
&& c_parser_next_token_is_not (parser, CPP_OPEN_BRACE))
|
||||
c_parser_declaration_or_fndef (parser, false, false, false,
|
||||
true, false);
|
||||
true, false, NULL);
|
||||
store_parm_decls ();
|
||||
DECL_STRUCT_FUNCTION (current_function_decl)->function_start_locus
|
||||
= c_parser_peek_token (parser)->location;
|
||||
@ -3743,7 +3793,7 @@ c_parser_compound_statement_nostart (c_parser *parser)
|
||||
{
|
||||
last_label = 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)
|
||||
pedwarn_c90 (loc,
|
||||
(pedantic && !flag_isoc99)
|
||||
@ -3771,7 +3821,7 @@ c_parser_compound_statement_nostart (c_parser *parser)
|
||||
last_label = false;
|
||||
mark_valid_location_for_stdc_pragma (false);
|
||||
c_parser_declaration_or_fndef (parser, true, true, true, true,
|
||||
true);
|
||||
true, NULL);
|
||||
/* Following the old parser, __extension__ does not
|
||||
disable this diagnostic. */
|
||||
restore_extension_diagnostics (ext);
|
||||
@ -3911,7 +3961,7 @@ c_parser_label (c_parser *parser)
|
||||
c_parser_declaration_or_fndef (parser, /*fndef_ok*/ false,
|
||||
/*static_assert_ok*/ true,
|
||||
/*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
|
||||
trailing ';', whereas the "declaration" production includes one.
|
||||
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
|
||||
c_parser_for_statement (c_parser *parser)
|
||||
{
|
||||
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 for_loc = c_parser_peek_token (parser)->location;
|
||||
bool is_foreach_statement = false;
|
||||
gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR));
|
||||
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 %<(%>"))
|
||||
{
|
||||
/* Parse the initialization declaration or expression. */
|
||||
cond = error_mark_node;
|
||||
object_expression = error_mark_node;
|
||||
|
||||
if (c_parser_next_token_is (parser, CPP_SEMICOLON))
|
||||
{
|
||||
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))
|
||||
{
|
||||
c_parser_declaration_or_fndef (parser, true, true, true, true, true);
|
||||
check_for_loop_decls (for_loc);
|
||||
parser->objc_could_be_foreach_context = true;
|
||||
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))
|
||||
{
|
||||
@ -4466,10 +4576,21 @@ c_parser_for_statement (c_parser *parser)
|
||||
int ext;
|
||||
ext = disable_extension_diagnostics ();
|
||||
c_parser_consume_token (parser);
|
||||
parser->objc_could_be_foreach_context = 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);
|
||||
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
|
||||
goto init_expr;
|
||||
@ -4477,25 +4598,62 @@ c_parser_for_statement (c_parser *parser)
|
||||
else
|
||||
{
|
||||
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. */
|
||||
if (c_parser_next_token_is (parser, CPP_SEMICOLON))
|
||||
/* Parse the loop condition. In the case of a foreach
|
||||
statement, there is no loop condition. */
|
||||
if (!is_foreach_statement)
|
||||
{
|
||||
c_parser_consume_token (parser);
|
||||
cond = NULL_TREE;
|
||||
if (c_parser_next_token_is (parser, CPP_SEMICOLON))
|
||||
{
|
||||
c_parser_consume_token (parser);
|
||||
cond = NULL_TREE;
|
||||
}
|
||||
else
|
||||
{
|
||||
cond = c_parser_condition (parser);
|
||||
c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cond = c_parser_condition (parser);
|
||||
c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
|
||||
}
|
||||
/* Parse the increment expression. */
|
||||
/* Parse the increment expression (the third expression in a
|
||||
for-statement). In the case of a foreach-statement, this is
|
||||
the expression that follows the 'in'. */
|
||||
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
|
||||
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 %<)%>");
|
||||
}
|
||||
else
|
||||
@ -4508,8 +4666,11 @@ c_parser_for_statement (c_parser *parser)
|
||||
save_cont = c_cont_label;
|
||||
c_cont_label = NULL_TREE;
|
||||
body = c_parser_c99_block_statement (parser);
|
||||
c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true);
|
||||
add_stmt (c_end_compound_stmt (loc, block, flag_isoc99));
|
||||
if (is_foreach_statement)
|
||||
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_cont_label = save_cont;
|
||||
}
|
||||
@ -6790,7 +6951,7 @@ c_parser_objc_methodprotolist (c_parser *parser)
|
||||
}
|
||||
else
|
||||
c_parser_declaration_or_fndef (parser, false, false, true,
|
||||
false, true);
|
||||
false, true, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -8439,8 +8600,8 @@ c_parser_omp_for_loop (location_t loc,
|
||||
{
|
||||
if (i > 0)
|
||||
VEC_safe_push (tree, gc, for_block, c_begin_compound_stmt (true));
|
||||
c_parser_declaration_or_fndef (parser, true, true, true, true, true);
|
||||
decl = check_for_loop_decls (for_loc);
|
||||
c_parser_declaration_or_fndef (parser, true, true, true, true, true, NULL);
|
||||
decl = check_for_loop_decls (for_loc, flag_isoc99);
|
||||
if (decl == NULL)
|
||||
goto error_init;
|
||||
if (DECL_INITIAL (decl) == error_mark_node)
|
||||
|
@ -429,7 +429,7 @@ extern struct c_declarator *build_array_declarator (location_t, tree,
|
||||
bool, bool);
|
||||
extern tree build_enumerator (location_t, location_t, struct c_enum_contents *,
|
||||
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 declare_parm_level (void);
|
||||
extern void undeclared_variable (location_t, tree);
|
||||
|
@ -20,6 +20,7 @@ several resources on the Internet that present the language.
|
||||
* compatibility_alias::
|
||||
* Exceptions::
|
||||
* Synchronization::
|
||||
* Fast enumeration::
|
||||
@end menu
|
||||
|
||||
@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
|
||||
exceptions enabled, that is with the command line option
|
||||
@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.
|
||||
|
@ -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>
|
||||
|
||||
Merge from 'apple/trunk' branch on FSF servers.
|
||||
@ -5,8 +33,8 @@
|
||||
2005-10-17 Fariborz Jahanian <fjahanian@apple.com>
|
||||
|
||||
Radar 4290840
|
||||
* objc-act.c (objc_start_method_definition): Check for error_mark_node for
|
||||
the selector name and make a quick exit.
|
||||
* objc-act.c (objc_start_method_definition): Check for
|
||||
error_mark_node for the selector name and make a quick exit.
|
||||
|
||||
2010-10-04 Andi Kleen <ak@linux.intel.com>
|
||||
|
||||
|
@ -171,6 +171,8 @@ static tree get_class_ivars (tree, bool);
|
||||
static tree generate_protocol_list (tree);
|
||||
static void build_protocol_reference (tree);
|
||||
|
||||
static void build_fast_enumeration_state_template (void);
|
||||
|
||||
#ifdef OBJCPLUS
|
||||
static void objc_generate_cxx_cdtors (void);
|
||||
#endif
|
||||
@ -240,6 +242,7 @@ static void generate_struct_by_value_array (void)
|
||||
ATTRIBUTE_NORETURN;
|
||||
static void mark_referenced_methods (void);
|
||||
static void generate_objc_image_info (void);
|
||||
static bool objc_type_valid_for_messaging (tree typ);
|
||||
|
||||
/*** Private Interface (data) ***/
|
||||
|
||||
@ -274,6 +277,9 @@ static void generate_objc_image_info (void);
|
||||
|
||||
#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_GETMETACLASS;
|
||||
static const char *TAG_MSGSEND;
|
||||
@ -1952,6 +1958,17 @@ synth_module_prologue (void)
|
||||
self_id = get_identifier ("self");
|
||||
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
|
||||
pop_lang_context ();
|
||||
#endif
|
||||
@ -3596,13 +3613,25 @@ get_class_ivars (tree interface, bool inherited)
|
||||
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
|
||||
objc_create_temporary_var (tree type)
|
||||
objc_create_temporary_var (tree type, const char *name)
|
||||
{
|
||||
tree decl;
|
||||
|
||||
decl = build_decl (input_location,
|
||||
VAR_DECL, NULL_TREE, type);
|
||||
if (name != NULL)
|
||||
{
|
||||
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;
|
||||
DECL_ARTIFICIAL (decl) = 1;
|
||||
DECL_IGNORED_P (decl) = 1;
|
||||
@ -3687,7 +3716,7 @@ objc_build_exc_ptr (void)
|
||||
tree var = cur_try_context->caught_decl;
|
||||
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;
|
||||
}
|
||||
return var;
|
||||
@ -3888,10 +3917,10 @@ next_sjlj_build_try_catch_finally (void)
|
||||
|
||||
/* Create the declarations involved. */
|
||||
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;
|
||||
|
||||
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;
|
||||
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
|
||||
}
|
||||
|
||||
/* 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"
|
||||
|
@ -269,6 +269,9 @@ enum objc_tree_index
|
||||
OCTI_ASSIGN_GLOBAL_DECL,
|
||||
OCTI_ASSIGN_STRONGCAST_DECL,
|
||||
|
||||
OCTI_FAST_ENUM_STATE_TEMP,
|
||||
OCTI_ENUM_MUTATION_DECL,
|
||||
|
||||
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 internal_const_str_type objc_global_trees[OCTI_INTERNAL_CNST_STR_TYPE]
|
||||
#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 */
|
||||
|
@ -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>
|
||||
|
||||
* gcc.c-torture/execute/cmpsi-2.c : Unsigned comparisons should use
|
||||
unsigned values.
|
||||
|
||||
|
||||
2010-10-05 Nicola Pero <nicola.pero@meta-innovation.com>
|
||||
|
||||
PR objc++/28050
|
||||
|
81
gcc/testsuite/objc.dg/foreach-1.m
Normal file
81
gcc/testsuite/objc.dg/foreach-1.m
Normal 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;
|
||||
}
|
279
gcc/testsuite/objc.dg/foreach-2.m
Normal file
279
gcc/testsuite/objc.dg/foreach-2.m
Normal 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;
|
||||
}
|
114
gcc/testsuite/objc.dg/foreach-3.m
Normal file
114
gcc/testsuite/objc.dg/foreach-3.m
Normal 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;
|
||||
}
|
259
gcc/testsuite/objc.dg/foreach-4.m
Normal file
259
gcc/testsuite/objc.dg/foreach-4.m
Normal 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;
|
||||
}
|
258
gcc/testsuite/objc.dg/foreach-5.m
Normal file
258
gcc/testsuite/objc.dg/foreach-5.m
Normal 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;
|
||||
}
|
52
gcc/testsuite/objc.dg/foreach-6.m
Normal file
52
gcc/testsuite/objc.dg/foreach-6.m
Normal 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;
|
||||
}
|
60
gcc/testsuite/objc.dg/foreach-7.m
Normal file
60
gcc/testsuite/objc.dg/foreach-7.m
Normal 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;
|
||||
}
|
||||
|
41
gcc/testsuite/objc.dg/objc-foreach-1.m
Normal file
41
gcc/testsuite/objc.dg/objc-foreach-1.m
Normal 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
|
||||
|
41
gcc/testsuite/objc.dg/objc-foreach-2.m
Normal file
41
gcc/testsuite/objc.dg/objc-foreach-2.m
Normal 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
|
||||
|
42
gcc/testsuite/objc.dg/objc-foreach-3.m
Normal file
42
gcc/testsuite/objc.dg/objc-foreach-3.m
Normal 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
|
||||
|
17
gcc/testsuite/objc.dg/objc-foreach-4.m
Normal file
17
gcc/testsuite/objc.dg/objc-foreach-4.m
Normal 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;
|
||||
}
|
37
gcc/testsuite/objc.dg/objc-foreach-5.m
Normal file
37
gcc/testsuite/objc.dg/objc-foreach-5.m
Normal 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:" } } */
|
@ -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>
|
||||
|
||||
* objc/deprecated/struct_objc_class.h: Add padding
|
||||
|
@ -120,6 +120,7 @@ OBJC_H = \
|
||||
message.h \
|
||||
objc-api.h \
|
||||
objc-decls.h \
|
||||
runtime.h \
|
||||
thr.h \
|
||||
\
|
||||
hash.h \
|
||||
@ -163,6 +164,7 @@ C_SOURCE_FILES = \
|
||||
init.c \
|
||||
memory.c \
|
||||
nil_method.c \
|
||||
objc-foreach.c \
|
||||
objc-sync.c \
|
||||
objects.c \
|
||||
sarray.c \
|
||||
|
52
libobjc/objc-foreach.c
Normal file
52
libobjc/objc-foreach.c
Normal 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
88
libobjc/objc/runtime.h
Normal 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
|
Loading…
Reference in New Issue
Block a user