Jun 29, 2008

Do it yourself

Hi, I'm a strong believer that the DIY (Dot it yourself) philosophy helps to improve knowledge on the topic you are working on. So if you If you are a developer using (or planing to use) db4o (and have the time), I highly recommend to try do build it yourself as explained here. One point that can be further explored is the possibility to run the tests on a ramdisk in order to improve performance (in may machine I get an improvement of 20%). To be able to do this you need to create a ramdisk (personally I use this one) and them set 2 properties in our machine.properties file: java.vmargs=-Ddb4ounit.file.path=DIR -Djava.io.tmpdir=DIR dir.tests.temp=DIR Here, DIR is an path to your ramdisk (in may case I use r:/temp). Adriano

Jun 25, 2008

Type safe / refactor friendly configuration for Db4o - Part III

Hi, In the first installment of this series I presented the idea of having a typesafe way to configure db4o. In the second one, I explored how we could use expression trees to figure out a property name being accessed. In this post I'll address the issue on how to dive into the property's IL (intermediate language) to come up with a field name which we want to configure (if you are not familiar to IL, please, read along some of the links here). The best tools I'm aware of (when it comes to read or even change a method's IL!) are the Mono Cecil library and Net Reflector, which is an invaluable tool when you want just to look at a method's IL without writing one line of code. I don't need to say that I used these tools a lot :) In order to come up with a field name to be indexed, the main approach is to find all return paths for the property getter in question and check the field being returned. Simple, right? Unfortunately no; what if the property getter returns only constants? what if there are more than one field involved? Before we dig into the details about IL handling, let's document the behavior for some possible use cases (the less obvious ones) regarding property getter return paths:
  • Only constants: Throw exception
  • More than one field involved: Throw exception
  • Chained method: returns the called method / property accessed field.
(we could come up with different requirements, for instance, every field referenced in one property return path must be indexed, but in order to keep it simple, let's stick to the rules presented above). From my previous posts, we already have a way to figure out the property get being accessed; the next step is to analyze the method body; I used Cecil.FlowAnalisys to accomplish this task (line 6) in the following function:
public FieldReference Resolve(MethodDefinition method)
{
if (CheckRecursion(FullName(method))) return null;
callStack.Push(FullName(method));

ControlFlowGraph graph = FlowGraphFactory.CreateControlFlowGraph(method);

graph.Dump(Console.Out);

Resolve(
graph.Blocks[0],
firstInstruction => Array.Find(
                   graph.Blocks,
                  candidate => candidate.FirstInstruction == firstInstruction));

callStack.Pop();

FieldReference reference = CheckReferencedFields(
                         referencedFields, method.Name);
referencedFields.Clear();

return reference;
}
Next, for each control block we inspect it's IL as shown in the following code:
void Resolve(
    InstructionBlock block,
    Func<Instruction, InstructionBlock> blockForStartingInstruction)
{
if (AnalyzeInstructions(block.FirstInstruction, block.LastInstruction))
{
 Resolve(
    blockForStartingInstruction(block.LastInstruction.Next),
    blockForStartingInstruction);
}

for (int i = 0; i < block.Successors.Length; i++)
{
  Resolve(block.Successors[i], blockForStartingInstruction);
}
}
In Line 5 we call a function that do the dirt work of analyzing the IL instructions. This function returns true if the block containing the next instruction (after the last instruction of the current block) should be handled as the "next block" (this is done to support finally blocks). Lines 12-15 just process the remaining blocks calling Resolve() recursively. The real fun is in AnalyzeInstructions() function which do the following:
  • Line 7/9: For Ldfld / Ret instruction sequences, add ldfld target to return field list (lines 11 and 12).
  • Line 16: Add an alias for the each Ldfld /Stloc sequence;
  • For Ldloc / Ret sequences, search the aliases to see if ldloc's operand is an alias for a field and add any alias found to the return field list (line 23).
  • If a method call is found, analyze the target's method body (lines 35 - 41).
bool AnalyzeInstructions(
Instruction current,
Instruction last)
{
while (current != last.Next)
{
if (IsLoadField(current))
{
 if (IsReturn(current.Next))
 {
     referencedFields.Add(
          (FieldReference)current.Operand);
 }
 else
 {
    EnsureLocalVariableAliasFor(current, () => (FieldReference)current.Operand);
 }
}
else if (IsLdloc(current.OpCode))
{
 if (IsReturn(InstructionOrBranchTarget(current.Next)))
 {
   FlushAliasesFor(current);
 }
 else if (IsStore(InstructionOrBranchTarget(current.Next)))
 {
    EnsureLocalVariableAliasFor(
        current,
        () => aliases.Find(
            candidate => candidate.OpCode == current.OpCode).Field);

    current = current.Next;
 }
}
else if (IsMethodCall(current))
{
 EnsureLocalVariableAliasFor(
   current,
   () => Resolve(
      ResolveMethod((MethodReference)current.Operand)));
}

current = current.Next;
}

return IsLeave(last);
}
That's it! You can download the full source code here. [Updated on 7/23/2008: Fixed the source code download link] Just in case you want to play with the code, one point can (need to) be improved is the handling of exception blocks. Thoughts? Adriano

Jun 23, 2008

Very interesting (and funny)

Hi, This morning a friend of mine send me this link about colaborative development in eclipse. Very interesting Adriano

Jun 21, 2008

You are not paid to introduce bugs :)

Hi, Today's post is in the series "what is wrong with this code" but before presenting the actual problematic code I want to give some background, so bear with me. At Db4o we run migration tests (regularly) in order to guarantee that we are able to load (and query) databases created by older versions of Db4o engine. The way it's accomplished is very simple:
  • for each configured version, create an database using it and perform a series of tests with the trunk version.
Some time ago, I was asked to fix an annoying bug in these migration tests. In order minimize the time required to run these tests, we do not run then against every existing Db4o version. To allow this selective running we introduced a way to specify the minimum version to be tested so, if one want to test versions 6.0 and above it is a simple matter of setting a constant in a C# source:
private const double MinimumVersionToTest = 6.0;
At some point in the code we called the following function (wrong version):
double VersionFromVersionName(string versionName)
{
Version version = new Version(versionName);
return version.Major + version.Minor / 10;
}
This function is supposed to return the double representation of an assembly version (string) but it fails miserably (this code works correctly only for versions with a decimal value 0 (zero); for instance, 6.0, 7.0, etc). Now the question is, what's is wrong with this function? To be fair I have to admit that it's not a hard to spot bug, but when you are in a hurry and have no test for your test code... The problem is that version.Minor is an integer field and so,
version.Minor / 10
will result in a integer number and the decimal part will be happily discarded. The developer who wrote this code (me) is aware of language type conversions but he didn't notice the error
(shame on me) ; so whenever we passed a version number not finished in 0 (zero) we got the wrong double representation for it!
Actual VersionFunction Return
6.06
6.16
77
7.27
The fix? Pretty easy: Just add a ".0" to the divisor :) (I could also add an F: 10F)
double VersionFromVersionName(string versionName)
{
 Version version = new Version(versionName);
 return version.Major + version.Minor / 10.0;
}
The bottom line? T
his episode just highlights (and reminds me about) the importance of double checking our test code! after all, as a friend of mine likes to ask:
who test that the tests test what they should test?
Have fun. Adriano

Jun 3, 2008

Running Sharpen Tests

Hi.

If you start using sharpen, you'll probably find some limitation / bug and, maybe, want to take a look into the issue.


In this case the best approach (IMHO) would be to i) start a discussion in Sharpen Forum and, if
appropriated (in the case there's no issue filed already), ii) file a new Jira issue into our issue tracking system.

Next, if you have the time, iii) try
to replicate the behavior with a minimal test case. The last step iv) would be trying to fix the issue.

This post is all about iii, or to be more clear, on how to run / write unit tests for Sharpen.

Let's start by opening an empty eclipse workspace:



Next, open SVN perspective and add a new SVN repository pointing to

https://source.db4o.com/db4o/trunk/sharpen/


and checkout all projects.

No install the plugin (as explained here):
  • In package explorer (Java perspective), right click on sharpen.core and select "Export" from the context menu.

  • Expand the "Plug-in Development" folder and select "Deployable plug-ins and fragments".

  • Set "Destination" to the root folder of your eclipse installation and click "Finish";
Now you can run Sharpen Tests by right clicking on "sharpen.ui.tests" and selecting "Run as / JUnit Plug-in Test";


after some processing your screen should look something like:


When the tests finishes you should see 0 (zero) errors and failures (as of today, there's some issue with sharpen.builder and sometimes you will get 2 errors in its tests but these errors don't prevent sharpen to work properly and, as you can see, when I executed the tests the errors didn't showed up).

Now let's explore the general look 'n' feel of a Sharpen test. In package explorer expand the node sharpen.ui.tests then testscases.


For each sharpen functionality there will be at least one pair of files (collectively called Test Case Resource):
  • One .java.txt that stores the java source to be converted;
  • One .cs.txt storing the expected sharpened file;
In the above picture we are testing interface conversion. Take the time to poke around testcases folder checking some of the tests.

The next logical step is to add new tests. Basically to add a new tests we need to:
  1. Create the original java source (.java.txt)

  2. Create the expected cs file (.cs.txt)

  3. Plug these two files into the test engine. In the common scenarios, all we have to do is to call runResourceTestCase() function.

To see an example, just expand sharpen.ui.tests/src/sharpen.ui.tests folder and double click on NativeInterfacesTestCase.java.


In a future post (soon I hope) I want to take a look in a few other points such as:
  • Getting a little better performance when starting sharpen using the approach presented in this post.
  • How to debug sharpen.
  • A trick to see the expected / actual values (useful when creating new tests).
Thoughts?

Adriano




Jun 2, 2008

Dumb security...

Hi! Am I in a bad mood today? Well, maybe, but I really feel upset when something supposed to help me to be safer on the net starts to get into my way or, even worse, pushes me in the other direction. That is what happened today when I tried to use a well know credit card website (which I'll not name here :) to see my billing information. Of course, as I don't use this service very often, I didn't remember my user name / password! Ok, (I thought) I just need to call their support service and ask them to reset my user name / password to one that I do know. So, I picked up the phone and called them. My first issue was that they didn't allowed me to choose my user name! Ack! Ok, this is not a big issue anyway (I can always write it down somewhere). The real problem (in my opinion) is that they requires that you MUST set a password which is at least 6 but no more than 8 characters in lenght! And you MUST use numbers also! Ok, I understand that mixing numbers, digits, punctuation, etc. in a password helps to make it stronger, but these rules forces me to give up on my "password schema" (I use different passwords for each site; each password is an encoding of a lengthy phrase). The result? I'd never be able to remember the password I choose (no matter which one I pick); it's even worse as probably I'd choose a weaker one in a desperate (hopeless) try to not forget it again. After this episode I decided to take the time (ok, I'm a little bit lazy) and give some password manager a try (at least they succeeded in the purpose of forcing users - at least me - to use a stronger password :). I'll be experimenting with a firefox plugin called iMacros. Let's see how it works :) See you. Adriano.

New programing language!

Hi! Today I got an "job opportunity" from one of jobs sites I'm registered (no, I'm not looking for a new job, I just didn't unregistered) with a really intriguing subject:
A skilled professional with 3 years of experience in C, Sharp and VB.NET.
I have to admit that I do know C and also a little of VB.NET, but I have never seen SHARP :) Ok, probably it was just a mistyping, but I bet the person who wrote this subject has no idea about programing languages whatsoever ;)