Skip to content

Commit b988fe2

Browse files
Introduce for-in opcode; implement handler for the opcode.
JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan [email protected]
1 parent 6c6e740 commit b988fe2

File tree

6 files changed

+293
-6
lines changed

6 files changed

+293
-6
lines changed

jerry-core/ecma/base/ecma-globals.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,11 @@ typedef uint32_t ecma_magic_string_ex_id_t;
793793
*/
794794
typedef uint8_t ecma_string_hash_t;
795795

796+
/**
797+
* Length of string hash, in bits
798+
*/
799+
#define ECMA_STRING_HASH_BITS (sizeof (ecma_string_hash_t) * JERRY_BITSINBYTE)
800+
796801
/**
797802
* Number of string's last characters to use for hash calculation
798803
*/

jerry-core/ecma/base/ecma-lcache.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ JERRY_STATIC_ASSERT (sizeof (ecma_lcache_hash_entry_t) == sizeof (uint64_t));
5050
/**
5151
* LCache hash value length, in bits
5252
*/
53-
#define ECMA_LCACHE_HASH_BITS (sizeof (ecma_string_hash_t) * JERRY_BITSINBYTE)
53+
#define ECMA_LCACHE_HASH_BITS (ECMA_STRING_HASH_BITS)
5454

5555
/**
5656
* Number of rows in LCache's hash table

jerry-core/vm/opcodes-for-in.cpp

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
/* Copyright 2015 Samsung Electronics Co., Ltd.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
#include "jrt.h"
17+
#include "opcodes.h"
18+
#include "opcodes-ecma-support.h"
19+
20+
/**
21+
* Enumerate properties and construct collection with their
22+
* names for further iteration in for-in opcode handler.
23+
*
24+
* See also:
25+
* ECMA-262 v5, 12.6.4
26+
*
27+
* @return header of constructed strings collection (should be freed with ecma_free_values_collection),
28+
* or NULL - if there are no properties to enumerate in for-in.
29+
*/
30+
static ecma_collection_header_t *
31+
vm_helper_for_in_enumerate_properties_names (ecma_object_t *obj_p) /**< starting object - result of ToObject
32+
* conversion (ECMA-262 v5, 12.6.4, step 4) */
33+
{
34+
const size_t bitmap_row_size = sizeof (uint32_t) * JERRY_BITSINBYTE;
35+
uint32_t names_hashes_bitmap[(1u << ECMA_STRING_HASH_BITS) / bitmap_row_size];
36+
37+
memset (names_hashes_bitmap, 0, sizeof (names_hashes_bitmap));
38+
39+
ecma_length_t all_properties_count = 0;
40+
41+
/* First pass: counting properties */
42+
for (ecma_object_t *prototype_chain_iter_p = obj_p;
43+
prototype_chain_iter_p != NULL;
44+
prototype_chain_iter_p = ecma_get_object_prototype (prototype_chain_iter_p))
45+
{
46+
for (ecma_property_t *prop_iter_p = ecma_get_property_list (prototype_chain_iter_p);
47+
prop_iter_p != NULL;
48+
prop_iter_p = ECMA_GET_POINTER (ecma_property_t, prop_iter_p->next_property_p))
49+
{
50+
if (prop_iter_p->type == ECMA_PROPERTY_NAMEDDATA
51+
|| prop_iter_p->type == ECMA_PROPERTY_NAMEDACCESSOR)
52+
{
53+
all_properties_count++;
54+
}
55+
else
56+
{
57+
JERRY_ASSERT (prop_iter_p->type == ECMA_PROPERTY_INTERNAL);
58+
}
59+
}
60+
}
61+
62+
if (all_properties_count == 0)
63+
{
64+
return NULL;
65+
}
66+
67+
ecma_collection_header_t *ret_p = NULL;
68+
69+
/* Second pass: collecting properties names */
70+
MEM_DEFINE_LOCAL_ARRAY (names_p, all_properties_count, ecma_string_t*);
71+
72+
ecma_length_t enumerated_properties_count = 0;
73+
ecma_length_t non_enumerated_properties_count = 0;
74+
75+
for (ecma_object_t *prototype_chain_iter_p = obj_p;
76+
prototype_chain_iter_p != NULL;
77+
prototype_chain_iter_p = ecma_get_object_prototype (prototype_chain_iter_p))
78+
{
79+
for (ecma_property_t *prop_iter_p = ecma_get_property_list (prototype_chain_iter_p);
80+
prop_iter_p != NULL;
81+
prop_iter_p = ECMA_GET_POINTER (ecma_property_t, prop_iter_p->next_property_p))
82+
{
83+
if (prop_iter_p->type == ECMA_PROPERTY_NAMEDDATA
84+
|| prop_iter_p->type == ECMA_PROPERTY_NAMEDACCESSOR)
85+
{
86+
bool is_enumerated;
87+
88+
ecma_string_t *prop_name_p;
89+
90+
if (prop_iter_p->type == ECMA_PROPERTY_NAMEDDATA)
91+
{
92+
prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, prop_iter_p->u.named_data_property.name_p);
93+
}
94+
else
95+
{
96+
prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, prop_iter_p->u.named_accessor_property.name_p);
97+
}
98+
99+
ecma_string_hash_t hash = prop_name_p->hash;
100+
uint32_t bitmap_row = hash / bitmap_row_size;
101+
uint32_t bitmap_column = hash % bitmap_row_size;
102+
103+
if (ecma_is_property_enumerable (prop_iter_p))
104+
{
105+
if ((names_hashes_bitmap[bitmap_row] & (1u << bitmap_column)) == 0)
106+
{
107+
/* no name with the hash occured during the iteration session */
108+
is_enumerated = true;
109+
}
110+
else
111+
{
112+
/* name with same hash already occured */
113+
bool is_equal_found = false;
114+
115+
for (uint32_t index = 0;
116+
!is_equal_found && index < enumerated_properties_count;
117+
index++)
118+
{
119+
if (ecma_compare_ecma_strings (prop_name_p,
120+
names_p[index]))
121+
{
122+
is_equal_found = true;
123+
}
124+
}
125+
126+
for (uint32_t index = 0;
127+
!is_equal_found && index < non_enumerated_properties_count;
128+
index++)
129+
{
130+
if (ecma_compare_ecma_strings (prop_name_p,
131+
names_p[all_properties_count - index - 1]))
132+
{
133+
is_equal_found = true;
134+
}
135+
}
136+
137+
is_enumerated = !is_equal_found;
138+
}
139+
}
140+
else
141+
{
142+
is_enumerated = false;
143+
}
144+
145+
names_hashes_bitmap[bitmap_row] |= (1u << bitmap_column);
146+
147+
if (is_enumerated)
148+
{
149+
names_p[enumerated_properties_count++] = prop_name_p;
150+
}
151+
else
152+
{
153+
names_p[all_properties_count - non_enumerated_properties_count++ - 1] = prop_name_p;
154+
}
155+
156+
JERRY_ASSERT (enumerated_properties_count + non_enumerated_properties_count <= all_properties_count);
157+
}
158+
else
159+
{
160+
JERRY_ASSERT (prop_iter_p->type == ECMA_PROPERTY_INTERNAL);
161+
}
162+
}
163+
}
164+
165+
if (enumerated_properties_count != 0)
166+
{
167+
ret_p = ecma_new_strings_collection (names_p, enumerated_properties_count);
168+
}
169+
170+
MEM_FINALIZE_LOCAL_ARRAY (names_p);
171+
172+
return ret_p;
173+
} /* vm_helper_for_in_enumerate_properties_names */
174+
175+
/**
176+
* 'for-in' opcode handler
177+
*
178+
* See also:
179+
* ECMA-262 v5, 12.6.4
180+
*
181+
* @return completion value
182+
* Returned value must be freed with ecma_free_completion_value
183+
*/
184+
ecma_completion_value_t
185+
opfunc_for_in (opcode_t opdata, /**< operation data */
186+
int_data_t *int_data_p) /**< interpreter context */
187+
{
188+
const idx_t expr_idx = opdata.data.for_in.expr;
189+
const idx_t block_end_oc_idx_1 = opdata.data.for_in.oc_idx_1;
190+
const idx_t block_end_oc_idx_2 = opdata.data.for_in.oc_idx_2;
191+
const opcode_counter_t for_in_end_oc = (opcode_counter_t) (
192+
calc_opcode_counter_from_idx_idx (block_end_oc_idx_1,
193+
block_end_oc_idx_2) + int_data_p->pos);
194+
195+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
196+
197+
/* 1., 2. */
198+
ECMA_TRY_CATCH (expr_value,
199+
get_variable_value (int_data_p,
200+
expr_idx,
201+
false),
202+
ret_value);
203+
204+
int_data_p->pos++;
205+
206+
opcode_t meta_opcode = vm_get_opcode (int_data_p->opcodes_p, for_in_end_oc);
207+
JERRY_ASSERT (meta_opcode.op_idx == __op__idx_meta);
208+
JERRY_ASSERT (meta_opcode.data.meta.type == OPCODE_META_TYPE_END_FOR_IN);
209+
210+
/* 3. */
211+
if (!ecma_is_value_undefined (expr_value)
212+
&& !ecma_is_value_null (expr_value))
213+
{
214+
/* 4. */
215+
ECMA_TRY_CATCH (obj_expr_value,
216+
ecma_op_to_object (expr_value),
217+
ret_value);
218+
219+
ecma_object_t *obj_p = ecma_get_object_from_value (obj_expr_value);
220+
221+
ecma_collection_iterator_t names_iterator;
222+
ecma_collection_header_t *names_p = vm_helper_for_in_enumerate_properties_names (obj_p);
223+
224+
if (names_p != NULL)
225+
{
226+
ecma_collection_iterator_init (&names_iterator, names_p);
227+
228+
const opcode_counter_t for_in_body_begin_oc = int_data_p->pos;
229+
const opcode_counter_t for_in_body_end_oc = for_in_end_oc;
230+
231+
while (ecma_collection_iterator_next (&names_iterator))
232+
{
233+
ecma_value_t name_value = *names_iterator.current_value_p;
234+
235+
ecma_string_t *name_p = ecma_get_string_from_value (name_value);
236+
237+
if (ecma_op_object_get_property (obj_p, name_p) != NULL)
238+
{
239+
ecma_completion_value_t completion = set_variable_value (int_data_p,
240+
int_data_p->pos,
241+
OPCODE_REG_SPECIAL_FOR_IN_PROPERTY_NAME,
242+
name_value);
243+
JERRY_ASSERT (ecma_is_completion_value_empty (completion));
244+
245+
vm_run_scope_t run_scope_for_in = { for_in_body_begin_oc, for_in_body_end_oc };
246+
247+
ecma_completion_value_t for_in_body_completion = vm_loop (int_data_p, &run_scope_for_in);
248+
if (ecma_is_completion_value_empty (for_in_body_completion))
249+
{
250+
JERRY_ASSERT (int_data_p->pos == for_in_body_end_oc);
251+
252+
int_data_p->pos = for_in_body_begin_oc;
253+
}
254+
else
255+
{
256+
JERRY_ASSERT (!ecma_is_completion_value_normal (for_in_body_completion));
257+
JERRY_ASSERT (int_data_p->pos <= for_in_body_end_oc);
258+
259+
ret_value = for_in_body_completion;
260+
break;
261+
}
262+
}
263+
}
264+
265+
ecma_free_values_collection (names_p, true);
266+
}
267+
268+
ECMA_FINALIZE (obj_expr_value);
269+
}
270+
271+
int_data_p->pos = (opcode_counter_t) (for_in_end_oc + 1u);
272+
273+
ECMA_FINALIZE (expr_value);
274+
275+
return ret_value;
276+
} /* opfunc_for_in */
277+

jerry-core/vm/opcodes.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,6 +1748,7 @@ opfunc_meta (opcode_t opdata, /**< operation data */
17481748
case OPCODE_META_TYPE_CATCH:
17491749
case OPCODE_META_TYPE_FINALLY:
17501750
case OPCODE_META_TYPE_END_TRY_CATCH_FINALLY:
1751+
case OPCODE_META_TYPE_END_FOR_IN:
17511752
{
17521753
return ecma_make_meta_completion_value ();
17531754
}

jerry-core/vm/opcodes.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ typedef enum
7272
OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER, /**< literal index containing name of variable with exception object */
7373
OPCODE_META_TYPE_FINALLY, /**< mark of beginning of finally block containing pointer to end of finally block */
7474
OPCODE_META_TYPE_END_TRY_CATCH_FINALLY, /**< mark of end of try-catch, try-finally, try-catch-finally blocks */
75-
OPCODE_META_TYPE_SCOPE_CODE_FLAGS /**< set of flags indicating various properties of the scope's code
76-
* (See also: opcode_scope_code_flags_t) */
75+
OPCODE_META_TYPE_SCOPE_CODE_FLAGS, /**< set of flags indicating various properties of the scope's code
76+
* (See also: opcode_scope_code_flags_t) */
77+
OPCODE_META_TYPE_END_FOR_IN /**< end of for-in statement */
7778
} opcode_meta_type;
7879

7980
typedef enum : idx_t
@@ -109,6 +110,8 @@ typedef enum : idx_t
109110
{
110111
OPCODE_REG_FIRST = 128, /** identifier of first special register */
111112
OPCODE_REG_SPECIAL_EVAL_RET = OPCODE_REG_FIRST, /**< eval return value */
113+
OPCODE_REG_SPECIAL_FOR_IN_PROPERTY_NAME, /**< variable, containing property name,
114+
* at start of for-in loop body */
112115
OPCODE_REG_GENERAL_FIRST, /** identifier of first non-special register */
113116
OPCODE_REG_GENERAL_LAST = 253, /** identifier of last non-special register */
114117
OPCODE_REG_LAST = OPCODE_REG_GENERAL_FIRST /**< identifier of last register */
@@ -186,6 +189,7 @@ opcode_counter_t read_meta_opcode_counter (opcode_meta_type expected_type, int_d
186189
p##_2 (a, delete_var, lhs, name) \
187190
p##_3 (a, delete_prop, lhs, base, name) \
188191
p##_2 (a, typeof, lhs, obj) \
192+
p##_3 (a, for_in, expr, oc_idx_1, oc_idx_2) \
189193
p##_3 (a, with, expr, oc_idx_1, oc_idx_2) \
190194
p##_2 (a, try_block, oc_idx_1, oc_idx_2) \
191195
p##_1 (a, throw_value, var)

tests/unit/test-parser.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ main (int __attr_unused___ argc,
9191
OPCODE_SCOPE_CODE_FLAGS_NOT_REF_ARGUMENTS_IDENTIFIER
9292
| OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER,
9393
INVALID_VALUE),
94-
getop_reg_var_decl (128, 129), // var tmp128 .. tmp129;
94+
getop_reg_var_decl (OPCODE_REG_FIRST, OPCODE_REG_GENERAL_FIRST),
9595
getop_var_decl (0), // var a;
96-
getop_assignment (129, 1, 1), // tmp129 = 1: SMALLINT;
97-
getop_assignment (0, 6, 129), // a = tmp129 : TYPEOF (tmp129);
96+
getop_assignment (130, 1, 1), // $tmp0 = 1;
97+
getop_assignment (0, 6, 130), // a = $tmp0;
9898
getop_exitval (0) // exit 0;
9999
};
100100

0 commit comments

Comments
 (0)