How malloc broke Serenity's JPGLoader, or: how to win the lottery
I got the chance to investigate an interesting bug in SerenityOS this week. It was related to the decoding of JPG images in the operating system. For some reason, when a JPG image is viewed, it comes out like this:
Weird, huh? Also seems like a simple confusion of RGB vs. BGR. And sure enough,
making the following change on JPGLoader.cpp
:
- const Color color { (u8)block.y[pixel_index], (u8)block.cb[pixel_index], (u8)block.cr[pixel_index] };
+ const Color color { (u8)block.cr[pixel_index], (u8)block.cb[pixel_index], (u8)block.y[pixel_index] };
context.bitmap->set_pixel(x, y, color);
makes the image show up correctly. Case closed!
…not. Why did this even break in the first place?
A look at Self's object system
When I first introduced Self, I mentioned that Self is a programming language which employs prototype-based inheritance; it does not depend on a class-instance distinction to facilitate object-oriented programming, but rather, it uses a prototype object from which copies are created. The methods associated with a prototype is stored in a traits object which is just another object storing methods that can be used on a prototype and all its copies through a parent slot.
The trouble begins when we actually try to implement this prototype-based
inheritance system as an actual virtual machine, particularly with regards to
memory consumption. Say that I have a prototype object point
with a constant
parent slot pointing to traits point
and two assignable slots, x
and
y
.1 When we copy this object, we would expect to copy all three fields. But
if we scale that up, that’s so much unnecessary data being copied! For one, for
every copy of the point
prototype we make, we’re copying that traits point
parent slot reference. Not to mention, because our language is dynamic, we can’t
simply treat x
and y
as offsets into memory, because they might be constant
or assignable (Self can’t intrinsically know). So our naive implementation would
be pretty inefficient.
Can we make this more memory-efficient? In fact, we can; we can exploit the fact that most Self objects that are copied only ever have their assignable slots modified. Let’s take a look at how the original developers of Self have optimized the memory usage of the Self virtual machine.
An introduction to Morphic: Self's UI toolkit
In my previous two posts, I talked about Self as the language, and the system which allows it to serialize objects into text format. Now let’s talk about another big part of Self, which is the programming environment and the UI toolkit that it is created from: Morphic.
Self Transporter: how to share your Self code
In my previous post, I talked about Self and gave a general tour of the language and the programming environment. I mentioned that Self was an image-based programming language. Of course, if you want to develop a Self application with multiple people, an image is not suitable for collaborating (except in real-time – we’ll come back to that in a later post). Your Self image contains everything in your programming environment (provided you don’t forget to save it!), including the active morphs you have on the screen, and stuff you are working on. Therefore, obviously, it is not possible to collaborate using your world images on version control.
So then the question becomes “How can we extract just the parts of our application so that they can be re-imported to other Self worlds independently?” And the answer to that is the Transporter system.