| author | |
| committer | |
| log | f0ec308e26ff957c7fbb50ccc69d3d549c42c4da |
| tree | 5fbb35b30640f8c4fd616c3132d556af9a3bc11c |
| parent | 11e8afb37cea3d4b951165f382eeee36a653978f |
| parent | 5f5364ad73dc6c31e1e189596f407970f852701a |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
Refresh from original.20 files changed, 813 insertions(+), 294 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -491,6 +491,7 @@ set(ZIG_STD_FILES |
| 491 | 491 | "heap.zig" |
| 492 | 492 | "index.zig" |
| 493 | 493 | "io.zig" |
| 494 | "io/seekable_stream.zig" | |
| 494 | 495 | "json.zig" |
| 495 | 496 | "lazy_init.zig" |
| 496 | 497 | "linked_list.zig" |
ci/azure/linux_script+3| ... | ... | @@ -34,6 +34,9 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then |
| 34 | 34 | |
| 35 | 35 | SHASUM=$(sha256sum $ARTIFACTSDIR/$TARBALL | cut '-d ' -f1) |
| 36 | 36 | BYTESIZE=$(wc -c < $ARTIFACTSDIR/$TARBALL) |
| 37 | # `set -x` causes these variables to be mangled. | |
| 38 | # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html | |
| 39 | set +x | |
| 37 | 40 | echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" |
| 38 | 41 | echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" |
| 39 | 42 | echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" |
ci/azure/macos_script+3| ... | ... | @@ -98,6 +98,9 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then |
| 98 | 98 | |
| 99 | 99 | SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1) |
| 100 | 100 | BYTESIZE=$(wc -c < $TARBALL) |
| 101 | # `set -x` causes these variables to be mangled. | |
| 102 | # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html | |
| 103 | set +x | |
| 101 | 104 | echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" |
| 102 | 105 | echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" |
| 103 | 106 | echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" |
ci/azure/windows_upload+3| ... | ... | @@ -25,6 +25,9 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then |
| 25 | 25 | |
| 26 | 26 | SHASUM=$(sha256sum $TARBALL | cut '-d ' -f1) |
| 27 | 27 | BYTESIZE=$(wc -c < $TARBALL) |
| 28 | # `set -x` causes these variables to be mangled. | |
| 29 | # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html | |
| 30 | set +x | |
| 28 | 31 | echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" |
| 29 | 32 | echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" |
| 30 | 33 | echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" |
deps/lld/ELF/OutputSections.cpp+1-1| ... | ... | @@ -95,7 +95,7 @@ void OutputSection::addSection(InputSection *IS) { |
| 95 | 95 | Flags = IS->Flags; |
| 96 | 96 | } else { |
| 97 | 97 | // Otherwise, check if new type or flags are compatible with existing ones. |
| 98 | unsigned Mask = SHF_ALLOC | SHF_TLS | SHF_LINK_ORDER; | |
| 98 | unsigned Mask = SHF_TLS | SHF_LINK_ORDER; | |
| 99 | 99 | if ((Flags & Mask) != (IS->Flags & Mask)) |
| 100 | 100 | error("incompatible section flags for " + Name + "\n>>> " + toString(IS) + |
| 101 | 101 | ": 0x" + utohexstr(IS->Flags) + "\n>>> output section " + Name + |
doc/langref.html.in+19-5| ... | ... | @@ -8,7 +8,13 @@ |
| 8 | 8 | body{ |
| 9 | 9 | background-color:#111; |
| 10 | 10 | color: #bbb; |
| 11 | font-family: sans-serif; | |
| 11 | font-family: system-ui, | |
| 12 | /* Fallbacks for browsers that don't support system-ui */ | |
| 13 | /* https://caniuse.com/#search=system-ui */ | |
| 14 | -apple-system, /* iOS and macOS */ | |
| 15 | Roboto, /* Android */ | |
| 16 | "Segoe UI", /* Windows */ | |
| 17 | sans-serif; | |
| 12 | 18 | } |
| 13 | 19 | a { |
| 14 | 20 | color: #88f; |
| ... | ... | @@ -4264,13 +4270,21 @@ fn foo() i32 { |
| 4264 | 4270 | return 1234; |
| 4265 | 4271 | } |
| 4266 | 4272 | {#code_end#} |
| 4267 | <p>However, if the expression has type {#syntax#}void{#endsyntax#}:</p> | |
| 4273 | <p>However, if the expression has type {#syntax#}void{#endsyntax#}, there will be no error. Function return values can also be explicitly ignored by assigning them to {#syntax#}_{#endsyntax#}. </p> | |
| 4268 | 4274 | {#code_begin|test#} |
| 4269 | test "ignoring expression value" { | |
| 4270 | foo(); | |
| 4275 | test "void is ignored" { | |
| 4276 | returnsVoid(); | |
| 4277 | } | |
| 4278 | ||
| 4279 | test "explicitly ignoring expression value" { | |
| 4280 | _ = foo(); | |
| 4271 | 4281 | } |
| 4272 | 4282 | |
| 4273 | fn foo() void {} | |
| 4283 | fn returnsVoid() void {} | |
| 4284 | ||
| 4285 | fn foo() i32 { | |
| 4286 | return 1234; | |
| 4287 | } | |
| 4274 | 4288 | {#code_end#} |
| 4275 | 4289 | {#header_close#} |
| 4276 | 4290 |
example/guess_number/main.zig+4-4| ... | ... | @@ -24,15 +24,15 @@ pub fn main() !void { |
| 24 | 24 | try stdout.print("\nGuess a number between 1 and 100: "); |
| 25 | 25 | var line_buf: [20]u8 = undefined; |
| 26 | 26 | |
| 27 | const line_len = io.readLine(line_buf[0..]) catch |err| switch (err) { | |
| 28 | error.InputTooLong => { | |
| 27 | const line = io.readLineSlice(line_buf[0..]) catch |err| switch (err) { | |
| 28 | error.OutOfMemory => { | |
| 29 | 29 | try stdout.print("Input too long.\n"); |
| 30 | 30 | continue; |
| 31 | 31 | }, |
| 32 | error.EndOfFile, error.StdInUnavailable => return err, | |
| 32 | else => return err, | |
| 33 | 33 | }; |
| 34 | 34 | |
| 35 | const guess = fmt.parseUnsigned(u8, line_buf[0..line_len], 10) catch { | |
| 35 | const guess = fmt.parseUnsigned(u8, line, 10) catch { | |
| 36 | 36 | try stdout.print("Invalid number.\n"); |
| 37 | 37 | continue; |
| 38 | 38 | }; |
src/ir.cpp+13-1| ... | ... | @@ -67,6 +67,8 @@ enum ConstCastResultId { |
| 67 | 67 | struct ConstCastOnly; |
| 68 | 68 | struct ConstCastArg { |
| 69 | 69 | size_t arg_index; |
| 70 | ZigType *actual_param_type; | |
| 71 | ZigType *expected_param_type; | |
| 70 | 72 | ConstCastOnly *child; |
| 71 | 73 | }; |
| 72 | 74 | |
| ... | ... | @@ -8638,6 +8640,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted |
| 8638 | 8640 | if (arg_child.id != ConstCastResultIdOk) { |
| 8639 | 8641 | result.id = ConstCastResultIdFnArg; |
| 8640 | 8642 | result.data.fn_arg.arg_index = i; |
| 8643 | result.data.fn_arg.actual_param_type = actual_param_info->type; | |
| 8644 | result.data.fn_arg.expected_param_type = expected_param_info->type; | |
| 8641 | 8645 | result.data.fn_arg.child = allocate_nonzero<ConstCastOnly>(1); |
| 8642 | 8646 | *result.data.fn_arg.child = arg_child; |
| 8643 | 8647 | return result; |
| ... | ... | @@ -10483,6 +10487,15 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa |
| 10483 | 10487 | } |
| 10484 | 10488 | break; |
| 10485 | 10489 | } |
| 10490 | case ConstCastResultIdFnArg: { | |
| 10491 | ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, | |
| 10492 | buf_sprintf("parameter %" ZIG_PRI_usize ": '%s' cannot cast into '%s'", | |
| 10493 | cast_result->data.fn_arg.arg_index, | |
| 10494 | buf_ptr(&cast_result->data.fn_arg.actual_param_type->name), | |
| 10495 | buf_ptr(&cast_result->data.fn_arg.expected_param_type->name))); | |
| 10496 | report_recursive_error(ira, source_node, cast_result->data.fn_arg.child, msg); | |
| 10497 | break; | |
| 10498 | } | |
| 10486 | 10499 | case ConstCastResultIdFnAlign: // TODO |
| 10487 | 10500 | case ConstCastResultIdFnCC: // TODO |
| 10488 | 10501 | case ConstCastResultIdFnVarArgs: // TODO |
| ... | ... | @@ -10490,7 +10503,6 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa |
| 10490 | 10503 | case ConstCastResultIdFnReturnType: // TODO |
| 10491 | 10504 | case ConstCastResultIdFnArgCount: // TODO |
| 10492 | 10505 | case ConstCastResultIdFnGenericArgCount: // TODO |
| 10493 | case ConstCastResultIdFnArg: // TODO | |
| 10494 | 10506 | case ConstCastResultIdFnArgNoAlias: // TODO |
| 10495 | 10507 | case ConstCastResultIdUnresolvedInferredErrSet: // TODO |
| 10496 | 10508 | case ConstCastResultIdAsyncAllocatorType: // TODO |
src/zig_llvm.cpp+163-12| ... | ... | @@ -681,18 +681,6 @@ void ZigLLVMParseCommandLineOptions(size_t argc, const char *const *argv) { |
| 681 | 681 | } |
| 682 | 682 | |
| 683 | 683 | |
| 684 | static_assert((Triple::ArchType)ZigLLVM_LastArchType == Triple::LastArchType, ""); | |
| 685 | static_assert((Triple::VendorType)ZigLLVM_LastVendorType == Triple::LastVendorType, ""); | |
| 686 | static_assert((Triple::OSType)ZigLLVM_LastOSType == Triple::LastOSType, ""); | |
| 687 | static_assert((Triple::EnvironmentType)ZigLLVM_LastEnvironmentType == Triple::LastEnvironmentType, ""); | |
| 688 | static_assert((Triple::SubArchType)ZigLLVM_KalimbaSubArch_v5 == Triple::KalimbaSubArch_v5, ""); | |
| 689 | ||
| 690 | static_assert((Triple::ObjectFormatType)ZigLLVM_UnknownObjectFormat == Triple::UnknownObjectFormat, ""); | |
| 691 | static_assert((Triple::ObjectFormatType)ZigLLVM_COFF == Triple::COFF, ""); | |
| 692 | static_assert((Triple::ObjectFormatType)ZigLLVM_ELF == Triple::ELF, ""); | |
| 693 | static_assert((Triple::ObjectFormatType)ZigLLVM_MachO == Triple::MachO, ""); | |
| 694 | static_assert((Triple::ObjectFormatType)ZigLLVM_Wasm == Triple::Wasm, ""); | |
| 695 | ||
| 696 | 684 | const char *ZigLLVMGetArchTypeName(ZigLLVM_ArchType arch) { |
| 697 | 685 | return (const char*)Triple::getArchTypeName((Triple::ArchType)arch).bytes_begin(); |
| 698 | 686 | } |
| ... | ... | @@ -919,3 +907,166 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ |
| 919 | 907 | assert(false); // unreachable |
| 920 | 908 | abort(); |
| 921 | 909 | } |
| 910 | ||
| 911 | static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); | |
| 912 | static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); | |
| 913 | static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); | |
| 914 | static_assert((Triple::ArchType)ZigLLVM_aarch64 == Triple::aarch64, ""); | |
| 915 | static_assert((Triple::ArchType)ZigLLVM_aarch64_be == Triple::aarch64_be, ""); | |
| 916 | static_assert((Triple::ArchType)ZigLLVM_arc == Triple::arc, ""); | |
| 917 | static_assert((Triple::ArchType)ZigLLVM_avr == Triple::avr, ""); | |
| 918 | static_assert((Triple::ArchType)ZigLLVM_bpfel == Triple::bpfel, ""); | |
| 919 | static_assert((Triple::ArchType)ZigLLVM_bpfeb == Triple::bpfeb, ""); | |
| 920 | static_assert((Triple::ArchType)ZigLLVM_hexagon == Triple::hexagon, ""); | |
| 921 | static_assert((Triple::ArchType)ZigLLVM_mips == Triple::mips, ""); | |
| 922 | static_assert((Triple::ArchType)ZigLLVM_mipsel == Triple::mipsel, ""); | |
| 923 | static_assert((Triple::ArchType)ZigLLVM_mips64 == Triple::mips64, ""); | |
| 924 | static_assert((Triple::ArchType)ZigLLVM_mips64el == Triple::mips64el, ""); | |
| 925 | static_assert((Triple::ArchType)ZigLLVM_msp430 == Triple::msp430, ""); | |
| 926 | static_assert((Triple::ArchType)ZigLLVM_nios2 == Triple::nios2, ""); | |
| 927 | static_assert((Triple::ArchType)ZigLLVM_ppc == Triple::ppc, ""); | |
| 928 | static_assert((Triple::ArchType)ZigLLVM_ppc64 == Triple::ppc64, ""); | |
| 929 | static_assert((Triple::ArchType)ZigLLVM_ppc64le == Triple::ppc64le, ""); | |
| 930 | static_assert((Triple::ArchType)ZigLLVM_r600 == Triple::r600, ""); | |
| 931 | static_assert((Triple::ArchType)ZigLLVM_amdgcn == Triple::amdgcn, ""); | |
| 932 | static_assert((Triple::ArchType)ZigLLVM_riscv32 == Triple::riscv32, ""); | |
| 933 | static_assert((Triple::ArchType)ZigLLVM_riscv64 == Triple::riscv64, ""); | |
| 934 | static_assert((Triple::ArchType)ZigLLVM_sparc == Triple::sparc, ""); | |
| 935 | static_assert((Triple::ArchType)ZigLLVM_sparcv9 == Triple::sparcv9, ""); | |
| 936 | static_assert((Triple::ArchType)ZigLLVM_sparcel == Triple::sparcel, ""); | |
| 937 | static_assert((Triple::ArchType)ZigLLVM_systemz == Triple::systemz, ""); | |
| 938 | static_assert((Triple::ArchType)ZigLLVM_tce == Triple::tce, ""); | |
| 939 | static_assert((Triple::ArchType)ZigLLVM_tcele == Triple::tcele, ""); | |
| 940 | static_assert((Triple::ArchType)ZigLLVM_thumb == Triple::thumb, ""); | |
| 941 | static_assert((Triple::ArchType)ZigLLVM_thumbeb == Triple::thumbeb, ""); | |
| 942 | static_assert((Triple::ArchType)ZigLLVM_x86 == Triple::x86, ""); | |
| 943 | static_assert((Triple::ArchType)ZigLLVM_x86_64 == Triple::x86_64, ""); | |
| 944 | static_assert((Triple::ArchType)ZigLLVM_xcore == Triple::xcore, ""); | |
| 945 | static_assert((Triple::ArchType)ZigLLVM_nvptx == Triple::nvptx, ""); | |
| 946 | static_assert((Triple::ArchType)ZigLLVM_nvptx64 == Triple::nvptx64, ""); | |
| 947 | static_assert((Triple::ArchType)ZigLLVM_le32 == Triple::le32, ""); | |
| 948 | static_assert((Triple::ArchType)ZigLLVM_le64 == Triple::le64, ""); | |
| 949 | static_assert((Triple::ArchType)ZigLLVM_amdil == Triple::amdil, ""); | |
| 950 | static_assert((Triple::ArchType)ZigLLVM_amdil64 == Triple::amdil64, ""); | |
| 951 | static_assert((Triple::ArchType)ZigLLVM_hsail == Triple::hsail, ""); | |
| 952 | static_assert((Triple::ArchType)ZigLLVM_hsail64 == Triple::hsail64, ""); | |
| 953 | static_assert((Triple::ArchType)ZigLLVM_spir == Triple::spir, ""); | |
| 954 | static_assert((Triple::ArchType)ZigLLVM_spir64 == Triple::spir64, ""); | |
| 955 | static_assert((Triple::ArchType)ZigLLVM_kalimba == Triple::kalimba, ""); | |
| 956 | static_assert((Triple::ArchType)ZigLLVM_shave == Triple::shave, ""); | |
| 957 | static_assert((Triple::ArchType)ZigLLVM_lanai == Triple::lanai, ""); | |
| 958 | static_assert((Triple::ArchType)ZigLLVM_wasm32 == Triple::wasm32, ""); | |
| 959 | static_assert((Triple::ArchType)ZigLLVM_wasm64 == Triple::wasm64, ""); | |
| 960 | static_assert((Triple::ArchType)ZigLLVM_renderscript32 == Triple::renderscript32, ""); | |
| 961 | static_assert((Triple::ArchType)ZigLLVM_renderscript64 == Triple::renderscript64, ""); | |
| 962 | // Uncomment this when testing LLVM 8.0.0 | |
| 963 | //static_assert((Triple::ArchType)ZigLLVM_LastArchType == Triple::LastArchType, ""); | |
| 964 | ||
| 965 | static_assert((Triple::SubArchType)ZigLLVM_NoSubArch == Triple::NoSubArch, ""); | |
| 966 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8_4a == Triple::ARMSubArch_v8_4a, ""); | |
| 967 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8_3a == Triple::ARMSubArch_v8_3a, ""); | |
| 968 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8_2a == Triple::ARMSubArch_v8_2a, ""); | |
| 969 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8_1a == Triple::ARMSubArch_v8_1a, ""); | |
| 970 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8 == Triple::ARMSubArch_v8, ""); | |
| 971 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8r == Triple::ARMSubArch_v8r, ""); | |
| 972 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8m_baseline == Triple::ARMSubArch_v8m_baseline, ""); | |
| 973 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8m_mainline == Triple::ARMSubArch_v8m_mainline, ""); | |
| 974 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7 == Triple::ARMSubArch_v7, ""); | |
| 975 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7em == Triple::ARMSubArch_v7em, ""); | |
| 976 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7m == Triple::ARMSubArch_v7m, ""); | |
| 977 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7s == Triple::ARMSubArch_v7s, ""); | |
| 978 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7k == Triple::ARMSubArch_v7k, ""); | |
| 979 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7ve == Triple::ARMSubArch_v7ve, ""); | |
| 980 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v6 == Triple::ARMSubArch_v6, ""); | |
| 981 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v6m == Triple::ARMSubArch_v6m, ""); | |
| 982 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v6k == Triple::ARMSubArch_v6k, ""); | |
| 983 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v6t2 == Triple::ARMSubArch_v6t2, ""); | |
| 984 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v5 == Triple::ARMSubArch_v5, ""); | |
| 985 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v5te == Triple::ARMSubArch_v5te, ""); | |
| 986 | static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v4t == Triple::ARMSubArch_v4t, ""); | |
| 987 | static_assert((Triple::SubArchType)ZigLLVM_KalimbaSubArch_v3 == Triple::KalimbaSubArch_v3, ""); | |
| 988 | static_assert((Triple::SubArchType)ZigLLVM_KalimbaSubArch_v4 == Triple::KalimbaSubArch_v4, ""); | |
| 989 | static_assert((Triple::SubArchType)ZigLLVM_KalimbaSubArch_v5 == Triple::KalimbaSubArch_v5, ""); | |
| 990 | ||
| 991 | static_assert((Triple::VendorType)ZigLLVM_UnknownVendor == Triple::UnknownVendor, ""); | |
| 992 | static_assert((Triple::VendorType)ZigLLVM_Apple == Triple::Apple, ""); | |
| 993 | static_assert((Triple::VendorType)ZigLLVM_PC == Triple::PC, ""); | |
| 994 | static_assert((Triple::VendorType)ZigLLVM_SCEI == Triple::SCEI, ""); | |
| 995 | static_assert((Triple::VendorType)ZigLLVM_BGP == Triple::BGP, ""); | |
| 996 | static_assert((Triple::VendorType)ZigLLVM_BGQ == Triple::BGQ, ""); | |
| 997 | static_assert((Triple::VendorType)ZigLLVM_Freescale == Triple::Freescale, ""); | |
| 998 | static_assert((Triple::VendorType)ZigLLVM_IBM == Triple::IBM, ""); | |
| 999 | static_assert((Triple::VendorType)ZigLLVM_ImaginationTechnologies == Triple::ImaginationTechnologies, ""); | |
| 1000 | static_assert((Triple::VendorType)ZigLLVM_MipsTechnologies == Triple::MipsTechnologies, ""); | |
| 1001 | static_assert((Triple::VendorType)ZigLLVM_NVIDIA == Triple::NVIDIA, ""); | |
| 1002 | static_assert((Triple::VendorType)ZigLLVM_CSR == Triple::CSR, ""); | |
| 1003 | static_assert((Triple::VendorType)ZigLLVM_Myriad == Triple::Myriad, ""); | |
| 1004 | static_assert((Triple::VendorType)ZigLLVM_AMD == Triple::AMD, ""); | |
| 1005 | static_assert((Triple::VendorType)ZigLLVM_Mesa == Triple::Mesa, ""); | |
| 1006 | static_assert((Triple::VendorType)ZigLLVM_SUSE == Triple::SUSE, ""); | |
| 1007 | static_assert((Triple::VendorType)ZigLLVM_OpenEmbedded == Triple::OpenEmbedded, ""); | |
| 1008 | // Uncomment this when testing LLVM 8.0.0 | |
| 1009 | //static_assert((Triple::VendorType)ZigLLVM_LastVendorType == Triple::LastVendorType, ""); | |
| 1010 | ||
| 1011 | static_assert((Triple::OSType)ZigLLVM_UnknownOS == Triple::UnknownOS, ""); | |
| 1012 | static_assert((Triple::OSType)ZigLLVM_Ananas == Triple::Ananas, ""); | |
| 1013 | static_assert((Triple::OSType)ZigLLVM_CloudABI == Triple::CloudABI, ""); | |
| 1014 | static_assert((Triple::OSType)ZigLLVM_Darwin == Triple::Darwin, ""); | |
| 1015 | static_assert((Triple::OSType)ZigLLVM_DragonFly == Triple::DragonFly, ""); | |
| 1016 | static_assert((Triple::OSType)ZigLLVM_FreeBSD == Triple::FreeBSD, ""); | |
| 1017 | static_assert((Triple::OSType)ZigLLVM_Fuchsia == Triple::Fuchsia, ""); | |
| 1018 | static_assert((Triple::OSType)ZigLLVM_IOS == Triple::IOS, ""); | |
| 1019 | static_assert((Triple::OSType)ZigLLVM_KFreeBSD == Triple::KFreeBSD, ""); | |
| 1020 | static_assert((Triple::OSType)ZigLLVM_Linux == Triple::Linux, ""); | |
| 1021 | static_assert((Triple::OSType)ZigLLVM_Lv2 == Triple::Lv2, ""); | |
| 1022 | static_assert((Triple::OSType)ZigLLVM_MacOSX == Triple::MacOSX, ""); | |
| 1023 | static_assert((Triple::OSType)ZigLLVM_NetBSD == Triple::NetBSD, ""); | |
| 1024 | static_assert((Triple::OSType)ZigLLVM_OpenBSD == Triple::OpenBSD, ""); | |
| 1025 | static_assert((Triple::OSType)ZigLLVM_Solaris == Triple::Solaris, ""); | |
| 1026 | static_assert((Triple::OSType)ZigLLVM_Win32 == Triple::Win32, ""); | |
| 1027 | static_assert((Triple::OSType)ZigLLVM_Haiku == Triple::Haiku, ""); | |
| 1028 | static_assert((Triple::OSType)ZigLLVM_Minix == Triple::Minix, ""); | |
| 1029 | static_assert((Triple::OSType)ZigLLVM_RTEMS == Triple::RTEMS, ""); | |
| 1030 | static_assert((Triple::OSType)ZigLLVM_NaCl == Triple::NaCl, ""); | |
| 1031 | static_assert((Triple::OSType)ZigLLVM_CNK == Triple::CNK, ""); | |
| 1032 | static_assert((Triple::OSType)ZigLLVM_AIX == Triple::AIX, ""); | |
| 1033 | static_assert((Triple::OSType)ZigLLVM_CUDA == Triple::CUDA, ""); | |
| 1034 | static_assert((Triple::OSType)ZigLLVM_NVCL == Triple::NVCL, ""); | |
| 1035 | static_assert((Triple::OSType)ZigLLVM_AMDHSA == Triple::AMDHSA, ""); | |
| 1036 | static_assert((Triple::OSType)ZigLLVM_PS4 == Triple::PS4, ""); | |
| 1037 | static_assert((Triple::OSType)ZigLLVM_ELFIAMCU == Triple::ELFIAMCU, ""); | |
| 1038 | static_assert((Triple::OSType)ZigLLVM_TvOS == Triple::TvOS, ""); | |
| 1039 | static_assert((Triple::OSType)ZigLLVM_WatchOS == Triple::WatchOS, ""); | |
| 1040 | static_assert((Triple::OSType)ZigLLVM_Mesa3D == Triple::Mesa3D, ""); | |
| 1041 | static_assert((Triple::OSType)ZigLLVM_Contiki == Triple::Contiki, ""); | |
| 1042 | static_assert((Triple::OSType)ZigLLVM_AMDPAL == Triple::AMDPAL, ""); | |
| 1043 | // Uncomment this when testing LLVM 8.0.0 | |
| 1044 | //static_assert((Triple::OSType)ZigLLVM_LastOSType == Triple::LastOSType, ""); | |
| 1045 | ||
| 1046 | static_assert((Triple::EnvironmentType)ZigLLVM_UnknownEnvironment == Triple::UnknownEnvironment, ""); | |
| 1047 | static_assert((Triple::EnvironmentType)ZigLLVM_GNU == Triple::GNU, ""); | |
| 1048 | static_assert((Triple::EnvironmentType)ZigLLVM_GNUABIN32 == Triple::GNUABIN32, ""); | |
| 1049 | static_assert((Triple::EnvironmentType)ZigLLVM_GNUABI64 == Triple::GNUABI64, ""); | |
| 1050 | static_assert((Triple::EnvironmentType)ZigLLVM_GNUEABI == Triple::GNUEABI, ""); | |
| 1051 | static_assert((Triple::EnvironmentType)ZigLLVM_GNUEABIHF == Triple::GNUEABIHF, ""); | |
| 1052 | static_assert((Triple::EnvironmentType)ZigLLVM_GNUX32 == Triple::GNUX32, ""); | |
| 1053 | static_assert((Triple::EnvironmentType)ZigLLVM_CODE16 == Triple::CODE16, ""); | |
| 1054 | static_assert((Triple::EnvironmentType)ZigLLVM_EABI == Triple::EABI, ""); | |
| 1055 | static_assert((Triple::EnvironmentType)ZigLLVM_EABIHF == Triple::EABIHF, ""); | |
| 1056 | static_assert((Triple::EnvironmentType)ZigLLVM_Android == Triple::Android, ""); | |
| 1057 | static_assert((Triple::EnvironmentType)ZigLLVM_Musl == Triple::Musl, ""); | |
| 1058 | static_assert((Triple::EnvironmentType)ZigLLVM_MuslEABI == Triple::MuslEABI, ""); | |
| 1059 | static_assert((Triple::EnvironmentType)ZigLLVM_MuslEABIHF == Triple::MuslEABIHF, ""); | |
| 1060 | static_assert((Triple::EnvironmentType)ZigLLVM_MSVC == Triple::MSVC, ""); | |
| 1061 | static_assert((Triple::EnvironmentType)ZigLLVM_Itanium == Triple::Itanium, ""); | |
| 1062 | static_assert((Triple::EnvironmentType)ZigLLVM_Cygnus == Triple::Cygnus, ""); | |
| 1063 | static_assert((Triple::EnvironmentType)ZigLLVM_CoreCLR == Triple::CoreCLR, ""); | |
| 1064 | static_assert((Triple::EnvironmentType)ZigLLVM_Simulator == Triple::Simulator, ""); | |
| 1065 | // Uncomment this when testing LLVM 8.0.0 | |
| 1066 | //static_assert((Triple::EnvironmentType)ZigLLVM_LastEnvironmentType == Triple::LastEnvironmentType, ""); | |
| 1067 | ||
| 1068 | static_assert((Triple::ObjectFormatType)ZigLLVM_UnknownObjectFormat == Triple::UnknownObjectFormat, ""); | |
| 1069 | static_assert((Triple::ObjectFormatType)ZigLLVM_COFF == Triple::COFF, ""); | |
| 1070 | static_assert((Triple::ObjectFormatType)ZigLLVM_ELF == Triple::ELF, ""); | |
| 1071 | static_assert((Triple::ObjectFormatType)ZigLLVM_MachO == Triple::MachO, ""); | |
| 1072 | static_assert((Triple::ObjectFormatType)ZigLLVM_Wasm == Triple::Wasm, ""); |
std/build.zig+31-8| ... | ... | @@ -150,7 +150,11 @@ pub const Builder = struct { |
| 150 | 150 | } |
| 151 | 151 | |
| 152 | 152 | pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { |
| 153 | return LibExeObjStep.createExecutable(self, name, root_src); | |
| 153 | return LibExeObjStep.createExecutable(self, name, root_src, false); | |
| 154 | } | |
| 155 | ||
| 156 | pub fn addStaticExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { | |
| 157 | return LibExeObjStep.createExecutable(self, name, root_src, true); | |
| 154 | 158 | } |
| 155 | 159 | |
| 156 | 160 | pub fn addObject(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { |
| ... | ... | @@ -807,6 +811,11 @@ pub const Target = union(enum) { |
| 807 | 811 | } |
| 808 | 812 | }; |
| 809 | 813 | |
| 814 | const Pkg = struct { | |
| 815 | name: []const u8, | |
| 816 | path: []const u8, | |
| 817 | }; | |
| 818 | ||
| 810 | 819 | pub const LibExeObjStep = struct { |
| 811 | 820 | step: Step, |
| 812 | 821 | builder: *Builder, |
| ... | ... | @@ -849,11 +858,6 @@ pub const LibExeObjStep = struct { |
| 849 | 858 | source_files: ArrayList([]const u8), |
| 850 | 859 | object_src: []const u8, |
| 851 | 860 | |
| 852 | const Pkg = struct { | |
| 853 | name: []const u8, | |
| 854 | path: []const u8, | |
| 855 | }; | |
| 856 | ||
| 857 | 861 | const Kind = enum { |
| 858 | 862 | Exe, |
| 859 | 863 | Lib, |
| ... | ... | @@ -891,8 +895,8 @@ pub const LibExeObjStep = struct { |
| 891 | 895 | return self; |
| 892 | 896 | } |
| 893 | 897 | |
| 894 | pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { | |
| 895 | const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Exe, false, builder.version(0, 0, 0))) catch unreachable; | |
| 898 | pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, static: bool) *LibExeObjStep { | |
| 899 | const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0))) catch unreachable; | |
| 896 | 900 | return self; |
| 897 | 901 | } |
| 898 | 902 | |
| ... | ... | @@ -1270,6 +1274,9 @@ pub const LibExeObjStep = struct { |
| 1270 | 1274 | zig_args.append("--ver-patch") catch unreachable; |
| 1271 | 1275 | zig_args.append(builder.fmt("{}", self.version.patch)) catch unreachable; |
| 1272 | 1276 | } |
| 1277 | if (self.kind == Kind.Exe and self.static) { | |
| 1278 | zig_args.append("--static") catch unreachable; | |
| 1279 | } | |
| 1273 | 1280 | |
| 1274 | 1281 | switch (self.target) { |
| 1275 | 1282 | Target.Native => {}, |
| ... | ... | @@ -1660,6 +1667,7 @@ pub const TestStep = struct { |
| 1660 | 1667 | exec_cmd_args: ?[]const ?[]const u8, |
| 1661 | 1668 | include_dirs: ArrayList([]const u8), |
| 1662 | 1669 | lib_paths: ArrayList([]const u8), |
| 1670 | packages: ArrayList(Pkg), | |
| 1663 | 1671 | object_files: ArrayList([]const u8), |
| 1664 | 1672 | no_rosegment: bool, |
| 1665 | 1673 | output_path: ?[]const u8, |
| ... | ... | @@ -1680,6 +1688,7 @@ pub const TestStep = struct { |
| 1680 | 1688 | .exec_cmd_args = null, |
| 1681 | 1689 | .include_dirs = ArrayList([]const u8).init(builder.allocator), |
| 1682 | 1690 | .lib_paths = ArrayList([]const u8).init(builder.allocator), |
| 1691 | .packages = ArrayList(Pkg).init(builder.allocator), | |
| 1683 | 1692 | .object_files = ArrayList([]const u8).init(builder.allocator), |
| 1684 | 1693 | .no_rosegment = false, |
| 1685 | 1694 | .output_path = null, |
| ... | ... | @@ -1695,6 +1704,13 @@ pub const TestStep = struct { |
| 1695 | 1704 | self.lib_paths.append(path) catch unreachable; |
| 1696 | 1705 | } |
| 1697 | 1706 | |
| 1707 | pub fn addPackagePath(self: *TestStep, name: []const u8, pkg_index_path: []const u8) void { | |
| 1708 | self.packages.append(Pkg{ | |
| 1709 | .name = name, | |
| 1710 | .path = pkg_index_path, | |
| 1711 | }) catch unreachable; | |
| 1712 | } | |
| 1713 | ||
| 1698 | 1714 | pub fn setVerbose(self: *TestStep, value: bool) void { |
| 1699 | 1715 | self.verbose = value; |
| 1700 | 1716 | } |
| ... | ... | @@ -1871,6 +1887,13 @@ pub const TestStep = struct { |
| 1871 | 1887 | try zig_args.append(lib_path); |
| 1872 | 1888 | } |
| 1873 | 1889 | |
| 1890 | for (self.packages.toSliceConst()) |pkg| { | |
| 1891 | zig_args.append("--pkg-begin") catch unreachable; | |
| 1892 | zig_args.append(pkg.name) catch unreachable; | |
| 1893 | zig_args.append(builder.pathFromRoot(pkg.path)) catch unreachable; | |
| 1894 | zig_args.append("--pkg-end") catch unreachable; | |
| 1895 | } | |
| 1896 | ||
| 1874 | 1897 | if (self.no_rosegment) { |
| 1875 | 1898 | try zig_args.append("--no-rosegment"); |
| 1876 | 1899 | } |
std/debug/index.zig+235-216| ... | ... | @@ -198,49 +198,44 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, |
| 198 | 198 | } |
| 199 | 199 | } |
| 200 | 200 | |
| 201 | pub inline fn getReturnAddress(frame_count: usize) usize { | |
| 202 | var fp = @ptrToInt(@frameAddress()); | |
| 203 | var i: usize = 0; | |
| 204 | while (fp != 0 and i < frame_count) { | |
| 205 | fp = @intToPtr(*const usize, fp).*; | |
| 206 | i += 1; | |
| 201 | pub const StackIterator = struct { | |
| 202 | first_addr: ?usize, | |
| 203 | fp: usize, | |
| 204 | ||
| 205 | pub fn init(first_addr: ?usize) StackIterator { | |
| 206 | return StackIterator{ | |
| 207 | .first_addr = first_addr, | |
| 208 | .fp = @ptrToInt(@frameAddress()), | |
| 209 | }; | |
| 207 | 210 | } |
| 208 | return @intToPtr(*const usize, fp + @sizeOf(usize)).*; | |
| 209 | } | |
| 211 | ||
| 212 | fn next(self: *StackIterator) ?usize { | |
| 213 | if (self.fp == 0) return null; | |
| 214 | self.fp = @intToPtr(*const usize, self.fp).*; | |
| 215 | if (self.fp == 0) return null; | |
| 216 | ||
| 217 | if (self.first_addr) |addr| { | |
| 218 | while (self.fp != 0) : (self.fp = @intToPtr(*const usize, self.fp).*) { | |
| 219 | const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; | |
| 220 | if (addr == return_address) { | |
| 221 | self.first_addr = null; | |
| 222 | return return_address; | |
| 223 | } | |
| 224 | } | |
| 225 | } | |
| 226 | ||
| 227 | const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; | |
| 228 | return return_address; | |
| 229 | } | |
| 230 | }; | |
| 210 | 231 | |
| 211 | 232 | pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { |
| 212 | 233 | switch (builtin.os) { |
| 213 | 234 | builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr), |
| 214 | 235 | else => {}, |
| 215 | 236 | } |
| 216 | const AddressState = union(enum) { | |
| 217 | NotLookingForStartAddress, | |
| 218 | LookingForStartAddress: usize, | |
| 219 | }; | |
| 220 | // TODO: I want to express like this: | |
| 221 | //var addr_state = if (start_addr) |addr| AddressState { .LookingForStartAddress = addr } | |
| 222 | // else AddressState.NotLookingForStartAddress; | |
| 223 | var addr_state: AddressState = undefined; | |
| 224 | if (start_addr) |addr| { | |
| 225 | addr_state = AddressState{ .LookingForStartAddress = addr }; | |
| 226 | } else { | |
| 227 | addr_state = AddressState.NotLookingForStartAddress; | |
| 228 | } | |
| 229 | ||
| 230 | var fp = @ptrToInt(@frameAddress()); | |
| 231 | while (fp != 0) : (fp = @intToPtr(*const usize, fp).*) { | |
| 232 | const return_address = @intToPtr(*const usize, fp + @sizeOf(usize)).*; | |
| 233 | ||
| 234 | switch (addr_state) { | |
| 235 | AddressState.NotLookingForStartAddress => {}, | |
| 236 | AddressState.LookingForStartAddress => |addr| { | |
| 237 | if (return_address == addr) { | |
| 238 | addr_state = AddressState.NotLookingForStartAddress; | |
| 239 | } else { | |
| 240 | continue; | |
| 241 | } | |
| 242 | }, | |
| 243 | } | |
| 237 | var it = StackIterator.init(start_addr); | |
| 238 | while (it.next()) |return_address| { | |
| 244 | 239 | try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); |
| 245 | 240 | } |
| 246 | 241 | } |
| ... | ... | @@ -284,7 +279,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres |
| 284 | 279 | const mod_index = for (di.sect_contribs) |sect_contrib| { |
| 285 | 280 | if (sect_contrib.Section > di.coff.sections.len) continue; |
| 286 | 281 | // Remember that SectionContribEntry.Section is 1-based. |
| 287 | coff_section = &di.coff.sections.toSlice()[sect_contrib.Section-1]; | |
| 282 | coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1]; | |
| 288 | 283 | |
| 289 | 284 | const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; |
| 290 | 285 | const vaddr_end = vaddr_start + sect_contrib.Size; |
| ... | ... | @@ -414,7 +409,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres |
| 414 | 409 | |
| 415 | 410 | if (opt_line_info) |line_info| { |
| 416 | 411 | try out_stream.print("\n"); |
| 417 | if (printLineFromFile(out_stream, line_info)) { | |
| 412 | if (printLineFromFileAnyOs(out_stream, line_info)) { | |
| 418 | 413 | if (line_info.column == 0) { |
| 419 | 414 | try out_stream.write("\n"); |
| 420 | 415 | } else { |
| ... | ... | @@ -598,7 +593,15 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt |
| 598 | 593 | } else "???"; |
| 599 | 594 | if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| { |
| 600 | 595 | defer line_info.deinit(); |
| 601 | try printLineInfo(di, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color); | |
| 596 | try printLineInfo( | |
| 597 | out_stream, | |
| 598 | line_info, | |
| 599 | address, | |
| 600 | symbol_name, | |
| 601 | compile_unit_name, | |
| 602 | tty_color, | |
| 603 | printLineFromFileAnyOs, | |
| 604 | ); | |
| 602 | 605 | } else |err| switch (err) { |
| 603 | 606 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 604 | 607 | if (tty_color) { |
| ... | ... | @@ -611,7 +614,15 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt |
| 611 | 614 | } |
| 612 | 615 | } |
| 613 | 616 | |
| 614 | pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | |
| 617 | /// This function works in freestanding mode. | |
| 618 | /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void | |
| 619 | pub fn printSourceAtAddressDwarf( | |
| 620 | debug_info: *DwarfInfo, | |
| 621 | out_stream: var, | |
| 622 | address: usize, | |
| 623 | tty_color: bool, | |
| 624 | comptime printLineFromFile: var, | |
| 625 | ) !void { | |
| 615 | 626 | const compile_unit = findCompileUnit(debug_info, address) catch { |
| 616 | 627 | if (tty_color) { |
| 617 | 628 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); |
| ... | ... | @@ -621,10 +632,18 @@ pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, addres |
| 621 | 632 | return; |
| 622 | 633 | }; |
| 623 | 634 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); |
| 624 | if (getLineNumberInfoLinux(debug_info, compile_unit, address - 1)) |line_info| { | |
| 635 | if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| { | |
| 625 | 636 | defer line_info.deinit(); |
| 626 | 637 | const symbol_name = "???"; |
| 627 | try printLineInfo(debug_info, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color); | |
| 638 | try printLineInfo( | |
| 639 | out_stream, | |
| 640 | line_info, | |
| 641 | address, | |
| 642 | symbol_name, | |
| 643 | compile_unit_name, | |
| 644 | tty_color, | |
| 645 | printLineFromFile, | |
| 646 | ); | |
| 628 | 647 | } else |err| switch (err) { |
| 629 | 648 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 630 | 649 | if (tty_color) { |
| ... | ... | @@ -637,14 +656,18 @@ pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, addres |
| 637 | 656 | } |
| 638 | 657 | } |
| 639 | 658 | |
| 659 | pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | |
| 660 | return printSourceAtAddressDwarf(debug_info, out_stream, address, tty_color, printLineFromFileAnyOs); | |
| 661 | } | |
| 662 | ||
| 640 | 663 | fn printLineInfo( |
| 641 | debug_info: *DebugInfo, | |
| 642 | 664 | out_stream: var, |
| 643 | 665 | line_info: LineInfo, |
| 644 | 666 | address: usize, |
| 645 | 667 | symbol_name: []const u8, |
| 646 | 668 | compile_unit_name: []const u8, |
| 647 | 669 | tty_color: bool, |
| 670 | comptime printLineFromFile: var, | |
| 648 | 671 | ) !void { |
| 649 | 672 | if (tty_color) { |
| 650 | 673 | try out_stream.print( |
| ... | ... | @@ -872,55 +895,68 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { |
| 872 | 895 | return list.toOwnedSlice(); |
| 873 | 896 | } |
| 874 | 897 | |
| 875 | fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo { | |
| 876 | var di = DebugInfo{ | |
| 877 | .self_exe_file = undefined, | |
| 878 | .elf = undefined, | |
| 879 | .debug_info = undefined, | |
| 880 | .debug_abbrev = undefined, | |
| 881 | .debug_str = undefined, | |
| 882 | .debug_line = undefined, | |
| 883 | .debug_ranges = null, | |
| 884 | .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), | |
| 885 | .compile_unit_list = ArrayList(CompileUnit).init(allocator), | |
| 898 | fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section { | |
| 899 | const elf_header = (try elf_file.findSection(name)) orelse return null; | |
| 900 | return DwarfInfo.Section{ | |
| 901 | .offset = elf_header.offset, | |
| 902 | .size = elf_header.size, | |
| 886 | 903 | }; |
| 887 | di.self_exe_file = try os.openSelfExe(); | |
| 888 | errdefer di.self_exe_file.close(); | |
| 889 | ||
| 890 | try di.elf.openFile(allocator, di.self_exe_file); | |
| 891 | errdefer di.elf.close(); | |
| 892 | ||
| 893 | di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo; | |
| 894 | di.debug_abbrev = (try di.elf.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo; | |
| 895 | di.debug_str = (try di.elf.findSection(".debug_str")) orelse return error.MissingDebugInfo; | |
| 896 | di.debug_line = (try di.elf.findSection(".debug_line")) orelse return error.MissingDebugInfo; | |
| 897 | di.debug_ranges = (try di.elf.findSection(".debug_ranges")); | |
| 898 | try scanAllCompileUnits(&di); | |
| 899 | return di; | |
| 900 | 904 | } |
| 901 | 905 | |
| 902 | pub fn findElfSection(elf: *Elf, name: []const u8) ?*elf.Shdr { | |
| 903 | var file_stream = elf.in_file.inStream(); | |
| 904 | const in = &file_stream.stream; | |
| 905 | ||
| 906 | section_loop: for (elf.section_headers) |*elf_section| { | |
| 907 | if (elf_section.sh_type == SHT_NULL) continue; | |
| 908 | ||
| 909 | const name_offset = elf.string_section.offset + elf_section.name; | |
| 910 | try elf.in_file.seekTo(name_offset); | |
| 911 | ||
| 912 | for (name) |expected_c| { | |
| 913 | const target_c = try in.readByte(); | |
| 914 | if (target_c == 0 or expected_c != target_c) continue :section_loop; | |
| 915 | } | |
| 906 | /// Initialize DWARF info. The caller has the responsibility to initialize most | |
| 907 | /// the DwarfInfo fields before calling. These fields can be left undefined: | |
| 908 | /// * abbrev_table_list | |
| 909 | /// * compile_unit_list | |
| 910 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { | |
| 911 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); | |
| 912 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); | |
| 913 | try scanAllCompileUnits(di); | |
| 914 | } | |
| 916 | 915 | |
| 917 | { | |
| 918 | const null_byte = try in.readByte(); | |
| 919 | if (null_byte == 0) return elf_section; | |
| 920 | } | |
| 921 | } | |
| 916 | pub fn openElfDebugInfo( | |
| 917 | allocator: *mem.Allocator, | |
| 918 | elf_seekable_stream: *DwarfSeekableStream, | |
| 919 | elf_in_stream: *DwarfInStream, | |
| 920 | ) !DwarfInfo { | |
| 921 | var efile: elf.Elf = undefined; | |
| 922 | try efile.openStream(allocator, elf_seekable_stream, elf_in_stream); | |
| 923 | errdefer efile.close(); | |
| 924 | ||
| 925 | var di = DwarfInfo{ | |
| 926 | .dwarf_seekable_stream = elf_seekable_stream, | |
| 927 | .dwarf_in_stream = elf_in_stream, | |
| 928 | .endian = efile.endian, | |
| 929 | .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo, | |
| 930 | .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo, | |
| 931 | .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo, | |
| 932 | .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo, | |
| 933 | .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), | |
| 934 | .abbrev_table_list = undefined, | |
| 935 | .compile_unit_list = undefined, | |
| 936 | }; | |
| 937 | try openDwarfDebugInfo(&di, allocator); | |
| 938 | return di; | |
| 939 | } | |
| 922 | 940 | |
| 923 | return null; | |
| 941 | fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DwarfInfo { | |
| 942 | const S = struct { | |
| 943 | var self_exe_file: os.File = undefined; | |
| 944 | var self_exe_seekable_stream: os.File.SeekableStream = undefined; | |
| 945 | var self_exe_in_stream: os.File.InStream = undefined; | |
| 946 | }; | |
| 947 | S.self_exe_file = try os.openSelfExe(); | |
| 948 | errdefer S.self_exe_file.close(); | |
| 949 | ||
| 950 | S.self_exe_seekable_stream = S.self_exe_file.seekableStream(); | |
| 951 | S.self_exe_in_stream = S.self_exe_file.inStream(); | |
| 952 | ||
| 953 | return openElfDebugInfo( | |
| 954 | allocator, | |
| 955 | // TODO https://github.com/ziglang/zig/issues/764 | |
| 956 | @ptrCast(*DwarfSeekableStream, &S.self_exe_seekable_stream.stream), | |
| 957 | // TODO https://github.com/ziglang/zig/issues/764 | |
| 958 | @ptrCast(*DwarfInStream, &S.self_exe_in_stream.stream), | |
| 959 | ); | |
| 924 | 960 | } |
| 925 | 961 | |
| 926 | 962 | fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| ... | ... | @@ -1000,7 +1036,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| 1000 | 1036 | }; |
| 1001 | 1037 | } |
| 1002 | 1038 | |
| 1003 | fn printLineFromFile(out_stream: var, line_info: LineInfo) !void { | |
| 1039 | fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { | |
| 1004 | 1040 | var f = try os.File.openRead(line_info.file_name); |
| 1005 | 1041 | defer f.close(); |
| 1006 | 1042 | // TODO fstat and make sure that the file has the correct size |
| ... | ... | @@ -1053,6 +1089,35 @@ const MachOFile = struct { |
| 1053 | 1089 | sect_debug_line: ?*const macho.section_64, |
| 1054 | 1090 | }; |
| 1055 | 1091 | |
| 1092 | pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); | |
| 1093 | pub const DwarfInStream = io.InStream(anyerror); | |
| 1094 | ||
| 1095 | pub const DwarfInfo = struct { | |
| 1096 | dwarf_seekable_stream: *DwarfSeekableStream, | |
| 1097 | dwarf_in_stream: *DwarfInStream, | |
| 1098 | endian: builtin.Endian, | |
| 1099 | debug_info: Section, | |
| 1100 | debug_abbrev: Section, | |
| 1101 | debug_str: Section, | |
| 1102 | debug_line: Section, | |
| 1103 | debug_ranges: ?Section, | |
| 1104 | abbrev_table_list: ArrayList(AbbrevTableHeader), | |
| 1105 | compile_unit_list: ArrayList(CompileUnit), | |
| 1106 | ||
| 1107 | pub const Section = struct { | |
| 1108 | offset: usize, | |
| 1109 | size: usize, | |
| 1110 | }; | |
| 1111 | ||
| 1112 | pub fn allocator(self: DwarfInfo) *mem.Allocator { | |
| 1113 | return self.abbrev_table_list.allocator; | |
| 1114 | } | |
| 1115 | ||
| 1116 | pub fn readString(self: *DwarfInfo) ![]u8 { | |
| 1117 | return readStringRaw(self.allocator(), self.dwarf_in_stream); | |
| 1118 | } | |
| 1119 | }; | |
| 1120 | ||
| 1056 | 1121 | pub const DebugInfo = switch (builtin.os) { |
| 1057 | 1122 | builtin.Os.macosx => struct { |
| 1058 | 1123 | symbols: []const MachoSymbol, |
| ... | ... | @@ -1076,32 +1141,7 @@ pub const DebugInfo = switch (builtin.os) { |
| 1076 | 1141 | sect_contribs: []pdb.SectionContribEntry, |
| 1077 | 1142 | modules: []Module, |
| 1078 | 1143 | }, |
| 1079 | builtin.Os.linux => struct { | |
| 1080 | self_exe_file: os.File, | |
| 1081 | elf: elf.Elf, | |
| 1082 | debug_info: *elf.SectionHeader, | |
| 1083 | debug_abbrev: *elf.SectionHeader, | |
| 1084 | debug_str: *elf.SectionHeader, | |
| 1085 | debug_line: *elf.SectionHeader, | |
| 1086 | debug_ranges: ?*elf.SectionHeader, | |
| 1087 | abbrev_table_list: ArrayList(AbbrevTableHeader), | |
| 1088 | compile_unit_list: ArrayList(CompileUnit), | |
| 1089 | ||
| 1090 | pub fn allocator(self: DebugInfo) *mem.Allocator { | |
| 1091 | return self.abbrev_table_list.allocator; | |
| 1092 | } | |
| 1093 | ||
| 1094 | pub fn readString(self: *DebugInfo) ![]u8 { | |
| 1095 | var in_file_stream = self.self_exe_file.inStream(); | |
| 1096 | const in_stream = &in_file_stream.stream; | |
| 1097 | return readStringRaw(self.allocator(), in_stream); | |
| 1098 | } | |
| 1099 | ||
| 1100 | pub fn close(self: *DebugInfo) void { | |
| 1101 | self.self_exe_file.close(); | |
| 1102 | self.elf.close(); | |
| 1103 | } | |
| 1104 | }, | |
| 1144 | builtin.Os.linux => DwarfInfo, | |
| 1105 | 1145 | builtin.Os.freebsd => struct {}, |
| 1106 | 1146 | else => @compileError("Unsupported OS"), |
| 1107 | 1147 | }; |
| ... | ... | @@ -1206,11 +1246,11 @@ const Die = struct { |
| 1206 | 1246 | }; |
| 1207 | 1247 | } |
| 1208 | 1248 | |
| 1209 | fn getAttrString(self: *const Die, st: *DebugInfo, id: u64) ![]u8 { | |
| 1249 | fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { | |
| 1210 | 1250 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 1211 | 1251 | return switch (form_value.*) { |
| 1212 | 1252 | FormValue.String => |value| value, |
| 1213 | FormValue.StrPtr => |offset| getString(st, offset), | |
| 1253 | FormValue.StrPtr => |offset| getString(di, offset), | |
| 1214 | 1254 | else => error.InvalidDebugInfo, |
| 1215 | 1255 | }; |
| 1216 | 1256 | } |
| ... | ... | @@ -1223,14 +1263,15 @@ const FileEntry = struct { |
| 1223 | 1263 | len_bytes: usize, |
| 1224 | 1264 | }; |
| 1225 | 1265 | |
| 1226 | const LineInfo = struct { | |
| 1266 | pub const LineInfo = struct { | |
| 1227 | 1267 | line: usize, |
| 1228 | 1268 | column: usize, |
| 1229 | file_name: []u8, | |
| 1230 | allocator: *mem.Allocator, | |
| 1269 | file_name: []const u8, | |
| 1270 | allocator: ?*mem.Allocator, | |
| 1231 | 1271 | |
| 1232 | fn deinit(self: *const LineInfo) void { | |
| 1233 | self.allocator.free(self.file_name); | |
| 1272 | fn deinit(self: LineInfo) void { | |
| 1273 | const allocator = self.allocator orelse return; | |
| 1274 | allocator.free(self.file_name); | |
| 1234 | 1275 | } |
| 1235 | 1276 | }; |
| 1236 | 1277 | |
| ... | ... | @@ -1321,10 +1362,10 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { |
| 1321 | 1362 | return buf.toSlice(); |
| 1322 | 1363 | } |
| 1323 | 1364 | |
| 1324 | fn getString(st: *DebugInfo, offset: u64) ![]u8 { | |
| 1325 | const pos = st.debug_str.offset + offset; | |
| 1326 | try st.self_exe_file.seekTo(pos); | |
| 1327 | return st.readString(); | |
| 1365 | fn getString(di: *DwarfInfo, offset: u64) ![]u8 { | |
| 1366 | const pos = di.debug_str.offset + offset; | |
| 1367 | try di.dwarf_seekable_stream.seekTo(pos); | |
| 1368 | return di.readString(); | |
| 1328 | 1369 | } |
| 1329 | 1370 | |
| 1330 | 1371 | fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { |
| ... | ... | @@ -1371,14 +1412,7 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type |
| 1371 | 1412 | return parseFormValueRefLen(allocator, in_stream, block_len); |
| 1372 | 1413 | } |
| 1373 | 1414 | |
| 1374 | const ParseFormValueError = error{ | |
| 1375 | EndOfStream, | |
| 1376 | InvalidDebugInfo, | |
| 1377 | EndOfFile, | |
| 1378 | OutOfMemory, | |
| 1379 | } || std.os.File.ReadError; | |
| 1380 | ||
| 1381 | fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { | |
| 1415 | fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue { | |
| 1382 | 1416 | return switch (form_id) { |
| 1383 | 1417 | DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, |
| 1384 | 1418 | DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), |
| ... | ... | @@ -1428,25 +1462,22 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64 |
| 1428 | 1462 | }; |
| 1429 | 1463 | } |
| 1430 | 1464 | |
| 1431 | fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable { | |
| 1432 | const in_file = st.self_exe_file; | |
| 1433 | var in_file_stream = in_file.inStream(); | |
| 1434 | const in_stream = &in_file_stream.stream; | |
| 1435 | var result = AbbrevTable.init(st.allocator()); | |
| 1465 | fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { | |
| 1466 | var result = AbbrevTable.init(di.allocator()); | |
| 1436 | 1467 | while (true) { |
| 1437 | const abbrev_code = try readULeb128(in_stream); | |
| 1468 | const abbrev_code = try readULeb128(di.dwarf_in_stream); | |
| 1438 | 1469 | if (abbrev_code == 0) return result; |
| 1439 | 1470 | try result.append(AbbrevTableEntry{ |
| 1440 | 1471 | .abbrev_code = abbrev_code, |
| 1441 | .tag_id = try readULeb128(in_stream), | |
| 1442 | .has_children = (try in_stream.readByte()) == DW.CHILDREN_yes, | |
| 1443 | .attrs = ArrayList(AbbrevAttr).init(st.allocator()), | |
| 1472 | .tag_id = try readULeb128(di.dwarf_in_stream), | |
| 1473 | .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, | |
| 1474 | .attrs = ArrayList(AbbrevAttr).init(di.allocator()), | |
| 1444 | 1475 | }); |
| 1445 | 1476 | const attrs = &result.items[result.len - 1].attrs; |
| 1446 | 1477 | |
| 1447 | 1478 | while (true) { |
| 1448 | const attr_id = try readULeb128(in_stream); | |
| 1449 | const form_id = try readULeb128(in_stream); | |
| 1479 | const attr_id = try readULeb128(di.dwarf_in_stream); | |
| 1480 | const form_id = try readULeb128(di.dwarf_in_stream); | |
| 1450 | 1481 | if (attr_id == 0 and form_id == 0) break; |
| 1451 | 1482 | try attrs.append(AbbrevAttr{ |
| 1452 | 1483 | .attr_id = attr_id, |
| ... | ... | @@ -1458,18 +1489,18 @@ fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable { |
| 1458 | 1489 | |
| 1459 | 1490 | /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, |
| 1460 | 1491 | /// seeks in the stream and parses it. |
| 1461 | fn getAbbrevTable(st: *DebugInfo, abbrev_offset: u64) !*const AbbrevTable { | |
| 1462 | for (st.abbrev_table_list.toSlice()) |*header| { | |
| 1492 | fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { | |
| 1493 | for (di.abbrev_table_list.toSlice()) |*header| { | |
| 1463 | 1494 | if (header.offset == abbrev_offset) { |
| 1464 | 1495 | return &header.table; |
| 1465 | 1496 | } |
| 1466 | 1497 | } |
| 1467 | try st.self_exe_file.seekTo(st.debug_abbrev.offset + abbrev_offset); | |
| 1468 | try st.abbrev_table_list.append(AbbrevTableHeader{ | |
| 1498 | try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); | |
| 1499 | try di.abbrev_table_list.append(AbbrevTableHeader{ | |
| 1469 | 1500 | .offset = abbrev_offset, |
| 1470 | .table = try parseAbbrevTable(st), | |
| 1501 | .table = try parseAbbrevTable(di), | |
| 1471 | 1502 | }); |
| 1472 | return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table; | |
| 1503 | return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; | |
| 1473 | 1504 | } |
| 1474 | 1505 | |
| 1475 | 1506 | fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { |
| ... | ... | @@ -1479,23 +1510,20 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con |
| 1479 | 1510 | return null; |
| 1480 | 1511 | } |
| 1481 | 1512 | |
| 1482 | fn parseDie(st: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { | |
| 1483 | const in_file = st.self_exe_file; | |
| 1484 | var in_file_stream = in_file.inStream(); | |
| 1485 | const in_stream = &in_file_stream.stream; | |
| 1486 | const abbrev_code = try readULeb128(in_stream); | |
| 1513 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { | |
| 1514 | const abbrev_code = try readULeb128(di.dwarf_in_stream); | |
| 1487 | 1515 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; |
| 1488 | 1516 | |
| 1489 | 1517 | var result = Die{ |
| 1490 | 1518 | .tag_id = table_entry.tag_id, |
| 1491 | 1519 | .has_children = table_entry.has_children, |
| 1492 | .attrs = ArrayList(Die.Attr).init(st.allocator()), | |
| 1520 | .attrs = ArrayList(Die.Attr).init(di.allocator()), | |
| 1493 | 1521 | }; |
| 1494 | 1522 | try result.attrs.resize(table_entry.attrs.len); |
| 1495 | 1523 | for (table_entry.attrs.toSliceConst()) |attr, i| { |
| 1496 | 1524 | result.attrs.items[i] = Die.Attr{ |
| 1497 | 1525 | .id = attr.attr_id, |
| 1498 | .value = try parseFormValue(st.allocator(), in_stream, attr.form_id, is_64), | |
| 1526 | .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), | |
| 1499 | 1527 | }; |
| 1500 | 1528 | } |
| 1501 | 1529 | return result; |
| ... | ... | @@ -1699,22 +1727,18 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 1699 | 1727 | return error.MissingDebugInfo; |
| 1700 | 1728 | } |
| 1701 | 1729 | |
| 1702 | fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, target_address: usize) !LineInfo { | |
| 1730 | fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { | |
| 1703 | 1731 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); |
| 1704 | 1732 | |
| 1705 | const in_file = di.self_exe_file; | |
| 1706 | 1733 | const debug_line_end = di.debug_line.offset + di.debug_line.size; |
| 1707 | 1734 | var this_offset = di.debug_line.offset; |
| 1708 | 1735 | var this_index: usize = 0; |
| 1709 | 1736 | |
| 1710 | var in_file_stream = in_file.inStream(); | |
| 1711 | const in_stream = &in_file_stream.stream; | |
| 1712 | ||
| 1713 | 1737 | while (this_offset < debug_line_end) : (this_index += 1) { |
| 1714 | try in_file.seekTo(this_offset); | |
| 1738 | try di.dwarf_seekable_stream.seekTo(this_offset); | |
| 1715 | 1739 | |
| 1716 | 1740 | var is_64: bool = undefined; |
| 1717 | const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64); | |
| 1741 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); | |
| 1718 | 1742 | if (unit_length == 0) return error.MissingDebugInfo; |
| 1719 | 1743 | const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); |
| 1720 | 1744 | |
| ... | ... | @@ -1723,35 +1747,35 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ |
| 1723 | 1747 | continue; |
| 1724 | 1748 | } |
| 1725 | 1749 | |
| 1726 | const version = try in_stream.readInt(di.elf.endian, u16); | |
| 1750 | const version = try di.dwarf_in_stream.readInt(di.endian, u16); | |
| 1727 | 1751 | // TODO support 3 and 5 |
| 1728 | 1752 | if (version != 2 and version != 4) return error.InvalidDebugInfo; |
| 1729 | 1753 | |
| 1730 | const prologue_length = if (is_64) try in_stream.readInt(di.elf.endian, u64) else try in_stream.readInt(di.elf.endian, u32); | |
| 1731 | const prog_start_offset = (try in_file.getPos()) + prologue_length; | |
| 1754 | const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(di.endian, u64) else try di.dwarf_in_stream.readInt(di.endian, u32); | |
| 1755 | const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length; | |
| 1732 | 1756 | |
| 1733 | const minimum_instruction_length = try in_stream.readByte(); | |
| 1757 | const minimum_instruction_length = try di.dwarf_in_stream.readByte(); | |
| 1734 | 1758 | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; |
| 1735 | 1759 | |
| 1736 | 1760 | if (version >= 4) { |
| 1737 | 1761 | // maximum_operations_per_instruction |
| 1738 | _ = try in_stream.readByte(); | |
| 1762 | _ = try di.dwarf_in_stream.readByte(); | |
| 1739 | 1763 | } |
| 1740 | 1764 | |
| 1741 | const default_is_stmt = (try in_stream.readByte()) != 0; | |
| 1742 | const line_base = try in_stream.readByteSigned(); | |
| 1765 | const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0; | |
| 1766 | const line_base = try di.dwarf_in_stream.readByteSigned(); | |
| 1743 | 1767 | |
| 1744 | const line_range = try in_stream.readByte(); | |
| 1768 | const line_range = try di.dwarf_in_stream.readByte(); | |
| 1745 | 1769 | if (line_range == 0) return error.InvalidDebugInfo; |
| 1746 | 1770 | |
| 1747 | const opcode_base = try in_stream.readByte(); | |
| 1771 | const opcode_base = try di.dwarf_in_stream.readByte(); | |
| 1748 | 1772 | |
| 1749 | 1773 | const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); |
| 1750 | 1774 | |
| 1751 | 1775 | { |
| 1752 | 1776 | var i: usize = 0; |
| 1753 | 1777 | while (i < opcode_base - 1) : (i += 1) { |
| 1754 | standard_opcode_lengths[i] = try in_stream.readByte(); | |
| 1778 | standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); | |
| 1755 | 1779 | } |
| 1756 | 1780 | } |
| 1757 | 1781 | |
| ... | ... | @@ -1769,9 +1793,9 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ |
| 1769 | 1793 | while (true) { |
| 1770 | 1794 | const file_name = try di.readString(); |
| 1771 | 1795 | if (file_name.len == 0) break; |
| 1772 | const dir_index = try readULeb128(in_stream); | |
| 1773 | const mtime = try readULeb128(in_stream); | |
| 1774 | const len_bytes = try readULeb128(in_stream); | |
| 1796 | const dir_index = try readULeb128(di.dwarf_in_stream); | |
| 1797 | const mtime = try readULeb128(di.dwarf_in_stream); | |
| 1798 | const len_bytes = try readULeb128(di.dwarf_in_stream); | |
| 1775 | 1799 | try file_entries.append(FileEntry{ |
| 1776 | 1800 | .file_name = file_name, |
| 1777 | 1801 | .dir_index = dir_index, |
| ... | ... | @@ -1780,15 +1804,15 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ |
| 1780 | 1804 | }); |
| 1781 | 1805 | } |
| 1782 | 1806 | |
| 1783 | try in_file.seekTo(prog_start_offset); | |
| 1807 | try di.dwarf_seekable_stream.seekTo(prog_start_offset); | |
| 1784 | 1808 | |
| 1785 | 1809 | while (true) { |
| 1786 | const opcode = try in_stream.readByte(); | |
| 1810 | const opcode = try di.dwarf_in_stream.readByte(); | |
| 1787 | 1811 | |
| 1788 | 1812 | if (opcode == DW.LNS_extended_op) { |
| 1789 | const op_size = try readULeb128(in_stream); | |
| 1813 | const op_size = try readULeb128(di.dwarf_in_stream); | |
| 1790 | 1814 | if (op_size < 1) return error.InvalidDebugInfo; |
| 1791 | var sub_op = try in_stream.readByte(); | |
| 1815 | var sub_op = try di.dwarf_in_stream.readByte(); | |
| 1792 | 1816 | switch (sub_op) { |
| 1793 | 1817 | DW.LNE_end_sequence => { |
| 1794 | 1818 | prog.end_sequence = true; |
| ... | ... | @@ -1796,14 +1820,14 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ |
| 1796 | 1820 | return error.MissingDebugInfo; |
| 1797 | 1821 | }, |
| 1798 | 1822 | DW.LNE_set_address => { |
| 1799 | const addr = try in_stream.readInt(di.elf.endian, usize); | |
| 1823 | const addr = try di.dwarf_in_stream.readInt(di.endian, usize); | |
| 1800 | 1824 | prog.address = addr; |
| 1801 | 1825 | }, |
| 1802 | 1826 | DW.LNE_define_file => { |
| 1803 | 1827 | const file_name = try di.readString(); |
| 1804 | const dir_index = try readULeb128(in_stream); | |
| 1805 | const mtime = try readULeb128(in_stream); | |
| 1806 | const len_bytes = try readULeb128(in_stream); | |
| 1828 | const dir_index = try readULeb128(di.dwarf_in_stream); | |
| 1829 | const mtime = try readULeb128(di.dwarf_in_stream); | |
| 1830 | const len_bytes = try readULeb128(di.dwarf_in_stream); | |
| 1807 | 1831 | try file_entries.append(FileEntry{ |
| 1808 | 1832 | .file_name = file_name, |
| 1809 | 1833 | .dir_index = dir_index, |
| ... | ... | @@ -1813,7 +1837,7 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ |
| 1813 | 1837 | }, |
| 1814 | 1838 | else => { |
| 1815 | 1839 | const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; |
| 1816 | try in_file.seekForward(fwd_amt); | |
| 1840 | try di.dwarf_seekable_stream.seekForward(fwd_amt); | |
| 1817 | 1841 | }, |
| 1818 | 1842 | } |
| 1819 | 1843 | } else if (opcode >= opcode_base) { |
| ... | ... | @@ -1832,19 +1856,19 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ |
| 1832 | 1856 | prog.basic_block = false; |
| 1833 | 1857 | }, |
| 1834 | 1858 | DW.LNS_advance_pc => { |
| 1835 | const arg = try readULeb128(in_stream); | |
| 1859 | const arg = try readULeb128(di.dwarf_in_stream); | |
| 1836 | 1860 | prog.address += arg * minimum_instruction_length; |
| 1837 | 1861 | }, |
| 1838 | 1862 | DW.LNS_advance_line => { |
| 1839 | const arg = try readILeb128(in_stream); | |
| 1863 | const arg = try readILeb128(di.dwarf_in_stream); | |
| 1840 | 1864 | prog.line += arg; |
| 1841 | 1865 | }, |
| 1842 | 1866 | DW.LNS_set_file => { |
| 1843 | const arg = try readULeb128(in_stream); | |
| 1867 | const arg = try readULeb128(di.dwarf_in_stream); | |
| 1844 | 1868 | prog.file = arg; |
| 1845 | 1869 | }, |
| 1846 | 1870 | DW.LNS_set_column => { |
| 1847 | const arg = try readULeb128(in_stream); | |
| 1871 | const arg = try readULeb128(di.dwarf_in_stream); | |
| 1848 | 1872 | prog.column = arg; |
| 1849 | 1873 | }, |
| 1850 | 1874 | DW.LNS_negate_stmt => { |
| ... | ... | @@ -1858,14 +1882,14 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ |
| 1858 | 1882 | prog.address += inc_addr; |
| 1859 | 1883 | }, |
| 1860 | 1884 | DW.LNS_fixed_advance_pc => { |
| 1861 | const arg = try in_stream.readInt(di.elf.endian, u16); | |
| 1885 | const arg = try di.dwarf_in_stream.readInt(di.endian, u16); | |
| 1862 | 1886 | prog.address += arg; |
| 1863 | 1887 | }, |
| 1864 | 1888 | DW.LNS_set_prologue_end => {}, |
| 1865 | 1889 | else => { |
| 1866 | 1890 | if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; |
| 1867 | 1891 | const len_bytes = standard_opcode_lengths[opcode - 1]; |
| 1868 | try in_file.seekForward(len_bytes); | |
| 1892 | try di.dwarf_seekable_stream.seekForward(len_bytes); | |
| 1869 | 1893 | }, |
| 1870 | 1894 | } |
| 1871 | 1895 | } |
| ... | ... | @@ -1877,36 +1901,33 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ |
| 1877 | 1901 | return error.MissingDebugInfo; |
| 1878 | 1902 | } |
| 1879 | 1903 | |
| 1880 | fn scanAllCompileUnits(st: *DebugInfo) !void { | |
| 1881 | const debug_info_end = st.debug_info.offset + st.debug_info.size; | |
| 1882 | var this_unit_offset = st.debug_info.offset; | |
| 1904 | fn scanAllCompileUnits(di: *DwarfInfo) !void { | |
| 1905 | const debug_info_end = di.debug_info.offset + di.debug_info.size; | |
| 1906 | var this_unit_offset = di.debug_info.offset; | |
| 1883 | 1907 | var cu_index: usize = 0; |
| 1884 | 1908 | |
| 1885 | var in_file_stream = st.self_exe_file.inStream(); | |
| 1886 | const in_stream = &in_file_stream.stream; | |
| 1887 | ||
| 1888 | 1909 | while (this_unit_offset < debug_info_end) { |
| 1889 | try st.self_exe_file.seekTo(this_unit_offset); | |
| 1910 | try di.dwarf_seekable_stream.seekTo(this_unit_offset); | |
| 1890 | 1911 | |
| 1891 | 1912 | var is_64: bool = undefined; |
| 1892 | const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64); | |
| 1913 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); | |
| 1893 | 1914 | if (unit_length == 0) return; |
| 1894 | 1915 | const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); |
| 1895 | 1916 | |
| 1896 | const version = try in_stream.readInt(st.elf.endian, u16); | |
| 1917 | const version = try di.dwarf_in_stream.readInt(di.endian, u16); | |
| 1897 | 1918 | if (version < 2 or version > 5) return error.InvalidDebugInfo; |
| 1898 | 1919 | |
| 1899 | const debug_abbrev_offset = if (is_64) try in_stream.readInt(st.elf.endian, u64) else try in_stream.readInt(st.elf.endian, u32); | |
| 1920 | const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(di.endian, u64) else try di.dwarf_in_stream.readInt(di.endian, u32); | |
| 1900 | 1921 | |
| 1901 | const address_size = try in_stream.readByte(); | |
| 1922 | const address_size = try di.dwarf_in_stream.readByte(); | |
| 1902 | 1923 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| 1903 | 1924 | |
| 1904 | const compile_unit_pos = try st.self_exe_file.getPos(); | |
| 1905 | const abbrev_table = try getAbbrevTable(st, debug_abbrev_offset); | |
| 1925 | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); | |
| 1926 | const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset); | |
| 1906 | 1927 | |
| 1907 | try st.self_exe_file.seekTo(compile_unit_pos); | |
| 1928 | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); | |
| 1908 | 1929 | |
| 1909 | const compile_unit_die = try st.allocator().create(try parseDie(st, abbrev_table, is_64)); | |
| 1930 | const compile_unit_die = try di.allocator().create(try parseDie(di, abbrev_table, is_64)); | |
| 1910 | 1931 | |
| 1911 | 1932 | if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; |
| 1912 | 1933 | |
| ... | ... | @@ -1934,7 +1955,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void { |
| 1934 | 1955 | } |
| 1935 | 1956 | }; |
| 1936 | 1957 | |
| 1937 | try st.compile_unit_list.append(CompileUnit{ | |
| 1958 | try di.compile_unit_list.append(CompileUnit{ | |
| 1938 | 1959 | .version = version, |
| 1939 | 1960 | .is_64 = is_64, |
| 1940 | 1961 | .pc_range = pc_range, |
| ... | ... | @@ -1947,20 +1968,18 @@ fn scanAllCompileUnits(st: *DebugInfo) !void { |
| 1947 | 1968 | } |
| 1948 | 1969 | } |
| 1949 | 1970 | |
| 1950 | fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit { | |
| 1951 | var in_file_stream = st.self_exe_file.inStream(); | |
| 1952 | const in_stream = &in_file_stream.stream; | |
| 1953 | for (st.compile_unit_list.toSlice()) |*compile_unit| { | |
| 1971 | fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { | |
| 1972 | for (di.compile_unit_list.toSlice()) |*compile_unit| { | |
| 1954 | 1973 | if (compile_unit.pc_range) |range| { |
| 1955 | 1974 | if (target_address >= range.start and target_address < range.end) return compile_unit; |
| 1956 | 1975 | } |
| 1957 | 1976 | if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { |
| 1958 | 1977 | var base_address: usize = 0; |
| 1959 | if (st.debug_ranges) |debug_ranges| { | |
| 1960 | try st.self_exe_file.seekTo(debug_ranges.offset + ranges_offset); | |
| 1978 | if (di.debug_ranges) |debug_ranges| { | |
| 1979 | try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); | |
| 1961 | 1980 | while (true) { |
| 1962 | const begin_addr = try in_stream.readIntLe(usize); | |
| 1963 | const end_addr = try in_stream.readIntLe(usize); | |
| 1981 | const begin_addr = try di.dwarf_in_stream.readIntLe(usize); | |
| 1982 | const end_addr = try di.dwarf_in_stream.readIntLe(usize); | |
| 1964 | 1983 | if (begin_addr == 0 and end_addr == 0) { |
| 1965 | 1984 | break; |
| 1966 | 1985 | } |
std/elf.zig+24-21| ... | ... | @@ -353,7 +353,8 @@ pub const SectionHeader = struct { |
| 353 | 353 | }; |
| 354 | 354 | |
| 355 | 355 | pub const Elf = struct { |
| 356 | in_file: os.File, | |
| 356 | seekable_stream: *io.SeekableStream(anyerror, anyerror), | |
| 357 | in_stream: *io.InStream(anyerror), | |
| 357 | 358 | auto_close_stream: bool, |
| 358 | 359 | is_64: bool, |
| 359 | 360 | endian: builtin.Endian, |
| ... | ... | @@ -370,19 +371,24 @@ pub const Elf = struct { |
| 370 | 371 | |
| 371 | 372 | /// Call close when done. |
| 372 | 373 | pub fn openPath(elf: *Elf, allocator: *mem.Allocator, path: []const u8) !void { |
| 373 | try elf.prealloc_file.open(path); | |
| 374 | try elf.openFile(allocator, *elf.prealloc_file); | |
| 375 | elf.auto_close_stream = true; | |
| 374 | @compileError("TODO implement"); | |
| 376 | 375 | } |
| 377 | 376 | |
| 378 | 377 | /// Call close when done. |
| 379 | 378 | pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: os.File) !void { |
| 380 | elf.allocator = allocator; | |
| 381 | elf.in_file = file; | |
| 382 | elf.auto_close_stream = false; | |
| 379 | @compileError("TODO implement"); | |
| 380 | } | |
| 383 | 381 | |
| 384 | var file_stream = elf.in_file.inStream(); | |
| 385 | const in = &file_stream.stream; | |
| 382 | pub fn openStream( | |
| 383 | elf: *Elf, | |
| 384 | allocator: *mem.Allocator, | |
| 385 | seekable_stream: *io.SeekableStream(anyerror, anyerror), | |
| 386 | in: *io.InStream(anyerror), | |
| 387 | ) !void { | |
| 388 | elf.auto_close_stream = false; | |
| 389 | elf.allocator = allocator; | |
| 390 | elf.seekable_stream = seekable_stream; | |
| 391 | elf.in_stream = in; | |
| 386 | 392 | |
| 387 | 393 | var magic: [4]u8 = undefined; |
| 388 | 394 | try in.readNoEof(magic[0..]); |
| ... | ... | @@ -404,7 +410,7 @@ pub const Elf = struct { |
| 404 | 410 | if (version_byte != 1) return error.InvalidFormat; |
| 405 | 411 | |
| 406 | 412 | // skip over padding |
| 407 | try elf.in_file.seekForward(9); | |
| 413 | try seekable_stream.seekForward(9); | |
| 408 | 414 | |
| 409 | 415 | elf.file_type = switch (try in.readInt(elf.endian, u16)) { |
| 410 | 416 | 1 => FileType.Relocatable, |
| ... | ... | @@ -441,7 +447,7 @@ pub const Elf = struct { |
| 441 | 447 | } |
| 442 | 448 | |
| 443 | 449 | // skip over flags |
| 444 | try elf.in_file.seekForward(4); | |
| 450 | try seekable_stream.seekForward(4); | |
| 445 | 451 | |
| 446 | 452 | const header_size = try in.readInt(elf.endian, u16); |
| 447 | 453 | if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) { |
| ... | ... | @@ -461,12 +467,12 @@ pub const Elf = struct { |
| 461 | 467 | const ph_byte_count = u64(ph_entry_size) * u64(ph_entry_count); |
| 462 | 468 | const end_ph = try math.add(u64, elf.program_header_offset, ph_byte_count); |
| 463 | 469 | |
| 464 | const stream_end = try elf.in_file.getEndPos(); | |
| 470 | const stream_end = try seekable_stream.getEndPos(); | |
| 465 | 471 | if (stream_end < end_sh or stream_end < end_ph) { |
| 466 | 472 | return error.InvalidFormat; |
| 467 | 473 | } |
| 468 | 474 | |
| 469 | try elf.in_file.seekTo(elf.section_header_offset); | |
| 475 | try seekable_stream.seekTo(elf.section_header_offset); | |
| 470 | 476 | |
| 471 | 477 | elf.section_headers = try elf.allocator.alloc(SectionHeader, sh_entry_count); |
| 472 | 478 | errdefer elf.allocator.free(elf.section_headers); |
| ... | ... | @@ -521,26 +527,23 @@ pub const Elf = struct { |
| 521 | 527 | pub fn close(elf: *Elf) void { |
| 522 | 528 | elf.allocator.free(elf.section_headers); |
| 523 | 529 | |
| 524 | if (elf.auto_close_stream) elf.in_file.close(); | |
| 530 | if (elf.auto_close_stream) elf.prealloc_file.close(); | |
| 525 | 531 | } |
| 526 | 532 | |
| 527 | 533 | pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader { |
| 528 | var file_stream = elf.in_file.inStream(); | |
| 529 | const in = &file_stream.stream; | |
| 530 | ||
| 531 | 534 | section_loop: for (elf.section_headers) |*elf_section| { |
| 532 | 535 | if (elf_section.sh_type == SHT_NULL) continue; |
| 533 | 536 | |
| 534 | 537 | const name_offset = elf.string_section.offset + elf_section.name; |
| 535 | try elf.in_file.seekTo(name_offset); | |
| 538 | try elf.seekable_stream.seekTo(name_offset); | |
| 536 | 539 | |
| 537 | 540 | for (name) |expected_c| { |
| 538 | const target_c = try in.readByte(); | |
| 541 | const target_c = try elf.in_stream.readByte(); | |
| 539 | 542 | if (target_c == 0 or expected_c != target_c) continue :section_loop; |
| 540 | 543 | } |
| 541 | 544 | |
| 542 | 545 | { |
| 543 | const null_byte = try in.readByte(); | |
| 546 | const null_byte = try elf.in_stream.readByte(); | |
| 544 | 547 | if (null_byte == 0) return elf_section; |
| 545 | 548 | } |
| 546 | 549 | } |
| ... | ... | @@ -549,7 +552,7 @@ pub const Elf = struct { |
| 549 | 552 | } |
| 550 | 553 | |
| 551 | 554 | pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void { |
| 552 | try elf.in_file.seekTo(elf_section.offset); | |
| 555 | try elf.seekable_stream.seekTo(elf_section.offset); | |
| 553 | 556 | } |
| 554 | 557 | }; |
| 555 | 558 |
std/fmt/index.zig+31-2| ... | ... | @@ -2,6 +2,7 @@ const std = @import("../index.zig"); |
| 2 | 2 | const math = std.math; |
| 3 | 3 | const debug = std.debug; |
| 4 | 4 | const assert = debug.assert; |
| 5 | const assertError = debug.assertError; | |
| 5 | 6 | const mem = std.mem; |
| 6 | 7 | const builtin = @import("builtin"); |
| 7 | 8 | const errol = @import("errol/index.zig"); |
| ... | ... | @@ -811,13 +812,41 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned |
| 811 | 812 | |
| 812 | 813 | for (buf) |c| { |
| 813 | 814 | const digit = try charToDigit(c, radix); |
| 814 | x = try math.mul(T, x, radix); | |
| 815 | x = try math.add(T, x, digit); | |
| 815 | ||
| 816 | if (x != 0) x = try math.mul(T, x, try math.cast(T, radix)); | |
| 817 | x = try math.add(T, x, try math.cast(T, digit)); | |
| 816 | 818 | } |
| 817 | 819 | |
| 818 | 820 | return x; |
| 819 | 821 | } |
| 820 | 822 | |
| 823 | test "parseUnsigned" { | |
| 824 | assert((try parseUnsigned(u16, "050124", 10)) == 50124); | |
| 825 | assert((try parseUnsigned(u16, "65535", 10)) == 65535); | |
| 826 | assertError(parseUnsigned(u16, "65536", 10), error.Overflow); | |
| 827 | ||
| 828 | assert((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); | |
| 829 | assertError(parseUnsigned(u64, "10000000000000000", 16), error.Overflow); | |
| 830 | ||
| 831 | assert((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); | |
| 832 | ||
| 833 | assert((try parseUnsigned(u7, "1", 10)) == 1); | |
| 834 | assert((try parseUnsigned(u7, "1000", 2)) == 8); | |
| 835 | ||
| 836 | assertError(parseUnsigned(u32, "f", 10), error.InvalidCharacter); | |
| 837 | assertError(parseUnsigned(u8, "109", 8), error.InvalidCharacter); | |
| 838 | ||
| 839 | assert((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); | |
| 840 | ||
| 841 | // these numbers should fit even though the radix itself doesn't fit in the destination type | |
| 842 | assert((try parseUnsigned(u1, "0", 10)) == 0); | |
| 843 | assert((try parseUnsigned(u1, "1", 10)) == 1); | |
| 844 | assertError(parseUnsigned(u1, "2", 10), error.Overflow); | |
| 845 | assert((try parseUnsigned(u1, "001", 16)) == 1); | |
| 846 | assert((try parseUnsigned(u2, "3", 16)) == 3); | |
| 847 | assertError(parseUnsigned(u2, "4", 16), error.Overflow); | |
| 848 | } | |
| 849 | ||
| 821 | 850 | pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { |
| 822 | 851 | const value = switch (c) { |
| 823 | 852 | '0'...'9' => c - '0', |
std/hash_map.zig+14| ... | ... | @@ -126,6 +126,14 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 |
| 126 | 126 | }; |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | pub fn getOrPutValue(self: *Self, key: K, value: V) !*KV { | |
| 130 | const res = try self.getOrPut(key); | |
| 131 | if (!res.found_existing) | |
| 132 | res.kv.value = value; | |
| 133 | ||
| 134 | return res.kv; | |
| 135 | } | |
| 136 | ||
| 129 | 137 | fn ensureCapacity(self: *Self) !void { |
| 130 | 138 | if (self.entries.len == 0) { |
| 131 | 139 | return self.initCapacity(16); |
| ... | ... | @@ -354,6 +362,12 @@ test "basic hash map usage" { |
| 354 | 362 | gop2.kv.value = 42; |
| 355 | 363 | assert(map.get(99).?.value == 42); |
| 356 | 364 | |
| 365 | const gop3 = try map.getOrPutValue(5, 5); | |
| 366 | assert(gop3.value == 77); | |
| 367 | ||
| 368 | const gop4 = try map.getOrPutValue(100, 41); | |
| 369 | assert(gop4.value == 41); | |
| 370 | ||
| 357 | 371 | assert(map.contains(2)); |
| 358 | 372 | assert(map.get(2).?.value == 22); |
| 359 | 373 | _ = map.remove(2); |
std/io.zig+64-14| ... | ... | @@ -32,6 +32,8 @@ pub fn getStdIn() GetStdIoErrs!File { |
| 32 | 32 | return File.openHandle(handle); |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; | |
| 36 | ||
| 35 | 37 | pub fn InStream(comptime ReadError: type) type { |
| 36 | 38 | return struct { |
| 37 | 39 | const Self = @This(); |
| ... | ... | @@ -683,25 +685,73 @@ test "import io tests" { |
| 683 | 685 | } |
| 684 | 686 | } |
| 685 | 687 | |
| 686 | pub fn readLine(buf: []u8) !usize { | |
| 687 | var stdin = getStdIn() catch return error.StdInUnavailable; | |
| 688 | var adapter = stdin.inStream(); | |
| 689 | var stream = &adapter.stream; | |
| 690 | var index: usize = 0; | |
| 688 | pub fn readLine(buf: *std.Buffer) ![]u8 { | |
| 689 | var stdin = try getStdIn(); | |
| 690 | var stdin_stream = stdin.inStream(); | |
| 691 | return readLineFrom(&stdin_stream.stream, buf); | |
| 692 | } | |
| 693 | ||
| 694 | /// Reads all characters until the next newline into buf, and returns | |
| 695 | /// a slice of the characters read (excluding the newline character(s)). | |
| 696 | pub fn readLineFrom(stream: var, buf: *std.Buffer) ![]u8 { | |
| 697 | const start = buf.len(); | |
| 691 | 698 | while (true) { |
| 692 | const byte = stream.readByte() catch return error.EndOfFile; | |
| 699 | const byte = try stream.readByte(); | |
| 693 | 700 | switch (byte) { |
| 694 | 701 | '\r' => { |
| 695 | 702 | // trash the following \n |
| 696 | _ = stream.readByte() catch return error.EndOfFile; | |
| 697 | return index; | |
| 698 | }, | |
| 699 | '\n' => return index, | |
| 700 | else => { | |
| 701 | if (index == buf.len) return error.InputTooLong; | |
| 702 | buf[index] = byte; | |
| 703 | index += 1; | |
| 703 | _ = try stream.readByte(); | |
| 704 | return buf.toSlice()[start..]; | |
| 704 | 705 | }, |
| 706 | '\n' => return buf.toSlice()[start..], | |
| 707 | else => try buf.appendByte(byte), | |
| 705 | 708 | } |
| 706 | 709 | } |
| 707 | 710 | } |
| 711 | ||
| 712 | test "io.readLineFrom" { | |
| 713 | var bytes: [128]u8 = undefined; | |
| 714 | const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; | |
| 715 | ||
| 716 | var buf = try std.Buffer.initSize(allocator, 0); | |
| 717 | var mem_stream = SliceInStream.init( | |
| 718 | \\Line 1 | |
| 719 | \\Line 22 | |
| 720 | \\Line 333 | |
| 721 | ); | |
| 722 | const stream = &mem_stream.stream; | |
| 723 | ||
| 724 | debug.assert(mem.eql(u8, "Line 1", try readLineFrom(stream, &buf))); | |
| 725 | debug.assert(mem.eql(u8, "Line 22", try readLineFrom(stream, &buf))); | |
| 726 | debug.assertError(readLineFrom(stream, &buf), error.EndOfStream); | |
| 727 | debug.assert(mem.eql(u8, buf.toSlice(), "Line 1Line 22Line 333")); | |
| 728 | } | |
| 729 | ||
| 730 | pub fn readLineSlice(slice: []u8) ![]u8 { | |
| 731 | var stdin = try getStdIn(); | |
| 732 | var stdin_stream = stdin.inStream(); | |
| 733 | return readLineSliceFrom(&stdin_stream.stream, slice); | |
| 734 | } | |
| 735 | ||
| 736 | /// Reads all characters until the next newline into slice, and returns | |
| 737 | /// a slice of the characters read (excluding the newline character(s)). | |
| 738 | pub fn readLineSliceFrom(stream: var, slice: []u8) ![]u8 { | |
| 739 | // We cannot use Buffer.fromOwnedSlice, as it wants to append a null byte | |
| 740 | // after taking ownership, which would always require an allocation. | |
| 741 | var buf = std.Buffer{ .list = std.ArrayList(u8).fromOwnedSlice(debug.failing_allocator, slice) }; | |
| 742 | try buf.resize(0); | |
| 743 | return try readLineFrom(stream, &buf); | |
| 744 | } | |
| 745 | ||
| 746 | test "io.readLineSliceFrom" { | |
| 747 | var buf: [7]u8 = undefined; | |
| 748 | var mem_stream = SliceInStream.init( | |
| 749 | \\Line 1 | |
| 750 | \\Line 22 | |
| 751 | \\Line 333 | |
| 752 | ); | |
| 753 | const stream = &mem_stream.stream; | |
| 754 | ||
| 755 | debug.assert(mem.eql(u8, "Line 1", try readLineSliceFrom(stream, buf[0..]))); | |
| 756 | debug.assertError(readLineSliceFrom(stream, buf[0..]), error.OutOfMemory); | |
| 757 | } |
std/io/seekable_stream.zig created+32| ... | ... | @@ -0,0 +1,32 @@ |
| 1 | const std = @import("../index.zig"); | |
| 2 | const InStream = std.io.InStream; | |
| 3 | ||
| 4 | pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type { | |
| 5 | return struct { | |
| 6 | const Self = @This(); | |
| 7 | pub const SeekError = SeekErrorType; | |
| 8 | pub const GetSeekPosError = GetSeekPosErrorType; | |
| 9 | ||
| 10 | seekToFn: fn (self: *Self, pos: usize) SeekError!void, | |
| 11 | seekForwardFn: fn (self: *Self, pos: isize) SeekError!void, | |
| 12 | ||
| 13 | getPosFn: fn (self: *Self) GetSeekPosError!usize, | |
| 14 | getEndPosFn: fn (self: *Self) GetSeekPosError!usize, | |
| 15 | ||
| 16 | pub fn seekTo(self: *Self, pos: usize) SeekError!void { | |
| 17 | return self.seekToFn(self, pos); | |
| 18 | } | |
| 19 | ||
| 20 | pub fn seekForward(self: *Self, amt: isize) SeekError!void { | |
| 21 | return self.seekForwardFn(self, amt); | |
| 22 | } | |
| 23 | ||
| 24 | pub fn getEndPos(self: *Self) GetSeekPosError!usize { | |
| 25 | return self.getEndPosFn(self); | |
| 26 | } | |
| 27 | ||
| 28 | pub fn getPos(self: *Self) GetSeekPosError!usize { | |
| 29 | return self.getPosFn(self); | |
| 30 | } | |
| 31 | }; | |
| 32 | } |
std/linked_list.zig+96| ... | ... | @@ -82,6 +82,28 @@ pub fn LinkedList(comptime T: type) type { |
| 82 | 82 | list.len += 1; |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | /// Concatenate list2 onto the end of list1, removing all entries from the former. | |
| 86 | /// | |
| 87 | /// Arguments: | |
| 88 | /// list1: the list to concatenate onto | |
| 89 | /// list2: the list to be concatenated | |
| 90 | pub fn concatByMoving(list1: *Self, list2: *Self) void { | |
| 91 | const l2_first = list2.first orelse return; | |
| 92 | if (list1.last) |l1_last| { | |
| 93 | l1_last.next = list2.first; | |
| 94 | l2_first.prev = list1.last; | |
| 95 | list1.len += list2.len; | |
| 96 | } else { | |
| 97 | // list1 was empty | |
| 98 | list1.first = list2.first; | |
| 99 | list1.len = list2.len; | |
| 100 | } | |
| 101 | list1.last = list2.last; | |
| 102 | list2.first = null; | |
| 103 | list2.last = null; | |
| 104 | list2.len = 0; | |
| 105 | } | |
| 106 | ||
| 85 | 107 | /// Insert a new node at the end of the list. |
| 86 | 108 | /// |
| 87 | 109 | /// Arguments: |
| ... | ... | @@ -247,3 +269,77 @@ test "basic linked list test" { |
| 247 | 269 | assert(list.last.?.data == 4); |
| 248 | 270 | assert(list.len == 2); |
| 249 | 271 | } |
| 272 | ||
| 273 | test "linked list concatenation" { | |
| 274 | const allocator = debug.global_allocator; | |
| 275 | var list1 = LinkedList(u32).init(); | |
| 276 | var list2 = LinkedList(u32).init(); | |
| 277 | ||
| 278 | var one = try list1.createNode(1, allocator); | |
| 279 | defer list1.destroyNode(one, allocator); | |
| 280 | var two = try list1.createNode(2, allocator); | |
| 281 | defer list1.destroyNode(two, allocator); | |
| 282 | var three = try list1.createNode(3, allocator); | |
| 283 | defer list1.destroyNode(three, allocator); | |
| 284 | var four = try list1.createNode(4, allocator); | |
| 285 | defer list1.destroyNode(four, allocator); | |
| 286 | var five = try list1.createNode(5, allocator); | |
| 287 | defer list1.destroyNode(five, allocator); | |
| 288 | ||
| 289 | list1.append(one); | |
| 290 | list1.append(two); | |
| 291 | list2.append(three); | |
| 292 | list2.append(four); | |
| 293 | list2.append(five); | |
| 294 | ||
| 295 | list1.concatByMoving(&list2); | |
| 296 | ||
| 297 | assert(list1.last == five); | |
| 298 | assert(list1.len == 5); | |
| 299 | assert(list2.first == null); | |
| 300 | assert(list2.last == null); | |
| 301 | assert(list2.len == 0); | |
| 302 | ||
| 303 | // Traverse forwards. | |
| 304 | { | |
| 305 | var it = list1.first; | |
| 306 | var index: u32 = 1; | |
| 307 | while (it) |node| : (it = node.next) { | |
| 308 | assert(node.data == index); | |
| 309 | index += 1; | |
| 310 | } | |
| 311 | } | |
| 312 | ||
| 313 | // Traverse backwards. | |
| 314 | { | |
| 315 | var it = list1.last; | |
| 316 | var index: u32 = 1; | |
| 317 | while (it) |node| : (it = node.prev) { | |
| 318 | assert(node.data == (6 - index)); | |
| 319 | index += 1; | |
| 320 | } | |
| 321 | } | |
| 322 | ||
| 323 | // Swap them back, this verifies that concating to an empty list works. | |
| 324 | list2.concatByMoving(&list1); | |
| 325 | ||
| 326 | // Traverse forwards. | |
| 327 | { | |
| 328 | var it = list2.first; | |
| 329 | var index: u32 = 1; | |
| 330 | while (it) |node| : (it = node.next) { | |
| 331 | assert(node.data == index); | |
| 332 | index += 1; | |
| 333 | } | |
| 334 | } | |
| 335 | ||
| 336 | // Traverse backwards. | |
| 337 | { | |
| 338 | var it = list2.last; | |
| 339 | var index: u32 = 1; | |
| 340 | while (it) |node| : (it = node.prev) { | |
| 341 | assert(node.data == (6 - index)); | |
| 342 | index += 1; | |
| 343 | } | |
| 344 | } | |
| 345 | } |
std/os/file.zig+59-5| ... | ... | @@ -228,7 +228,14 @@ pub const File = struct { |
| 228 | 228 | return os.isTty(self.handle); |
| 229 | 229 | } |
| 230 | 230 | |
| 231 | pub fn seekForward(self: File, amount: isize) !void { | |
| 231 | pub const SeekError = error{ | |
| 232 | /// TODO make this error impossible to get | |
| 233 | Overflow, | |
| 234 | Unseekable, | |
| 235 | Unexpected, | |
| 236 | }; | |
| 237 | ||
| 238 | pub fn seekForward(self: File, amount: isize) SeekError!void { | |
| 232 | 239 | switch (builtin.os) { |
| 233 | 240 | Os.linux, Os.macosx, Os.ios, Os.freebsd => { |
| 234 | 241 | const result = posix.lseek(self.handle, amount, posix.SEEK_CUR); |
| ... | ... | @@ -259,7 +266,7 @@ pub const File = struct { |
| 259 | 266 | } |
| 260 | 267 | } |
| 261 | 268 | |
| 262 | pub fn seekTo(self: File, pos: usize) !void { | |
| 269 | pub fn seekTo(self: File, pos: usize) SeekError!void { | |
| 263 | 270 | switch (builtin.os) { |
| 264 | 271 | Os.linux, Os.macosx, Os.ios, Os.freebsd => { |
| 265 | 272 | const ipos = try math.cast(isize, pos); |
| ... | ... | @@ -293,7 +300,14 @@ pub const File = struct { |
| 293 | 300 | } |
| 294 | 301 | } |
| 295 | 302 | |
| 296 | pub fn getPos(self: File) !usize { | |
| 303 | pub const GetSeekPosError = error{ | |
| 304 | Overflow, | |
| 305 | SystemResources, | |
| 306 | Unseekable, | |
| 307 | Unexpected, | |
| 308 | }; | |
| 309 | ||
| 310 | pub fn getPos(self: File) GetSeekPosError!usize { | |
| 297 | 311 | switch (builtin.os) { |
| 298 | 312 | Os.linux, Os.macosx, Os.ios, Os.freebsd => { |
| 299 | 313 | const result = posix.lseek(self.handle, 0, posix.SEEK_CUR); |
| ... | ... | @@ -323,13 +337,13 @@ pub const File = struct { |
| 323 | 337 | } |
| 324 | 338 | |
| 325 | 339 | assert(pos >= 0); |
| 326 | return math.cast(usize, pos) catch error.FilePosLargerThanPointerRange; | |
| 340 | return math.cast(usize, pos); | |
| 327 | 341 | }, |
| 328 | 342 | else => @compileError("unsupported OS"), |
| 329 | 343 | } |
| 330 | 344 | } |
| 331 | 345 | |
| 332 | pub fn getEndPos(self: File) !usize { | |
| 346 | pub fn getEndPos(self: File) GetSeekPosError!usize { | |
| 333 | 347 | if (is_posix) { |
| 334 | 348 | const stat = try os.posixFStat(self.handle); |
| 335 | 349 | return @intCast(usize, stat.size); |
| ... | ... | @@ -431,6 +445,18 @@ pub const File = struct { |
| 431 | 445 | }; |
| 432 | 446 | } |
| 433 | 447 | |
| 448 | pub fn seekableStream(file: File) SeekableStream { | |
| 449 | return SeekableStream{ | |
| 450 | .file = file, | |
| 451 | .stream = SeekableStream.Stream{ | |
| 452 | .seekToFn = SeekableStream.seekToFn, | |
| 453 | .seekForwardFn = SeekableStream.seekForwardFn, | |
| 454 | .getPosFn = SeekableStream.getPosFn, | |
| 455 | .getEndPosFn = SeekableStream.getEndPosFn, | |
| 456 | }, | |
| 457 | }; | |
| 458 | } | |
| 459 | ||
| 434 | 460 | /// Implementation of io.InStream trait for File |
| 435 | 461 | pub const InStream = struct { |
| 436 | 462 | file: File, |
| ... | ... | @@ -458,4 +484,32 @@ pub const File = struct { |
| 458 | 484 | return self.file.write(bytes); |
| 459 | 485 | } |
| 460 | 486 | }; |
| 487 | ||
| 488 | /// Implementation of io.SeekableStream trait for File | |
| 489 | pub const SeekableStream = struct { | |
| 490 | file: File, | |
| 491 | stream: Stream, | |
| 492 | ||
| 493 | pub const Stream = io.SeekableStream(SeekError, GetSeekPosError); | |
| 494 | ||
| 495 | pub fn seekToFn(seekable_stream: *Stream, pos: usize) SeekError!void { | |
| 496 | const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); | |
| 497 | return self.file.seekTo(pos); | |
| 498 | } | |
| 499 | ||
| 500 | pub fn seekForwardFn(seekable_stream: *Stream, amt: isize) SeekError!void { | |
| 501 | const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); | |
| 502 | return self.file.seekForward(amt); | |
| 503 | } | |
| 504 | ||
| 505 | pub fn getEndPosFn(seekable_stream: *Stream) GetSeekPosError!usize { | |
| 506 | const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); | |
| 507 | return self.file.getEndPos(); | |
| 508 | } | |
| 509 | ||
| 510 | pub fn getPosFn(seekable_stream: *Stream) GetSeekPosError!usize { | |
| 511 | const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); | |
| 512 | return self.file.getPos(); | |
| 513 | } | |
| 514 | }; | |
| 461 | 515 | }; |
std/unicode.zig+1-1| ... | ... | @@ -208,7 +208,7 @@ pub const Utf8View = struct { |
| 208 | 208 | } |
| 209 | 209 | }; |
| 210 | 210 | |
| 211 | const Utf8Iterator = struct { | |
| 211 | pub const Utf8Iterator = struct { | |
| 212 | 212 | bytes: []const u8, |
| 213 | 213 | i: usize, |
| 214 | 214 |
test/compile_errors.zig+16-4| ... | ... | @@ -1,6 +1,18 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4 | cases.add( | |
| 5 | "error note for function parameter incompatibility", | |
| 6 | \\fn do_the_thing(func: fn (arg: i32) void) void {} | |
| 7 | \\fn bar(arg: bool) void {} | |
| 8 | \\export fn entry() void { | |
| 9 | \\ do_the_thing(bar); | |
| 10 | \\} | |
| 11 | , | |
| 12 | ".tmp_source.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void", | |
| 13 | ".tmp_source.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'", | |
| 14 | ); | |
| 15 | ||
| 4 | 16 | cases.add( |
| 5 | 17 | "cast negative value to unsigned integer", |
| 6 | 18 | \\comptime { |
| ... | ... | @@ -5248,8 +5260,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5248 | 5260 | \\export fn foo() void { |
| 5249 | 5261 | \\ asm volatile ("" : : [bar]"r"(3) : ""); |
| 5250 | 5262 | \\} |
| 5251 | , | |
| 5252 | ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_int", | |
| 5263 | , | |
| 5264 | ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_int", | |
| 5253 | 5265 | ); |
| 5254 | 5266 | |
| 5255 | 5267 | cases.add( |
| ... | ... | @@ -5257,7 +5269,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5257 | 5269 | \\export fn foo() void { |
| 5258 | 5270 | \\ asm volatile ("" : : [bar]"r"(3.17) : ""); |
| 5259 | 5271 | \\} |
| 5260 | , | |
| 5261 | ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float", | |
| 5272 | , | |
| 5273 | ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float", | |
| 5262 | 5274 | ); |
| 5263 | 5275 | } |