Skip to content

Commit

Permalink
Added Time C++ class and primitives.
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Ford committed Aug 22, 2008
1 parent 1686604 commit 44d61f8
Show file tree
Hide file tree
Showing 12 changed files with 240 additions and 38 deletions.
28 changes: 16 additions & 12 deletions kernel/bootstrap/time.rb
@@ -1,22 +1,26 @@
class Time
def self.gettimeofday
Ruby.primitive :gettimeofday
raise PrimitiveFailure, "primitive failed"
def self.allocate
Ruby.primitive :time_allocate
raise PrimitiveFailure, "Time.allocate primitive failed"
end


def gettimeofday
Ruby.primitive :time_gettimeofday
raise PrimitiveFailure, "Time#gettimeofday primitive failed"
end

def time_switch(sec, to_gmt)
Ruby.primitive :time_switch
raise PrimitiveFailure, "primitive failed"
raise PrimitiveFailure, "Time#time_switch primitive failed"
end

def time_mktime(sec, min, hour, mday, mon, year, usec, isdst, from_gmt)
Ruby.primitive :mktime
Ruby.primitive :time_mktime
raise ArgumentError, "time out of range"
end
def __strftime(tm, format)
Ruby.primitive :strftime
raise PrimitiveFailure, "primitive failed"

def __strftime__(tm, format)
Ruby.primitive :time_strftime
raise PrimitiveFailure, "Time#__strftime__ primitive failed"
end

end
42 changes: 19 additions & 23 deletions kernel/core/time.rb
Expand Up @@ -54,16 +54,6 @@ class Time
:usec => 1,
}

def initialize
@timeval = Time.gettimeofday

# this flag specifies whether this Time instance represents
# local time or GMT. it is independent of the actual time zone.
@is_gmt = false

@tm = time_switch(@timeval.first, false)
end

#--
# TODO: doesn't load nsec or ivars
#++
Expand Down Expand Up @@ -101,26 +91,32 @@ def self._load(data)
#++

def _dump(limit = nil)
tm = time_switch @timeval.first, true
year = tm[TM_FIELDS[:year]]
tm = @tm
is_gmt = @is_gmt

time_switch true
year = @tm[TM_FIELDS[:year]]

if (year & 0xffff) != year then
raise ArgumentError, "year too big to marshal: #{year}"
end

gmt = @is_gmt ? 1 : 0

major = 1 << 31 | # 1 bit
gmt << 30 | # 1 bit
tm[TM_FIELDS[:year]] << 14 | # 16 bits
tm[TM_FIELDS[:mon]] << 10 | # 4 bits
tm[TM_FIELDS[:mday]] << 5 | # 5 bits
tm[TM_FIELDS[:hour]] # 5 bits
minor = tm[TM_FIELDS[:min]] << 26 | # 6 bits
tm[TM_FIELDS[:sec]] << 20 | # 6 bits
major = 1 << 31 | # 1 bit
(@is_gmt ? 1 : 0) << 30 | # 1 bit
@tm[TM_FIELDS[:year]] << 14 | # 16 bits
@tm[TM_FIELDS[:mon]] << 10 | # 4 bits
@tm[TM_FIELDS[:mday]] << 5 | # 5 bits
@tm[TM_FIELDS[:hour]] # 5 bits
minor = @tm[TM_FIELDS[:min]] << 26 | # 6 bits
@tm[TM_FIELDS[:sec]] << 20 | # 6 bits
@timeval[TIMEVAL_FIELDS[:usec]] # 20 bits

[major, minor].pack 'VV'
ensure
@tm = tm
@is_gmt = is_gmt
end

def dup
Expand Down Expand Up @@ -202,7 +198,7 @@ def self.at(secs_or_time, msecs = nil)
end

def strftime(format)
__strftime(@tm, format.to_str)
__strftime__(@tm, format.to_str)
end

def inspect
Expand Down Expand Up @@ -376,14 +372,14 @@ def hash
end

def force_localtime
@tm = time_switch(@timeval.first, false)
time_switch false
@is_gmt = false

self
end

def force_gmtime
@tm = time_switch(@timeval.first, true)
time_switch true
@is_gmt = true

self
Expand Down
1 change: 1 addition & 0 deletions rakelib/vm.rake
Expand Up @@ -79,6 +79,7 @@ field_extract_headers = %w[
vm/builtin/thread.hpp
vm/builtin/tuple.hpp
vm/builtin/compactlookuptable.hpp
vm/builtin/time.hpp
]

BC = "vm/instructions.bc"
Expand Down
3 changes: 3 additions & 0 deletions vm/builtin/object.cpp
Expand Up @@ -240,6 +240,9 @@ namespace rubinius {
case CompactLookupTableType:
type = "CompactLookupTable";
break;
case TimeType:
type = "Time";
break;
default:
type = "unknown";
break;
Expand Down
76 changes: 76 additions & 0 deletions vm/builtin/time.cpp
@@ -0,0 +1,76 @@
#include "builtin/array.hpp"
#include "builtin/integer.hpp"
#include "builtin/time.hpp"
#include "objectmemory.hpp"

#include <sys/time.h>
#include <time.h>

namespace rubinius {
Time* Time::create(STATE) {
Time* tm = (Time*)state->om->new_object(G(time_class), Time::fields);

tm->gettimeofday(state);
tm->time_switch(state, Qfalse);

return tm;
}

Time* Time::gettimeofday(STATE) {
struct timeval tv;

/* don't fill in the 2nd argument here. getting the timezone here
* this way is not portable and broken anyway.
*/
::gettimeofday(&tv, NULL);

/* update Time::TIMEVAL_FIELDS when changing order of fields */
Array* ary = Array::create(state, 2);
ary->set(state, 0, Integer::from(state, tv.tv_sec));
ary->set(state, 1, Integer::from(state, tv.tv_usec));

SET(this, timeval, ary);

return this;
}

Time* Time::time_switch(STATE, OBJECT gmt) {
time_t seconds = ((Integer*)this->timeval->get(state, 0))->to_native();
struct tm *tm;

if(gmt == Qtrue) {
tm = gmtime(&seconds);
} else {
tm = localtime(&seconds);
}

/* update Time::TM_FIELDS when changing order of fields */
Array* ary = Array::create(state, 11);
ary->set(state, 0, Integer::from(state, tm->tm_sec));
ary->set(state, 1, Integer::from(state, tm->tm_min));
ary->set(state, 2, Integer::from(state, tm->tm_hour));
ary->set(state, 3, Integer::from(state, tm->tm_mday));
ary->set(state, 4, Integer::from(state, tm->tm_mon));
ary->set(state, 5, Integer::from(state, tm->tm_year));
ary->set(state, 6, Integer::from(state, tm->tm_wday));
ary->set(state, 7, Integer::from(state, tm->tm_yday));
ary->set(state, 8, Integer::from(state, tm->tm_isdst));

#ifdef HAVE_STRUCT_TM_TM_GMTOFF
ary->set(state, 9, Integer::from(state, tm->tm_gmtoff));
#else
ary->set(state, 9, Qnil);
#endif

#ifdef HAVE_STRUCT_TM_TM_ZONE
ary->set(state, 10, String::create(state, tm->tm_zone));
#else
ary->set(state, 10, Qnil);
#endif

SET(this, tm, ary);
SET(this, is_gmt, gmt);

return this;
}
}
35 changes: 35 additions & 0 deletions vm/builtin/time.hpp
@@ -0,0 +1,35 @@
#ifndef RBX_BUILTIN_TIME_HPP
#define RBX_BUILTIN_TIME_HPP

#include "builtin/object.hpp"
#include "type_info.hpp"

namespace rubinius {
class Array;

class Time : public Object {
public:
const static size_t fields = 3;
const static object_type type = TimeType;

Array* timeval; // slot
Array* tm; // slot
OBJECT is_gmt; // slot

// Ruby.primitive :time_allocate
static Time* create(STATE);

// Ruby.primitive :time_gettimeofday
Time* gettimeofday(STATE);

// Ruby.primitive :time_witch
Time* time_switch(STATE, OBJECT gmt);

class Info : public TypeInfo {
public:
BASIC_TYPEINFO(TypeInfo)
};
};
};

#endif
4 changes: 3 additions & 1 deletion vm/globals.hpp
Expand Up @@ -65,6 +65,7 @@ namespace rubinius {
TypedRoot<Class*> compactlookuptable;
TypedRoot<Class*> access_variable;
TypedRoot<Module*> rubinius;
TypedRoot<Class*> time_class;

/* Add new globals above this line. */

Expand Down Expand Up @@ -161,7 +162,8 @@ namespace rubinius {
dir(&roots),
compactlookuptable(&roots),
access_variable(&roots),
rubinius(&roots)
rubinius(&roots),
time_class(&roots)

/* Add initialize of globals above this line. */
{ }
Expand Down
1 change: 1 addition & 0 deletions vm/object_types.hpp
Expand Up @@ -54,6 +54,7 @@ namespace rubinius {
DirType ,
CompactLookupTableType,
AccessVariableType,
TimeType ,

LastObjectType // must remain at end
} object_type;
Expand Down
14 changes: 13 additions & 1 deletion vm/objects.cpp
Expand Up @@ -4,6 +4,7 @@
#include "objectmemory.hpp"
#include "vm.hpp"

#include "builtin/access_variable.hpp"
#include "builtin/array.hpp"
#include "builtin/block_environment.hpp"
#include "builtin/class.hpp"
Expand All @@ -26,8 +27,8 @@
#include "builtin/symbol.hpp"
#include "builtin/task.hpp"
#include "builtin/thread.hpp"
#include "builtin/time.hpp"
#include "builtin/tuple.hpp"
#include "builtin/access_variable.hpp"

#define SPECIAL_CLASS_MASK 0x1f
#define SPECIAL_CLASS_SIZE 32
Expand Down Expand Up @@ -65,31 +66,37 @@ namespace rubinius {
return cls;
}

// TODO: document
Class* VM::new_class(OBJECT sup, size_t fields) {
Class *cls = new_basic_class(sup, fields);
MetaClass::attach(this, cls);
return cls;
}

// TODO: document
Class* VM::new_class(const char* name) {
return new_class(name, G(object), G(object)->instance_fields->to_native());
}

// TODO: document
Class* VM::new_class(const char* name, size_t fields) {
return new_class(name, G(object), fields);
}

// TODO: document
Class* VM::new_class(const char* name, Module* under) {
size_t fields = G(object)->instance_fields->to_native();
Class* cls = new_class(name, G(object), fields);
cls->setup(this, name, under);
return cls;
}

// TODO: document
Class* VM::new_class(const char* name, OBJECT sup, size_t fields) {
return new_class(name, sup, fields, G(object));
}

// TODO: document
Class* VM::new_class(const char* name, OBJECT sup, size_t fields, Module* under) {
Class* cls = new_class(sup, fields);
cls->setup(this, name, under);
Expand All @@ -102,6 +109,7 @@ namespace rubinius {
return mod;
}

// TODO: document all the sections of bootstrap_ontology
/* Creates the rubinius object universe from scratch. */
void VM::bootstrap_ontology() {
/* Class is created first by hand, and twittle to setup the internal
Expand Down Expand Up @@ -182,6 +190,9 @@ namespace rubinius {
GO(compactlookuptable).set(new_class(G(tuple), CompactLookupTable::fields));
G(compactlookuptable)->instance_type = Fixnum::from(CompactLookupTableType);

GO(time_class).set(new_class(object, Time::fields));
G(time_class)->instance_type = Fixnum::from(TimeType);

bootstrap_symbol();

G(object)->setup(this, "Object");
Expand All @@ -203,6 +214,7 @@ namespace rubinius {
G(symbol)->setup(this, "Symbol");
G(dir)->setup(this, "Dir");
G(compactlookuptable)->setup(this, "CompactLookupTable");
G(time_class)->setup(this, "Time");

GO(nil_class).set(new_class("NilClass", object, 0));
GO(true_class).set(new_class("TrueClass", object, 0));
Expand Down
11 changes: 11 additions & 0 deletions vm/test/test_objects.hpp
Expand Up @@ -174,4 +174,15 @@ class TestObjects : public CxxTest::TestSuite {
TS_ASSERT_EQUALS(cls->instance_type, Fixnum::from(CompactLookupTableType));
check_const(compactlookuptable, "CompactLookupTable");
}

void test_time_class() {
Class *cls;

cls = G(time_class);

TS_ASSERT_EQUALS(cls->class_object(state), G(klass));
TS_ASSERT_EQUALS(cls->superclass, G(object));
TS_ASSERT_EQUALS(cls->instance_type, Fixnum::from(TimeType));
check_const(time_class, "Time");
}
};

0 comments on commit 44d61f8

Please sign in to comment.