mirror of
				https://github.com/bellard/quickjs.git
				synced 2025-05-29 01:49:18 +08:00 
			
		
		
		
	optional chaining fixes (github issue #103)
This commit is contained in:
		
							parent
							
								
									e1e65aca91
								
							
						
					
					
						commit
						f25e5d4094
					
				@ -286,6 +286,8 @@ def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phas
 | 
				
			|||||||
def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
 | 
					def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
 | 
				
			||||||
def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
 | 
					def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
 | 
				
			||||||
def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */
 | 
					def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */
 | 
				
			||||||
 | 
					def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */
 | 
				
			||||||
 | 
					def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */
 | 
				
			||||||
def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
 | 
					def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
def(       line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
 | 
					def(       line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										116
									
								
								quickjs.c
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								quickjs.c
									
									
									
									
									
								
							@ -21530,6 +21530,14 @@ static int new_label(JSParseState *s)
 | 
				
			|||||||
    return new_label_fd(s->cur_func, -1);
 | 
					    return new_label_fd(s->cur_func, -1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* don't update the last opcode and don't emit line number info */
 | 
				
			||||||
 | 
					static void emit_label_raw(JSParseState *s, int label)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    emit_u8(s, OP_label);
 | 
				
			||||||
 | 
					    emit_u32(s, label);
 | 
				
			||||||
 | 
					    s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* return the label ID offset */
 | 
					/* return the label ID offset */
 | 
				
			||||||
static int emit_label(JSParseState *s, int label)
 | 
					static int emit_label(JSParseState *s, int label)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -24643,6 +24651,25 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
 | 
				
			|||||||
                    fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
 | 
					                    fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
 | 
				
			||||||
                    drop_count = 2;
 | 
					                    drop_count = 2;
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					                case OP_get_field_opt_chain:
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        int opt_chain_label, next_label;
 | 
				
			||||||
 | 
					                        opt_chain_label = get_u32(fd->byte_code.buf +
 | 
				
			||||||
 | 
					                                                  fd->last_opcode_pos + 1 + 4 + 1);
 | 
				
			||||||
 | 
					                        /* keep the object on the stack */
 | 
				
			||||||
 | 
					                        fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
 | 
				
			||||||
 | 
					                        fd->byte_code.size = fd->last_opcode_pos + 1 + 4;
 | 
				
			||||||
 | 
					                        next_label = emit_goto(s, OP_goto, -1);
 | 
				
			||||||
 | 
					                        emit_label(s, opt_chain_label);
 | 
				
			||||||
 | 
					                        /* need an additional undefined value for the
 | 
				
			||||||
 | 
					                           case where the optional field does not
 | 
				
			||||||
 | 
					                           exists */
 | 
				
			||||||
 | 
					                        emit_op(s, OP_undefined);
 | 
				
			||||||
 | 
					                        emit_label(s, next_label);
 | 
				
			||||||
 | 
					                        drop_count = 2;
 | 
				
			||||||
 | 
					                        opcode = OP_get_field;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
                case OP_scope_get_private_field:
 | 
					                case OP_scope_get_private_field:
 | 
				
			||||||
                    /* keep the object on the stack */
 | 
					                    /* keep the object on the stack */
 | 
				
			||||||
                    fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;
 | 
					                    fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;
 | 
				
			||||||
@ -24653,6 +24680,25 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
 | 
				
			|||||||
                    fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
 | 
					                    fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
 | 
				
			||||||
                    drop_count = 2;
 | 
					                    drop_count = 2;
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					                case OP_get_array_el_opt_chain:
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        int opt_chain_label, next_label;
 | 
				
			||||||
 | 
					                        opt_chain_label = get_u32(fd->byte_code.buf +
 | 
				
			||||||
 | 
					                                                  fd->last_opcode_pos + 1 + 1);
 | 
				
			||||||
 | 
					                        /* keep the object on the stack */
 | 
				
			||||||
 | 
					                        fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
 | 
				
			||||||
 | 
					                        fd->byte_code.size = fd->last_opcode_pos + 1;
 | 
				
			||||||
 | 
					                        next_label = emit_goto(s, OP_goto, -1);
 | 
				
			||||||
 | 
					                        emit_label(s, opt_chain_label);
 | 
				
			||||||
 | 
					                        /* need an additional undefined value for the
 | 
				
			||||||
 | 
					                           case where the optional field does not
 | 
				
			||||||
 | 
					                           exists */
 | 
				
			||||||
 | 
					                        emit_op(s, OP_undefined);
 | 
				
			||||||
 | 
					                        emit_label(s, next_label);
 | 
				
			||||||
 | 
					                        drop_count = 2;
 | 
				
			||||||
 | 
					                        opcode = OP_get_array_el;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
                case OP_scope_get_var:
 | 
					                case OP_scope_get_var:
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        JSAtom name;
 | 
					                        JSAtom name;
 | 
				
			||||||
@ -24935,8 +24981,23 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
 | 
				
			|||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (optional_chaining_label >= 0)
 | 
					    if (optional_chaining_label >= 0) {
 | 
				
			||||||
        emit_label(s, optional_chaining_label);
 | 
					        JSFunctionDef *fd = s->cur_func;
 | 
				
			||||||
 | 
					        int opcode;
 | 
				
			||||||
 | 
					        emit_label_raw(s, optional_chaining_label);
 | 
				
			||||||
 | 
					        /* modify the last opcode so that it is an indicator of an
 | 
				
			||||||
 | 
					           optional chain */
 | 
				
			||||||
 | 
					        opcode = get_prev_opcode(fd);
 | 
				
			||||||
 | 
					        if (opcode == OP_get_field || opcode == OP_get_array_el) {
 | 
				
			||||||
 | 
					            if (opcode == OP_get_field)
 | 
				
			||||||
 | 
					                opcode = OP_get_field_opt_chain;
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                opcode = OP_get_array_el_opt_chain;
 | 
				
			||||||
 | 
					            fd->byte_code.buf[fd->last_opcode_pos] = opcode;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            fd->last_opcode_pos = -1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -24952,27 +25013,57 @@ static __exception int js_parse_delete(JSParseState *s)
 | 
				
			|||||||
        return -1;
 | 
					        return -1;
 | 
				
			||||||
    switch(opcode = get_prev_opcode(fd)) {
 | 
					    switch(opcode = get_prev_opcode(fd)) {
 | 
				
			||||||
    case OP_get_field:
 | 
					    case OP_get_field:
 | 
				
			||||||
 | 
					    case OP_get_field_opt_chain:
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            JSValue val;
 | 
					            JSValue val;
 | 
				
			||||||
            int ret;
 | 
					            int ret, opt_chain_label, next_label;
 | 
				
			||||||
 | 
					            if (opcode == OP_get_field_opt_chain) {
 | 
				
			||||||
 | 
					                opt_chain_label = get_u32(fd->byte_code.buf +
 | 
				
			||||||
 | 
					                                          fd->last_opcode_pos + 1 + 4 + 1);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                opt_chain_label = -1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
 | 
					            name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
 | 
				
			||||||
            fd->byte_code.size = fd->last_opcode_pos;
 | 
					            fd->byte_code.size = fd->last_opcode_pos;
 | 
				
			||||||
            fd->last_opcode_pos = -1;
 | 
					 | 
				
			||||||
            val = JS_AtomToValue(s->ctx, name);
 | 
					            val = JS_AtomToValue(s->ctx, name);
 | 
				
			||||||
            ret = emit_push_const(s, val, 1);
 | 
					            ret = emit_push_const(s, val, 1);
 | 
				
			||||||
            JS_FreeValue(s->ctx, val);
 | 
					            JS_FreeValue(s->ctx, val);
 | 
				
			||||||
            JS_FreeAtom(s->ctx, name);
 | 
					            JS_FreeAtom(s->ctx, name);
 | 
				
			||||||
            if (ret)
 | 
					            if (ret)
 | 
				
			||||||
                return ret;
 | 
					                return ret;
 | 
				
			||||||
 | 
					            emit_op(s, OP_delete);
 | 
				
			||||||
 | 
					            if (opt_chain_label >= 0) {
 | 
				
			||||||
 | 
					                next_label = emit_goto(s, OP_goto, -1);
 | 
				
			||||||
 | 
					                emit_label(s, opt_chain_label);
 | 
				
			||||||
 | 
					                /* if the optional chain is not taken, return 'true' */
 | 
				
			||||||
 | 
					                emit_op(s, OP_drop);
 | 
				
			||||||
 | 
					                emit_op(s, OP_push_true);
 | 
				
			||||||
 | 
					                emit_label(s, next_label);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            fd->last_opcode_pos = -1;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        goto do_delete;
 | 
					        break;
 | 
				
			||||||
    case OP_get_array_el:
 | 
					    case OP_get_array_el:
 | 
				
			||||||
        fd->byte_code.size = fd->last_opcode_pos;
 | 
					        fd->byte_code.size = fd->last_opcode_pos;
 | 
				
			||||||
        fd->last_opcode_pos = -1;
 | 
					        fd->last_opcode_pos = -1;
 | 
				
			||||||
    do_delete:
 | 
					 | 
				
			||||||
        emit_op(s, OP_delete);
 | 
					        emit_op(s, OP_delete);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					    case OP_get_array_el_opt_chain:
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            int opt_chain_label, next_label;
 | 
				
			||||||
 | 
					            opt_chain_label = get_u32(fd->byte_code.buf +
 | 
				
			||||||
 | 
					                                      fd->last_opcode_pos + 1 + 1);
 | 
				
			||||||
 | 
					            fd->byte_code.size = fd->last_opcode_pos;
 | 
				
			||||||
 | 
					            emit_op(s, OP_delete);
 | 
				
			||||||
 | 
					            next_label = emit_goto(s, OP_goto, -1);
 | 
				
			||||||
 | 
					            emit_label(s, opt_chain_label);
 | 
				
			||||||
 | 
					            /* if the optional chain is not taken, return 'true' */
 | 
				
			||||||
 | 
					            emit_op(s, OP_drop);
 | 
				
			||||||
 | 
					            emit_op(s, OP_push_true);
 | 
				
			||||||
 | 
					            emit_label(s, next_label);
 | 
				
			||||||
 | 
					            fd->last_opcode_pos = -1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
    case OP_scope_get_var:
 | 
					    case OP_scope_get_var:
 | 
				
			||||||
        /* 'delete this': this is not a reference */
 | 
					        /* 'delete this': this is not a reference */
 | 
				
			||||||
        name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
 | 
					        name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
 | 
				
			||||||
@ -31606,6 +31697,17 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
 | 
				
			|||||||
        case OP_set_class_name:
 | 
					        case OP_set_class_name:
 | 
				
			||||||
            /* only used during parsing */
 | 
					            /* only used during parsing */
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case OP_get_field_opt_chain: /* equivalent to OP_get_field */
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                JSAtom name = get_u32(bc_buf + pos + 1);
 | 
				
			||||||
 | 
					                dbuf_putc(&bc_out, OP_get_field);
 | 
				
			||||||
 | 
					                dbuf_put_u32(&bc_out, name);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case OP_get_array_el_opt_chain: /* equivalent to OP_get_array_el */
 | 
				
			||||||
 | 
					            dbuf_putc(&bc_out, OP_get_array_el);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
        no_change:
 | 
					        no_change:
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,4 @@ test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeErro
 | 
				
			|||||||
test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called
 | 
					test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called
 | 
				
			||||||
test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: unexpected error type: Test262: This statement should not be evaluated.
 | 
					test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: unexpected error type: Test262: This statement should not be evaluated.
 | 
				
			||||||
test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: strict mode: unexpected error type: Test262: This statement should not be evaluated.
 | 
					test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: strict mode: unexpected error type: Test262: This statement should not be evaluated.
 | 
				
			||||||
test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:21: TypeError: cannot read property 'c' of undefined
 | 
					 | 
				
			||||||
test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:15: strict mode: TypeError: cannot read property '_b' of undefined
 | 
					 | 
				
			||||||
test262/test/language/global-code/script-decl-lex-var-declared-via-eval-sloppy.js:13: Test262Error: variable Expected a SyntaxError to be thrown but no exception was thrown at all
 | 
					test262/test/language/global-code/script-decl-lex-var-declared-via-eval-sloppy.js:13: Test262Error: variable Expected a SyntaxError to be thrown but no exception was thrown at all
 | 
				
			||||||
 | 
				
			|||||||
@ -558,6 +558,31 @@ function test_parse_semicolon()
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* optional chaining tests not present in test262 */
 | 
				
			||||||
 | 
					function test_optional_chaining()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    var a, z;
 | 
				
			||||||
 | 
					    z = null;
 | 
				
			||||||
 | 
					    a = { b: { c: 2 } };
 | 
				
			||||||
 | 
					    assert(delete z?.b.c, true);
 | 
				
			||||||
 | 
					    assert(delete a?.b.c, true);
 | 
				
			||||||
 | 
					    assert(JSON.stringify(a), '{"b":{}}', "optional chaining delete");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    a = { b: { c: 2 } };
 | 
				
			||||||
 | 
					    assert(delete z?.b["c"], true);
 | 
				
			||||||
 | 
					    assert(delete a?.b["c"], true);
 | 
				
			||||||
 | 
					    assert(JSON.stringify(a), '{"b":{}}');
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    a = {
 | 
				
			||||||
 | 
					        b() { return this._b; },
 | 
				
			||||||
 | 
					        _b: { c: 42 }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert((a?.b)().c, 42);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert((a?.["b"])().c, 42);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test_op1();
 | 
					test_op1();
 | 
				
			||||||
test_cvt();
 | 
					test_cvt();
 | 
				
			||||||
test_eq();
 | 
					test_eq();
 | 
				
			||||||
@ -578,3 +603,4 @@ test_function_length();
 | 
				
			|||||||
test_argument_scope();
 | 
					test_argument_scope();
 | 
				
			||||||
test_function_expr_name();
 | 
					test_function_expr_name();
 | 
				
			||||||
test_parse_semicolon();
 | 
					test_parse_semicolon();
 | 
				
			||||||
 | 
					test_optional_chaining();
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user