mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Implement parse of for-in statement.
JerryScript-DCO-1.0-Signed-off-by: Evgeny Gavrin e.gavrin@samsung.com JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com
This commit is contained in:
parent
b988fe2fce
commit
507411f0a0
@ -708,6 +708,23 @@ eval_ret_operand (void)
|
||||
return ret;
|
||||
} /* eval_ret_operand */
|
||||
|
||||
/**
|
||||
* Creates operand for taking iterator value (next property name)
|
||||
* from for-in opcode handler.
|
||||
*
|
||||
* @return constructed operand
|
||||
*/
|
||||
operand
|
||||
jsp_create_operand_for_in_special_reg (void)
|
||||
{
|
||||
operand ret;
|
||||
|
||||
ret.type = OPERAND_TMP;
|
||||
ret.data.uid = OPCODE_REG_SPECIAL_FOR_IN_PROPERTY_NAME;
|
||||
|
||||
return ret;
|
||||
} /* jsp_create_operand_for_in_special_reg */
|
||||
|
||||
bool
|
||||
operand_is_empty (operand op)
|
||||
{
|
||||
@ -2362,6 +2379,62 @@ dump_with_end (void)
|
||||
serializer_dump_op_meta (create_op_meta_000 (opcode));
|
||||
} /* dump_with_end */
|
||||
|
||||
/**
|
||||
* Dump template of 'for_in' instruction.
|
||||
*
|
||||
* Note:
|
||||
* the instruction's flags field is written later (see also: rewrite_for_in).
|
||||
*
|
||||
* @return position of dumped instruction
|
||||
*/
|
||||
opcode_counter_t
|
||||
dump_for_in_for_rewrite (operand op) /**< operand - result of evaluating Expression
|
||||
* in for-in statement */
|
||||
{
|
||||
opcode_counter_t oc = serializer_get_current_opcode_counter ();
|
||||
|
||||
if (op.type == OPERAND_LITERAL)
|
||||
{
|
||||
const opcode_t opcode = getop_for_in (LITERAL_TO_REWRITE, INVALID_VALUE, INVALID_VALUE);
|
||||
serializer_dump_op_meta (create_op_meta_100 (opcode, op.data.lit_id));
|
||||
}
|
||||
else
|
||||
{
|
||||
JERRY_ASSERT (op.type == OPERAND_TMP);
|
||||
|
||||
const opcode_t opcode = getop_for_in (op.data.uid, INVALID_VALUE, INVALID_VALUE);
|
||||
serializer_dump_op_meta (create_op_meta_000 (opcode));
|
||||
}
|
||||
|
||||
return oc;
|
||||
} /* dump_for_in_for_rewrite */
|
||||
|
||||
/**
|
||||
* Write position of 'for_in' block's end to specified 'for_in' instruction template,
|
||||
* dumped earlier (see also: dump_for_in_for_rewrite).
|
||||
*/
|
||||
void
|
||||
rewrite_for_in (opcode_counter_t oc) /**< opcode counter of the instruction template */
|
||||
{
|
||||
op_meta for_in_op_meta = serializer_get_op_meta (oc);
|
||||
|
||||
idx_t id1, id2;
|
||||
split_opcode_counter (get_diff_from (oc), &id1, &id2);
|
||||
for_in_op_meta.op.data.for_in.oc_idx_1 = id1;
|
||||
for_in_op_meta.op.data.for_in.oc_idx_2 = id2;
|
||||
serializer_rewrite_op_meta (oc, for_in_op_meta);
|
||||
} /* rewrite_for_in */
|
||||
|
||||
/**
|
||||
* Dump 'meta' instruction of 'end for_in' type
|
||||
*/
|
||||
void
|
||||
dump_for_in_end (void)
|
||||
{
|
||||
const opcode_t opcode = getop_meta (OPCODE_META_TYPE_END_FOR_IN, INVALID_VALUE, INVALID_VALUE);
|
||||
serializer_dump_op_meta (create_op_meta_000 (opcode));
|
||||
} /* dump_for_in_end */
|
||||
|
||||
void
|
||||
dump_try_for_rewrite (void)
|
||||
{
|
||||
|
||||
@ -49,6 +49,7 @@ typedef enum __attr_packed___
|
||||
operand empty_operand (void);
|
||||
operand literal_operand (lit_cpointer_t);
|
||||
operand eval_ret_operand (void);
|
||||
operand jsp_create_operand_for_in_special_reg (void);
|
||||
bool operand_is_empty (operand);
|
||||
|
||||
void dumper_init (void);
|
||||
@ -211,6 +212,10 @@ opcode_counter_t dump_with_for_rewrite (operand);
|
||||
void rewrite_with (opcode_counter_t);
|
||||
void dump_with_end (void);
|
||||
|
||||
opcode_counter_t dump_for_in_for_rewrite (operand);
|
||||
void rewrite_for_in (opcode_counter_t);
|
||||
void dump_for_in_end (void);
|
||||
|
||||
void dump_try_for_rewrite (void);
|
||||
void rewrite_try (void);
|
||||
void dump_catch_for_rewrite (operand);
|
||||
|
||||
@ -1768,7 +1768,7 @@ parse_expression (bool in_allowed, /**< flag indicating if 'in' is allowed insid
|
||||
initialiser
|
||||
: '=' LT!* assignment_expression
|
||||
; */
|
||||
static void
|
||||
static operand
|
||||
parse_variable_declaration (void)
|
||||
{
|
||||
current_token_must_be (TOK_NAME);
|
||||
@ -1785,6 +1785,8 @@ parse_variable_declaration (void)
|
||||
{
|
||||
lexer_save_token (tok);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/* variable_declaration_list
|
||||
@ -1933,15 +1935,176 @@ jsp_parse_for_statement (jsp_label_t *outermost_stmt_label_p, /**< outermost (fi
|
||||
}
|
||||
} /* jsp_parse_for_statement */
|
||||
|
||||
static void
|
||||
parse_for_in (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, corresponding to
|
||||
* the statement (or NULL, if there are no named
|
||||
* labels associated with the statement) */
|
||||
/**
|
||||
* Parse VariableDeclarationNoIn / LeftHandSideExpression (iterator part) of for-in statement
|
||||
*
|
||||
* See also:
|
||||
* jsp_parse_for_in_statement
|
||||
*
|
||||
* @return true - if iterator consists of base and property name,
|
||||
* false - otherwise, iterator consists of an identifier name (without base).
|
||||
*/
|
||||
static bool
|
||||
jsp_parse_for_in_statement_iterator (operand *base_p, /**< out: base value of member expression, if any,
|
||||
* empty operand - otherwise */
|
||||
operand *identifier_p) /**< out: property name (if base value is not empty),
|
||||
* identifier - otherwise */
|
||||
{
|
||||
(void) outermost_stmt_label_p;
|
||||
JERRY_ASSERT (base_p != NULL);
|
||||
JERRY_ASSERT (identifier_p != NULL);
|
||||
|
||||
EMIT_SORRY ("'for in' loops are not supported yet");
|
||||
}
|
||||
if (is_keyword (KW_VAR))
|
||||
{
|
||||
skip_newlines ();
|
||||
|
||||
*base_p = empty_operand ();
|
||||
*identifier_p = parse_variable_declaration ();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
operand base, identifier;
|
||||
|
||||
/*
|
||||
* FIXME:
|
||||
* Remove evaluation of last part of identifier chain
|
||||
*/
|
||||
operand i = parse_left_hand_side_expression (&base, &identifier);
|
||||
|
||||
if (operand_is_empty (base))
|
||||
{
|
||||
*base_p = empty_operand ();
|
||||
*identifier_p = i;
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
*base_p = base;
|
||||
*identifier_p = identifier;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} /* jsp_parse_for_in_statement_iterator */
|
||||
|
||||
/**
|
||||
* Parse for-in statement
|
||||
*
|
||||
* See also:
|
||||
* ECMA-262 v5, 12.6.4
|
||||
*
|
||||
* Note:
|
||||
* Syntax:
|
||||
* Iterator Collection Body LoopEnd
|
||||
* - for ( LeftHandSideExpression in Expression) Statement
|
||||
* - for (var VariableDeclarationNoIn in Expression) Statement
|
||||
*
|
||||
* Note:
|
||||
* Layout of generate byte-code is the following:
|
||||
* tmp <- Collection (Expression)
|
||||
* for_in instruction (tmp, opcode counter of for-in end mark)
|
||||
* {
|
||||
* Assignment of OPCODE_REG_SPECIAL_FOR_IN_PROPERTY_NAME to
|
||||
* Iterator (VariableDeclarationNoIn / LeftHandSideExpression)
|
||||
* }
|
||||
* Body (Statement)
|
||||
* ContinueTarget:
|
||||
* meta (OPCODE_META_TYPE_END_FOR_IN)
|
||||
*/
|
||||
static void
|
||||
jsp_parse_for_in_statement (jsp_label_t *outermost_stmt_label_p, /**< outermost (first) label,
|
||||
* corresponding to the statement
|
||||
* (or NULL, if there are no name
|
||||
* labels associated with the statement) */
|
||||
locus for_body_statement_loc) /**< locus of loop body statement */
|
||||
{
|
||||
jsp_label_raise_nested_jumpable_border ();
|
||||
|
||||
current_token_must_be (TOK_OPEN_PAREN);
|
||||
skip_newlines ();
|
||||
|
||||
// Save Iterator location
|
||||
locus iterator_loc = tok.loc;
|
||||
|
||||
while (tok.loc < for_body_statement_loc)
|
||||
{
|
||||
if (jsp_find_next_token_before_the_locus (TOK_KEYWORD,
|
||||
for_body_statement_loc,
|
||||
true))
|
||||
{
|
||||
if (is_keyword (KW_IN))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
skip_token ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EMIT_ERROR ("Invalid for statement");
|
||||
}
|
||||
}
|
||||
|
||||
JERRY_ASSERT (is_keyword (KW_IN));
|
||||
skip_newlines ();
|
||||
|
||||
// Collection
|
||||
operand collection = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP);
|
||||
current_token_must_be (TOK_CLOSE_PAREN);
|
||||
skip_token ();
|
||||
|
||||
// Dump for-in instruction
|
||||
opcode_counter_t for_in_oc = dump_for_in_for_rewrite (collection);
|
||||
|
||||
// Dump assignment VariableDeclarationNoIn / LeftHandSideExpression <- OPCODE_REG_SPECIAL_FOR_IN_PROPERTY_NAME
|
||||
lexer_seek (iterator_loc);
|
||||
tok = lexer_next_token ();
|
||||
|
||||
operand iterator_base, iterator_identifier, for_in_special_reg;
|
||||
for_in_special_reg = jsp_create_operand_for_in_special_reg ();
|
||||
|
||||
if (jsp_parse_for_in_statement_iterator (&iterator_base, &iterator_identifier))
|
||||
{
|
||||
dump_prop_setter (iterator_base, iterator_identifier, for_in_special_reg);
|
||||
}
|
||||
else
|
||||
{
|
||||
JERRY_ASSERT (operand_is_empty (iterator_base));
|
||||
dump_variable_assignment (iterator_identifier, for_in_special_reg);
|
||||
}
|
||||
|
||||
// Body
|
||||
lexer_seek (for_body_statement_loc);
|
||||
tok = lexer_next_token ();
|
||||
|
||||
parse_statement (NULL);
|
||||
|
||||
// Save LoopEnd locus
|
||||
const locus loop_end_loc = tok.loc;
|
||||
|
||||
// Setup ContinueTarget
|
||||
jsp_label_setup_continue_target (outermost_stmt_label_p,
|
||||
serializer_get_current_opcode_counter ());
|
||||
|
||||
// Write position of for-in end to for_in instruction
|
||||
rewrite_for_in (for_in_oc);
|
||||
|
||||
// Dump meta (OPCODE_META_TYPE_END_FOR_IN)
|
||||
dump_for_in_end ();
|
||||
|
||||
lexer_seek (loop_end_loc);
|
||||
tok = lexer_next_token ();
|
||||
if (tok.type != TOK_CLOSE_BRACE)
|
||||
{
|
||||
lexer_save_token (tok);
|
||||
}
|
||||
|
||||
jsp_label_remove_nested_jumpable_border ();
|
||||
} /* jsp_parse_for_in_statement */
|
||||
|
||||
/**
|
||||
* Parse for/for-in statements
|
||||
@ -1982,7 +2145,7 @@ jsp_parse_for_or_for_in_statement (jsp_label_t *outermost_stmt_label_p) /**< out
|
||||
}
|
||||
else
|
||||
{
|
||||
parse_for_in (outermost_stmt_label_p);
|
||||
jsp_parse_for_in_statement (outermost_stmt_label_p, for_body_statement_loc);
|
||||
}
|
||||
} /* jsp_parse_for_or_for_in_statement */
|
||||
|
||||
|
||||
@ -306,6 +306,7 @@ generate_opcode (scopes_tree tree, opcode_counter_t opc_index, lit_id_hash_table
|
||||
case OPCODE (obj_decl):
|
||||
case OPCODE (this_binding):
|
||||
case OPCODE (with):
|
||||
case OPCODE (for_in):
|
||||
case OPCODE (throw_value):
|
||||
case OPCODE (is_true_jmp_up):
|
||||
case OPCODE (is_true_jmp_down):
|
||||
|
||||
@ -232,6 +232,7 @@ pp_op_meta (const opcode_t *opcodes_p,
|
||||
PP_OP (delete_prop, "%s = delete %s.%s;");
|
||||
PP_OP (typeof, "%s = typeof %s;");
|
||||
PP_OP (with, "with (%s);");
|
||||
PP_OP (for_in, "for_in (%s);");
|
||||
case NAME_TO_ID (is_true_jmp_up): printf ("if (%s) goto %d;", VAR (1), oc - OC (2, 3)); break;
|
||||
case NAME_TO_ID (is_false_jmp_up): printf ("if (%s == false) goto %d;", VAR (1), oc - OC (2, 3)); break;
|
||||
case NAME_TO_ID (is_true_jmp_down): printf ("if (%s) goto %d;", VAR (1), oc + OC (2, 3)); break;
|
||||
@ -554,6 +555,11 @@ pp_op_meta (const opcode_t *opcodes_p,
|
||||
printf ("end with;");
|
||||
break;
|
||||
}
|
||||
case OPCODE_META_TYPE_END_FOR_IN:
|
||||
{
|
||||
printf ("end for-in;");
|
||||
break;
|
||||
}
|
||||
case OPCODE_META_TYPE_FUNCTION_END:
|
||||
{
|
||||
printf ("function end: %d;", oc + OC (2, 3));
|
||||
|
||||
283
tests/jerry/for-in.js
Normal file
283
tests/jerry/for-in.js
Normal file
@ -0,0 +1,283 @@
|
||||
// Copyright 2015 Samsung Electronics Co., Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// 1.
|
||||
var simple_obj = {a: 1, b: 2, c: 3, d: 4};
|
||||
for (var prop_of_simple_obj in simple_obj) {
|
||||
simple_obj[prop_of_simple_obj] += 4;
|
||||
}
|
||||
|
||||
assert(simple_obj.a === 5
|
||||
&& simple_obj.b === 6
|
||||
&& simple_obj.c === 7
|
||||
&& simple_obj.d === 8);
|
||||
|
||||
// 2.
|
||||
for
|
||||
(
|
||||
var
|
||||
prop_of_simple_obj in simple_obj
|
||||
) {
|
||||
simple_obj[prop_of_simple_obj] -= 4;
|
||||
}
|
||||
|
||||
assert(simple_obj.a === 1
|
||||
&& simple_obj.b === 2
|
||||
&& simple_obj.c === 3
|
||||
&& simple_obj.d === 4);
|
||||
|
||||
// 3.
|
||||
function test() {
|
||||
var cnt = 0;
|
||||
|
||||
for (var prop_of_simple_obj in simple_obj) {
|
||||
if (prop_of_simple_obj === 'b')
|
||||
continue;
|
||||
|
||||
cnt++;
|
||||
|
||||
simple_obj[prop_of_simple_obj] += 4;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
var ret_val = test();
|
||||
|
||||
assert((simple_obj.a === 5
|
||||
&& simple_obj.b === 2
|
||||
&& simple_obj.c === 7
|
||||
&& simple_obj.d == 8)
|
||||
&& ret_val === 3);
|
||||
|
||||
// 4.
|
||||
var array_obj = new Array(1, 2, 3, 4, 5, 6, 7);
|
||||
var prop_of_array_obj;
|
||||
|
||||
array_obj.eight = 8;
|
||||
|
||||
for (prop_of_array_obj in array_obj) {
|
||||
array_obj[prop_of_array_obj] += 1;
|
||||
}
|
||||
|
||||
assert(array_obj[0] === 2
|
||||
&& array_obj[1] === 3
|
||||
&& array_obj[2] === 4
|
||||
&& array_obj[3] === 5
|
||||
&& array_obj[4] === 6
|
||||
&& array_obj[5] === 7
|
||||
&& array_obj[6] === 8
|
||||
&& array_obj['eight'] === 9);
|
||||
|
||||
// 5.
|
||||
var null_obj = null;
|
||||
for (var prop_of_null_obj in null_obj) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// 6.
|
||||
var empty_object = {};
|
||||
for (var prop_of_empty_object in empty_object) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// 7.
|
||||
for (var i in undefined) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// 8.
|
||||
var base_obj = {base_prop: "base"};
|
||||
|
||||
function constr() {
|
||||
this.derived_prop = "derived";
|
||||
}
|
||||
|
||||
constr.prototype = base_obj;
|
||||
|
||||
var derived_obj = new constr();
|
||||
|
||||
for (var prop_of_derived_obj in derived_obj) {
|
||||
derived_obj[prop_of_derived_obj] += "A";
|
||||
}
|
||||
|
||||
assert(derived_obj.base_prop === "baseA" && derived_obj.derived_prop === "derivedA");
|
||||
|
||||
// 9.
|
||||
log = {};
|
||||
count = 0;
|
||||
|
||||
for (i in {q : 1})
|
||||
{
|
||||
log [i] = true;
|
||||
count++;
|
||||
}
|
||||
|
||||
assert (count == 1 && 'q' in log);
|
||||
|
||||
// 10.
|
||||
log = {};
|
||||
count = 0;
|
||||
|
||||
for (i in {q : 1, p : 2, get f() { ; }, set f (v) { ; }, get t () { }, set c (v) {}})
|
||||
{
|
||||
log [i] = true;
|
||||
count++;
|
||||
}
|
||||
|
||||
assert (count == 5
|
||||
&& 'q' in log
|
||||
&& 'p' in log
|
||||
&& 'f' in log
|
||||
&& 't' in log
|
||||
&& 'c' in log);
|
||||
|
||||
// 11.
|
||||
log = {};
|
||||
count = 0;
|
||||
|
||||
var a = [];
|
||||
a[5] = 5;
|
||||
for (var x in a)
|
||||
{
|
||||
log[x] = true;
|
||||
count++;
|
||||
}
|
||||
|
||||
assert (count == 1
|
||||
&& '5' in log);
|
||||
|
||||
// 12.
|
||||
log = {};
|
||||
count = 0;
|
||||
|
||||
q = { c : 3, d : 4 };
|
||||
|
||||
function p_constructor ()
|
||||
{
|
||||
this.a = 1;
|
||||
this.b = 2;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
p_constructor.prototype = q;
|
||||
p = new p_constructor ();
|
||||
|
||||
Object.defineProperty (p, 'h', { value : 5, enumerable : false, configurable : true });
|
||||
Object.defineProperty (q, 'h', { value : 6, enumerable : true, configurable : true });
|
||||
|
||||
for (var i in p)
|
||||
{
|
||||
log[i] = true;
|
||||
count++;
|
||||
}
|
||||
|
||||
assert (count == 4
|
||||
&& 'a' in log
|
||||
&& 'b' in log
|
||||
&& 'c' in log
|
||||
&& 'd' in log);
|
||||
|
||||
// 13.
|
||||
log = {};
|
||||
count = 0;
|
||||
|
||||
function f()
|
||||
{
|
||||
var tmp = { a: 1, b: 2, c: 3, d: 4 };
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
for (var i in f())
|
||||
{
|
||||
log[i] = true;
|
||||
count++;
|
||||
}
|
||||
|
||||
assert (count == 4
|
||||
&& 'a' in log
|
||||
&& 'b' in log
|
||||
&& 'c' in log
|
||||
&& 'd' in log);
|
||||
|
||||
// 14.
|
||||
log = {};
|
||||
count = 0;
|
||||
|
||||
b = 'prop';
|
||||
c = { prop : 1 };
|
||||
Boolean.prototype.boolean_prototype_prop = 1;
|
||||
|
||||
for (a in b in c)
|
||||
{
|
||||
log[a] = true;
|
||||
count++;
|
||||
}
|
||||
|
||||
assert (count == 1
|
||||
&& 'boolean_prototype_prop' in log);
|
||||
|
||||
// 15.
|
||||
log = {};
|
||||
count = 0;
|
||||
|
||||
for (a in 'prop' in { prop : 1 })
|
||||
{
|
||||
log[a] = true;
|
||||
count++;
|
||||
}
|
||||
|
||||
assert (count == 1
|
||||
&& 'boolean_prototype_prop' in log);
|
||||
|
||||
// 16.
|
||||
a = 'str';
|
||||
b = {};
|
||||
for ((a in b) ; ; )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// 17.
|
||||
log = {};
|
||||
count = 0;
|
||||
|
||||
var base_obj = { base_prop1: "base1", base_prop2: "base2" };
|
||||
|
||||
function l () {
|
||||
this.derived_prop1 = "derived1";
|
||||
this.derived_prop2 = "derived2";
|
||||
}
|
||||
|
||||
l.prototype = base_obj;
|
||||
|
||||
var derived_obj = new l();
|
||||
|
||||
for (var prop_of_derived_obj in derived_obj) {
|
||||
delete derived_obj.derived_prop1;
|
||||
delete derived_obj.derived_prop2;
|
||||
delete base_obj.base_prop1;
|
||||
delete base_obj.base_prop2;
|
||||
|
||||
log[prop_of_derived_obj] = true;
|
||||
count++;
|
||||
}
|
||||
|
||||
assert(count == 1
|
||||
&& ('base_prop1' in log
|
||||
|| 'base_prop2' in log
|
||||
|| 'derived_prop1' in log
|
||||
|| 'derived_prop2' in log));
|
||||
Loading…
x
Reference in New Issue
Block a user