mirror of
				https://github.com/bellard/quickjs.git
				synced 2025-05-29 01:49:18 +08:00 
			
		
		
		
	fixed module cyclic imports (#329)
This commit is contained in:
		
							parent
							
								
									c1bf4e99db
								
							
						
					
					
						commit
						159fe289e3
					
				
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							@ -435,6 +435,7 @@ test: qjs
 | 
				
			|||||||
	./qjs tests/test_bigint.js
 | 
						./qjs tests/test_bigint.js
 | 
				
			||||||
	./qjs tests/test_std.js
 | 
						./qjs tests/test_std.js
 | 
				
			||||||
	./qjs tests/test_worker.js
 | 
						./qjs tests/test_worker.js
 | 
				
			||||||
 | 
						./qjs tests/test_cyclic_import.js
 | 
				
			||||||
ifdef CONFIG_SHARED_LIBS
 | 
					ifdef CONFIG_SHARED_LIBS
 | 
				
			||||||
	./qjs tests/test_bjson.js
 | 
						./qjs tests/test_bjson.js
 | 
				
			||||||
	./qjs examples/test_point.js
 | 
						./qjs examples/test_point.js
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										58
									
								
								quickjs.c
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								quickjs.c
									
									
									
									
									
								
							@ -7428,12 +7428,14 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
 | 
				
			|||||||
    JSValue val;
 | 
					    JSValue val;
 | 
				
			||||||
    JSContext *realm;
 | 
					    JSContext *realm;
 | 
				
			||||||
    JSAutoInitFunc *func;
 | 
					    JSAutoInitFunc *func;
 | 
				
			||||||
 | 
					    JSAutoInitIDEnum id;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (js_shape_prepare_update(ctx, p, &prs))
 | 
					    if (js_shape_prepare_update(ctx, p, &prs))
 | 
				
			||||||
        return -1;
 | 
					        return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    realm = js_autoinit_get_realm(pr);
 | 
					    realm = js_autoinit_get_realm(pr);
 | 
				
			||||||
    func = js_autoinit_func_table[js_autoinit_get_id(pr)];
 | 
					    id = js_autoinit_get_id(pr);
 | 
				
			||||||
 | 
					    func = js_autoinit_func_table[id];
 | 
				
			||||||
    /* 'func' shall not modify the object properties 'pr' */
 | 
					    /* 'func' shall not modify the object properties 'pr' */
 | 
				
			||||||
    val = func(realm, p, prop, pr->u.init.opaque);
 | 
					    val = func(realm, p, prop, pr->u.init.opaque);
 | 
				
			||||||
    js_autoinit_free(ctx->rt, pr);
 | 
					    js_autoinit_free(ctx->rt, pr);
 | 
				
			||||||
@ -7441,7 +7443,15 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
 | 
				
			|||||||
    pr->u.value = JS_UNDEFINED;
 | 
					    pr->u.value = JS_UNDEFINED;
 | 
				
			||||||
    if (JS_IsException(val))
 | 
					    if (JS_IsException(val))
 | 
				
			||||||
        return -1;
 | 
					        return -1;
 | 
				
			||||||
    pr->u.value = val;
 | 
					    if (id == JS_AUTOINIT_ID_MODULE_NS &&
 | 
				
			||||||
 | 
					        JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
 | 
				
			||||||
 | 
					        /* WARNING: a varref is returned as a string  ! */
 | 
				
			||||||
 | 
					        prs->flags |= JS_PROP_VARREF;
 | 
				
			||||||
 | 
					        pr->u.var_ref = JS_VALUE_GET_PTR(val);
 | 
				
			||||||
 | 
					        pr->u.var_ref->header.ref_count++;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        pr->u.value = val;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -27600,7 +27610,7 @@ static void js_resolve_export_throw_error(JSContext *ctx,
 | 
				
			|||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
    EXPORTED_NAME_AMBIGUOUS,
 | 
					    EXPORTED_NAME_AMBIGUOUS,
 | 
				
			||||||
    EXPORTED_NAME_NORMAL,
 | 
					    EXPORTED_NAME_NORMAL,
 | 
				
			||||||
    EXPORTED_NAME_NS,
 | 
					    EXPORTED_NAME_DELAYED,
 | 
				
			||||||
} ExportedNameEntryEnum;
 | 
					} ExportedNameEntryEnum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct ExportedNameEntry {
 | 
					typedef struct ExportedNameEntry {
 | 
				
			||||||
@ -27609,7 +27619,6 @@ typedef struct ExportedNameEntry {
 | 
				
			|||||||
    union {
 | 
					    union {
 | 
				
			||||||
        JSExportEntry *me; /* using when the list is built */
 | 
					        JSExportEntry *me; /* using when the list is built */
 | 
				
			||||||
        JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */
 | 
					        JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */
 | 
				
			||||||
        JSModuleDef *module; /* for EXPORTED_NAME_NS */
 | 
					 | 
				
			||||||
    } u;
 | 
					    } u;
 | 
				
			||||||
} ExportedNameEntry;
 | 
					} ExportedNameEntry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -27719,7 +27728,29 @@ static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
 | 
				
			|||||||
                                     void *opaque)
 | 
					                                     void *opaque)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    JSModuleDef *m = opaque;
 | 
					    JSModuleDef *m = opaque;
 | 
				
			||||||
    return JS_GetModuleNamespace(ctx, m);
 | 
					    JSResolveResultEnum res;
 | 
				
			||||||
 | 
					    JSExportEntry *res_me;
 | 
				
			||||||
 | 
					    JSModuleDef *res_m;
 | 
				
			||||||
 | 
					    JSVarRef *var_ref;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res = js_resolve_export(ctx, &res_m, &res_me, m, atom);
 | 
				
			||||||
 | 
					    if (res != JS_RESOLVE_RES_FOUND) {
 | 
				
			||||||
 | 
					        /* fail safe: normally no error should happen here except for memory */
 | 
				
			||||||
 | 
					        js_resolve_export_throw_error(ctx, res, m, atom);
 | 
				
			||||||
 | 
					        return JS_EXCEPTION;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (res_me->local_name == JS_ATOM__star_) {
 | 
				
			||||||
 | 
					        return JS_GetModuleNamespace(ctx, res_m->req_module_entries[res_me->u.req_module_idx].module);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (res_me->u.local.var_ref) {
 | 
				
			||||||
 | 
					            var_ref = res_me->u.local.var_ref;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
 | 
				
			||||||
 | 
					            var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        /* WARNING: a varref is returned as a string ! */
 | 
				
			||||||
 | 
					        return JS_MKPTR(JS_TAG_STRING, var_ref);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
 | 
					static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
 | 
				
			||||||
@ -27764,17 +27795,18 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
 | 
				
			|||||||
            en->export_type = EXPORTED_NAME_AMBIGUOUS;
 | 
					            en->export_type = EXPORTED_NAME_AMBIGUOUS;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            if (res_me->local_name == JS_ATOM__star_) {
 | 
					            if (res_me->local_name == JS_ATOM__star_) {
 | 
				
			||||||
                en->export_type = EXPORTED_NAME_NS;
 | 
					                en->export_type = EXPORTED_NAME_DELAYED;
 | 
				
			||||||
                en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module;
 | 
					 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                en->export_type = EXPORTED_NAME_NORMAL;
 | 
					 | 
				
			||||||
                if (res_me->u.local.var_ref) {
 | 
					                if (res_me->u.local.var_ref) {
 | 
				
			||||||
                    en->u.var_ref = res_me->u.local.var_ref;
 | 
					                    en->u.var_ref = res_me->u.local.var_ref;
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
 | 
					                    JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
 | 
				
			||||||
                    p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
 | 
					 | 
				
			||||||
                    en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
 | 
					                    en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                if (en->u.var_ref == NULL)
 | 
				
			||||||
 | 
					                    en->export_type = EXPORTED_NAME_DELAYED;
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    en->export_type = EXPORTED_NAME_NORMAL;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -27798,13 +27830,13 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
 | 
				
			|||||||
                pr->u.var_ref = var_ref;
 | 
					                pr->u.var_ref = var_ref;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case EXPORTED_NAME_NS:
 | 
					        case EXPORTED_NAME_DELAYED:
 | 
				
			||||||
            /* the exported namespace must be created on demand */
 | 
					            /* the exported namespace or reference may depend on
 | 
				
			||||||
 | 
					               circular references, so we resolve it lazily */
 | 
				
			||||||
            if (JS_DefineAutoInitProperty(ctx, obj,
 | 
					            if (JS_DefineAutoInitProperty(ctx, obj,
 | 
				
			||||||
                                          en->export_name,
 | 
					                                          en->export_name,
 | 
				
			||||||
                                          JS_AUTOINIT_ID_MODULE_NS,
 | 
					                                          JS_AUTOINIT_ID_MODULE_NS,
 | 
				
			||||||
                                          en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
 | 
					                                          m, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
 | 
				
			||||||
                goto fail;
 | 
					 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										49
									
								
								tests/assert.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								tests/assert.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					export function assert(actual, expected, message) {
 | 
				
			||||||
 | 
					    if (arguments.length === 1)
 | 
				
			||||||
 | 
					        expected = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (typeof actual === typeof expected) {
 | 
				
			||||||
 | 
					        if (actual === expected) {
 | 
				
			||||||
 | 
					            if (actual !== 0 || (1 / actual) === (1 / expected))
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (typeof actual === 'number') {
 | 
				
			||||||
 | 
					            if (isNaN(actual) && isNaN(expected))
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (typeof actual === 'object') {
 | 
				
			||||||
 | 
					            if (actual !== null && expected !== null
 | 
				
			||||||
 | 
					            &&  actual.constructor === expected.constructor
 | 
				
			||||||
 | 
					            &&  actual.toString() === expected.toString())
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    throw Error("assertion failed: got |" + actual + "|" +
 | 
				
			||||||
 | 
					                ", expected |" + expected + "|" +
 | 
				
			||||||
 | 
					                (message ? " (" + message + ")" : ""));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function assertThrows(err, func)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    var ex;
 | 
				
			||||||
 | 
					    ex = false;
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        func();
 | 
				
			||||||
 | 
					    } catch(e) {
 | 
				
			||||||
 | 
					        ex = true;
 | 
				
			||||||
 | 
					        assert(e instanceof err);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    assert(ex, true, "exception expected");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function assertArrayEquals(a, b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!Array.isArray(a) || !Array.isArray(b))
 | 
				
			||||||
 | 
					        return assert(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(a.length, b.length);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    a.forEach((value, idx) => {
 | 
				
			||||||
 | 
					        assert(b[idx], value);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								tests/fixture_cyclic_import.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/fixture_cyclic_import.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					import * as a from "./test_cyclic_import.js"
 | 
				
			||||||
 | 
					export function f(x) { return 2 * a.g(x) }
 | 
				
			||||||
							
								
								
									
										12
									
								
								tests/test_cyclic_import.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/test_cyclic_import.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					/*---
 | 
				
			||||||
 | 
					negative:
 | 
				
			||||||
 | 
					  phase: resolution
 | 
				
			||||||
 | 
					  type: SyntaxError
 | 
				
			||||||
 | 
					---*/
 | 
				
			||||||
 | 
					// FIXME(bnoordhuis) shouldn't throw SyntaxError but that's still better
 | 
				
			||||||
 | 
					// than segfaulting, see https://github.com/quickjs-ng/quickjs/issues/567
 | 
				
			||||||
 | 
					import {assert} from "./assert.js"
 | 
				
			||||||
 | 
					import {f} from "./fixture_cyclic_import.js"
 | 
				
			||||||
 | 
					export {f}
 | 
				
			||||||
 | 
					export function g(x) { return x + 1 }
 | 
				
			||||||
 | 
					assert(f(1), 4)
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user