[PATCH vkd3d 2/5] vkd3d-shader: Move hlsl_ctx initialization and cleanup to hlsl_compile_shader().

Zebediah Figura zfigura at codeweavers.com
Thu Feb 4 16:33:50 CST 2021


Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
 libs/vkd3d-shader/hlsl.c | 182 +++++++++++++++++++++++++++++++++++----
 libs/vkd3d-shader/hlsl.h |  11 +--
 libs/vkd3d-shader/hlsl.l |   5 +-
 libs/vkd3d-shader/hlsl.y | 153 ++------------------------------
 4 files changed, 180 insertions(+), 171 deletions(-)

diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c
index 5ad4484d..e3d11108 100644
--- a/libs/vkd3d-shader/hlsl.c
+++ b/libs/vkd3d-shader/hlsl.c
@@ -328,6 +328,15 @@ struct hlsl_type *hlsl_type_clone(struct hlsl_type *old, unsigned int default_ma
     return type;
 }
 
+bool hlsl_scope_add_type(struct hlsl_scope *scope, struct hlsl_type *type)
+{
+    if (hlsl_get_type(scope, type->name, false))
+        return false;
+
+    rb_put(&scope->types, type->name, &type->scope_entry);
+    return true;
+}
+
 struct hlsl_ir_expr *hlsl_new_cast(struct hlsl_ir_node *node, struct hlsl_type *type,
         struct source_location *loc)
 {
@@ -622,19 +631,6 @@ static int compare_function_decl_rb(const void *key, const struct rb_entry *entr
     return 0;
 }
 
-static int compare_function_rb(const void *key, const struct rb_entry *entry)
-{
-    const char *name = key;
-    const struct hlsl_ir_function *func = RB_ENTRY_VALUE(entry, const struct hlsl_ir_function,entry);
-
-    return strcmp(name, func->name);
-}
-
-void init_functions_tree(struct rb_tree *funcs)
-{
-    rb_init(&hlsl_ctx.functions, compare_function_rb);
-}
-
 const char *hlsl_base_type_to_string(const struct hlsl_type *type)
 {
     const char *name = "(unknown)";
@@ -1230,7 +1226,7 @@ static void free_function(struct hlsl_ir_function *func)
     vkd3d_free(func);
 }
 
-void hlsl_free_function_rb(struct rb_entry *entry, void *context)
+static void free_function_rb(struct rb_entry *entry, void *context)
 {
     free_function(RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry));
 }
@@ -1372,11 +1368,159 @@ static const struct hlsl_profile_info *get_target_info(const char *target)
     return NULL;
 }
 
+static int compare_function_rb(const void *key, const struct rb_entry *entry)
+{
+    const char *name = key;
+    const struct hlsl_ir_function *func = RB_ENTRY_VALUE(entry, const struct hlsl_ir_function,entry);
+
+    return strcmp(name, func->name);
+}
+
+static void declare_predefined_types(struct hlsl_scope *scope)
+{
+    struct hlsl_type *type;
+    unsigned int x, y, bt;
+    static const char * const names[] =
+    {
+        "float",
+        "half",
+        "double",
+        "int",
+        "uint",
+        "bool",
+    };
+    char name[10];
+
+    static const char *const sampler_names[] =
+    {
+        "sampler",
+        "sampler1D",
+        "sampler2D",
+        "sampler3D",
+        "samplerCUBE"
+    };
+
+    for (bt = 0; bt <= HLSL_TYPE_LAST_SCALAR; ++bt)
+    {
+        for (y = 1; y <= 4; ++y)
+        {
+            for (x = 1; x <= 4; ++x)
+            {
+                sprintf(name, "%s%ux%u", names[bt], y, x);
+                type = hlsl_new_type(vkd3d_strdup(name), HLSL_CLASS_MATRIX, bt, x, y);
+                hlsl_scope_add_type(scope, type);
+
+                if (y == 1)
+                {
+                    sprintf(name, "%s%u", names[bt], x);
+                    type = hlsl_new_type(vkd3d_strdup(name), HLSL_CLASS_VECTOR, bt, x, y);
+                    hlsl_scope_add_type(scope, type);
+                    hlsl_ctx.builtin_types.vector[bt][x - 1] = type;
+
+                    if (x == 1)
+                    {
+                        sprintf(name, "%s", names[bt]);
+                        type = hlsl_new_type(vkd3d_strdup(name), HLSL_CLASS_SCALAR, bt, x, y);
+                        hlsl_scope_add_type(scope, type);
+                        hlsl_ctx.builtin_types.scalar[bt] = type;
+                    }
+                }
+            }
+        }
+    }
+
+    for (bt = 0; bt <= HLSL_SAMPLER_DIM_MAX; ++bt)
+    {
+        type = hlsl_new_type(vkd3d_strdup(sampler_names[bt]), HLSL_CLASS_OBJECT, HLSL_TYPE_SAMPLER, 1, 1);
+        type->sampler_dim = bt;
+        hlsl_ctx.builtin_types.sampler[bt] = type;
+    }
+
+    hlsl_ctx.builtin_types.Void = hlsl_new_type(vkd3d_strdup("void"), HLSL_CLASS_OBJECT, HLSL_TYPE_VOID, 1, 1);
+
+    /* DX8 effects predefined types */
+    type = hlsl_new_type(vkd3d_strdup("DWORD"), HLSL_CLASS_SCALAR, HLSL_TYPE_INT, 1, 1);
+    hlsl_scope_add_type(scope, type);
+    type = hlsl_new_type(vkd3d_strdup("FLOAT"), HLSL_CLASS_SCALAR, HLSL_TYPE_FLOAT, 1, 1);
+    hlsl_scope_add_type(scope, type);
+    type = hlsl_new_type(vkd3d_strdup("VECTOR"), HLSL_CLASS_VECTOR, HLSL_TYPE_FLOAT, 4, 1);
+    hlsl_scope_add_type(scope, type);
+    type = hlsl_new_type(vkd3d_strdup("MATRIX"), HLSL_CLASS_MATRIX, HLSL_TYPE_FLOAT, 4, 4);
+    hlsl_scope_add_type(scope, type);
+    type = hlsl_new_type(vkd3d_strdup("STRING"), HLSL_CLASS_OBJECT, HLSL_TYPE_STRING, 1, 1);
+    hlsl_scope_add_type(scope, type);
+    type = hlsl_new_type(vkd3d_strdup("TEXTURE"), HLSL_CLASS_OBJECT, HLSL_TYPE_TEXTURE, 1, 1);
+    hlsl_scope_add_type(scope, type);
+    type = hlsl_new_type(vkd3d_strdup("PIXELSHADER"), HLSL_CLASS_OBJECT, HLSL_TYPE_PIXELSHADER, 1, 1);
+    hlsl_scope_add_type(scope, type);
+    type = hlsl_new_type(vkd3d_strdup("VERTEXSHADER"), HLSL_CLASS_OBJECT, HLSL_TYPE_VERTEXSHADER, 1, 1);
+    hlsl_scope_add_type(scope, type);
+}
+
+static bool hlsl_ctx_init(struct hlsl_parse_ctx *ctx, struct vkd3d_shader_message_context *message_context)
+{
+    memset(ctx, 0, sizeof(*ctx));
+
+    ctx->message_context = message_context;
+
+    ctx->line_no = ctx->column = 1;
+    if (!(ctx->source_file = vkd3d_strdup("")))
+        return false;
+    if (!(ctx->source_files = vkd3d_malloc(sizeof(*ctx->source_files))))
+    {
+        vkd3d_free((void *)ctx->source_file);
+        return false;
+    }
+    ctx->source_files[0] = ctx->source_file;
+    ctx->source_files_count = 1;
+
+    ctx->matrix_majority = HLSL_COLUMN_MAJOR;
+
+    list_init(&ctx->scopes);
+    hlsl_push_scope(ctx);
+    ctx->globals = ctx->cur_scope;
+
+    list_init(&ctx->types);
+    declare_predefined_types(ctx->globals);
+
+    rb_init(&ctx->functions, compare_function_rb);
+
+    list_init(&ctx->static_initializers);
+
+    return true;
+}
+
+static void hlsl_ctx_cleanup(struct hlsl_parse_ctx *ctx)
+{
+    struct hlsl_scope *scope, *next_scope;
+    struct hlsl_ir_var *var, *next_var;
+    struct hlsl_type *type, *next_type;
+    unsigned int i;
+
+    for (i = 0; i < ctx->source_files_count; ++i)
+        vkd3d_free((void *)ctx->source_files[i]);
+    vkd3d_free(ctx->source_files);
+
+    rb_destroy(&ctx->functions, free_function_rb, NULL);
+
+    LIST_FOR_EACH_ENTRY_SAFE(scope, next_scope, &ctx->scopes, struct hlsl_scope, entry)
+    {
+        LIST_FOR_EACH_ENTRY_SAFE(var, next_var, &scope->vars, struct hlsl_ir_var, scope_entry)
+            hlsl_free_var(var);
+        rb_destroy(&scope->types, NULL, NULL);
+        vkd3d_free(scope);
+    }
+
+    LIST_FOR_EACH_ENTRY_SAFE(type, next_type, &ctx->types, struct hlsl_type, entry)
+        hlsl_free_type(type);
+}
+
 int hlsl_compile_shader(const char *text, const struct vkd3d_shader_compile_info *compile_info,
         struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context)
 {
     const struct vkd3d_shader_hlsl_source_info *hlsl_source_info;
     const struct hlsl_profile_info *profile;
+    int ret;
 
     if (!(hlsl_source_info = vkd3d_find_struct(compile_info->next, HLSL_SOURCE_INFO)))
     {
@@ -1392,6 +1536,12 @@ int hlsl_compile_shader(const char *text, const struct vkd3d_shader_compile_info
 
     vkd3d_shader_dump_shader(profile->type, &compile_info->source);
 
-    return hlsl_lexer_compile(text, profile->type, profile->sm_major, profile->sm_minor,
-            hlsl_source_info->entry_point ? hlsl_source_info->entry_point : "main", dxbc, message_context);
+    if (!hlsl_ctx_init(&hlsl_ctx, message_context))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+
+    ret = hlsl_lexer_compile(text, hlsl_source_info->entry_point ? hlsl_source_info->entry_point : "main");
+
+    hlsl_ctx_cleanup(&hlsl_ctx);
+
+    return ret;
 }
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h
index a3c16c51..48067956 100644
--- a/libs/vkd3d-shader/hlsl.h
+++ b/libs/vkd3d-shader/hlsl.h
@@ -518,8 +518,6 @@ static inline void set_parse_status(enum parse_status *current, enum parse_statu
         *current = PARSE_WARN;
 }
 
-void init_functions_tree(struct rb_tree *funcs) DECLSPEC_HIDDEN;
-
 const char *hlsl_base_type_to_string(const struct hlsl_type *type) DECLSPEC_HIDDEN;
 const char *debug_hlsl_type(const struct hlsl_type *type) DECLSPEC_HIDDEN;
 const char *hlsl_debug_modifiers(DWORD modifiers) DECLSPEC_HIDDEN;
@@ -536,7 +534,6 @@ void hlsl_dump_function(const struct hlsl_ir_function_decl *func) DECLSPEC_HIDDE
 
 void hlsl_free_instr(struct hlsl_ir_node *node) DECLSPEC_HIDDEN;
 void hlsl_free_instr_list(struct list *list) DECLSPEC_HIDDEN;
-void hlsl_free_function_rb(struct rb_entry *entry, void *context) DECLSPEC_HIDDEN;
 void hlsl_free_type(struct hlsl_type *type) DECLSPEC_HIDDEN;
 void hlsl_free_var(struct hlsl_ir_var *decl) DECLSPEC_HIDDEN;
 
@@ -578,15 +575,15 @@ void hlsl_report_message(const struct source_location loc,
 void hlsl_push_scope(struct hlsl_parse_ctx *ctx) DECLSPEC_HIDDEN;
 void hlsl_pop_scope(struct hlsl_parse_ctx *ctx) DECLSPEC_HIDDEN;
 
+bool hlsl_scope_add_type(struct hlsl_scope *scope, struct hlsl_type *type) DECLSPEC_HIDDEN;
+
 struct hlsl_type *hlsl_type_clone(struct hlsl_type *old, unsigned int default_majority) DECLSPEC_HIDDEN;
 bool hlsl_type_compare(const struct hlsl_type *t1, const struct hlsl_type *t2) DECLSPEC_HIDDEN;
 unsigned int hlsl_type_component_count(struct hlsl_type *type) DECLSPEC_HIDDEN;
 bool hlsl_type_is_row_major(const struct hlsl_type *type) DECLSPEC_HIDDEN;
 bool hlsl_type_is_void(const struct hlsl_type *type) DECLSPEC_HIDDEN;
 
-int hlsl_lexer_compile(const char *text, enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint,
-        struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) DECLSPEC_HIDDEN;
-int hlsl_parser_compile(enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint,
-        struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) DECLSPEC_HIDDEN;
+int hlsl_lexer_compile(const char *text, const char *entrypoint) DECLSPEC_HIDDEN;
+int hlsl_parser_compile(const char *entrypoint) DECLSPEC_HIDDEN;
 
 #endif
diff --git a/libs/vkd3d-shader/hlsl.l b/libs/vkd3d-shader/hlsl.l
index 2a951ab0..0dea3d4c 100644
--- a/libs/vkd3d-shader/hlsl.l
+++ b/libs/vkd3d-shader/hlsl.l
@@ -271,8 +271,7 @@ row_major               {return KW_ROW_MAJOR;           }
 
 %%
 
-int hlsl_lexer_compile(const char *text, enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint,
-        struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context)
+int hlsl_lexer_compile(const char *text, const char *entrypoint)
 {
     YY_BUFFER_STATE buffer;
     int ret;
@@ -280,7 +279,7 @@ int hlsl_lexer_compile(const char *text, enum vkd3d_shader_type type, DWORD majo
     buffer = hlsl__scan_string(text);
     hlsl__switch_to_buffer(buffer);
 
-    ret = hlsl_parser_compile(type, major, minor, entrypoint, dxbc, message_context);
+    ret = hlsl_parser_compile(entrypoint);
 
     hlsl__delete_buffer(buffer);
     return ret;
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
index 5c3160f4..81e2be1d 100644
--- a/libs/vkd3d-shader/hlsl.y
+++ b/libs/vkd3d-shader/hlsl.y
@@ -341,96 +341,6 @@ static DWORD add_modifiers(DWORD modifiers, DWORD mod, const struct source_locat
     return modifiers | mod;
 }
 
-static bool add_type_to_scope(struct hlsl_scope *scope, struct hlsl_type *def)
-{
-    if (hlsl_get_type(scope, def->name, false))
-        return false;
-
-    rb_put(&scope->types, def->name, &def->scope_entry);
-    return true;
-}
-
-static void declare_predefined_types(struct hlsl_scope *scope)
-{
-    struct hlsl_type *type;
-    unsigned int x, y, bt;
-    static const char * const names[] =
-    {
-        "float",
-        "half",
-        "double",
-        "int",
-        "uint",
-        "bool",
-    };
-    char name[10];
-
-    static const char *const sampler_names[] =
-    {
-        "sampler",
-        "sampler1D",
-        "sampler2D",
-        "sampler3D",
-        "samplerCUBE"
-    };
-
-    for (bt = 0; bt <= HLSL_TYPE_LAST_SCALAR; ++bt)
-    {
-        for (y = 1; y <= 4; ++y)
-        {
-            for (x = 1; x <= 4; ++x)
-            {
-                sprintf(name, "%s%ux%u", names[bt], y, x);
-                type = hlsl_new_type(vkd3d_strdup(name), HLSL_CLASS_MATRIX, bt, x, y);
-                add_type_to_scope(scope, type);
-
-                if (y == 1)
-                {
-                    sprintf(name, "%s%u", names[bt], x);
-                    type = hlsl_new_type(vkd3d_strdup(name), HLSL_CLASS_VECTOR, bt, x, y);
-                    add_type_to_scope(scope, type);
-                    hlsl_ctx.builtin_types.vector[bt][x - 1] = type;
-
-                    if (x == 1)
-                    {
-                        sprintf(name, "%s", names[bt]);
-                        type = hlsl_new_type(vkd3d_strdup(name), HLSL_CLASS_SCALAR, bt, x, y);
-                        add_type_to_scope(scope, type);
-                        hlsl_ctx.builtin_types.scalar[bt] = type;
-                    }
-                }
-            }
-        }
-    }
-
-    for (bt = 0; bt <= HLSL_SAMPLER_DIM_MAX; ++bt)
-    {
-        type = hlsl_new_type(vkd3d_strdup(sampler_names[bt]), HLSL_CLASS_OBJECT, HLSL_TYPE_SAMPLER, 1, 1);
-        type->sampler_dim = bt;
-        hlsl_ctx.builtin_types.sampler[bt] = type;
-    }
-
-    hlsl_ctx.builtin_types.Void = hlsl_new_type(vkd3d_strdup("void"), HLSL_CLASS_OBJECT, HLSL_TYPE_VOID, 1, 1);
-
-    /* DX8 effects predefined types */
-    type = hlsl_new_type(vkd3d_strdup("DWORD"), HLSL_CLASS_SCALAR, HLSL_TYPE_INT, 1, 1);
-    add_type_to_scope(scope, type);
-    type = hlsl_new_type(vkd3d_strdup("FLOAT"), HLSL_CLASS_SCALAR, HLSL_TYPE_FLOAT, 1, 1);
-    add_type_to_scope(scope, type);
-    type = hlsl_new_type(vkd3d_strdup("VECTOR"), HLSL_CLASS_VECTOR, HLSL_TYPE_FLOAT, 4, 1);
-    add_type_to_scope(scope, type);
-    type = hlsl_new_type(vkd3d_strdup("MATRIX"), HLSL_CLASS_MATRIX, HLSL_TYPE_FLOAT, 4, 4);
-    add_type_to_scope(scope, type);
-    type = hlsl_new_type(vkd3d_strdup("STRING"), HLSL_CLASS_OBJECT, HLSL_TYPE_STRING, 1, 1);
-    add_type_to_scope(scope, type);
-    type = hlsl_new_type(vkd3d_strdup("TEXTURE"), HLSL_CLASS_OBJECT, HLSL_TYPE_TEXTURE, 1, 1);
-    add_type_to_scope(scope, type);
-    type = hlsl_new_type(vkd3d_strdup("PIXELSHADER"), HLSL_CLASS_OBJECT, HLSL_TYPE_PIXELSHADER, 1, 1);
-    add_type_to_scope(scope, type);
-    type = hlsl_new_type(vkd3d_strdup("VERTEXSHADER"), HLSL_CLASS_OBJECT, HLSL_TYPE_VERTEXSHADER, 1, 1);
-    add_type_to_scope(scope, type);
-}
-
 static bool append_conditional_break(struct list *cond_list)
 {
     struct hlsl_ir_node *condition, *not;
@@ -867,7 +777,7 @@ static bool add_typedef(DWORD modifiers, struct hlsl_type *orig_type, struct lis
                 && (type->modifiers & HLSL_MODIFIER_ROW_MAJOR))
             hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, "more than one matrix majority keyword");
 
-        ret = add_type_to_scope(hlsl_ctx.cur_scope, type);
+        ret = hlsl_scope_add_type(hlsl_ctx.cur_scope, type);
         if (!ret)
             hlsl_report_message(v->loc, HLSL_LEVEL_ERROR,
                     "redefinition of custom type '%s'", v->name);
@@ -1910,7 +1820,7 @@ named_struct_spec:
                 YYABORT;
             }
 
-            ret = add_type_to_scope(hlsl_ctx.cur_scope, $$);
+            ret = hlsl_scope_add_type(hlsl_ctx.cur_scope, $$);
             if (!ret)
             {
                 hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, "redefinition of struct '%s'", $2);
@@ -3222,44 +3132,19 @@ static void compute_liveness(struct hlsl_ir_function_decl *entry_func)
     compute_liveness_recurse(entry_func->body, 0, 0);
 }
 
-int hlsl_parser_compile(enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint,
-        struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context)
+int hlsl_parser_compile(const char *entrypoint)
 {
     struct hlsl_ir_function_decl *entry_func;
-    struct hlsl_scope *scope, *next_scope;
-    struct hlsl_type *hlsl_type, *next_type;
-    struct hlsl_ir_var *var, *next_var;
-    int ret = VKD3D_ERROR;
-    unsigned int i;
-
-    hlsl_ctx.status = PARSE_SUCCESS;
-    hlsl_ctx.message_context = message_context;
-    hlsl_ctx.line_no = hlsl_ctx.column = 1;
-    hlsl_ctx.source_file = vkd3d_strdup("");
-    hlsl_ctx.source_files = vkd3d_malloc(sizeof(*hlsl_ctx.source_files));
-    if (hlsl_ctx.source_files)
-        hlsl_ctx.source_files[0] = hlsl_ctx.source_file;
-    hlsl_ctx.source_files_count = 1;
-    hlsl_ctx.cur_scope = NULL;
-    hlsl_ctx.matrix_majority = HLSL_COLUMN_MAJOR;
-    list_init(&hlsl_ctx.scopes);
-    list_init(&hlsl_ctx.types);
-    init_functions_tree(&hlsl_ctx.functions);
-    list_init(&hlsl_ctx.static_initializers);
-
-    hlsl_push_scope(&hlsl_ctx);
-    hlsl_ctx.globals = hlsl_ctx.cur_scope;
-    declare_predefined_types(hlsl_ctx.globals);
 
     hlsl_parse();
 
     if (hlsl_ctx.status == PARSE_ERR)
-        goto out;
+        return VKD3D_ERROR_INVALID_SHADER;
 
     if (!(entry_func = get_func_entry(entrypoint)))
     {
         hlsl_message("error: entry point %s is not defined\n", debugstr_a(entrypoint));
-        goto out;
+        return VKD3D_ERROR_INVALID_SHADER;
     }
 
     if (!hlsl_type_is_void(entry_func->return_type)
@@ -3282,29 +3167,7 @@ int hlsl_parser_compile(enum vkd3d_shader_type type, DWORD major, DWORD minor, c
 
     compute_liveness(entry_func);
 
-    if (hlsl_ctx.status != PARSE_ERR)
-        ret = VKD3D_ERROR_NOT_IMPLEMENTED;
-
-out:
-    for (i = 0; i < hlsl_ctx.source_files_count; ++i)
-        vkd3d_free((void *)hlsl_ctx.source_files[i]);
-    vkd3d_free(hlsl_ctx.source_files);
-
-    TRACE("Freeing functions IR.\n");
-    rb_destroy(&hlsl_ctx.functions, hlsl_free_function_rb, NULL);
-
-    TRACE("Freeing variables.\n");
-    LIST_FOR_EACH_ENTRY_SAFE(scope, next_scope, &hlsl_ctx.scopes, struct hlsl_scope, entry)
-    {
-        LIST_FOR_EACH_ENTRY_SAFE(var, next_var, &scope->vars, struct hlsl_ir_var, scope_entry)
-            hlsl_free_var(var);
-        rb_destroy(&scope->types, NULL, NULL);
-        vkd3d_free(scope);
-    }
-
-    TRACE("Freeing types.\n");
-    LIST_FOR_EACH_ENTRY_SAFE(hlsl_type, next_type, &hlsl_ctx.types, struct hlsl_type, entry)
-        hlsl_free_type(hlsl_type);
-
-    return ret;
+    if (hlsl_ctx.status == PARSE_ERR)
+        return VKD3D_ERROR_INVALID_SHADER;
+    return VKD3D_ERROR_NOT_IMPLEMENTED;
 }
-- 
2.30.0




More information about the wine-devel mailing list