| ... | @@ -739,6 +739,7 @@ pub const Scope = struct { | ... | @@ -739,6 +739,7 @@ pub const Scope = struct { |
| 739 | } | 739 | } |
| 740 | | 740 | |
| 741 | pub fn deinit(file: *File, gpa: *Allocator) void { | 741 | pub fn deinit(file: *File, gpa: *Allocator) void { |
| | 742 | gpa.free(file.sub_file_path); |
| 742 | file.unload(gpa); | 743 | file.unload(gpa); |
| 743 | file.* = undefined; | 744 | file.* = undefined; |
| 744 | } | 745 | } |
| ... | @@ -746,6 +747,11 @@ pub const Scope = struct { | ... | @@ -746,6 +747,11 @@ pub const Scope = struct { |
| 746 | pub fn getSource(file: *File, gpa: *Allocator) ![:0]const u8 { | 747 | pub fn getSource(file: *File, gpa: *Allocator) ![:0]const u8 { |
| 747 | if (file.source_loaded) return file.source; | 748 | if (file.source_loaded) return file.source; |
| 748 | | 749 | |
| | 750 | const root_dir_path = file.pkg.root_src_directory.path orelse "."; |
| | 751 | log.debug("File.getSource, not cached. pkgdir={s} sub_file_path={s}", .{ |
| | 752 | root_dir_path, file.sub_file_path, |
| | 753 | }); |
| | 754 | |
| 749 | // Keep track of inode, file size, mtime, hash so we can detect which files | 755 | // Keep track of inode, file size, mtime, hash so we can detect which files |
| 750 | // have been modified when an incremental update is requested. | 756 | // have been modified when an incremental update is requested. |
| 751 | var f = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{}); | 757 | var f = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{}); |
| ... | @@ -2633,20 +2639,70 @@ pub const ImportFileResult = struct { | ... | @@ -2633,20 +2639,70 @@ pub const ImportFileResult = struct { |
| 2633 | is_new: bool, | 2639 | is_new: bool, |
| 2634 | }; | 2640 | }; |
| 2635 | | 2641 | |
| | 2642 | pub fn importPkg(mod: *Module, cur_pkg: *Package, pkg: *Package) !ImportFileResult { |
| | 2643 | const gpa = mod.gpa; |
| | 2644 | |
| | 2645 | // The resolved path is used as the key in the import table, to detect if |
| | 2646 | // an import refers to the same as another, despite different relative paths |
| | 2647 | // or differently mapped package names. |
| | 2648 | const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{ |
| | 2649 | pkg.root_src_directory.path orelse ".", pkg.root_src_path, |
| | 2650 | }); |
| | 2651 | var keep_resolved_path = false; |
| | 2652 | defer if (!keep_resolved_path) gpa.free(resolved_path); |
| | 2653 | |
| | 2654 | const gop = try mod.import_table.getOrPut(gpa, resolved_path); |
| | 2655 | if (gop.found_existing) return ImportFileResult{ |
| | 2656 | .file = gop.entry.value, |
| | 2657 | .is_new = false, |
| | 2658 | }; |
| | 2659 | keep_resolved_path = true; // It's now owned by import_table. |
| | 2660 | |
| | 2661 | const sub_file_path = try gpa.dupe(u8, pkg.root_src_path); |
| | 2662 | errdefer gpa.free(sub_file_path); |
| | 2663 | |
| | 2664 | const new_file = try gpa.create(Scope.File); |
| | 2665 | errdefer gpa.destroy(new_file); |
| | 2666 | |
| | 2667 | gop.entry.value = new_file; |
| | 2668 | new_file.* = .{ |
| | 2669 | .sub_file_path = sub_file_path, |
| | 2670 | .source = undefined, |
| | 2671 | .source_loaded = false, |
| | 2672 | .tree_loaded = false, |
| | 2673 | .zir_loaded = false, |
| | 2674 | .stat_size = undefined, |
| | 2675 | .stat_inode = undefined, |
| | 2676 | .stat_mtime = undefined, |
| | 2677 | .tree = undefined, |
| | 2678 | .zir = undefined, |
| | 2679 | .status = .never_loaded, |
| | 2680 | .pkg = pkg, |
| | 2681 | .namespace = undefined, |
| | 2682 | }; |
| | 2683 | return ImportFileResult{ |
| | 2684 | .file = new_file, |
| | 2685 | .is_new = true, |
| | 2686 | }; |
| | 2687 | } |
| | 2688 | |
| 2636 | pub fn importFile( | 2689 | pub fn importFile( |
| 2637 | mod: *Module, | 2690 | mod: *Module, |
| 2638 | cur_pkg: *Package, | 2691 | cur_file: *Scope.File, |
| 2639 | import_string: []const u8, | 2692 | import_string: []const u8, |
| 2640 | ) !ImportFileResult { | 2693 | ) !ImportFileResult { |
| | 2694 | if (cur_file.pkg.table.get(import_string)) |pkg| { |
| | 2695 | return mod.importPkg(cur_file.pkg, pkg); |
| | 2696 | } |
| 2641 | const gpa = mod.gpa; | 2697 | const gpa = mod.gpa; |
| 2642 | | 2698 | |
| 2643 | const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse "."; | 2699 | // The resolved path is used as the key in the import table, to detect if |
| 2644 | const found_pkg = cur_pkg.table.get(import_string); | 2700 | // an import refers to the same as another, despite different relative paths |
| 2645 | | 2701 | // or differently mapped package names. |
| 2646 | const resolved_path = if (found_pkg) |pkg| | 2702 | const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse "."; |
| 2647 | try std.fs.path.resolve(gpa, &[_][]const u8{ pkg.root_src_directory.path orelse ".", pkg.root_src_path }) | 2703 | const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{ |
| 2648 | else | 2704 | cur_pkg_dir_path, cur_file.sub_file_path, "..", import_string, |
| 2649 | try std.fs.path.resolve(gpa, &[_][]const u8{ cur_pkg_dir_path, import_string }); | 2705 | }); |
| 2650 | var keep_resolved_path = false; | 2706 | var keep_resolved_path = false; |
| 2651 | defer if (!keep_resolved_path) gpa.free(resolved_path); | 2707 | defer if (!keep_resolved_path) gpa.free(resolved_path); |
| 2652 | | 2708 | |
| ... | @@ -2655,20 +2711,28 @@ pub fn importFile( | ... | @@ -2655,20 +2711,28 @@ pub fn importFile( |
| 2655 | .file = gop.entry.value, | 2711 | .file = gop.entry.value, |
| 2656 | .is_new = false, | 2712 | .is_new = false, |
| 2657 | }; | 2713 | }; |
| | 2714 | keep_resolved_path = true; // It's now owned by import_table. |
| 2658 | | 2715 | |
| 2659 | if (found_pkg == null) { | 2716 | const new_file = try gpa.create(Scope.File); |
| 2660 | const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path}); | 2717 | errdefer gpa.destroy(new_file); |
| 2661 | defer gpa.free(resolved_root_path); | | |
| 2662 | | 2718 | |
| 2663 | if (!mem.startsWith(u8, resolved_path, resolved_root_path)) { | 2719 | const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path}); |
| 2664 | return error.ImportOutsidePkgPath; | 2720 | defer gpa.free(resolved_root_path); |
| 2665 | } | 2721 | |
| | 2722 | if (!mem.startsWith(u8, resolved_path, resolved_root_path)) { |
| | 2723 | return error.ImportOutsidePkgPath; |
| 2666 | } | 2724 | } |
| | 2725 | // +1 for the directory separator here. |
| | 2726 | const sub_file_path = try gpa.dupe(u8, resolved_path[resolved_root_path.len + 1 ..]); |
| | 2727 | errdefer gpa.free(sub_file_path); |
| | 2728 | |
| | 2729 | log.debug("new importFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, import_string={s}", .{ |
| | 2730 | resolved_root_path, resolved_path, sub_file_path, import_string, |
| | 2731 | }); |
| 2667 | | 2732 | |
| 2668 | const new_file = try gpa.create(Scope.File); | | |
| 2669 | gop.entry.value = new_file; | 2733 | gop.entry.value = new_file; |
| 2670 | new_file.* = .{ | 2734 | new_file.* = .{ |
| 2671 | .sub_file_path = resolved_path, | 2735 | .sub_file_path = sub_file_path, |
| 2672 | .source = undefined, | 2736 | .source = undefined, |
| 2673 | .source_loaded = false, | 2737 | .source_loaded = false, |
| 2674 | .tree_loaded = false, | 2738 | .tree_loaded = false, |
| ... | @@ -2679,10 +2743,9 @@ pub fn importFile( | ... | @@ -2679,10 +2743,9 @@ pub fn importFile( |
| 2679 | .tree = undefined, | 2743 | .tree = undefined, |
| 2680 | .zir = undefined, | 2744 | .zir = undefined, |
| 2681 | .status = .never_loaded, | 2745 | .status = .never_loaded, |
| 2682 | .pkg = found_pkg orelse cur_pkg, | 2746 | .pkg = cur_file.pkg, |
| 2683 | .namespace = undefined, | 2747 | .namespace = undefined, |
| 2684 | }; | 2748 | }; |
| 2685 | keep_resolved_path = true; | | |
| 2686 | return ImportFileResult{ | 2749 | return ImportFileResult{ |
| 2687 | .file = new_file, | 2750 | .file = new_file, |
| 2688 | .is_new = true, | 2751 | .is_new = true, |