Skip to content

Instantly share code, notes, and snippets.

@matthewd
Created April 9, 2010 20:00
Show Gist options
  • Save matthewd/361526 to your computer and use it in GitHub Desktop.
Save matthewd/361526 to your computer and use it in GitHub Desktop.
From 78de83ee04902e003647369c55dd85ff1b58238f Mon Sep 17 00:00:00 2001
From: Matthew Draper <matthew@trebex.net>
Date: Sat, 10 Apr 2010 05:29:35 +0930
Subject: [PATCH] Revised 'add a builtin class' howto
---
doc/howto/add_a_builtin_class.txt | 213 ++++++++++++++++++++++---------------
1 files changed, 128 insertions(+), 85 deletions(-)
diff --git a/doc/howto/add_a_builtin_class.txt b/doc/howto/add_a_builtin_class.txt
index 329d68c..f1ddecf 100644
--- a/doc/howto/add_a_builtin_class.txt
+++ b/doc/howto/add_a_builtin_class.txt
@@ -1,17 +1,16 @@
# Howto - Add a Builtin Class to the VM
-TODO: Imported from Lighthouse wiki. Needs review and update.
+TODO: Imported from Lighthouse wiki. Updated; needs review.
The following documents adding the C++ class `Dir` to the VM. This class
provides primitives and other functionality used by the Ruby `Dir` class.
-The following lines of code are taken from this commit: 9488e4ac00. The line
-numbers (where shown) should be accurate for `git show 9488e4ac00`.
+The following lines of code were originally taken from commit 9488e4ac00. They
+have since been modified to reflect changes to this procedure.
All the C++ classes that correspond to Ruby classes are currently in files
-named like `builtin_someclass.[hc]pp`. In this case, we added
-`builtin_dir.hpp` and `builtin_dir.cpp`. This may be restructured into a
-subdirectory like `builtin/dir.cpp` so check the current source.
+named like `builtin/someclass.[hc]pp`. In this case, we added
+`builtin/dir.hpp` and `builtin/dir.cpp`.
All the header files are guarded with preprocessor directives to prevent
multiple inclusion. The format of the constant is shown below.
@@ -19,150 +18,194 @@ multiple inclusion. The format of the constant is shown below.
#ifndef RBX_BUILTIN_DIR_HPP
#define RBX_BUILTIN_DIR_HPP
-The C++ code for the VM uses the namespace `rubinius`. The variable `fields`
-identifies the number of fields in this object following the header, and
-`object_type` is a unique identifier for this Ruby class.
+The C++ code for the VM uses the namespace `rubinius`. The `object_type` is a
+unique identifier for this Ruby class.
The fields for this class are identified by the <code>// slot</code> comment
-that follows. In this case, there is one field, `data`. The `data` field will
-contain a `MemoryPointer` instance that carries the <code>DIR*</code>
-(directory stream) for the open directory. `data` will be accessible from
-normal Ruby code as an instance of `MemoryPointer` (or `nil` if the directory
-is not open).
+that follows. In this case, there is one field, `path_`. The `path_` field
+will contain a `String` instance that carries the path name for the open
+directory, which is then made accessible via attr_accessor.
+
+This class also contains an internal DIR* (directory stream) for the open
+directory. This pointer will not be accessible from normal Ruby code.
namespace rubinius {
class Dir : public Object {
public:
- const static size_t fields = 1;
- const static object_type type = DirType;
+ const static object_type type = DirType;
- MemoryPointer* data; // slot
+ private:
+ DIR *os_;
+ String *path_; // slot
-Following the field declarations, there are prototypes for the `Dir` methods.
-Several of these are annotated as primitives.
-
- static Dir* create(STATE);
- // Ruby.primitive :dir_open
- OBJECT open(STATE, String *path);
- // Ruby.primitive :dir_close
- OBJECT close(STATE);
- // Ruby.primitive :dir_closed_p
- OBJECT closed_p(STATE);
- // Ruby.primitive :dir_read
- OBJECT read(STATE);
- // Ruby.primitive :dir_control
- OBJECT control(STATE, FIXNUM kind, INTEGER pos);
- void guard(STATE);
-
- class Info : public TypeInfo {
public:
- BASIC_TYPEINFO(TypeInfo)
- };
+ attr_accessor(path, String);
+
+Following the field declarations, there are prototypes for the `Dir` methods.
+Several of these are annotated as primitives. The only method whose name is
+significant is the `init` static method, which is responsible for defining the
+class and object type.
+
+ static void init(STATE);
+
+ static Dir* create(STATE);
+
+ static void finalize(STATE, Dir* dir);
+
+ // Ruby.primitive :dir_allocate
+ static Dir* allocate(STATE, Object* self);
+
+ // Ruby.primitive :dir_open
+ Object* open(STATE, String *path);
+
+ // Ruby.primitive :dir_close
+ Object* close(STATE);
+
+ // Ruby.primitive :dir_closed_p
+ Object* closed_p(STATE);
+
+ // Ruby.primitive :dir_read
+ Object* read(STATE);
+
+ // Ruby.primitive :dir_control
+ Object* control(STATE, Fixnum* kind, Integer* pos);
+ void guard(STATE);
+
+ class Info : public TypeInfo {
+ public:
+ BASIC_TYPEINFO(TypeInfo)
+ };
};
};
#endif
-The rest of the (not yet complete) implementation for `Dir` is in
-`builtin_dir.cpp`. Note that in the <code>Dir::create</code> sets the `data`
-field to `Qnil` (nil in Ruby code). The <code>Dir::open</code> method sets
-`data` to a new `MemoryPointer` instance. <code>Dir::read</code>,
-<code>Dir::control</code>, and <code>Dir::close</code> reference the `DIR*`
-stored in the `data` field.
+The rest of the implementation for `Dir` is in `builtin/dir.cpp`.
- #include "builtin.hpp"
- #include "builtin_dir.hpp"
+Note that `init` defines the `Dir` class within the VM, and registers its
+object_type. `create` and `allocate` (the latter a primitive) deal with
+creation of object instances, along with registering the finalizer for each
+object as it's created.
+
+ #include "builtin/dir.hpp"
#include "ffi.hpp"
+ // [..]
#include <sys/types.h>
#include <dirent.h>
namespace rubinius {
+ void Dir::init(STATE) {
+ GO(dir).set(state->new_class("Dir", G(object)));
+ G(dir)->set_object_type(state, DirType);
+ }
+
Dir* Dir::create(STATE) {
- Dir* d = (Dir*)state->om->new_object(G(dir), Dir::fields);
- SET(d, data, Qnil);
+ Dir* d = state->new_object<Dir>(G(dir));
+ d->os_ = 0;
+
+ state->om->needs_finalization(d, (FinalizerFunction)&Dir::finalize);
return d;
}
+ Dir* Dir::allocate(STATE, Object* self) {
+ Dir* dir = create(state);
+
+ if(Class* cls = try_as<Class>(self)) {
+ dir->klass(state, cls);
+ }
+
+ return dir;
+ }
+
+ void Dir::finalize(STATE, Dir* dir) {
+ if(dir->os_) {
+ closedir(dir->os_);
+ dir->os_ = 0;
+ }
+ }
+
+
void Dir::guard(STATE) {
- // TODO: raise IOError, "closed directory"
- if(data->nil_p()) {
- throw std::runtime_error("dir->data is nil");
+ if(!os_) {
+ Exception::io_error(state, "closed directory");
}
}
- OBJECT Dir::open(STATE, String* path) {
- DIR* d = opendir(path->byte_address(state));
+ Object* Dir::open(STATE, String* path) {
+ if(os_) closedir(os_);
- if(!d) state->raise_from_errno("Unable to open directory");
- SET(this, data, MemoryPointer::create(state, d));
+ os_ = opendir(path->c_str());
- return Qnil;
+ if(!os_) {
+ Exception::errno_error(state, "Unable to open directory", errno, path->c_str());
+ return 0;
+ }
+
+ this->path(state, path);
+
+ return Qtrue;
}
- OBJECT Dir::close(STATE) {
+ Object* Dir::close(STATE) {
guard(state);
- DIR* d = (DIR*)data->pointer;
- if(d) {
- SET(this, data, Qnil);
- closedir(d);
- return Qtrue;
+ if(os_) {
+ closedir(os_);
+ os_ = 0;
}
- return Qfalse;
+ return Qnil;
}
- OBJECT Dir::closed_p(STATE) {
- return data->nil_p() ? Qtrue : Qfalse;
+ Object* Dir::closed_p(STATE) {
+ return os_ ? Qfalse : Qtrue;
}
- OBJECT Dir::read(STATE) {
+ Object* Dir::read(STATE) {
guard(state);
- DIR* d = (DIR*)data->pointer;
- struct dirent *ent = readdir(d);
+ struct dirent *ent = readdir(os_);
if(!ent) return Qnil;
return String::create(state, ent->d_name);
}
- OBJECT Dir::control(STATE, FIXNUM kind, INTEGER pos) {
+ Object* Dir::control(STATE, Fixnum* kind, Integer* pos) {
guard(state);
+ switch(kind->to_native()) {
+ case 0:
+ seekdir(os_, pos->to_native());
+ return Qtrue;
+ case 1:
+ rewinddir(os_);
+ return Qtrue;
+ case 2:
+ return Integer::from(state, telldir(os_));
+ }
+ return Qnil;
}
}
-In `object_types.hpp` we add our `DirType` to the types enumeration.
-
- 169 DirType
-
-In `builtin/object.cpp` we add an entry to convert `DirType` into a text
-representation "Dir".
-
- 124 case DirType:
- 125 type = "Dir";
- 126 break;
-
-In `globals.hpp` we appease the GC gods (TODO: explain this).
+In `globals.hpp` we define a global GC root to hold the class, so it doesn't
+get GCed.
147 TypedRoot<Class*> dir;
...
157 dir(&roots)
-In `objects.cpp` we bootstrap the `Dir` class.
+In `rakelib/vm.rake` we add our new header file.
- 181 GO(dir).set(new_class(object, Dir::fields));
- 182 G(dir)->instance_type = Object::i2n(DirType);
- ...
- 191 G(dir)->setup(this, "Dir");
+ 101 vm/builtin/dir.hpp
One of the great things about the new C++ VM is that there are tests. We added
tests for `Dir` in <code>test/test_dir.hpp</code>.
- #include "builtin_dir.hpp"
+ #include "vm/test/test.hpp"
+ #include "builtin/dir.hpp"
#include <cstdio>
#include <sys/stat.h>
--
1.7.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment