Pages

Saturday, June 3, 2023

Vim vs. Neovim: it's yet immature

I had a chance to try Neovim, which is quite a hot potato among Vim users. As far as I know, Neovim is started as a fork of Vim to include some features rejected by Vim maintainers. What I know about Neovim is only two: they adopted Lua as their script language along with VimScript, and LSP client is included(after v0.4).

I was really not satisfied with LSP support from Vim, so I was curious about how Neovim native LSP works. Conclusion? Well...... Before jumping into LSP, it looks like their big file management is still living the era of Vim 7. The memory consumption of Vim 9 and Neovim 0.9 are quite similar, but while in Vim you could easily navigate from here to there without any delay from Vim 8, Neovim showed quite formiddable delay when I tried to jump from the start to end at once. Also, the initial loading was a bit slow....... Though others are unanimously saying that "it's GREAT!", for me it lacks some basics as a text editor. Also, Windows installer was a bit premature compared with vim-win32-installer.

Though LSP is important for me too, but my job frequently requires processing of multi-GB size text files, I think I'll reside on Vim for some time being.

P.S.1: My Main Development Environment
For your reference, I use Visual Studio Code as my main development environment. Vim is mainly used to edit some texts.

P.S.2: About Vim's LSP Plugins
Well, for me, CoC(https://github.com/neoclide/coc.nvim), the final boss(?), is isolating itself outside Vim ecosystem(if I have to run Node.js inside Vim, then I'd use Visual Studio Code instead), vim-lsp(https://github.com/prabirshrestha/vim-lsp) lacks support on how to show the diagnosis results, and ALE(https://github.com/dense-analysis/ale) doesn't show function signatures and code formatting is not supported....... Everything loses at least one core value from their stuff.

And today, I found this: https://github.com/yegappan/lsp

You've got to make your hands a bit dirty to configure(even you've got to register your LSP servers manually), but anyway it's editing some text files(:P). And with propre configuration it provides useful information as you see in the above screenshot. And one more thing: this is developed with Vim9Script - in other words, it doesn't work in Neovim.

P.S.3: Vim vs. Neovim - a Bridge You Can't Turn Back
While Neovim concentrates on Lua Vim made Vim9script, and Neovim announced they won't support Vim9script. I think this will be the marker of separation between these two projects. Personally it reminds me of the end of "full compatiblity" between MySQL 8 and MariaDB 10 due to differences in JSON support. The difference? In MySQL / MariaDB case the fork, MariaDB, shows better performance than MySQL, while in Vim / Neovim case the original, Vim, is better(at least to me).

Vim vs. Neovim: 아직은 좀 설익은 과일

최근 몇년간 Vim 사용자들에게 최고의 화두였던 Neovim을 잠깐 만져봤습니다. 제가 듣기로 Neovim은 Vim에서 이것좀 구현해주세요 했다가 "야 Vim은 지금 이 상태로 완전하거든?" 이란 말과 함께 퇴짜맞은 기능들을 넣은 fork로 출발한 프로젝트로 알고 있습니다. 주요한 특징이라면 스크립트 언어로 Lua를 채용했다거나, LSP를 내장했다거나(v0.4 이후) 정도로군요.

그러잖아도 Vim의 LSP 지원이 다들 영 마음에 안 들어서 좀 고민하던 차에, 내장 LSP가 어떻게 생겨먹었나 궁금해서 Neovim을 써봤습니다. 결론은 뭐....... LSP는 고사하고 대용량 파일 처리가 Vim 7 시절 그대로인 것 같더군요. 메모리 사용량은 Neovim 0.9나 Vim 9이나 비슷한데, Vim 8은 큰 파일도 날라댕기면서(......) 왔다갔다 하는게 가능한 반면 Neovim은 파일 처음에서 끝까지 가려면 한참을 기다려야 하더군요. 초기 로딩도 슬쩍 느리고...... 뭐랄까요. 써본 사람들은 다들 좋다 좋다 하는데 제게는 기본기가 좀 어설프다는 느낌이었습니다. Windows용 설치파일도 Vim Win32 Installer에 비하면 여러가지로 부족한 점이 많아 보였고요.

LSP도 LSP지만, 전 아무래도 대용량 텍스트(수 GB 이상)를 처리해야 되는 경우가 꽤 되어서, 당분간은 Vim에 계속 남아있게 될 것 같습니다.

P.S.1: 나의 메인 개발환경
아, 참고로 전 제 메인 개발환경으로 Visual Studio Code를 씁니다. Vim은 텍스트 편집이 주 용도에요.

P.S.2: Vim의 LSP 플러그인 이야기
일단 대충 끝판왕(......)인 CoC(https://github.com/neoclide/coc.nvim)는 Vim의 ecosystem 바깥에서 따로 놀고 있고(Vim 안에서 Node.js 돌릴거면 그냥 Visual Studio Code 쓰고 말죠), vim-lsp(https://github.com/prabirshrestha/vim-lsp)는 화면 표시 기능이 빈약하고, ALE(https://github.com/dense-analysis/ale)는 function signature가 표시가 안 될 뿐더러 code formatting 기능이 없고....... 뭐 다들 뭔가 한가지씩 필요한게 빠진 느낌이었습니다.

그러다가 오늘 괜찮은걸 발견했습니다. 이겁니다: https://github.com/yegappan/lsp


설정이 좀 번거롭긴 한데(LSP 서버도 수동으로 등록해야 됩니다), 그래봐야 텍스트 파일 편집이 전부이기도 하고, 잘 만져주면 위쪽 화면처럼 표시도 해줘서 보기가 엄청 편합니다. 그리고 결정적으로....... Vim9script로 만들었더군요. 옙. Neovim에서는 못 씁니다(......).

P.S.3: Vim vs. Neovim - 돌아올 수 없는 다리
Neovim이 Lua를 미는 동안 Vim은 Vim9script를 만들었고, Neovim은 Vim9script를 지원하지 않겠다고 선언했지요. 저는 이게 아마 둘의 갈림길이 될 것 같습니다. 개인적으로는 JSON 지원 방식의 차이로 인해 MySQL 8과 MariaDB 10 사이의 호환성이 갈라진게 떠오르는군요. 차이라면 MySQL / MariaDB는 fork본인 MariaDB가 MySQL보다 여러모로 더 좋은 성능을 보여주는 반면 Vim / Neovim은 원본인 (최소한 제 환경에서는) Vim이 Neovim보다 더 좋아보이는 것 같군요.

Tuesday, January 31, 2023

std::move = when std::optional should be launched

Recently I had a chance to take a look at Rust. When returning from a function Rust uses std::option to return either class A in success or class B in exception.

And I found out something similar in C++, namely std::optional. Most of C++ users argued that "why use  std::optional when we can fully make use of null pointers?" According to C++ Committee, it was due to minimize human errors. Then we can ask one thing: what kind of errors, then?

Let's take a look at the code below:

struct Insider {
    /* whatever great data structure */
};

struct anti_memory_leak {
    Insider *insider=nullptr;
    ~anti_memory_leak() {
        if(insider) delete insider;
    }
};

There's nothing special in this code. Since the class Insider can be used optionally, it can be allocated to heap. When anti_memory_leak is removed from memory, Insider object will be also removed in destructor so we have means for memory leak. We proved that we can do it without std::optional.

...... Did we?

Then let's investigate the code below:

void doSomethingGreat()
{
    anti_memory_leak object1;
    object1.insider=new Insider();

    std::vector<anti_memory_leak> vector1;
    vector1.push_back(std::move(object1));
    vector1.back().insider->value1=20; // CRASH!
}
 

This function crashed in the last line. Why? The reason is in the one line above. When you call  vector1.push_back(), even though you use std::move() object1 is destructed and recreated. And when destructing, the destructor of anti_memory_leak is called, and it surely remove insider from the heap. In other words, vector1.back().insider becomes a dangling pointer. It's kind of unfortunate, the result is the same if you use emplace_back() instead of push_back(). Anyway the application crash.

Now is the time std::optional should be used. If you declare an object with std::optional, the memory is initialized without allocating that optional object, and it is initialized when the optional object is explicitly created. Of course there's a small overhead, but say, it's also same for other similar(?) classes like std::shared_ptr. We have raw pointers, but to manage our precious heap more safely, we can automate some of the management so that we can solve potential incidents(including both memory leak and dangling pointer) more easily.

If we refactor the code above using std::optional, it will be like this:

struct Insider {
    int value1;
    /* whatever great data structure */
};

struct anti_memory_leak {
    std::optional<Insider> insider;
    ~anti_memory_leak() {
        if(insider) delete insider;
    }
};

void doSomethingGreat()
{
    anti_memory_leak object1;
    object1.insider=Insider();

    std::vector<anti_memory_leak> vector1;
    vector1.push_back(std::move(object1));
    vector1.back().insider.value1=20; // OK
}

If the flow of the code is simple it won't be a big problem. However, if the flow becomes anyway compilcated(e.g. multithreading), there should be chances to free memory or miss the chance when we have to, regardless of my intention. Let's think of std::optional as some kind of insurance policy; though we all agree that insurance fee is somewhat "waste of money"(lol), but we spend money to prepare for the worst? I think it's the same for std::optional.

std::move = std::optional이 출동할 때

최근 Rust를 살펴볼 기회가 있었습니다. Rust는 function이 결과를 반환할때 std::option이라는걸 사용해서 정상이면 A 클래스를, 비정상이면 B 클래스를 반환하는 형태를 취하고 있더군요.

그래서...... 혹시나 해서 찾아봤더니, C++에도 std::optional이 있는 것을 발견했습니다. 다만 대부분의 사용자들은 std::optional을 왜 쓰느냐는 입장이었습니다. 우리에겐 (밉고도 고운) nullptr이 있는데 뭐하러 저런걸 쓰느냐...... 하는 거였습니다. C++ Committee의 입장은 실수를 줄이기 위해서라고 하던데, 그렇다면 대체 무슨 실수가 나올 수 있을까요?

우선, 다음의 코드를 살펴봅시다.

struct Insider {
    /* whatever great data structure */
};

struct anti_memory_leak {
    Insider *insider=nullptr;
    ~anti_memory_leak() {
        if(insider) delete insider;
    }
};

 확실히, 이 코드는 별 특이사항이 없어 보입니다. Insider는 쓰일 때도 있고 안 쓰일때도 있어서 필요한 경우에만 heap에서 생성하도록 해 두었습니다. anti_memory_leak이 삭제될 경우 Insider 객체가 존재하면 같이 지우도록 해서 메모리 누수 방지도 해 두었습니다. 우리는 std::optional이 없어도 아무런 문제가 없음을 증명했습니다.

...... 과연 그럴까요?

다음의 코드를 봅시다.

void doSomethingGreat()
{
    anti_memory_leak object1;
    object1.insider=new Insider();

    std::vector<anti_memory_leak> vector1;
    vector1.push_back(std::move(object1));
    vector1.back().insider->value1=20; // CRASH!
}
 

이 코드는 맨 마지막줄에서 프로그램이 터지는 결과를 가져옵니다. 왜 그럴까요? 이유는 바로 윗줄에 있습니다. vector1.push_back()을 호출할 경우, std::move()를 쓴다고 하더라도 object1은 파괴 후 재생성됩니다. 이 때 anti_memory_leak의 파괴자가 호출되고, 파괴자는 insider를 heap에서 제거합니다. 뒤집어서 말하면, vector1.back().insider는 dangling pointer가 되어버리는 셈이죠. 아울러 유감스럽지만, push_back()이 아니라 emplace_back()을 사용하더라도 동작은 마찬가지입니다. 어쨌든 터지는 것은 매한가지 되겠습니다.

바로 이 때가 std::optional이 출동할 차례입니다. std::optional을 사용하여 객체를 선언하면 일단 메모리는 사용되지 않은 채로 초기화되고, 추가 메모리는 실제로 해당 객체를 선언할 때 사용을 시작합니다. 물론 객체 생성에 따른 overhead가 좀 더 있긴 합니다만, 사실 그건 유사한(?) 역할을 수행하는 std::shared_ptr 같은 클래스도 마찬가지지요. raw pointer가 있지만 해당 heap 영역을 좀 더 안전하게 사용하기 위해서 운영의 일부를 자동화하여 개발자가 자칫 놓칠 수 있는 메모리 관리상의 문제점들(메모리 누수와 dangling pointer 모두)을 쉽게 해결하고자 하는 것입니다.

위의 코드를 std::optional을 사용하여 다시 작성한다면 이렇게 작성할 수 있겠습니다.

struct Insider {
    int value1;
    /* whatever great data structure */
};

struct anti_memory_leak {
    std::optional<Insider> insider;
    ~anti_memory_leak() {
        if(insider) delete insider;
    }
};

void doSomethingGreat()
{
    anti_memory_leak object1;
    object1.insider=Insider();

    std::vector<anti_memory_leak> vector1;
    vector1.push_back(std::move(object1));
    vector1.back().insider.value1=20; // OK
}

사실 객체를 다루는 코드의 흐름이 단순하다면 크게 문제가 없겠지만, 멀티스레딩이라던가, 아니면 코드가 어떤 식으로든 복잡해지게 되면 분명히 나도 모르게 메모리를 해제하게 되거나, 내지는 해제해야 할 때 못하는 경우가 생겨나게 됩니다. std::optional은 이런 경우에 대비한 일종의 보험이라 하겠습니다. 보험료를 내는 사람들은 (뭐 좀 아깝긴 하지만) 그래도 만일의 경우에 대비해서 보험료를 내는 거잖아요? std::optional도 마찬가지라고 봅니다.

Tuesday, August 9, 2022

Handshake One: SYN, DoS 공격지점, 또는 접속하는 IP들의 목록을 알아보자

코로나19가 한창 여름 내내 감염 신기록을 갈아치우던 뜨거운 여름이었습니다. 저는 한 작은 온라인 커뮤니티의 관리자들이 DoS(Denial of Service, 서비스 거부 공격)에 대항하는걸 지켜보고 있었습니다. 그들은 열정적이었지만, 작업은 그다지 효율적이지 못했습니다. 그들이 쓸 수 있는 도구라고는 웹서버 로그와 그들의 오래된 친구, iptables 뿐이었지요.

사실 보안에 투자할만한 여력이 없는 환경에서는, 이 돌대가리같은(......), 하지만 꽤나 효율적인 공격에 대항해 의존할만한 도구라고는 iptables나 nftables같은 것들 밖에는 없습니다. 하지만 일단 공격을 받고 나면, 사람들은 머리가 새하얘지죠. 로그는 너무 길고 복잡하고, 공격자 IP를 사용자 IP로부터 '발라내는' 작업은 꽤나 어렵습니다.

해서, 이런 분들을 도와드리고자 하는 순수한 목적에서 Handshake One이라고 이름붙인 작은 프로그램을 하나 만들었습니다. 이 프로그램을 이용하면 DoS 공격의 원천 IP를 빠르게 확인하거나, 아니면 최소한 현재 서비스에 어떤 IP들이 접속하고 있는지를 파악할 수 있습니다. 이 프로그램은 최근 60초동안 클라이언트 IP에서 발생하는 SYN 패킷을 수집하여 아래와 같은 보고서를 만들어냅니다:

Handshake One TCP SYN counter report
(C)Copyright 2022 Robert Teminian.
This application is provided free of charge, and provided AS IS: though the developer hopes that this would help the user in any way, the software does NOT guarantee anything at all.

Stop by the developer's blog and leave a comment! Visit http://codenested.blogspot.com

====================
At 1660000053
IP Hits
192.168.1.26 14
Total 14

====================
At 1660000054
IP Hits
192.168.1.26 11
Total 11

====================
At 1660000055
IP Hits
192.168.1.26 1
Total 1

현재 세 가지 운영체제에 대한 실행파일이 제공되고 있습니다만, 만일 다른 운영체제를 위한 실행파일이 필요하신 경우 제게 말씀해주세요. 아래 링크를 클릭하면 파일을 다운로드받을 수 있습니다.

위 파일은 모두 압축파일입니다. 원하시는 곳에 압축을 풀어서 사용해주시면 됩니다.

사용법은 아래와 같습니다
  • 설치: Windows
    • 원하는 곳에 ZIP 파일의 압축을 풉니다
    • Windows 환경에서, Handshake One은 패킷 캡쳐를 위해 npcap(https://npcap.com/) 을 사용하고 있습니다. 라이브러리를 직접 설치하시거나, Wireshark(https://www.wireshark.org/)를 설치하면 분석프로그램과 npcap을 같이 설치합니다
    • npcap을 설치한 뒤, C:\Windows\Systems32\Npcap에서 Packet.dll과 wpcap.dll을 프로그램 디렉토리에 복사합니다
  • 설치: Linux
    • 원하는 곳에 TGZ 파일의 압축을 풉니다
    • Linux 환경에서, Handshake One은 libpcap을 이용합니다. 일반적으로 libpcap은 tcpdump를 설치하면서 함께 설치되는 경우가 대부분입니다만, 만일 설치되지 않았다면 해당 리눅스 배포본의 패키지 관리자(apt, yum, ......)의 설명을 참고하여 설치해 주세요
  • 환경 설정
    • Handshake One의 환경을 설정할 수 있는 유일한 방법은 HandshakeOne.json 파일을 편집하는 것입니다. 현재 지원되는 설정은 아래와 같습니다
      • resultpath: 보고서 파일을 저장할 디렉토리를 설정합니다. Handshake One은 해당 디렉토리에 "HandshakeOneReport.txt" 파일을 생성하고, 매 30초마다 갱신(덮어쓰기)을 수행합니다
        • 리눅스를 사용하시는 경우, RAM disk 파티션(예: /tmp)으로 설정하여 시스템의 I/O 부하를 줄일 것을 권장합니다
      • sniffer: 패킷을 수집할 디바이스 이름입니다. Linux에서는 일반적으로 ip link나 ifconfig 같은 명령어에 나오는 이름을 사용하면 됩니다만, Windows에서는 일반적으로 보이는 이름이 아닌 내부 디바이스 이름(예:  \Device\NPF_{12345678-9ABC-DEF0-1234-567890ABCDEF})을 사용하여야 해서 설정이 조금 어려울 수 있습니다. 만일 디바이스 이름과 그에 해당하는 설명(예: Realtek PCIe GbE Family Controller)을 함께 보시려면, Handshake One을 "show" 파라미터와 함께 실행해 주세요(예: HandshakeOne show)
      • reportsizelimit: 보고서 내용을 갱신할 때, 보고서 파일의 크기를 지정된 크기로 제한합니다(바이트 단위). 만일 지정된 크기를 넘어서게 될 경우, 현재 작성되는 timestamp까지의 데이터만을 작성합니다. 이를테면, 10:00:00(09:00:00~09:00:59의 데이터를 포함)에 작성되는 데이터가 1.5MB였다면 갱신시 모든 내용이 저장되고, 10:01:00(10:00:00~10:00:59의 데이터를 포함)에 작성되는 과거 1분간의 데이터가 총 2.5MB이고 10:00:47초 데이터를 쓰던 도중 보고서의 크기가 2MB를 넘었다면 10:00:47까지의 내용만을 작성 완료하고 보고서 쓰기작업을 종료한 후 10:00:02에 보고서를 갱신하게 됩니다.
  • 프로그램 실행
    • Windows: HandshakeOne.exe를 실행해 주시면 됩니다
      • 만일 Handshake One을 Windows Service로 실행하시고 싶으시다면, nssm(http://nssm.cc/)을 이용하실 수 있을 것으로 생각됩니다. 직접 써 본 적은 없지만 평가가 꽤 좋은 프로그램이었습니다
    • Linux: 두 가지 방법이 있습니다
      • HandshakeOne을 직접 실행합니다. 참고: libpcap이 root 권한을 필요로 하므로, HandshakeOne은 sudo나 su 등을 이용하여 root 계정으로 실행되어야 합니다
      • Handshake One을 systemd service로 설정할 수 있습니다. HandshakeOne.service 파일을 필요에 따라 편집하시고(최소한 ExecStart 와 WorkingDirectory가 현재 프로그램의 위치를 반영하게끔 조정되어야 합니다), 아래의 명령을 참조하여 프로그램을 systemd service로 등록해 주세요
        • sudo cp systemd.service /etc/systemd/system
        • sudo systemctl enable HandshakeOne
        • sudo systemctl start HandshakeOne
프로그램에 대한 설명은 여기까집니다! 유용하게 쓰실 수 있으셨으면 좋겠습니다. 제언, 의견, 또는 질문이 있으시면 아래에 코멘트를 남겨주세요. 감사합니다.

Handshake One: Know Your SYN, source of DoS attack, or client IP profiles at least

It was a hot summer in the middle of COVID-19 renewing its infection top day by day. I saw managers of a small online community defend the server against a Dos(Denial of Service) attack. They were enthusiastic, but I found out the work is quite inefficient. The only thing they could rely on was some web server logs and our good old friend iptables.

Well, being too small to invest in some security, we know the only viable option we have is either iptables or nftables against those dull and stupid, yet quite efficient attacks. However, when we encounter the attacks, usually we're puzzled and stuck; log is too long and complicated to read so that we have difficulties on distinguishing attacker IPs against user IPs.

So, I developed a small utility named Handshake One to help server engineers who want to find out sources of DoS attacks as early as possible, or, learn IP profiles for your service at least. This small utility collects SYN packets from clients IPs for past 60 seconds to generate reports as you see below:
Handshake One TCP SYN counter report
(C)Copyright 2022 Robert Teminian.
This application is provided free of charge, and provided AS IS: though the developer hopes that this would help the user in any way, the software does NOT guarantee anything at all.

Stop by the developer's blog and leave a comment! Visit http://codenested.blogspot.com

====================
At 1660000053
IP Hits
192.168.1.26 14
Total 14

====================
At 1660000054
IP Hits
192.168.1.26 11
Total 11

====================
At 1660000055
IP Hits
192.168.1.26 1
Total 1

Currently binaries for three operating systems are supported, but you can contact me to build binaries for other OSes. Click the link below to download the executable binary for each:

They're just compressed files. You can just decompress the file in any directory you want to use.

The usage is as follows:
  • Install: Windows
    • Decompress the ZIP file in any location you want
    • On Windows, Handshake One depends on npcap(https://npcap.com/) for capturing packets. Please install the separate binary, or install Wireshark(https://www.wireshark.org/) which installs both packet analysis tool and npcap.
    • After installing npcap, copy Packet.dll and wpcap.dll from C:\Windows\Systems32\Npcap to the executable directory
  • Install: Linux
    • Decompress the TGZ file in any location you want
    • On Linux, Handshake One depends on libpcap. Usually it is installed alongside with tcpdump but if it's not, consult your Linux distribution's package manager(apt, yum, ......) to install the package
  • Configuration
    • The only way to configure Handshake One is via its configuration file, HandshakeOne.json. Currently there are only two keys
      • resultpath: the directory to save report file. Handshake One will automatically generate and update(overwrite) file named "HandshakeOneReport.txt"on that directory every 30 seconds.
        • In Linux,  I recommend to set the directory to some RAM disk(e.g. /tmp) so that the 
      • sniffer: device name to capture the packets. In Linux its the device name shown on commands like ip link or ifconfig, but in Windows it's a bit tricky since the name npcap refers to is NOT the "human readable name" for the interface, but internal device name like \Device\NPF_{12345678-9ABC-DEF0-1234-567890ABCDEF}. To see the device names and corresponding human readable descriptions(e.g. Realtek PCIe GbE Family Controller), run Handshake One with "show" parameter, e.g. HandshakeOne show
      • reportsizelimit: when updating(actually overwriting) the report file, limit the size of report file in bytes. If the file size is bigger than designated size, the application will write until the last data for the timestamp currently being written and finish. For example, if the report size at 10:00:00(including data for 09:00:00~09:00:59) was 1.5MB the report will contain everything, yet at 10:01:00(including data for 10:00:00~10:00:59)the actual size is 2.5MB and it hits the set limit (i.e. 2MB) around writing data for 10:00:47, it'll complete write data up to 10:00:47, finish writing, and refresh the report at 10:00:02
  • Run the application
    • In Windows, just run HandshakeOne.exe
      • If you're interested in using Handshake One as Windows Service, I think you can use nssm(http://nssm.cc/). Though I have no experience using it, I find very positive reviews in many places.
    • In Linux, you have two choices
      • Run HandshakeOne directly. Caution: since libpcap needs root privilege, HandshakeOne must be run with commands like sudo or su.
      • You can register Handshake One as systemd service. Edit HandshakeOne.service as needed(at least ExecStart and WorkingDirectory must be changed to match exact path for the binary) and refer to following command to register the binary as systemd service
        • sudo cp systemd.service /etc/systemd/system
        • sudo systemctl enable HandshakeOne
        • sudo systemctl start HandshakeOne
So...... That's all, folks! I hope you enjoy the application. If you have any comments, opinions, or questions, please leave a comment.

Sunday, January 16, 2022

Flutter/DART: First Impression

All of sudden, I had a chance to briefly review Flutter. Covering all desktop, mobile, web in single programming language, and for web, compiled to Javascript instead of WebAssembly which takes a forever-log real time compilation time - isn't it lovely and beautiful? (Oh, Qt has WebAssembly port, but it takes forever to compile the code, which may be because of that big fat core library).

Someone said that the biggest weakness of Flutter is DART, but I don't agree. The structure of DART is quite interesting, and considering the accessibility from other programming languages it is both understandable(I'm looking at you, Rust). The only thing I dislike is that darn garbage collector, but I think I should admit if DART doesn't want to manually control pointers in any way. Or, if they force RAII in whatever way everyone will be angry. :P

I think, the biggest drawback of Flutter is state management. For other frameworks you don't have to distinguish stateful against stateless widgets, but in Flutter you must do it verbosely. What's more, if build() is called it automatically finds only changes and applies only found changes. For someone the word "automatic" will say "yeah so it's Google!". But as a C++ developer myself, I throw a question like "why do you compare everything when you can recognize changed area?" For me, it looks like that the Flutter developers designed to change everything, yet distinguishing stateful widgets against stateless ones to minimize overhead to GC(the garbage collector). I think, for me, the property binding of Qt is better than this.

I'm afraid the approach of Google in this way would drop the readability of the code. If you read Introduction to widgets, one of the official documentation for Flutter, you see "the separation of responsibility allows greater complexity to be encapsulated in the individual widgets, while maintaining simplicity in the parent". For me, it sounds like a warning to prepare for the hellish burning(......) hell, when you have to change the stateless widget. What if you have to separate some part of stateless to stateful side? And there's no absolute way to prevent you from doing it. Most of the people select Python or Javascript because they provide very flexible development environment for requirements changed in real time, but I think, for Flutter that kind of flexibility would be a bit hard.

I'm not sure about others' opinions, but for me DART is okay but I'm a bit against Flutter. It's like...... it forces specific development process and design concepts. Do I really have to do it this way? I'm not sure.