Objo: A Very Promising Development Environment

You joke but it’s already on the roadmap… :grinning_face:

Are you sure???

I wanted to believe that so I opened the app to test and the whole Objo.Studio.Services.Licensing source code is there in plain C#

Maybe but a lot of experience and time is needed to get something from the assembler. In this case .Net takes like 5 seconds to get the plain C# source code :roll_eyes:

By the way, @Garry, I hope there is an EULA with the classic you shall not decompile but an EULA is NOT enforceable In contract law if you never showed it at any stage of downloading or using the app and get the user to agree with it.

I didn’t say Garry uses AOT, I have no idea about that. Only that you can compile that way.

I suppose that since .NET APIs are well known and there’s reference source for a most of it, some sense could be made of calls into that, even from the machine code generated by AOT compilation.

In any case, the bytecode of a compiled Objo app resides in a separate .objoc file and that is run by a custom VM and the byecode format has not been reverse engineered. It probably could be, by a sufficiently motivated person. But Garry has already said he will be offering encryption for it.

My own view is that your best defense against bad actors is to innovate too fast for them to keep up – and Garry is definitely doing that.

There are two separate things being discussed here, so it’s worth clarifying them.

Objo Studio is written in C# and .NET. A .NET decompiler can reconstruct a C# representation from compiled assemblies. That does not mean the original source files are stored inside the application in plain text. The original comments, formatting, project structure and other information are absent but decompiled output could be readable. NativeAOT or obfuscation can raise the effort required, but neither makes software impossible to analyse.

Apps created with Objo are different. They do not contain the developer’s original Objo source code. Published apps contain custom Objo bytecode, and release builds remove comments, source locations, line numbers and local-variable names. It would be misleading to claim that reverse engineering is impossible, because anything running on somebody else’s computer can potentially be analysed, but this is very different from recovering the original project and source code in a few seconds.

Further bytecode-protection options are on the roadmap. I will not describe them as “uncrackable”; the goal is to provide commercially sensible protection that raises the effort involved without compromising application reliability.

for pretty obvious reason, I don’t discuss the implementation of Studio’s licensing or other security-sensitive measures publicly.

Objo Studio already has published terms and conditions, linked from within Studio, which address reverse engineering subject to applicable law. The separate point about how prominently those terms are presented is fair, and I’ll review that. Their enforceability in any particular situation is a legal and jurisdiction-specific question, not something that can be settled by me in a forum post.

Could it not compile to .NET byte code ? I mean is there good reason why you have your own byte code on top of already interpreted language ?

MS has good API to help you compile any language into the .NET byte code.

Yes, Objo could compile to .NET IL, and it’s something I have considered.

My custom VM was chosen deliberately. It gives Objo complete control over its language semantics, memory management, async scheduler, event context and runtime object model. It also makes the edit–run–debug cycle very fast and allows the debugger and profiler to operate directly in terms of Objo source rather than translated CLR concepts.

Strictly speaking, this is not an interpreted language running on another interpreter. The C# implementation of the VM is normally JIT-compiled to native machine code by .NET; the VM then interprets Objo bytecode.

Producing IL is certainly possible, especially with the APIs available in recent .NET versions. Emitting instructions is only the easy part, though. Preserving Objo’s exact behaviour, including ARC and deterministic destruction, closures, events, generics, async fibres, native methods, introspection and debugging, would effectively mean maintaining a second compiler backend and then validating it against the VM.

Ordinary .NET IL would make decompilation easier, not harder, because excellent .NET decompilers already exist. The more interesting possibility would be an optional IL backend followed by NativeAOT compilation. That could improve execution speed and make published applications harder to analyse, although NativeAOT brings its own restrictions and compatibility work.

My preferred architecture would therefore be to retain the custom VM for development, running and debugging, while leaving open the possibility of an optional IL/NativeAOT release backend in the future. It is a worthwhile idea, but not a simple substitution for the existing bytecode VM.

I have gone from “please spare me” to guardedly enthusiastic about async / await because the “exact behavior” you mention provides asyc/await/task functionality pretty much the same as .NET but at the same time guarantees synchronous code has the VM to itself. It removes whole classes of headaches that are present in .NET, and renders hacks like ConcurrentDictionary unnecessary.

Spend much time in online forums where .NET devs discuss async / await “gotchas”, and you find people falling back on “solutions” like “you must go all-in; the entire call chain must be async right from the entry point to solve the problem your’e describing”. Which always struck me as “do what’s already not working, only harder”. There’s something missing or outright broken in a metaphor that doesn’t quite work as expected on a regular basis, or that requires pervasive refactoring of your code base for it to really be comfortable in its own skin, so to speak. Most code bases don’t need “async everywhere”.

That’s why I’ve gone out of my way to avoid async or threads in my C# code; the extra cognitive burden doesn’t buy me enough in the kinds of apps I’m building anyway. There’s literally only one place in my C# code base where I’d like to go async to cut the latency down from 3 seconds to maybe 100 ms or so but for me it’s easier to live with that in an infrequently used feature than to try to make it work without the risk of destabilizing everything else.

I’m still feeling my way but I think Objo has actually solved most of those issues, and if that comes at the price of a custom VM I’m all for it. I don’t know that a better async implementation is even the main reason you went this route, Garry, but it is one major boon that has fallen out of it, from my perspective anyway.

I also think it’s an advantage of an individual or very small team with good architectural instincts vs the dynamic of a much larger team building a more general purpose ecosystem. The latter is more prone to what I call “committee effects” both from within and without. Even apart from that dynamic, there are just different tradeoffs.

From what I’ve seen, you have very good design instincts combined with high but realistic standards, and that, in my view, is worth the somewhat theoretical tradeoffs in memory and performance, which aren’t noticeable in real world terms yet for me either.

Thanks for the kind words :slight_smile:

One of the advantages of rolling my own VM was to have more granular control over asynchronous work, yes, but mostly I just love that I own the entire pipeline. If I don’t like something or there’s a bug or a great feature is requested by an Objo user, I am the only one getting in the way of making it right. That flexibility is core to Objo’s agileness.