ReSharper beta license: it’s Diamondback all over again

What is up with people making public betas and then insisting that they be kept top-secret?

I decided to download the ReSharper 4.0 beta, so I could finally get some ReSharpery goodness in Visual Studio 2008. Then I read their license agreement, and it’s almost as bad as the public embarrassment that was the Delphi 2005 beta license. You’d think someone posting a public beta would want advance publicity and grassroots buzz, but I guess not.

Some excerpts (which I will later have to deny ever posting, or indeed reading):

Any and all test results, error data, reports or other information, feedback or materials made or provided by Licensee relating to Software (collectively, “Feedback”) are the exclusive property of JetBrains…

Read that a few times. It says that anything I could possibly have to say about the beta (“reports or other information”), whether I send it to JetBrains or not (“made or provided”), is not mine. Which means I would be in violation of copyright (by illegally copying JetBrains’ intellectual property) if I posted anything on my blog about the beta.

(They note elsewhere that I should treat my feedback as confidential information. My reports or other information, on the other hand, are not confidential; they’re just not mine.)

[“Confidential Information” means] … (iii) the terms, conditions, and existence of this Agreement.

Even though it’s an open beta, and anyone can download it and look at the license agreement, I will hereafter have to disavow any knowledge of said license agreement. I’ll tell you I don’t even know there is a license agreement for the beta. (At least I can admit the existence of the beta itself, if not the beta license. That’s a pleasant change from Delphi betas. Oops — what Delphi betas?)

Licensee’s obligations regarding Confidential Information will expire no less than five (5) years from the date of receipt of the Confidential Information

The first time I read this, I thought it meant I couldn’t talk about ReSharper at all for five years. Fortunately, it wasn’t quite that bad:

«Software» means the ReSharper pre-release software

They were, in fact, smart enough to only make me promise to keep the beta top-secret (along with any other pre-release software they may make public over the next five years). I can still talk about released versions. Whew!

They’re also nice enough to say that I don’t have to disavow knowledge that was already publicly available. Again, a step up from Delphi. But still a disappointment.

Anyway, don’t expect me to help hype their public beta. I wish I could; it looks like it’ll have loads of cool stuff in it. But they don’t want my hype; they want their public beta kept secret… so I will be contractually obligated to disavow this blog post, starting about two minutes from now.

Beyond Compare and binary DFMs

Binary DFMs are a pain, because you can’t see what changed between two versions. Right?

That’s what we thought. We’ve been avoiding binary DFMs since time immemorial, especially back in the Dark Ages when we used VSS, which won’t even keep revision history for binary files. Even now that we’re on Subversion — which has no problem with binary files, thank you — we still avoid binary DFMs. Among other things, it’s hard to write a Ruby script to grep through a directory of binary DFMs. And, of course, they’re hard to diff. Or so I thought.

So imagine my surprise yesterday when I was writing a Ruby script to grep through our code and DFMs, and it dumped out some gibberish and beeped a couple of times. I looked, and sure enough, we had a binary DFM. It wasn’t even some fringe GUI that we had written once and never modified — we had, in fact, made several changes to the DFM in the past year.

This really puzzled me. Did this file get accidentally changed to binary format recently? But we should’ve noticed when we checked in. We shouldn’t ever check in without looking at the diffs first. And of course you can’t diff a binary DFM, right?

But I looked at the file history. That file had been in our repository for years, binary all the way. It had been modified several times. Surely people had tried to look at the diffs. So what happened?

I will, at this point, call your attention to two things:

  1. We’ve been happy users of the superb Beyond Compare diff tool for almost three years, and have made it our default diff viewer for TortoiseSVN.
  2. Taskbar right-click menu for Beyond Compare

    Beyond Compare is written in Delphi.

Yep. Of course Beyond Compare, as a Delphi app, has some Delphi-isms built in. It’s so cool that it can diff binary DFMs, and make them look just like text in the diff viewer. Internally, I’m sure they call Classes.ObjectBinaryToText (handy thing about writing a tool in Delphi that works with Delphi code, you get the library functions for free), so as far as you can tell when you’re looking at diffs, the file looks like it’s been a text DFM all along.

My only gripe is that it does this utterly silently. There’s no indication that this is a binary DFM that it converted to text for your convenience. And since binary DFMs are inconvenient in other ways (e.g., Trac can’t show diffs on them), I’d rather not have any binary DFMs in our code base at all. So in some ways, I’d almost rather not have this feature — I’d notice the binary DFMs a lot sooner if they looked like garbage in the diff window.

Then again, when I re-saved the DFM as text, it was sure nice to have those diffs to show that nothing important had changed. Binary DFM on the left, text DFM on the right, and the handful of changes highlighted just as nice as you please.

A way to tell if an app was written in Delphi

I’m often curious about whether an app I’m using was written in Delphi. Here’s an easy way to tell, that works most of the time.

(Note: this won’t work for apps that use Delphi 2007’s new Vista-compatibility MainFormOnTaskbar property.)

All you do is right-click the taskbar button. Non-Delphi apps will have an ordinary window menu:

Taskbar right-click menu for a non-Delphi application, with the usual window menu items

Delphi apps have an abbreviated menu instead, because it’s actually the context menu for the invisible TApplication window, instead of the context menu for the form:

Taskbar right-click menu for a Delphi application, with only Restore, Minimize, and Close

This isn’t true just of Delphi for Win32 — it also works for VCL.NET applications. (It’s VCL-specific, though, so you can’t identify a Delphi/.NET WinForms app this way.)

If a Delphi app does use Delphi 2007’s MainFormOnTaskbar, then it will have the normal window menu, so a normal menu doesn’t necessarily mean it wasn’t written in Delphi. But if you right-click the taskbar button and do see only Restore, Minimize, and Close, it’s a pretty sure thing that it’s a Delphi app.

DUnitLite 0.5 released

Version 0.5 of DUnitLite is now available. This is a minor enhancement release.

Download it here: DUnitLite 0.5

What’s new in 0.5

  • Should.ReferTo now works with interfaces, and can mix and match between objects and interfaces. It also correctly works around the interface reference-equality problem.
  • Added Given as an alias for Specify. So if you want to document your assumptions at the beginning of your test, you can use Given.That(...) to improve readability.
  • Added Should.[Not.]Be.False and Should.[Not.]Be.True.
  • Added Should.[Not.]Be.Null and Should.[Not.]Be.Assigned. Both work with either objects or interfaces.
  • Added Should.[Not.]Be.GreaterThanOrEqualTo and Should.[Not.]Be.LessThanOrEqualTo, as aliases for AtLeast and AtMost.

Interfaces and reference equality: beware

In Delphi for Win32, you can’t necessarily use the “equals” operator to tell whether two interfaces both point to the same object. The following code:

var
  Foo: IFoo;
  Bar: IBar;
begin
  Foo := MyObj as IFoo;
  Bar := MyObj as IBar;
  Assert(Foo = Bar);

will fail — even though both Foo and Bar point to the same object.

Let’s look at why that happens, and what you can do if you want a comparison that would succeed.

Memory layout of an interface reference

Note: If you don’t care about the technical details, and just want to know how to work around the problem, skip ahead to the “Workaround” section.

Let’s dig into the memory layout of an interface reference:

var
  Foo: IFoo;
begin
  Foo := TFoo.Create;

Memory layout of a Delphi/Win32 interface reference

An interface is a pointer to a pointer to an interface VMT. This means there’s a slot in the object’s instance data, just like any other instance field (except that it’s not visible in code), that automatically holds a VMT pointer. This field is automatically initialized during object construction. This is exactly the same way VMTs for Delphi classes are handled, except that the class’s VMT pointer is always at offset 0 within the block of memory allocated for the instance. Interface VMT pointers have to be at a nonzero offset, since offset 0 is already spoken for; they’re added at the end just like fields. (This will be familiar to those who remember Turbo Pascal objects, whose VMTs didn’t have to be at offset zero.)

And there’s the reason for the problem. Every interface you define gets a VMT all its own (even if it has the exact same methods as its parent interface, it still has its own VMT), so there has to be a slot in the instance data for the IFoo interface VMT, and another slot for the IBar interface VMT.

When you cast the object instance to IFoo, you’re really taking a pointer to the invisible “IFoo VMT pointer” field inside the object. When you cast to IBar, you’re taking a pointer to the “IBar VMT pointer” field. Those two are at different memory locations, so a reference-equality check will fail.

Bonus trivia: Because the interface pointer (which is also what’s passed as the implicit Self pointer when you call a method on the interface) isn’t at the same address as the object, the compiler has to generate a method stub that does a Self-pointer fixup, then call the actual method. The interface VMT points to this thunk method, not to the actual method body. You always wanted to know that, didn’t you?

A failed workaround: changing the variable types

So if the problem is the variables being of different types, let’s just make them both the same type:

var
  Foo: IInterface;
  Bar: IInterface;
begin
  Foo := MyObj as IFoo;
  Bar := MyObj as IBar;
  Assert(Foo = Bar);

The Assert will still fail, even though both variables are of type IInterface! Why?

That’s because the Foo variable still points to the IFoo VMT pointer inside the instance. IFoo descends from IInterface (as all interfaces do), so an IFoo reference is assignment-compatible with an IInterface variable, and is stored as-is in that variable — still pointing to the IFoo VMT pointer.

So even though a reference may be stored in a variable of type IInterface, it does not necessarily point to the IInterface VMT pointer for that class. This becomes a particular problem when you add different interfaces to an IInterfaceList: they’re stored in a list of IInterface variables, but they’re not canonicalized to point to the IInterface VMT pointer. So, for example, TInterfaceList.IndexOf can give you the wrong answer, if you’re not careful about the types of your interface references.

The workaround

The fix is really simple: just as-cast both variables to IInterface. This normalizes them, and puts them on an even footing for comparison.

When you as-cast to an interface type, you get back a pointer to the interface VMT pointer for that exact interface — none of that weirdness with assignment-compatibility and putting an IFoo reference into an IInterface variable: when you cast to IInterface, by golly, you get an IInterface all the way. QueryInterface and SysUtils.Supports will also canonicalize to the interface you specify.

(You could even use something other than IInterface, as long as you know both instances will support it. But every interfaced object can be cast to IInterface, so using IInterface makes for a nice general solution.)

So to revisit the code at the top of the article, the correct way to compare the interfaces would be something like this:

var
  Foo: IFoo;
  Bar: IBar;
begin
  Foo := MyObj as IFoo;
  Bar := MyObj as IBar;
  Assert(Foo as IInterface = Bar as IInterface);

Now, the assertion will pass.

Released: Tyler v0.01 alpha

Tyler version 0.01 is now available for download. Currently there’s just a source distribution, but it includes compiled EXEs. The ZIP download is no longer available, but you can check the code out of the Subversion repository: version 0.01 or latest code.

The feature set is pretty sparse at the moment, but what’s there, works. It’s the bare minimum that I decided would be worth releasing. Here’s a brief rundown:

  • Pluggable display libraries. You can select from “compatibility mode” (basic GDI via TCanvas), Asphyre eXtreme, and (un)DelphiX (but see note 1 below). Windowed mode only — no fullscreen support in this version.
  • Graphics. These are primitive — everything is a solid-color square. Walls are light gray, floors are dark gray, the hero is yellow, the wandering NPC is white, and that’s all you get. (Well, except for black.)
  • Movement. You can use the cursor keys to move the hero around the map. There’s nothing yet to make you stop when you run into a wall or the NPC, though.
  • Map editing. TylerEdit.exe lets you edit the map and save your changes. It’s primitive, but it works. (But see note 2 below.)

Currently you can only have one map. Later you’ll be able to have lots of them — a world map, a map for each town, a map for each dungeon level.

Some stuff is hard-coded in this version, like the initial position of the hero, and the position and movement of the NPC. This will be improved in the next few versions.

Note 1: The license agreement for (un)DelphiX does not allow me to redistribute its source code. So, the source distribution is (un)DelphiX-less. But it still compiles: if you download and compile the source distribution, (un)DelphiX support will be automatically disabled. To enable (un)DelphiX, see the notes in the vendor\undelphix directory.

Note 2: The map file format will change in future versions. Any maps you create in this version probably will not work in v0.02 and later.

What’s next

The next few releases will be working toward a major milestone: a finished game. Not a long game — it’ll probably take less than a minute to play from start to finish. And not a very exciting one. In fact, it’ll be pretty stupid. But it’ll be playable, and everything will be editable in the game editor. Stay tuned for more news of… Stupid Quest.