<commit_before>//
// Copyright (c) 2008-2018 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include <fmt/format.h>
#include <Urho3D/IO/File.h>
#include <Urho3D/IO/FileSystem.h>
#include <Urho3D/IO/Log.h>
#include <cppast/cpp_namespace.hpp>
#include "GeneratorContext.h"
#include "GeneratePInvokePass.h"

namespace Urho3D
{

void GeneratePInvokePass::Start()
{
    printer_ << "using System;";
    printer_ << "using System.Threading;";
    printer_ << "using System.Collections.Concurrent;";
    printer_ << "using System.Runtime.InteropServices;";
    printer_ << "using CSharp;";
    printer_ << "";
}

bool GeneratePInvokePass::Visit(MetaEntity* entity, cppast::visitor_info info)
{
    const char* dllImport = "[DllImport(\"Urho3DCSharp\", CallingConvention = CallingConvention.Cdecl)]";

    if (entity->kind_ == cppast::cpp_entity_kind::namespace_t)
    {
        if (entity->children_.empty())
            return false;

        if (info.event == info.container_entity_enter)
        {
            printer_ << fmt::format("namespace {}", entity->name_);
            printer_.Indent();
        }
        else if (info.event == info.container_entity_exit)
        {
            printer_.Dedent();
            printer_ << "";
        }
        return true;
    }
    else if (entity->kind_ == cppast::cpp_entity_kind::class_t)
    {
        if (info.event == info.container_entity_enter)
        {
            if (entity->ast_ == nullptr /* stinks */ || IsStatic(*entity->ast_))
            {
                // A class will not have any methods. This is likely a dummy class for constant storage or something
                // similar.
                printer_ << fmt::format("public static partial class {}", entity->name_);
                printer_.Indent();
                return true;
            }

            bool hasBases = false;
            if (entity->ast_->kind() == cppast::cpp_entity_kind::class_t)
            {
                const auto& cls = entity->Ast<cppast::cpp_class>();

                for (const auto& base : cls.bases())
                {
                    if (const auto* baseClass = GetEntity(base.type()))
                    {
                        hasBases = true;
                        break;
                    }
                }
            }

            printer_ << fmt::format("public unsafe partial class {} : IDisposable", entity->name_);
            printer_.Indent();
            if (!hasBases)
            {
                printer_ << "internal IntPtr instance_;";
                printer_ << "protected volatile int disposed_;";
                printer_ << "";

                // Constructor that initializes form a instance
                printer_ << fmt::format("internal {}(IntPtr instance)", entity->name_);
                printer_.Indent();
                {
                    // Parent class may calls this constructor with null pointer when parent class constructor itself is
                    // creating instance.
                    printer_ << "if (instance != IntPtr.Zero)";
                    printer_.Indent();
                    {
                        printer_ << "instance_ = instance;";
                    }
                    printer_.Dedent();
                }
                printer_.Dedent();
                printer_ << "";
            }
            else
            {
                // Proxy constructor to one defined above
                printer_ << fmt::format("internal {}(IntPtr instance) : base(instance) {{ }}", entity->name_);
                printer_ << "";
            }

            printer_ << fmt::format("public {}void Dispose()", hasBases ? "" : "new ");
            printer_.Indent();
            {
                printer_ << "if (Interlocked.Increment(ref disposed_) == 1)";
                printer_.Indent();
                printer_ << fmt::format("InstanceCache.Remove<{}>(instance_);", entity->name_);
                if (entity->uniqueName_ == "Urho3D::Context")
                {
                    // When context is disposing we are still likely holding on to some objects. This causes a crash
                    // upon application exit, because references we are holding on will be disposed after Context no
                    // longer exists. If such references inherit from Object they will try to access Context and crash.
                    // In order to avoid this crash we explicitly dispose of all remaining native references just before
                    // destroying Context instance. It is critical that native objects are not interacted with after
                    // Context is destroyed. Creating and destroying multiple contexts will make things go boom as well.
                    // It is very likely that same thing will happen when trying to have multiple Context instances in
                    // the same process. This usage is very atypical, so do not do it.
                    printer_ << "InstanceCache.Dispose();";
                }
                printer_ << Sanitize(entity->uniqueName_) + "_destructor(instance_);";
                printer_.Dedent();
                printer_ << "instance_ = IntPtr.Zero;";
            }
            printer_.Dedent();
            printer_ << "";

            printer_ << fmt::format("~{}()", entity->name_);
            printer_.Indent();
            {
                printer_ << "Dispose();";
            }
            printer_.Dedent();
            printer_ << "";

            // Helpers for marshalling type between public and pinvoke APIs
            printer_ << fmt::format("internal {}static {} __FromPInvoke(IntPtr source)", hasBases ? "" : "new ", entity->name_);
            printer_.Indent();
            {
                printer_ << fmt::format("return InstanceCache.GetOrAdd<{className}>(source, ptr => new {className}(ptr));",
                    fmt::arg("className", entity->name_));
            }
            printer_.Dedent();
            printer_ << "";

            printer_ << fmt::format("internal static IntPtr __ToPInvoke({} source)", entity->name_);
            printer_.Indent();
            {
                printer_ << "return source.instance_;";<commit_msg>Fix bunch of warnings due to misplaced or missing `new` keyword.<commit_after>//
// Copyright (c) 2008-2018 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include <fmt/format.h>
#include <Urho3D/IO/File.h>
#include <Urho3D/IO/FileSystem.h>
#include <Urho3D/IO/Log.h>
#include <cppast/cpp_namespace.hpp>
#include "GeneratorContext.h"
#include "GeneratePInvokePass.h"

namespace Urho3D
{

void GeneratePInvokePass::Start()
{
    printer_ << "using System;";
    printer_ << "using System.Threading;";
    printer_ << "using System.Collections.Concurrent;";
    printer_ << "using System.Runtime.InteropServices;";
    printer_ << "using CSharp;";
    printer_ << "";
}

bool GeneratePInvokePass::Visit(MetaEntity* entity, cppast::visitor_info info)
{
    const char* dllImport = "[DllImport(\"Urho3DCSharp\", CallingConvention = CallingConvention.Cdecl)]";

    if (entity->kind_ == cppast::cpp_entity_kind::namespace_t)
    {
        if (entity->children_.empty())
            return false;

        if (info.event == info.container_entity_enter)
        {
            printer_ << fmt::format("namespace {}", entity->name_);
            printer_.Indent();
        }
        else if (info.event == info.container_entity_exit)
        {
            printer_.Dedent();
            printer_ << "";
        }
        return true;
    }
    else if (entity->kind_ == cppast::cpp_entity_kind::class_t)
    {
        if (info.event == info.container_entity_enter)
        {
            if (entity->ast_ == nullptr /* stinks */ || IsStatic(*entity->ast_))
            {
                // A class will not have any methods. This is likely a dummy class for constant storage or something
                // similar.
                printer_ << fmt::format("public static partial class {}", entity->name_);
                printer_.Indent();
                return true;
            }

            bool hasBases = false;
            if (entity->ast_->kind() == cppast::cpp_entity_kind::class_t)
            {
                const auto& cls = entity->Ast<cppast::cpp_class>();

                for (const auto& base : cls.bases())
                {
                    if (const auto* baseClass = GetEntity(base.type()))
                    {
                        hasBases = true;
                        break;
                    }
                }
            }
            auto newTag = hasBases ? "new " : " ";

            printer_ << fmt::format("public unsafe partial class {} : IDisposable", entity->name_);
            printer_.Indent();
            if (!hasBases)
            {
                printer_ << "internal IntPtr instance_;";
                printer_ << "protected volatile int disposed_;";
                printer_ << "";

                // Constructor that initializes form a instance
                printer_ << fmt::format("internal {}(IntPtr instance)", entity->name_);
                printer_.Indent();
                {
                    // Parent class may calls this constructor with null pointer when parent class constructor itself is
                    // creating instance.
                    printer_ << "if (instance != IntPtr.Zero)";
                    printer_.Indent();
                    {
                        printer_ << "instance_ = instance;";
                    }
                    printer_.Dedent();
                }
                printer_.Dedent();
                printer_ << "";
            }
            else
            {
                // Proxy constructor to one defined above
                printer_ << fmt::format("internal {}(IntPtr instance) : base(instance) {{ }}", entity->name_);
                printer_ << "";
            }

            printer_ << fmt::format("public {}void Dispose()", newTag);
            printer_.Indent();
            {
                printer_ << "if (Interlocked.Increment(ref disposed_) == 1)";
                printer_.Indent();
                printer_ << fmt::format("InstanceCache.Remove<{}>(instance_);", entity->name_);
                if (entity->uniqueName_ == "Urho3D::Context")
                {
                    // When context is disposing we are still likely holding on to some objects. This causes a crash
                    // upon application exit, because references we are holding on will be disposed after Context no
                    // longer exists. If such references inherit from Object they will try to access Context and crash.
                    // In order to avoid this crash we explicitly dispose of all remaining native references just before
                    // destroying Context instance. It is critical that native objects are not interacted with after
                    // Context is destroyed. Creating and destroying multiple contexts will make things go boom as well.
                    // It is very likely that same thing will happen when trying to have multiple Context instances in
                    // the same process. This usage is very atypical, so do not do it.
                    printer_ << "InstanceCache.Dispose();";
                }
                printer_ << Sanitize(entity->uniqueName_) + "_destructor(instance_);";
                printer_.Dedent();
                printer_ << "instance_ = IntPtr.Zero;";
            }
            printer_.Dedent();
            printer_ << "";

            printer_ << fmt::format("~{}()", entity->name_);
            printer_.Indent();
            {
                printer_ << "Dispose();";
            }
            printer_.Dedent();
            printer_ << "";

            // Helpers for marshalling type between public and pinvoke APIs
            printer_ << fmt::format("internal {}static {} __FromPInvoke(IntPtr source)", newTag, entity->name_);
            printer_.Indent();
            {
                printer_ << fmt::format("return InstanceCache.GetOrAdd<{className}>(source, ptr => new {className}(ptr));",
                    fmt::arg("className", entity->name_));
            }
            printer_.Dedent();
            printer_ << "";

            printer_ << fmt::format("internal static IntPtr __ToPInvoke({} source)", entity->name_);
            printer_.Indent();
            {
                printer_ << "return source.instance_;";
{'fraction_non_alphanumeric': 0.10878517501715855, 'fraction_numerical': 0.002196293754289636, 'mean_word_length': 1.83813790416829, 'pattern_counts': {'":': 0, '<': 155, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '968283', 'n_tokens_mistral': 3649, 'n_tokens_neox': 3548, 'n_words': 1157}