mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:22:45 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			568 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			568 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <AK/Array.h>
 | |
| #include <AK/DeprecatedString.h>
 | |
| #include <AK/JsonObject.h>
 | |
| #include <AK/NumericLimits.h>
 | |
| #include <AK/Optional.h>
 | |
| #include <AK/SourceGenerator.h>
 | |
| #include <AK/StringBuilder.h>
 | |
| #include <AK/StringView.h>
 | |
| #include <AK/Vector.h>
 | |
| #include <LibCore/ArgsParser.h>
 | |
| #include <LibCore/File.h>
 | |
| #include <LibMain/Main.h>
 | |
| 
 | |
| struct ArgumentDefinition {
 | |
|     Optional<DeprecatedString> name;
 | |
|     Optional<DeprecatedString> cpp_type;
 | |
|     DeprecatedString expression;
 | |
|     Optional<DeprecatedString> cast_to;
 | |
| };
 | |
| 
 | |
| struct FunctionDefinition {
 | |
|     DeprecatedString name;
 | |
|     DeprecatedString return_type;
 | |
|     Vector<ArgumentDefinition> arguments;
 | |
|     DeprecatedString implementation;
 | |
|     bool unimplemented;
 | |
|     DeprecatedString variant_gl_type;
 | |
| };
 | |
| 
 | |
| struct VariantType {
 | |
|     DeprecatedString encoded_type;
 | |
|     Optional<DeprecatedString> implementation;
 | |
|     bool unimplemented;
 | |
| };
 | |
| 
 | |
| struct Variants {
 | |
|     Vector<DeprecatedString> api_suffixes { "" };
 | |
|     Vector<u32> argument_counts { NumericLimits<u32>::max() };
 | |
|     Vector<DeprecatedString> argument_defaults { "" };
 | |
|     bool convert_range { false };
 | |
|     Vector<VariantType> types {
 | |
|         {
 | |
|             .encoded_type = "",
 | |
|             .implementation = Optional<DeprecatedString> {},
 | |
|             .unimplemented = false,
 | |
|         }
 | |
|     };
 | |
|     DeprecatedString pointer_argument { "" };
 | |
| };
 | |
| 
 | |
| struct EncodedTypeEntry {
 | |
|     StringView encoded_type;
 | |
|     StringView cpp_type;
 | |
|     StringView gl_type;
 | |
| };
 | |
| 
 | |
| // clang-format off
 | |
| constexpr static Array<EncodedTypeEntry, 9> type_definitions = {
 | |
|     EncodedTypeEntry { "b"sv,  "GLbyte"sv,   "GL_BYTE"sv },
 | |
|     EncodedTypeEntry { "d"sv,  "GLdouble"sv, "GL_DOUBLE"sv },
 | |
|     EncodedTypeEntry { "f"sv,  "GLfloat"sv,  "GL_FLOAT"sv },
 | |
|     EncodedTypeEntry { "i"sv,  "GLint"sv,    "GL_INT"sv },
 | |
|     EncodedTypeEntry { "s"sv,  "GLshort"sv,  "GL_SHORT"sv },
 | |
|     EncodedTypeEntry { "ub"sv, "GLubyte"sv,  "GL_UNSIGNED_BYTE"sv },
 | |
|     EncodedTypeEntry { "ui"sv, "GLuint"sv,   "GL_UNSIGNED_INT"sv },
 | |
|     EncodedTypeEntry { "us"sv, "GLushort"sv, "GL_UNSIGNED_SHORT"sv },
 | |
|     EncodedTypeEntry { "x"sv,  "GLfixed"sv,  "GL_INT"sv },
 | |
| };
 | |
| // clang-format on
 | |
| 
 | |
| struct EncodedType {
 | |
|     EncodedTypeEntry type_entry;
 | |
|     DeprecatedString cpp_type;
 | |
|     DeprecatedString function_name_suffix;
 | |
|     bool is_pointer;
 | |
|     bool is_const_pointer;
 | |
| };
 | |
| 
 | |
| Vector<DeprecatedString> get_name_list(Optional<JsonValue const&> name_definition)
 | |
| {
 | |
|     if (!name_definition.has_value() || name_definition->is_null())
 | |
|         return {};
 | |
| 
 | |
|     Vector<DeprecatedString, 1> names;
 | |
|     if (name_definition->is_string()) {
 | |
|         names.append(name_definition->as_string());
 | |
|     } else if (name_definition->is_array()) {
 | |
|         name_definition->as_array().for_each([&names](auto& value) {
 | |
|             VERIFY(value.is_string());
 | |
|             names.append(value.as_string());
 | |
|         });
 | |
|     } else {
 | |
|         VERIFY_NOT_REACHED();
 | |
|     }
 | |
|     return names;
 | |
| }
 | |
| 
 | |
| Optional<EncodedType> get_encoded_type(DeprecatedString encoded_type)
 | |
| {
 | |
|     bool is_const_pointer = !encoded_type.ends_with('!');
 | |
|     if (!is_const_pointer)
 | |
|         encoded_type = encoded_type.substring_view(0, encoded_type.length() - 1);
 | |
|     DeprecatedString function_name_suffix = encoded_type;
 | |
| 
 | |
|     bool is_pointer = encoded_type.ends_with('v');
 | |
|     if (is_pointer)
 | |
|         encoded_type = encoded_type.substring_view(0, encoded_type.length() - 1);
 | |
| 
 | |
|     VERIFY(is_const_pointer || is_pointer);
 | |
| 
 | |
|     Optional<EncodedTypeEntry> type_definition;
 | |
|     for (size_t i = 0; i < type_definitions.size(); ++i) {
 | |
|         if (type_definitions[i].encoded_type == encoded_type) {
 | |
|             type_definition = type_definitions[i];
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!type_definition.has_value())
 | |
|         return {};
 | |
| 
 | |
|     return EncodedType {
 | |
|         .type_entry = type_definition.value(),
 | |
|         .cpp_type = DeprecatedString::formatted(
 | |
|             "{}{}{}",
 | |
|             type_definition->cpp_type,
 | |
|             is_pointer && is_const_pointer ? " const" : "",
 | |
|             is_pointer ? "*" : ""),
 | |
|         .function_name_suffix = function_name_suffix,
 | |
|         .is_pointer = is_pointer,
 | |
|         .is_const_pointer = is_const_pointer,
 | |
|     };
 | |
| }
 | |
| 
 | |
| DeprecatedString wrap_expression_in_range_conversion(DeprecatedString source_type, DeprecatedString target_type, DeprecatedString expression)
 | |
| {
 | |
|     VERIFY(target_type == "GLfloat" || target_type == "GLdouble");
 | |
| 
 | |
|     // No range conversion required
 | |
|     if (source_type == target_type || source_type == "GLdouble")
 | |
|         return expression;
 | |
| 
 | |
|     if (source_type == "GLbyte")
 | |
|         return DeprecatedString::formatted("({} + 128.) / 127.5 - 1.", expression);
 | |
|     else if (source_type == "GLfloat")
 | |
|         return DeprecatedString::formatted("static_cast<GLdouble>({})", expression);
 | |
|     else if (source_type == "GLint")
 | |
|         return DeprecatedString::formatted("({} + 2147483648.) / 2147483647.5 - 1.", expression);
 | |
|     else if (source_type == "GLshort")
 | |
|         return DeprecatedString::formatted("({} + 32768.) / 32767.5 - 1.", expression);
 | |
|     else if (source_type == "GLubyte")
 | |
|         return DeprecatedString::formatted("{} / 255.", expression);
 | |
|     else if (source_type == "GLuint")
 | |
|         return DeprecatedString::formatted("{} / 4294967296.", expression);
 | |
|     else if (source_type == "GLushort")
 | |
|         return DeprecatedString::formatted("{} / 65536.", expression);
 | |
|     VERIFY_NOT_REACHED();
 | |
| }
 | |
| 
 | |
| Variants read_variants_settings(JsonObject const& variants_obj)
 | |
| {
 | |
|     Variants variants;
 | |
| 
 | |
|     if (variants_obj.has_array("argument_counts"sv)) {
 | |
|         variants.argument_counts.clear_with_capacity();
 | |
|         variants_obj.get_array("argument_counts"sv)->for_each([&](auto const& argument_count_value) {
 | |
|             variants.argument_counts.append(argument_count_value.to_u32());
 | |
|         });
 | |
|     }
 | |
|     if (variants_obj.has_array("argument_defaults"sv)) {
 | |
|         variants.argument_defaults.clear_with_capacity();
 | |
|         variants_obj.get_array("argument_defaults"sv)->for_each([&](auto const& argument_default_value) {
 | |
|             variants.argument_defaults.append(argument_default_value.as_string());
 | |
|         });
 | |
|     }
 | |
|     if (variants_obj.has_bool("convert_range"sv)) {
 | |
|         variants.convert_range = variants_obj.get_bool("convert_range"sv).value();
 | |
|     }
 | |
|     if (variants_obj.has_array("api_suffixes"sv)) {
 | |
|         variants.api_suffixes.clear_with_capacity();
 | |
|         variants_obj.get_array("api_suffixes"sv)->for_each([&](auto const& suffix_value) {
 | |
|             variants.api_suffixes.append(suffix_value.as_string());
 | |
|         });
 | |
|     }
 | |
|     if (variants_obj.has_string("pointer_argument"sv)) {
 | |
|         variants.pointer_argument = variants_obj.get_deprecated_string("pointer_argument"sv).value();
 | |
|     }
 | |
|     if (variants_obj.has_object("types"sv)) {
 | |
|         variants.types.clear_with_capacity();
 | |
|         variants_obj.get_object("types"sv)->for_each_member([&](auto const& key, auto const& type_value) {
 | |
|             auto const& type = type_value.as_object();
 | |
|             variants.types.append(VariantType {
 | |
|                 .encoded_type = key,
 | |
|                 .implementation = type.get_deprecated_string("implementation"sv),
 | |
|                 .unimplemented = type.get_bool("unimplemented"sv).value_or(false),
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     return variants;
 | |
| }
 | |
| 
 | |
| Vector<ArgumentDefinition> copy_arguments_for_variant(Vector<ArgumentDefinition> arguments, Variants variants,
 | |
|     u32 argument_count, EncodedType encoded_type)
 | |
| {
 | |
|     Vector<ArgumentDefinition> variant_arguments = arguments;
 | |
|     auto base_cpp_type = encoded_type.type_entry.cpp_type;
 | |
| 
 | |
|     size_t variadic_index = 0;
 | |
|     for (size_t i = 0; i < variant_arguments.size(); ++i) {
 | |
|         // Skip arguments with a fixed type
 | |
|         if (variant_arguments[i].cpp_type.has_value())
 | |
|             continue;
 | |
| 
 | |
|         variant_arguments[i].cpp_type = encoded_type.cpp_type;
 | |
|         auto cast_to = variant_arguments[i].cast_to;
 | |
| 
 | |
|         // Pointer argument
 | |
|         if (encoded_type.is_pointer) {
 | |
|             variant_arguments[i].name = (variadic_index == 0) ? variants.pointer_argument : Optional<DeprecatedString> {};
 | |
| 
 | |
|             if (variadic_index >= argument_count) {
 | |
|                 // If this variable argument is past the argument count, fall back to the defaults
 | |
|                 variant_arguments[i].expression = variants.argument_defaults[variadic_index];
 | |
|                 variant_arguments[i].cast_to = Optional<DeprecatedString> {};
 | |
| 
 | |
|             } else if (argument_count == 1 && variants.argument_counts.size() == 1) {
 | |
|                 // Otherwise, if the pointer is the only variadic argument, pass it through unchanged
 | |
|                 variant_arguments[i].cast_to = Optional<DeprecatedString> {};
 | |
| 
 | |
|             } else {
 | |
|                 // Otherwise, index into the pointer argument
 | |
|                 auto indexed_expression = DeprecatedString::formatted("{}[{}]", variants.pointer_argument, variadic_index);
 | |
|                 if (variants.convert_range && cast_to.has_value())
 | |
|                     indexed_expression = wrap_expression_in_range_conversion(base_cpp_type, cast_to.value(), indexed_expression);
 | |
|                 variant_arguments[i].expression = indexed_expression;
 | |
|             }
 | |
| 
 | |
|         } else {
 | |
|             // Regular argument
 | |
|             if (variadic_index >= argument_count) {
 | |
|                 // If the variable argument is past the argument count, fall back to the defaults
 | |
|                 variant_arguments[i].name = Optional<DeprecatedString> {};
 | |
|                 variant_arguments[i].expression = variants.argument_defaults[variadic_index];
 | |
|                 variant_arguments[i].cast_to = Optional<DeprecatedString> {};
 | |
| 
 | |
|             } else if (variants.convert_range && cast_to.has_value()) {
 | |
|                 // Otherwise, if we need to convert the input values, wrap the expression in a range conversion
 | |
|                 variant_arguments[i].expression = wrap_expression_in_range_conversion(
 | |
|                     base_cpp_type,
 | |
|                     cast_to.value(),
 | |
|                     variant_arguments[i].expression);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Determine if we can skip casting to the target type
 | |
|         if (cast_to == base_cpp_type || (variants.convert_range && cast_to == "GLdouble"))
 | |
|             variant_arguments[i].cast_to = Optional<DeprecatedString> {};
 | |
| 
 | |
|         variadic_index++;
 | |
|     }
 | |
| 
 | |
|     return variant_arguments;
 | |
| }
 | |
| 
 | |
| Vector<FunctionDefinition> create_function_definitions(DeprecatedString function_name, JsonObject const& function_definition)
 | |
| {
 | |
|     // A single function definition can expand to multiple generated functions by way of:
 | |
|     //   - differing API suffices (ARB, EXT, etc.);
 | |
|     //   - differing argument counts;
 | |
|     //   - differing argument types.
 | |
|     // These can all be combined.
 | |
| 
 | |
|     // Parse base argument definitions first; these may later be modified by variants
 | |
|     Vector<ArgumentDefinition> argument_definitions;
 | |
|     JsonArray const& arguments = function_definition.get_array("arguments"sv).value_or(JsonArray {});
 | |
|     arguments.for_each([&argument_definitions](auto const& argument_value) {
 | |
|         VERIFY(argument_value.is_object());
 | |
|         auto const& argument = argument_value.as_object();
 | |
| 
 | |
|         auto type = argument.get_deprecated_string("type"sv);
 | |
|         auto argument_names = get_name_list(argument.get("name"sv));
 | |
|         auto expression = argument.get_deprecated_string("expression"sv).value_or("@argument_name@");
 | |
|         auto cast_to = argument.get_deprecated_string("cast_to"sv);
 | |
| 
 | |
|         // Add an empty dummy name when all we have is an expression
 | |
|         if (argument_names.is_empty() && !expression.is_empty())
 | |
|             argument_names.append("");
 | |
| 
 | |
|         for (auto const& argument_name : argument_names) {
 | |
|             argument_definitions.append({ .name = argument_name.is_empty() ? Optional<DeprecatedString> {} : argument_name,
 | |
|                 .cpp_type = type,
 | |
|                 .expression = expression,
 | |
|                 .cast_to = cast_to });
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     // Create functions for each name and/or variant
 | |
|     Vector<FunctionDefinition> functions;
 | |
| 
 | |
|     auto return_type = function_definition.get_deprecated_string("return_type"sv).value_or("void");
 | |
|     auto function_implementation = function_definition.get_deprecated_string("implementation"sv).value_or(function_name.to_snakecase());
 | |
|     auto function_unimplemented = function_definition.get_bool("unimplemented"sv).value_or(false);
 | |
| 
 | |
|     if (!function_definition.has("variants"sv)) {
 | |
|         functions.append({
 | |
|             .name = function_name,
 | |
|             .return_type = return_type,
 | |
|             .arguments = argument_definitions,
 | |
|             .implementation = function_implementation,
 | |
|             .unimplemented = function_unimplemented,
 | |
|             .variant_gl_type = "",
 | |
|         });
 | |
|         return functions;
 | |
|     }
 | |
| 
 | |
|     // Read variants settings for this function
 | |
|     auto variants_obj = function_definition.get_object("variants"sv).value();
 | |
|     auto variants = read_variants_settings(variants_obj);
 | |
| 
 | |
|     for (auto argument_count : variants.argument_counts) {
 | |
|         for (auto const& variant_type : variants.types) {
 | |
|             auto encoded_type = get_encoded_type(variant_type.encoded_type);
 | |
|             auto variant_arguments = encoded_type.has_value()
 | |
|                 ? copy_arguments_for_variant(argument_definitions, variants, argument_count, encoded_type.value())
 | |
|                 : argument_definitions;
 | |
|             auto variant_type_implementation = variant_type.implementation.has_value()
 | |
|                 ? variant_type.implementation.value()
 | |
|                 : function_implementation;
 | |
| 
 | |
|             for (auto const& api_suffix : variants.api_suffixes) {
 | |
|                 functions.append({
 | |
|                     .name = DeprecatedString::formatted(
 | |
|                         "{}{}{}{}",
 | |
|                         function_name,
 | |
|                         variants.argument_counts.size() > 1 ? DeprecatedString::formatted("{}", argument_count) : "",
 | |
|                         encoded_type.has_value() && variants.types.size() > 1 ? encoded_type->function_name_suffix : "",
 | |
|                         api_suffix),
 | |
|                     .return_type = return_type,
 | |
|                     .arguments = variant_arguments,
 | |
|                     .implementation = variant_type_implementation,
 | |
|                     .unimplemented = variant_type.unimplemented || function_unimplemented,
 | |
|                     .variant_gl_type = encoded_type.has_value() ? encoded_type->type_entry.gl_type : ""sv,
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return functions;
 | |
| }
 | |
| 
 | |
| ErrorOr<void> generate_header_file(JsonObject& api_data, Core::File& file)
 | |
| {
 | |
|     StringBuilder builder;
 | |
|     SourceGenerator generator { builder };
 | |
| 
 | |
|     generator.appendln("#pragma once");
 | |
|     generator.append("\n");
 | |
|     generator.appendln("#include <LibGL/GL/glplatform.h>");
 | |
|     generator.append("\n");
 | |
|     generator.appendln("#ifdef __cplusplus");
 | |
|     generator.appendln("extern \"C\" {");
 | |
|     generator.appendln("#endif");
 | |
|     generator.append("\n");
 | |
| 
 | |
|     api_data.for_each_member([&](auto& function_name, auto& value) {
 | |
|         VERIFY(value.is_object());
 | |
|         auto const& function = value.as_object();
 | |
|         auto function_definitions = create_function_definitions(function_name, function);
 | |
| 
 | |
|         for (auto const& function_definition : function_definitions) {
 | |
|             auto function_generator = generator.fork();
 | |
| 
 | |
|             function_generator.set("name", function_definition.name);
 | |
|             function_generator.set("return_type", function_definition.return_type);
 | |
| 
 | |
|             function_generator.append("GLAPI @return_type@ gl@name@(");
 | |
| 
 | |
|             bool first = true;
 | |
|             for (auto const& argument_definition : function_definition.arguments) {
 | |
|                 if (!argument_definition.name.has_value() || !argument_definition.cpp_type.has_value())
 | |
|                     continue;
 | |
| 
 | |
|                 auto argument_generator = function_generator.fork();
 | |
|                 argument_generator.set("argument_type", argument_definition.cpp_type.value());
 | |
|                 argument_generator.set("argument_name", argument_definition.name.value());
 | |
| 
 | |
|                 if (!first)
 | |
|                     argument_generator.append(", ");
 | |
|                 first = false;
 | |
|                 argument_generator.append("@argument_type@ @argument_name@");
 | |
|             }
 | |
| 
 | |
|             function_generator.appendln(");");
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     generator.appendln("#ifdef __cplusplus");
 | |
|     generator.appendln("}");
 | |
|     generator.appendln("#endif");
 | |
| 
 | |
|     TRY(file.write_until_depleted(generator.as_string_view().bytes()));
 | |
|     return {};
 | |
| }
 | |
| 
 | |
| ErrorOr<void> generate_implementation_file(JsonObject& api_data, Core::File& file)
 | |
| {
 | |
|     StringBuilder builder;
 | |
|     SourceGenerator generator { builder };
 | |
| 
 | |
|     generator.appendln("#include <LibGL/GL/glapi.h>");
 | |
|     generator.appendln("#include <LibGL/GLContext.h>");
 | |
|     generator.append("\n");
 | |
|     generator.appendln("extern GL::GLContext* g_gl_context;");
 | |
|     generator.append("\n");
 | |
| 
 | |
|     api_data.for_each_member([&](auto& function_name, auto& value) {
 | |
|         VERIFY(value.is_object());
 | |
|         JsonObject const& function = value.as_object();
 | |
|         auto function_definitions = create_function_definitions(function_name, function);
 | |
| 
 | |
|         for (auto const& function_definition : function_definitions) {
 | |
|             auto function_generator = generator.fork();
 | |
|             auto return_type = function_definition.return_type;
 | |
| 
 | |
|             function_generator.set("name"sv, function_definition.name);
 | |
|             function_generator.set("return_type"sv, return_type);
 | |
|             function_generator.set("implementation"sv, function_definition.implementation);
 | |
|             function_generator.set("variant_gl_type"sv, function_definition.variant_gl_type);
 | |
| 
 | |
|             function_generator.append("@return_type@ gl@name@(");
 | |
| 
 | |
|             bool first = true;
 | |
|             for (auto const& argument_definition : function_definition.arguments) {
 | |
|                 if (!argument_definition.name.has_value() || !argument_definition.cpp_type.has_value())
 | |
|                     continue;
 | |
| 
 | |
|                 auto argument_generator = function_generator.fork();
 | |
|                 argument_generator.set("argument_type", argument_definition.cpp_type.value());
 | |
|                 argument_generator.set("argument_name", argument_definition.name.value());
 | |
| 
 | |
|                 if (!first)
 | |
|                     argument_generator.append(", ");
 | |
|                 first = false;
 | |
|                 argument_generator.append("@argument_type@ @argument_name@");
 | |
|             }
 | |
|             function_generator.appendln(")");
 | |
|             function_generator.appendln("{");
 | |
| 
 | |
|             if (function_definition.unimplemented) {
 | |
|                 function_generator.append("    dbgln(\"gl@name@(");
 | |
| 
 | |
|                 first = true;
 | |
|                 for (auto const& argument_definition : function_definition.arguments) {
 | |
|                     if (!argument_definition.name.has_value())
 | |
|                         continue;
 | |
|                     if (!first)
 | |
|                         function_generator.append(", ");
 | |
|                     first = false;
 | |
|                     if (argument_definition.cpp_type.value().ends_with('*'))
 | |
|                         function_generator.append("{:p}");
 | |
|                     else if (argument_definition.cpp_type.value() == "GLenum")
 | |
|                         function_generator.append("{:#x}");
 | |
|                     else
 | |
|                         function_generator.append("{}");
 | |
|                 }
 | |
| 
 | |
|                 function_generator.append("): unimplemented\"");
 | |
| 
 | |
|                 for (auto const& argument_definition : function_definition.arguments) {
 | |
|                     if (!argument_definition.name.has_value())
 | |
|                         continue;
 | |
| 
 | |
|                     function_generator.append(", ");
 | |
|                     function_generator.append(argument_definition.name.value());
 | |
|                 }
 | |
| 
 | |
|                 function_generator.appendln(");");
 | |
|                 function_generator.appendln("    TODO();");
 | |
|             } else {
 | |
|                 function_generator.appendln("    if (!g_gl_context)");
 | |
|                 if (return_type.ends_with('*'))
 | |
|                     function_generator.appendln("        return nullptr;");
 | |
|                 else if (return_type == "GLboolean"sv)
 | |
|                     function_generator.appendln("        return GL_FALSE;");
 | |
|                 else if (return_type == "GLenum"sv)
 | |
|                     function_generator.appendln("        return GL_INVALID_OPERATION;");
 | |
|                 else if (return_type == "GLuint"sv)
 | |
|                     function_generator.appendln("        return 0;");
 | |
|                 else if (return_type == "void"sv)
 | |
|                     function_generator.appendln("        return;");
 | |
|                 else
 | |
|                     VERIFY_NOT_REACHED();
 | |
|                 function_generator.append("    ");
 | |
|                 if (return_type != "void"sv)
 | |
|                     function_generator.append("return ");
 | |
|                 function_generator.append("g_gl_context->gl_@implementation@(");
 | |
| 
 | |
|                 first = true;
 | |
|                 for (auto const& argument_definition : function_definition.arguments) {
 | |
|                     auto argument_generator = function_generator.fork();
 | |
| 
 | |
|                     auto cast_to = argument_definition.cast_to;
 | |
|                     argument_generator.set("argument_name", argument_definition.name.value_or(""));
 | |
|                     argument_generator.set("cast_to", cast_to.value_or(""));
 | |
| 
 | |
|                     if (!first)
 | |
|                         argument_generator.append(", ");
 | |
|                     first = false;
 | |
| 
 | |
|                     if (cast_to.has_value())
 | |
|                         argument_generator.append("static_cast<@cast_to@>(");
 | |
|                     argument_generator.append(argument_definition.expression);
 | |
|                     if (cast_to.has_value())
 | |
|                         argument_generator.append(")");
 | |
|                 }
 | |
| 
 | |
|                 function_generator.appendln(");");
 | |
|             }
 | |
| 
 | |
|             function_generator.appendln("}");
 | |
|             function_generator.append("\n");
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     TRY(file.write_until_depleted(generator.as_string_view().bytes()));
 | |
|     return {};
 | |
| }
 | |
| 
 | |
| ErrorOr<JsonValue> read_entire_file_as_json(StringView filename)
 | |
| {
 | |
|     auto file = TRY(Core::File::open(filename, Core::File::OpenMode::Read));
 | |
|     auto json_size = TRY(file->size());
 | |
|     auto json_data = TRY(ByteBuffer::create_uninitialized(json_size));
 | |
|     TRY(file->read_until_filled(json_data.bytes()));
 | |
|     return JsonValue::from_string(json_data);
 | |
| }
 | |
| 
 | |
| ErrorOr<int> serenity_main(Main::Arguments arguments)
 | |
| {
 | |
|     StringView generated_header_path;
 | |
|     StringView generated_implementation_path;
 | |
|     StringView api_json_path;
 | |
| 
 | |
|     Core::ArgsParser args_parser;
 | |
|     args_parser.add_option(generated_header_path, "Path to the OpenGL API header file to generate", "generated-header-path", 'h', "generated-header-path");
 | |
|     args_parser.add_option(generated_implementation_path, "Path to the OpenGL API implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path");
 | |
|     args_parser.add_option(api_json_path, "Path to the JSON file to read from", "json-path", 'j', "json-path");
 | |
|     args_parser.parse(arguments);
 | |
| 
 | |
|     auto json = TRY(read_entire_file_as_json(api_json_path));
 | |
|     VERIFY(json.is_object());
 | |
|     auto api_data = json.as_object();
 | |
| 
 | |
|     auto generated_header_file = TRY(Core::File::open(generated_header_path, Core::File::OpenMode::Write));
 | |
|     auto generated_implementation_file = TRY(Core::File::open(generated_implementation_path, Core::File::OpenMode::Write));
 | |
| 
 | |
|     TRY(generate_header_file(api_data, *generated_header_file));
 | |
|     TRY(generate_implementation_file(api_data, *generated_implementation_file));
 | |
| 
 | |
|     return 0;
 | |
| }
 | 
