1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
34 # define NAMLEN(dirent) strlen ((dirent)->d_name)
36 # define dirent direct
37 # define NAMLEN(dirent) ((dirent)->d_namlen)
38 # ifdef HAVE_SYS_NDIR_H
39 # include <sys/ndir.h>
41 # ifdef HAVE_SYS_DIR_H
52 #if PSI_THREADED_PARSER
56 #include "php_scandir.h"
66 PHP_MINIT_FUNCTION(psi_context
);
67 PHP_MINIT_FUNCTION(psi_context
)
70 struct psi_context_ops
*ops
= NULL
;
73 if (!strcasecmp(PSI_G(engine
), "jit")) {
74 ops
= psi_libjit_ops();
78 ops
= psi_libffi_ops();
82 php_error(E_WARNING
, "No PSI engine found");
87 if (ops
->load
&& !ops
->load()) {
91 if (psi_check_env("PSI_DEBUG")) {
94 if (psi_check_env("PSI_SILENT")) {
98 PSI_G(context
) = psi_context_init(NULL
, PSI_G(ops
), psi_error_wrapper
, flags
);
99 psi_context_build(PSI_G(context
), PSI_G(directory
));
104 PHP_MSHUTDOWN_FUNCTION(psi_context
);
105 PHP_MSHUTDOWN_FUNCTION(psi_context
)
107 if (psi_check_env("PSI_DUMP")) {
108 struct psi_dump dump
= {{.hn
= stdout
}, (psi_dump_cb
) fprintf
};
110 psi_context_dump(&dump
, PSI_G(context
));
113 psi_context_free(&PSI_G(context
));
115 if (PSI_G(ops
)->free
) {
122 struct psi_context
*psi_context_init(struct psi_context
*C
, struct psi_context_ops
*ops
, psi_error_cb error
, unsigned flags
)
125 C
= pemalloc(sizeof(*C
), 1);
127 memset(C
, 0, sizeof(*C
));
129 psi_data_ctor(PSI_DATA(C
), error
, flags
);
132 if (ops
->init
&& !ops
->init(C
)) {
139 static bool psi_context_add(struct psi_context
*C
, struct psi_parser
*P
)
143 struct psi_validate_scope scope
= {0};
145 C
->data
= safe_perealloc(C
->data
, (C
->count
+ 1), sizeof(*C
->data
), 0, 1);
146 D
= psi_data_exchange(&C
->data
[C
->count
++], PSI_DATA(P
));
148 psi_validate_scope_ctor(&scope
);
149 scope
.cpp
= P
->preproc
;
150 valid
= psi_validate(&scope
, PSI_DATA(C
), D
);
151 psi_validate_scope_dtor(&scope
);
156 struct psi_context_build_worker
{
158 struct psi_parser parser
;
159 struct psi_parser_input
*input
;
160 char psi_file
[MAXPATHLEN
];
163 static struct psi_context_build_worker
*psi_context_build_worker_init(
164 struct psi_context
*C
, const char *dir
, const char *file
)
166 struct psi_context_build_worker
*w
= pecalloc(1, sizeof(*w
), 1);
168 if (MAXPATHLEN
<= slprintf(w
->psi_file
, MAXPATHLEN
, "%s/%s", dir
, file
)) {
169 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Path to PSI file too long: %s/%s",
174 if (!psi_parser_init(&w
->parser
, C
->error
, C
->flags
)) {
175 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to init PSI parser (%s): %s",
176 w
->psi_file
, strerror(errno
));
183 #if PSI_THREADED_PARSER
184 static void *psi_context_build_worker_thread(void *thread_ptr
)
186 struct psi_context_build_worker
*thread
= thread_ptr
;
187 psi_parser_parse(&thread
->parser
, thread
->input
);
191 static bool psi_context_build_worker_thread_start(
192 struct psi_context_build_worker
*w
)
198 rc
= pthread_create(&w
->tid
, NULL
, psi_context_build_worker_thread
, w
);
209 w
->parser
.error(PSI_DATA(&w
->parser
), NULL
, PSI_WARNING
,
210 "Failed to start parser thread: %s", strerror(rc
));
217 static bool psi_context_build_worker_exec(struct psi_context_build_worker
*w
)
219 if (!(w
->input
= psi_parser_open_file(&w
->parser
, w
->psi_file
, true))) {
220 w
->parser
.error(PSI_DATA(&w
->parser
), NULL
, PSI_WARNING
,
221 "Failed to open PSI file (%s): %s", w
->psi_file
, strerror(errno
));
224 #if PSI_THREADED_PARSER
225 return psi_context_build_worker_thread_start(w
);
227 return psi_parser_parse(&w
->parser
, w
->input
);
231 static bool psi_context_build_worker_done(struct psi_context_build_worker
*w
)
233 #if PSI_THREADED_PARSER
240 # if HAVE_PTHREAD_TRYJOIN_NP
241 if (0 == pthread_tryjoin_np(w
->tid
, &rval
)) {
246 if (0 == pthread_join(w
->tid
, &rval
)) {
257 static void psi_context_build_worker_dtor(struct psi_context_build_worker
*w
)
259 #if PSI_THREADED_PARSER
262 int rc
= pthread_join(w
->tid
, &rval
);
265 w
->parser
.error(PSI_DATA(&w
->parser
), NULL
, PSI_WARNING
,
266 "Failed to finish parser thread: %s", strerror(errno
));
270 psi_parser_input_free(&w
->input
);
271 psi_parser_dtor(&w
->parser
);
274 static void psi_context_build_worker_free(struct psi_context_build_worker
**w
)
277 psi_context_build_worker_dtor(*w
);
283 static int psi_select_dirent(const struct dirent
*entry
)
286 # define FNM_CASEFOLD 0
288 return 0 == fnmatch("*.psi", entry
->d_name
, FNM_CASEFOLD
);
291 void psi_context_build(struct psi_context
*C
, const char *paths
)
293 char *sep
= NULL
, *cpy
= strdup(paths
), *ptr
= cpy
;
294 struct psi_context_build_worker
*worker
;
295 struct psi_plist
*workers
= psi_plist_init(
296 (psi_plist_dtor
) psi_context_build_worker_free
);
299 struct dirent
**entries
= NULL
;
302 if ((sep
= strchr(ptr
, ':'))) {
306 n
= php_scandir(ptr
, &entries
, psi_select_dirent
, alphasort
);
310 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
,
311 "Failed to scan PSI directory '%s%s%s': %s",
312 *ptr
== '/' ? "" : getcwd(cwd
, PATH_MAX
),
313 *ptr
!= '/' && *ptr
!= '.' ? "/" : "",
314 ptr
, strerror(errno
));
316 for (i
= 0; i
< n
; ++i
) {
317 worker
= psi_context_build_worker_init(C
, ptr
, entries
[i
]->d_name
);
318 PSI_DEBUG_PRINT(C
, "PSI: init worker(%p) for %s/%s\n",
319 worker
, ptr
, entries
[i
]->d_name
);
321 workers
= psi_plist_add(workers
, &worker
);
332 if (psi_plist_count(workers
)) {
333 struct psi_plist
*running
= psi_plist_init(
334 (psi_plist_dtor
) psi_context_build_worker_free
);
336 #ifdef _SC_NPROCESSORS_ONLN
337 long pool
= sysconf(_SC_NPROCESSORS_ONLN
);
342 while (psi_plist_count(workers
) && active
< pool
) {
343 if (psi_plist_pop(workers
, &worker
)) {
344 PSI_DEBUG_PRINT(C
, "PSI: starting worker %p\n", worker
);
345 if (psi_context_build_worker_exec(worker
)) {
346 running
= psi_plist_add(running
, &worker
);
354 while (psi_plist_get(running
, i
++, &worker
)) {
355 if (psi_context_build_worker_done(worker
)) {
356 PSI_DEBUG_PRINT(C
, "PSI: collecting worker %p\n", worker
);
357 psi_context_add(C
, &worker
->parser
);
359 psi_plist_del(running
, --i
, NULL
);
360 psi_context_build_worker_free(&worker
);
362 if (psi_plist_pop(workers
, &worker
)) {
363 PSI_DEBUG_PRINT(C
, "PSI: starting worker %p\n", worker
);
364 if (psi_context_build_worker_exec(worker
)) {
365 running
= psi_plist_add(running
, &worker
);
375 psi_context_compile(C
);
379 static inline bool prefix_match(zend_string
*a
, zend_string
*b
)
383 for (i
= 0; i
< a
->len
&& i
< b
->len
; ++i
) {
384 if (tolower(a
->val
[i
]) != tolower(b
->val
[i
])) {
387 if (i
&& a
->val
[i
] == '_') {
395 static inline void psi_context_consts_init(struct psi_context
*C
)
399 ZEND_CONSTANT_SET_FLAGS(&zc
, CONST_CS
|CONST_PERSISTENT
, EG(current_module
)->module_number
);
405 while (psi_plist_get(C
->consts
, i
++, &c
)) {
407 if (zend_get_constant(c
->name
)) {
411 zc
.name
= zend_string_copy(c
->name
);
412 psi_impl_def_val_get_zval(c
->val
, c
->type
? c
->type
->type
: PSI_T_MIXED
, &zc
.value
);
414 zend_register_constant(&zc
);
419 struct psi_decl_enum
*e
;
421 while (psi_plist_get(C
->enums
, i
++, &e
)) {
423 struct psi_decl_enum_item
*item
;
425 while (psi_plist_get(e
->items
, j
++, &item
)) {
428 if (psi_decl_type_is_anon(e
->name
, "enum")
429 || prefix_match(e
->name
, item
->name
)) {
430 name
= strpprintf(0, "psi\\%s", item
->name
->val
);
433 name
= strpprintf(0, "psi\\%s\\%s", e
->name
->val
, item
->name
->val
);
436 if (zend_get_constant(name
)) {
437 zend_string_release(name
);
441 zc
.name
= zend_string_dup(name
, 1);
442 ZVAL_LONG(&zc
.value
, psi_num_exp_get_long(item
->num
, NULL
, NULL
));
443 zend_register_constant(&zc
);
444 zend_string_release(name
);
450 static inline void psi_context_extvars_init(struct psi_context
*C
)
454 struct psi_decl_extvar
*evar
;
456 while (psi_plist_get(C
->vars
, v
++, &evar
)) {
457 C
->ops
->extvar_init(C
, evar
);
462 static inline void psi_context_callback_init(struct psi_context
*C
,
463 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
465 struct psi_let_func
*fn
= let_exp
->data
.func
;
467 switch (let_exp
->kind
) {
468 case PSI_LET_CALLBACK
:
469 C
->ops
->cb_init(C
, let_exp
, impl
);
471 fn
= let_exp
->data
.callback
->func
;
476 struct psi_let_exp
*inner_let
;
478 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
479 psi_context_callback_init(C
, inner_let
, impl
);
488 static inline void psi_context_callback_dtor(struct psi_context
*C
,
489 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
491 struct psi_let_func
*fn
= let_exp
->data
.func
;
493 switch (let_exp
->kind
) {
494 case PSI_LET_CALLBACK
:
495 C
->ops
->cb_dtor(C
, let_exp
, impl
);
497 fn
= let_exp
->data
.callback
->func
;
502 struct psi_let_exp
*cb
;
504 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
505 psi_context_callback_dtor(C
, cb
, impl
);
514 static inline void psi_context_impls_init(struct psi_context
*C
)
517 zend_function_entry
*zfe
= NULL
;
521 struct psi_impl
*impl
;
523 zfe
= pecalloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
), 1);
525 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
526 zend_function_entry
*zf
= &zfe
[nf
];
527 struct psi_let_stmt
*let
;
533 if (!C
->ops
->decl_init(C
, impl
->decl
)) {
536 if (!C
->ops
->impl_init(C
, impl
, &zf
->handler
)) {
539 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
540 psi_context_callback_init(C
, let
->exp
, impl
);
543 zf
->fname
= impl
->func
->name
->val
+ (impl
->func
->name
->val
[0] == '\\');
544 zf
->num_args
= psi_plist_count(impl
->func
->args
);
545 zf
->arg_info
= psi_internal_arginfo(impl
);
553 static inline void psi_context_decls_init(struct psi_context
*C
)
557 struct psi_decl
*decl
;
559 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
561 C
->ops
->decl_init(C
, decl
);
567 struct psi_struct_type_data
{
568 struct psi_plist
*els
;
573 static inline void psi_struct_type_pad(struct psi_context
*C
,
574 struct psi_struct_type_data
*data
, size_t padding
)
576 void *ele
= C
->ops
->typeof_decl(C
, PSI_T_INT8
);
579 void *pad
= C
->ops
->copyof_type(C
, ele
);
580 data
->els
= psi_plist_add(data
->els
, &pad
);
584 static inline void psi_struct_type_element(struct psi_context
*C
,
585 struct psi_struct_type_data
*data
, struct psi_decl_arg
*darg
)
589 struct psi_layout type_layout
;
591 if (darg
->layout
->pos
) {
592 assert(data
->offset
<= darg
->layout
->pos
);
593 psi_struct_type_pad(C
, data
, darg
->layout
->pos
- data
->offset
);
594 data
->offset
= darg
->layout
->pos
;
597 type
= psi_context_decl_arg_full_type(C
, darg
);
598 C
->ops
->layoutof_type(C
, type
, &type_layout
);
600 if (type_layout
.pos
> data
->max_align
) {
601 data
->max_align
= type_layout
.pos
;
604 assert(type_layout
.len
<= darg
->layout
->len
);
606 for (i
= 0; i
< (darg
->var
->array_size
?: 1); ++i
) {
607 copy
= C
->ops
->copyof_type(C
, type
);
608 data
->els
= psi_plist_add(data
->els
, ©
);
610 assert(darg
->layout
->len
== type_layout
.len
* (darg
->var
->array_size
?: 1));
611 data
->offset
+= darg
->layout
->len
;
614 static inline void psi_context_decl_struct_type_elements(struct psi_context
*C
,
615 struct psi_decl_struct
*strct
, struct psi_plist
**els
)
618 struct psi_decl_arg
*darg
, *prev
= NULL
;
619 struct psi_struct_type_data data
= {0};
622 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
623 if (prev
&& prev
->layout
->pos
== darg
->layout
->pos
) {
624 /* skip bit fields */
627 psi_struct_type_element(C
, &data
, darg
);
630 data
.offset
= (data
.offset
+ data
.max_align
- 1) & ~(data
.max_align
- 1);
631 assert(data
.offset
<= strct
->size
);
632 psi_struct_type_pad(C
, &data
, strct
->size
- data
.offset
);
637 static inline void *psi_context_decl_arg_type(struct psi_context
*C
,
638 struct psi_decl_arg
*darg
)
640 struct psi_decl_type
*real
= psi_decl_type_get_real(darg
->type
);
642 if (real
!= darg
->type
&& darg
->type
->real
.def
->var
->pointer_level
) {
643 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
646 if (real
->type
== PSI_T_UNION
) {
647 struct psi_decl_arg
*arg
;
648 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
649 return psi_context_decl_arg_full_type(C
, arg
);
652 if (real
->type
== PSI_T_STRUCT
) {
653 C
->ops
->composite_init(C
, darg
);
654 return darg
->engine
.type
;
657 return C
->ops
->typeof_decl(C
, real
->type
);
660 void *psi_context_decl_arg_call_type(struct psi_context
*C
,
661 struct psi_decl_arg
*darg
)
663 if (darg
->var
->pointer_level
) {
664 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
667 return psi_context_decl_arg_type(C
, darg
);
670 void *psi_context_decl_arg_full_type(struct psi_context
*C
,
671 struct psi_decl_arg
*darg
)
673 if (darg
->var
->array_size
) {
674 C
->ops
->composite_init(C
, darg
);
675 return darg
->engine
.type
;
677 if (darg
->var
->pointer_level
) {
678 return C
->ops
->typeof_decl(C
, PSI_T_POINTER
);
681 return psi_context_decl_arg_type(C
, darg
);
684 void **psi_context_composite_type_elements(struct psi_context
*C
,
685 struct psi_decl_arg
*darg
, struct psi_plist
**eles
)
687 struct psi_decl_type
*dtype
;
688 struct psi_decl_arg
*tmp
;
691 dtype
= psi_decl_type_get_real(darg
->type
);
693 switch (dtype
->type
) {
695 psi_context_decl_struct_type_elements(C
, dtype
->real
.strct
, eles
);
698 if (psi_plist_bottom(dtype
->real
.unn
->args
, &tmp
)) {
699 type
= psi_context_decl_arg_full_type(C
, tmp
);
700 copy
= C
->ops
->copyof_type(C
, type
);
701 *eles
= psi_plist_add(*eles
, ©
);
705 type
= psi_context_decl_arg_type(C
, darg
);
706 for (size_t i
= 0; i
< darg
->var
->array_size
; ++i
) {
707 copy
= C
->ops
->copyof_type(C
, type
);
708 *eles
= psi_plist_add(*eles
, ©
);
712 return psi_plist_eles(*eles
);
716 void psi_context_decl_func_array_elements(struct psi_context *C,
717 struct psi_decl *fn, struct psi_plist **els)
722 if (fn->func->var->pointer_level > 1) {
723 type = C->ops->typeof_decl(C, PSI_T_POINTER);
725 type = psi_context_decl_type(C, fn->func->type);
728 for (i = 0; i < fn->func->var->array_size; ++i) {
729 void *copy = C->ops->copyof_type(C, type);
730 *els = psi_plist_add(*els, ©);
734 void *psi_context_decl_func_type(struct psi_context *C, struct psi_decl *fn)
736 struct psi_decl_arg *darg = fn->func;
738 if (darg->engine.type) {
739 return darg->engine.type;
742 if (darg->var->pointer_level) {
743 if (!darg->var->array_size) {
744 return C->ops->typeof_decl(C, PSI_T_POINTER);
746 C->ops->composite_init(C, darg);
747 return darg->engine.type;
751 return psi_context_decl_type(C, darg->type);
755 void psi_context_compile(struct psi_context
*C
)
757 psi_context_consts_init(C
);
758 psi_context_extvars_init(C
);
759 psi_context_impls_init(C
);
760 psi_context_decls_init(C
);
762 /* zend_register_functions depends on EG(current_module) pointing into module */
763 EG(current_module
) = zend_hash_str_find_ptr(&module_registry
, "psi", sizeof("psi") - 1);
764 if (SUCCESS
!= zend_register_functions(NULL
, C
->closures
, NULL
, MODULE_PERSISTENT
)) {
765 C
->error(PSI_DATA(C
), NULL
, PSI_WARNING
, "Failed to register functions!");
767 EG(current_module
) = NULL
;
770 ZEND_RESULT_CODE
psi_context_call(struct psi_context
*C
, zend_execute_data
*execute_data
, zval
*return_value
, struct psi_impl
*impl
)
772 struct psi_call_frame
*frame
;
774 frame
= psi_call_frame_init(C
, impl
->decl
, impl
);
776 if (SUCCESS
!= psi_call_frame_parse_args(frame
, execute_data
)) {
777 psi_call_frame_free(frame
);
782 psi_call_frame_enter(frame
);
784 if (SUCCESS
!= psi_call_frame_do_let(frame
)) {
785 psi_call_frame_do_return(frame
, return_value
);
786 psi_call_frame_free(frame
);
791 if (SUCCESS
!= psi_call_frame_do_assert(frame
, PSI_ASSERT_PRE
)) {
792 psi_call_frame_do_return(frame
, return_value
);
793 psi_call_frame_free(frame
);
798 if (psi_call_frame_num_var_args(frame
)) {
799 C
->ops
->call_va(frame
);
804 if (SUCCESS
!= psi_call_frame_do_assert(frame
, PSI_ASSERT_POST
)) {
805 psi_call_frame_do_return(frame
, return_value
);
806 psi_call_frame_free(frame
);
811 psi_call_frame_do_return(frame
, return_value
);
812 psi_call_frame_do_set(frame
);
813 psi_call_frame_do_free(frame
);
814 psi_call_frame_free(frame
);
820 void psi_context_dtor(struct psi_context
*C
)
823 zend_function_entry
*zfe
;
827 struct psi_decl
*decl
;
829 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
831 struct psi_decl_arg
*darg
;
833 while (psi_plist_get(decl
->args
, j
++, &darg
)) {
834 C
->ops
->composite_dtor(C
, darg
);
836 C
->ops
->composite_dtor(C
, decl
->func
);
837 C
->ops
->decl_dtor(C
, decl
);
843 struct psi_decl_extvar
*evar
;
845 while (psi_plist_get(C
->vars
, i
++, &evar
)) {
846 C
->ops
->composite_dtor(C
, evar
->getter
->func
);
847 C
->ops
->composite_dtor(C
, evar
->arg
);
848 C
->ops
->extvar_dtor(C
, evar
);
853 struct psi_impl
*impl
;
855 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
856 struct psi_let_stmt
*let
;
859 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
860 psi_context_callback_dtor(C
, let
->exp
, impl
);
863 C
->ops
->impl_dtor(C
, impl
);
871 psi_data_dtor(PSI_DATA(C
));
874 for (i
= 0; i
< C
->count
; ++i
) {
875 psi_data_dtor(&C
->data
[i
]);
881 for (zfe
= C
->closures
; zfe
->fname
; ++zfe
) {
882 pefree((void *) zfe
->arg_info
, 1);
884 pefree(C
->closures
, 1);
888 void psi_context_free(struct psi_context
**C
)
891 psi_context_dtor(*C
);
897 void psi_context_dump(struct psi_dump
*dump
, struct psi_context
*C
)
899 PSI_DUMP(dump
, "// psi.engine=%s\n// %lu files\n",
900 C
->ops
->name
, C
->count
);
902 psi_data_dump(dump
, PSI_DATA(C
));
905 if (C
->flags
& PSI_DEBUG
) {
908 for (i
= 0; i
< C
->count
; ++i
) {
909 psi_data_dump(dump
, &C
->data
[i
]);