From 942491c86f6caaba419628ee517854195e8285c2 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Fri, 28 Aug 2020 17:19:06 +0200 Subject: [PATCH] Fix version with NULLPTR in JSON output global._version has been changed at some point to an null-terminated string. However, its type was still a D string. Some parts of the code base were updated to do `.length - 1`, but other like the `compilerInfo` were forgotten. This change remedies this and: - removes null terminator from the D string of _version - initializes versionNumber at compile-time - expose _version as versionString (D) and versionChars (C++) --- src/dmd/backend/dwarfdbginf.d | 2 +- src/dmd/backend/elfobj.d | 5 ++- src/dmd/dmsc.d | 2 +- src/dmd/frontend.h | 4 +- src/dmd/globals.d | 84 +++++++++++++++++++++-------------- src/dmd/globals.h | 6 ++- src/dmd/json.d | 2 +- src/dmd/mars.d | 6 +-- src/tests/cxxfrontend.c | 9 ++++ 9 files changed, 76 insertions(+), 44 deletions(-) diff --git a/src/dmd/backend/dwarfdbginf.d b/src/dmd/backend/dwarfdbginf.d index d6c2054caaf7..72e1bf1669ec 100644 --- a/src/dmd/backend/dwarfdbginf.d +++ b/src/dmd/backend/dwarfdbginf.d @@ -1126,7 +1126,7 @@ static if (ELFOBJ || MACHOBJ) version (MARS) { debug_info.buf.write("Digital Mars D "); - debug_info.buf.write(config._version); // DW_AT_producer + debug_info.buf.writeString(config._version); // DW_AT_producer // DW_AT_language debug_info.buf.writeByte((config.fulltypes == CVDWARF_D) ? DW_LANG_D : DW_LANG_C89); } diff --git a/src/dmd/backend/elfobj.d b/src/dmd/backend/elfobj.d index dd02345db0f9..4051ac3d4ff3 100644 --- a/src/dmd/backend/elfobj.d +++ b/src/dmd/backend/elfobj.d @@ -1500,10 +1500,11 @@ void Obj_compiler() enum n = compilerHeader.length; char[n + maxVersionLength] compiler = compilerHeader; - assert(config._version.length < maxVersionLength); + assert(config._version.length + 1 < maxVersionLength); const newLength = n + config._version.length; compiler[n .. newLength] = config._version; - comment_data.write(compiler[0 .. newLength]); + compiler[newLength] = 0; + comment_data.write(compiler[0 .. newLength + 1]); //dbg_printf("Comment data size %d\n",comment_data.length()); } diff --git a/src/dmd/dmsc.d b/src/dmd/dmsc.d index 76c1ce716610..7900a49d43c5 100644 --- a/src/dmd/dmsc.d +++ b/src/dmd/dmsc.d @@ -100,7 +100,7 @@ void backend_init() params.useModuleInfo && Module.moduleinfo, params.useTypeInfo && Type.dtypeinfo, params.useExceptions && ClassDeclaration.throwable, - global._version + global.versionString() ); debug diff --git a/src/dmd/frontend.h b/src/dmd/frontend.h index b0fc1adf0ae7..9876c64207cf 100644 --- a/src/dmd/frontend.h +++ b/src/dmd/frontend.h @@ -12497,7 +12497,6 @@ struct Global DArray< char > written; Array* path; Array* filePath; - DArray< char > _version; DArray< const char > vendor; Param params; uint32_t errors; @@ -12515,8 +12514,9 @@ struct Global void increaseErrorCount(); void _init(); uint32_t versionNumber(); + const char* const versionChars(); ~Global(); - Global() : inifilename(), mars_ext("d"), obj_ext(), lib_ext(), dll_ext(), doc_ext("html"), ddoc_ext("ddoc"), hdr_ext("di"), cxxhdr_ext("h"), json_ext("json"), map_ext("map"), run_noext(), copyright("Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved"), written("written by Walter Bright"), path(), filePath(), _version(), vendor(), params(), errors(), warnings(), gag(), gaggedErrors(), gaggedWarnings(), console(), versionids(), debugids() {} + Global() : inifilename(), mars_ext("d"), obj_ext(), lib_ext(), dll_ext(), doc_ext("html"), ddoc_ext("ddoc"), hdr_ext("di"), cxxhdr_ext("h"), json_ext("json"), map_ext("map"), run_noext(), copyright("Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved"), written("written by Walter Bright"), path(), filePath(), vendor(), params(), errors(), warnings(), gag(), gaggedErrors(), gaggedWarnings(), console(), versionids(), debugids() {} }; typedef uint64_t dinteger_t; diff --git a/src/dmd/globals.d b/src/dmd/globals.d index ef2e13e16708..af8703cabc83 100644 --- a/src/dmd/globals.d +++ b/src/dmd/globals.d @@ -325,7 +325,9 @@ extern (C++) struct Global Array!(const(char)*)* path; // Array of char*'s which form the import lookup path Array!(const(char)*)* filePath; // Array of char*'s which form the file import lookup path - string _version; + private enum string _version = import("VERSION"); + private enum uint _versionNumber = parseVersionNumber(_version); + const(char)[] vendor; // Compiler backend name Param params; @@ -380,8 +382,6 @@ extern (C++) struct Global extern (C++) void _init() { - _version = import("VERSION") ~ '\0'; - version (MARS) { vendor = "Digital Mars D"; @@ -469,41 +469,59 @@ extern (C++) struct Global } /** - Returns: the version as the number that would be returned for __VERSION__ - */ - extern(C++) uint versionNumber() + * Computes the version number __VERSION__ from the compiler version string. + */ + extern (D) private static uint parseVersionNumber(string version_) { - import core.stdc.ctype; - __gshared uint cached = 0; - if (cached == 0) + // + // parse _version + // + uint major = 0; + uint minor = 0; + bool point = false; + // skip initial 'v' + foreach (const c; version_[1..$]) { - // - // parse _version - // - uint major = 0; - uint minor = 0; - bool point = false; - for (const(char)* p = _version.ptr + 1;; p++) + if ('0' <= c && c <= '9') // isdigit { - const c = *p; - if (isdigit(cast(char)c)) - { - minor = minor * 10 + c - '0'; - } - else if (c == '.') - { - if (point) - break; // ignore everything after second '.' - point = true; - major = minor; - minor = 0; - } - else - break; + minor = minor * 10 + c - '0'; } - cached = major * 1000 + minor; + else if (c == '.') + { + if (point) + break; // ignore everything after second '.' + point = true; + major = minor; + minor = 0; + } + else + break; } - return cached; + return major * 1000 + minor; + } + + /** + Returns: the version as the number that would be returned for __VERSION__ + */ + extern(C++) uint versionNumber() + { + return _versionNumber; + } + + /** + Returns: compiler version string. + */ + extern(D) string versionString() + { + return _version; + } + + /** + Returns: compiler version as char string. + */ + extern(C++) const(char*) versionChars() + { + return _version.ptr; } /** diff --git a/src/dmd/globals.h b/src/dmd/globals.h index de3dcd455de8..fdad284e708b 100644 --- a/src/dmd/globals.h +++ b/src/dmd/globals.h @@ -290,7 +290,6 @@ struct Global Array *path; // Array of char*'s which form the import lookup path Array *filePath; // Array of char*'s which form the file import lookup path - DString version; // Compiler version string DString vendor; // Compiler backend name Param params; @@ -326,6 +325,11 @@ struct Global Returns: the version as the number that would be returned for __VERSION__ */ unsigned versionNumber(); + + /** + Returns: the compiler version string. + */ + const char * versionChars(); }; extern Global global; diff --git a/src/dmd/json.d b/src/dmd/json.d index 939107133e48..e2bb0c8c67bb 100644 --- a/src/dmd/json.d +++ b/src/dmd/json.d @@ -829,7 +829,7 @@ public: { objectStart(); requiredProperty("vendor", global.vendor); - requiredProperty("version", global._version); + requiredProperty("version", global.versionString()); property("__VERSION__", global.versionNumber()); requiredProperty("interface", determineCompilerInterface()); property("size_t", size_t.sizeof); diff --git a/src/dmd/mars.d b/src/dmd/mars.d index 446d55dbd09f..4bc69f540207 100644 --- a/src/dmd/mars.d +++ b/src/dmd/mars.d @@ -73,7 +73,7 @@ private void logo() { printf("DMD%llu D Compiler %.*s\n%.*s %.*s\n", cast(ulong)size_t.sizeof * 8, - cast(int) global._version.length - 1, global._version.ptr, + cast(int) global.versionString().length, global.versionString().ptr, cast(int)global.copyright.length, global.copyright.ptr, cast(int)global.written.length, global.written.ptr ); @@ -93,7 +93,7 @@ extern(C) void printInternalFailure(FILE* stream) "with, preferably, a reduced, reproducible example and the information below.\n" ~ "DustMite (https://github.com/CyberShadow/DustMite/wiki) can help with the reduction.\n" ~ "---\n").ptr, stream); - stream.fprintf("DMD %.*s\n", cast(int) global._version.length - 1, global._version.ptr); + stream.fprintf("DMD %.*s\n", cast(int) global.versionString().length, global.versionString().ptr); stream.printPredefinedVersions; stream.printGlobalConfigs(); fputs("---\n".ptr, stream); @@ -1347,7 +1347,7 @@ private void printPredefinedVersions(FILE* stream) extern(C) void printGlobalConfigs(FILE* stream) { stream.fprintf("binary %.*s\n", cast(int)global.params.argv0.length, global.params.argv0.ptr); - stream.fprintf("version %.*s\n", cast(int) global._version.length - 1, global._version.ptr); + stream.fprintf("version %.*s\n", cast(int) global.versionString().length, global.versionString().ptr); const iniOutput = global.inifilename ? global.inifilename : "(none)"; stream.fprintf("config %.*s\n", cast(int)iniOutput.length, iniOutput.ptr); // Print DFLAGS environment variable diff --git a/src/tests/cxxfrontend.c b/src/tests/cxxfrontend.c index f042efef5b76..27c8019cd6b4 100644 --- a/src/tests/cxxfrontend.c +++ b/src/tests/cxxfrontend.c @@ -105,6 +105,14 @@ void test_tokens() assert(strcmp(Token::toChars(TOKvectorarray), "vectorarray") == 0); } +void test_compiler_globals() +{ + // only check constant prefix of version + assert(strncmp(global.versionChars(), "v2.", 3) == 0); + unsigned versionNumber = global.versionNumber(); + assert(versionNumber >= 2060 && versionNumber <= 3000); +} + /**********************************/ class TestVisitor : public Visitor @@ -457,6 +465,7 @@ int main(int argc, char **argv) frontend_init(); test_tokens(); + test_compiler_globals(); test_visitors(); test_semantic(); test_expression();