From 1ceabdeb2bb4e65bf77c6594beb9872be8b0d711 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Wed, 3 May 2017 11:44:35 -0700 Subject: [PATCH 01/10] Initial work on ruby::Value Soon, we will have more functionality in ruby::Value as well as other types like ruby::Array, ruby::Hash, etc. --- Cargo.toml | 2 +- examples/primitive/.gitignore | 3 ++ examples/primitive/Cargo.toml | 11 +++++ examples/primitive/Gemfile | 5 ++ examples/primitive/Gemfile.lock | 39 ++++++++++++++++ examples/primitive/Rakefile | 20 ++++++++ examples/primitive/lib/primitive.rb | 2 + examples/primitive/spec/primitive_spec.rb | 23 +++++++++ examples/primitive/spec/spec_helper.rb | 2 + examples/primitive/src/lib.rs | 11 +++++ src/coercions/bool.rs | 4 +- src/coercions/float.rs | 4 +- src/coercions/integers.rs | 16 +++---- src/coercions/mod.rs | 8 ++-- src/coercions/option.rs | 6 +-- src/coercions/string.rs | 4 +- src/coercions/unit.rs | 2 +- src/lib.rs | 1 + src/macros/coercions.rs | 4 +- src/macros/init.rs | 4 +- src/ruby/mod.rs | 5 ++ src/ruby/types.rs | 57 +++++++++++++++++++++++ src/ruby/value.rs | 36 ++++++++++++++ 23 files changed, 243 insertions(+), 26 deletions(-) create mode 100644 examples/primitive/.gitignore create mode 100644 examples/primitive/Cargo.toml create mode 100644 examples/primitive/Gemfile create mode 100644 examples/primitive/Gemfile.lock create mode 100755 examples/primitive/Rakefile create mode 100644 examples/primitive/lib/primitive.rb create mode 100644 examples/primitive/spec/primitive_spec.rb create mode 100644 examples/primitive/spec/spec_helper.rb create mode 100644 examples/primitive/src/lib.rs create mode 100644 src/ruby/mod.rs create mode 100644 src/ruby/types.rs create mode 100644 src/ruby/value.rs diff --git a/Cargo.toml b/Cargo.toml index 5cf773bc..5e55f43b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ appveyor = { repository = "tildeio/helix", branch = "master", service = "github" [workspace] -members = ["examples/calculator", "examples/console", "examples/duration", "examples/membership", "examples/text_transform", "examples/turbo_blank"] +members = ["examples/primitive", "examples/calculator", "examples/console", "examples/duration", "examples/membership", "examples/text_transform", "examples/turbo_blank"] [dependencies] libc = "0.2.0" diff --git a/examples/primitive/.gitignore b/examples/primitive/.gitignore new file mode 100644 index 00000000..4268bc75 --- /dev/null +++ b/examples/primitive/.gitignore @@ -0,0 +1,3 @@ +target +*.bundle +*.gem diff --git a/examples/primitive/Cargo.toml b/examples/primitive/Cargo.toml new file mode 100644 index 00000000..fc57c1c3 --- /dev/null +++ b/examples/primitive/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "primitive" +version = "0.1.0" +authors = ["Peter Wagenet "] + +[lib] + +crate-type = ["cdylib"] + +[dependencies.helix] +path = "../.." diff --git a/examples/primitive/Gemfile b/examples/primitive/Gemfile new file mode 100644 index 00000000..36c54510 --- /dev/null +++ b/examples/primitive/Gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'helix_runtime', path: '../../ruby' +gem 'rake', '~> 10.0' +gem 'rspec', '~> 3.4' diff --git a/examples/primitive/Gemfile.lock b/examples/primitive/Gemfile.lock new file mode 100644 index 00000000..0cd1b4be --- /dev/null +++ b/examples/primitive/Gemfile.lock @@ -0,0 +1,39 @@ +PATH + remote: ../../ruby + specs: + helix_runtime (0.5.0) + rake (>= 10.0) + thor (~> 0.19.4) + +GEM + remote: https://rubygems.org/ + specs: + diff-lcs (1.3) + rake (10.5.0) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.4) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-mocks (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) + thor (0.19.4) + +PLATFORMS + ruby + x64-mingw32 + x86-mingw32 + +DEPENDENCIES + helix_runtime! + rake (~> 10.0) + rspec (~> 3.4) + +BUNDLED WITH + 1.14.6 diff --git a/examples/primitive/Rakefile b/examples/primitive/Rakefile new file mode 100755 index 00000000..354b075a --- /dev/null +++ b/examples/primitive/Rakefile @@ -0,0 +1,20 @@ +require 'bundler/setup' +require 'helix_runtime/build_task' +require 'rspec/core/rake_task' +require_relative '../shared.rb' + +# For Windows +$stdout.sync = true + +HelixRuntime::BuildTask.new("primitive") do |t| + t.build_root = File.expand_path("../..", __dir__) + t.helix_lib_dir = File.expand_path("../../ruby/windows_build", __dir__) + t.pre_build = HelixRuntime::Tests.pre_build +end + +RSpec::Core::RakeTask.new(:spec) do |t| + t.verbose = false +end + +task :spec => :build +task :default => :spec diff --git a/examples/primitive/lib/primitive.rb b/examples/primitive/lib/primitive.rb new file mode 100644 index 00000000..6350743d --- /dev/null +++ b/examples/primitive/lib/primitive.rb @@ -0,0 +1,2 @@ +require 'helix_runtime' +require 'primitive/native' diff --git a/examples/primitive/spec/primitive_spec.rb b/examples/primitive/spec/primitive_spec.rb new file mode 100644 index 00000000..58aa19fe --- /dev/null +++ b/examples/primitive/spec/primitive_spec.rb @@ -0,0 +1,23 @@ +require "spec_helper" + +describe Primitive do + describe "#is_bool" do + cases = { + true => true, + false => true, + nil => false, + "true" => false, + "" => false, + 0 => false, + 0.5 => false, + {} => false, + Object.new => false + } + + cases.each do |test, expected| + it "#{test.inspect} => #{expected}" do + expect(Primitive.is_bool(test)).to eq(expected) + end + end + end +end diff --git a/examples/primitive/spec/spec_helper.rb b/examples/primitive/spec/spec_helper.rb new file mode 100644 index 00000000..93368651 --- /dev/null +++ b/examples/primitive/spec/spec_helper.rb @@ -0,0 +1,2 @@ +$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +require 'primitive' diff --git a/examples/primitive/src/lib.rs b/examples/primitive/src/lib.rs new file mode 100644 index 00000000..ea44f125 --- /dev/null +++ b/examples/primitive/src/lib.rs @@ -0,0 +1,11 @@ +#[macro_use] +extern crate helix; +use helix::ruby; + +ruby! { + class Primitive { + def is_bool(value: ruby::Value) -> bool { + value.is_type(ruby::Type::True) || value.is_type(ruby::Type::False) + } + } +} diff --git a/src/coercions/bool.rs b/src/coercions/bool.rs index 98c0cbf8..98fd8431 100644 --- a/src/coercions/bool.rs +++ b/src/coercions/bool.rs @@ -23,8 +23,8 @@ impl ToRust for CheckedValue { } impl ToRuby for bool { - fn to_ruby(self) -> VALUE { - if self { + fn to_ruby(&self) -> VALUE { + if *self { unsafe { Qtrue } } else { unsafe { Qfalse } diff --git a/src/coercions/float.rs b/src/coercions/float.rs index 17e92cb3..30542503 100644 --- a/src/coercions/float.rs +++ b/src/coercions/float.rs @@ -20,7 +20,7 @@ impl ToRust for CheckedValue { } impl ToRuby for f64 { - fn to_ruby(self) -> VALUE { - unsafe { sys::F642NUM(self) } + fn to_ruby(&self) -> VALUE { + unsafe { sys::F642NUM(*self) } } } diff --git a/src/coercions/integers.rs b/src/coercions/integers.rs index 4a237883..cab4a08e 100644 --- a/src/coercions/integers.rs +++ b/src/coercions/integers.rs @@ -20,8 +20,8 @@ impl ToRust for CheckedValue { } impl ToRuby for u64 { - fn to_ruby(self) -> VALUE { - unsafe { sys::U642NUM(self) } + fn to_ruby(&self) -> VALUE { + unsafe { sys::U642NUM(*self) } } } @@ -43,8 +43,8 @@ impl ToRust for CheckedValue { } impl ToRuby for i64 { - fn to_ruby(self) -> VALUE { - unsafe { sys::I642NUM(self) } + fn to_ruby(&self) -> VALUE { + unsafe { sys::I642NUM(*self) } } } @@ -66,8 +66,8 @@ impl ToRust for CheckedValue { } impl ToRuby for u32 { - fn to_ruby(self) -> VALUE { - unsafe { sys::U322NUM(self) } + fn to_ruby(&self) -> VALUE { + unsafe { sys::U322NUM(*self) } } } @@ -89,7 +89,7 @@ impl ToRust for CheckedValue { } impl ToRuby for i32 { - fn to_ruby(self) -> VALUE { - unsafe { sys::I322NUM(self) } + fn to_ruby(&self) -> VALUE { + unsafe { sys::I322NUM(*self) } } } diff --git a/src/coercions/mod.rs b/src/coercions/mod.rs index b7aee43b..23bbac12 100644 --- a/src/coercions/mod.rs +++ b/src/coercions/mod.rs @@ -15,6 +15,8 @@ pub struct CheckedValue { } impl CheckedValue { + // This is unsafe because it's the primary way that the coercion + // protocol asserts that subsequent operations are safe pub unsafe fn new(inner: VALUE) -> CheckedValue { CheckedValue { inner: inner, marker: PhantomData } } @@ -31,11 +33,11 @@ pub trait ToRust { } pub trait ToRuby { - fn to_ruby(self) -> VALUE; + fn to_ruby(&self) -> VALUE; } impl ToRuby for VALUE { - fn to_ruby(self) -> VALUE { - self + fn to_ruby(&self) -> VALUE { + *self } } diff --git a/src/coercions/option.rs b/src/coercions/option.rs index 9001fd98..a8205f6e 100644 --- a/src/coercions/option.rs +++ b/src/coercions/option.rs @@ -26,9 +26,9 @@ impl ToRust> for CheckedValue> where CheckedValue: ToR } impl ToRuby for Option where T: ToRuby { - fn to_ruby(self) -> VALUE { - match self { - Some(value) => value.to_ruby(), + fn to_ruby(&self) -> VALUE { + match *self { + Some(ref value) => value.to_ruby(), None => unsafe { Qnil } } } diff --git a/src/coercions/string.rs b/src/coercions/string.rs index 87fad11a..8b6222f7 100644 --- a/src/coercions/string.rs +++ b/src/coercions/string.rs @@ -28,7 +28,7 @@ impl ToRust for CheckedValue { } impl ToRuby for String { - fn to_ruby(self) -> VALUE { + fn to_ruby(&self) -> VALUE { let ptr = self.as_ptr(); let len = self.len(); unsafe { sys::rb_utf8_str_new(ptr as *const libc::c_char, len as libc::c_long) } @@ -36,7 +36,7 @@ impl ToRuby for String { } impl<'a> ToRuby for &'a str { - fn to_ruby(self) -> VALUE { + fn to_ruby(&self) -> VALUE { let ptr = self.as_ptr(); let len = self.len(); unsafe { sys::rb_utf8_str_new(ptr as *const libc::c_char, len as libc::c_long) } diff --git a/src/coercions/unit.rs b/src/coercions/unit.rs index d7c8a65e..1c7b00d5 100644 --- a/src/coercions/unit.rs +++ b/src/coercions/unit.rs @@ -2,7 +2,7 @@ use sys::{self, VALUE}; use ToRuby; impl ToRuby for () { - fn to_ruby(self) -> VALUE { + fn to_ruby(&self) -> VALUE { unsafe { sys::Qnil } } } diff --git a/src/lib.rs b/src/lib.rs index f11b2a35..ab6d533b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ use sys::VALUE; mod macros; mod class_definition; mod coercions; +pub mod ruby; pub use coercions::*; diff --git a/src/macros/coercions.rs b/src/macros/coercions.rs index 14398044..4e3a3e09 100644 --- a/src/macros/coercions.rs +++ b/src/macros/coercions.rs @@ -59,7 +59,7 @@ macro_rules! impl_to_ruby { ($cls:ty) => { item! { impl<'a> $crate::ToRuby for $cls { - fn to_ruby(self) -> $crate::sys::VALUE { + fn to_ruby(&self) -> $crate::sys::VALUE { self.helix } } @@ -94,4 +94,4 @@ macro_rules! impl_struct_to_rust { } } } -} \ No newline at end of file +} diff --git a/src/macros/init.rs b/src/macros/init.rs index e462ae85..c39d809f 100644 --- a/src/macros/init.rs +++ b/src/macros/init.rs @@ -113,7 +113,7 @@ macro_rules! codegen_define_method { let checked = __checked_call__($($arg),*); match checked { - Ok(val) => CallResult { error_klass: unsafe { Qnil }, value: $crate::ToRuby::to_ruby(val) }, + Ok(ref val) => CallResult { error_klass: unsafe { Qnil }, value: $crate::ToRuby::to_ruby(val) }, Err(err) => CallResult { error_klass: err.exception.inner(), value: err.message } } } @@ -184,7 +184,7 @@ macro_rules! codegen_define_method { let checked = __checked_call__(rb_self, $($arg),*); match checked { - Ok(val) => CallResult { error_klass: unsafe { Qnil }, value: $crate::ToRuby::to_ruby(val) }, + Ok(ref val) => CallResult { error_klass: unsafe { Qnil }, value: $crate::ToRuby::to_ruby(val) }, Err(err) => CallResult { error_klass: err.exception.inner(), value: err.message } } } diff --git a/src/ruby/mod.rs b/src/ruby/mod.rs new file mode 100644 index 00000000..e591f39e --- /dev/null +++ b/src/ruby/mod.rs @@ -0,0 +1,5 @@ +mod value; +mod types; + +pub use self::value::{Value}; +pub use self::types::{Type}; diff --git a/src/ruby/types.rs b/src/ruby/types.rs new file mode 100644 index 00000000..9410cdea --- /dev/null +++ b/src/ruby/types.rs @@ -0,0 +1,57 @@ +use sys; +use ToRuby; +use super::Value; + +pub enum Type { + String, + Array, + True, + False, + Fixnum, + Bignum, + Float, + Unknown(isize) +} + +impl Type { + pub fn from(ty: isize) -> Type { + if ty == unsafe { sys::T_STRING } { + Type::String + } else if ty == unsafe { sys::T_ARRAY } { + Type::Array + } else if ty == unsafe { sys::T_TRUE } { + Type::True + } else if ty == unsafe { sys::T_FALSE } { + Type::False + } else if ty == unsafe { sys::T_FIXNUM } { + Type::Fixnum + } else if ty == unsafe { sys::T_BIGNUM } { + Type::Bignum + } else if ty == unsafe { sys::T_FLOAT } { + Type::Float + } else { + Type::Unknown(ty) + } + } + + pub fn of(val: &Value) -> Type { + Type::from(unsafe { sys::TYPE(val.to_ruby()) }) + } + + pub fn to_ruby(&self) -> isize { + match *self { + Type::String => unsafe { sys::T_STRING }, + Type::Array => unsafe { sys::T_ARRAY }, + Type::True => unsafe { sys::T_TRUE }, + Type::False => unsafe { sys::T_FALSE }, + Type::Fixnum => unsafe { sys::T_FIXNUM }, + Type::Bignum => unsafe { sys::T_BIGNUM }, + Type::Float => unsafe { sys::T_FLOAT }, + Type::Unknown(val) => val + } + } + + pub fn matches(&self, value: &Value) -> bool { + unsafe { sys::RB_TYPE_P(value.to_ruby(), self.to_ruby()) } + } +} diff --git a/src/ruby/value.rs b/src/ruby/value.rs new file mode 100644 index 00000000..a1f9353d --- /dev/null +++ b/src/ruby/value.rs @@ -0,0 +1,36 @@ +use sys::VALUE; +use super::Type; + +pub struct Value { + inner: VALUE +} + +impl Value { + pub fn is_type(&self, ty: Type) -> bool { + ty.matches(self) + } + + pub fn ruby_type(&self) -> Type { + Type::of(self) + } +} + +use coercions::*; + +impl ToRuby for Value { + fn to_ruby(&self) -> VALUE { + self.inner + } +} + +impl UncheckedValue for VALUE { + fn to_checked(self) -> CheckResult { + Ok(unsafe { CheckedValue::new(self) }) + } +} + +impl ToRust for CheckedValue { + fn to_rust(self) -> Value { + Value { inner: self.inner } + } +} From 896013569e045b48b12fd12d131b7d9535cdb10f Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Wed, 3 May 2017 17:44:57 -0700 Subject: [PATCH 02/10] Significant WIP refactor --- crates/libcruby-sys/src/lib.rs | 2 ++ examples/primitive/spec/primitive_spec.rb | 20 +++++++++++ examples/primitive/src/lib.rs | 8 +++++ src/coercions/bool.rs | 8 +++-- src/coercions/float.rs | 7 ++-- src/coercions/integers.rs | 25 ++++++++----- src/coercions/mod.rs | 23 +++++++++--- src/coercions/option.rs | 38 +++++++++++++------- src/coercions/slice.rs | 5 ++- src/coercions/string.rs | 7 ++-- src/macros/coercions.rs | 2 +- src/macros/init.rs | 2 ++ src/macros/mod.rs | 1 - src/ruby/array.rs | 44 +++++++++++++++++++++++ src/ruby/mod.rs | 2 ++ src/ruby/value.rs | 40 ++++++++++++++------- 16 files changed, 187 insertions(+), 47 deletions(-) create mode 100644 src/ruby/array.rs diff --git a/crates/libcruby-sys/src/lib.rs b/crates/libcruby-sys/src/lib.rs index f6f3b9c6..5e40a17b 100644 --- a/crates/libcruby-sys/src/lib.rs +++ b/crates/libcruby-sys/src/lib.rs @@ -158,6 +158,8 @@ extern "C" { #[link_name = "HELIX_T_BIGNUM"] pub static T_BIGNUM: isize; + pub fn rb_ary_aref(obj: VALUE, offset: libc::c_long) -> VALUE; + // unknown if working? // fn rb_define_variable(name: c_string, value: *const VALUE); pub fn rb_obj_class(obj: VALUE) -> VALUE; diff --git a/examples/primitive/spec/primitive_spec.rb b/examples/primitive/spec/primitive_spec.rb index 58aa19fe..2cfbf4f0 100644 --- a/examples/primitive/spec/primitive_spec.rb +++ b/examples/primitive/spec/primitive_spec.rb @@ -20,4 +20,24 @@ end end end + + describe "#as_bool" do + it "true" do + expect(Primitive.as_bool(true)).to eq(true) + end + + it "nil" do + expect(-> { Primitive.as_bool(nil) }).to raise_error(RuntimeError) + end + end + + describe "#first" do + it "[1, 2, 3]" do + expect(Primitive.first([1,2,3])).to eq(1) + end + + it "['a', 2, 'c']" do + expect(Primitive.first(['a', 2, 'c'])).to eq('a') + end + end end diff --git a/examples/primitive/src/lib.rs b/examples/primitive/src/lib.rs index ea44f125..68dc5554 100644 --- a/examples/primitive/src/lib.rs +++ b/examples/primitive/src/lib.rs @@ -7,5 +7,13 @@ ruby! { def is_bool(value: ruby::Value) -> bool { value.is_type(ruby::Type::True) || value.is_type(ruby::Type::False) } + + def as_bool(value: ruby::Value) -> bool { + value.to_rust() + } + + def first(ary: ruby::Array) -> ruby::Value { + ary[0] + } } } diff --git a/src/coercions/bool.rs b/src/coercions/bool.rs index 98fd8431..7b23e3e9 100644 --- a/src/coercions/bool.rs +++ b/src/coercions/bool.rs @@ -1,9 +1,11 @@ use sys::{self, VALUE, Qtrue, Qfalse}; - +use coercions::*; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; impl UncheckedValue for VALUE { - fn to_checked(self) -> CheckResult { + type ToRust = CheckedValue; + + fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, sys::T_TRUE) || sys::RB_TYPE_P(self, sys::T_FALSE) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -12,7 +14,7 @@ impl UncheckedValue for VALUE { } } -impl ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue { fn to_rust(self) -> bool { if self.inner == unsafe { Qtrue } { true diff --git a/src/coercions/float.rs b/src/coercions/float.rs index 30542503..9e086bc4 100644 --- a/src/coercions/float.rs +++ b/src/coercions/float.rs @@ -1,9 +1,12 @@ use sys::{self, VALUE, T_FLOAT, T_FIXNUM, T_BIGNUM}; +use coercions::*; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; impl UncheckedValue for VALUE { - fn to_checked(self) -> CheckResult { + type ToRust = CheckedValue; + + fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, T_FLOAT) || sys::RB_TYPE_P(self, T_FIXNUM) || sys::RB_TYPE_P(self, T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -13,7 +16,7 @@ impl UncheckedValue for VALUE { } } -impl ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue { fn to_rust(self) -> f64 { unsafe { sys::NUM2F64(self.inner) } } diff --git a/src/coercions/integers.rs b/src/coercions/integers.rs index cab4a08e..69f84a28 100644 --- a/src/coercions/integers.rs +++ b/src/coercions/integers.rs @@ -1,9 +1,12 @@ use sys::{self, VALUE, T_FIXNUM, T_BIGNUM}; +use coercions::*; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; impl UncheckedValue for VALUE { - fn to_checked(self) -> CheckResult { + type ToRust = CheckedValue; + + fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, T_FIXNUM) || sys::RB_TYPE_P(self, T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -13,7 +16,7 @@ impl UncheckedValue for VALUE { } } -impl ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue { fn to_rust(self) -> u64 { unsafe { sys::NUM2U64(self.inner) } } @@ -26,7 +29,9 @@ impl ToRuby for u64 { } impl UncheckedValue for VALUE { - fn to_checked(self) -> CheckResult { + type ToRust = CheckedValue; + + fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, sys::T_FIXNUM) || sys::RB_TYPE_P(self, sys::T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -36,7 +41,7 @@ impl UncheckedValue for VALUE { } } -impl ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue { fn to_rust(self) -> i64 { unsafe { sys::NUM2I64(self.inner) } } @@ -49,7 +54,9 @@ impl ToRuby for i64 { } impl UncheckedValue for VALUE { - fn to_checked(self) -> CheckResult { + type ToRust = CheckedValue; + + fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, T_FIXNUM) || sys::RB_TYPE_P(self, T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -59,7 +66,7 @@ impl UncheckedValue for VALUE { } } -impl ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue { fn to_rust(self) -> u32 { unsafe { sys::NUM2U32(self.inner) } } @@ -72,7 +79,9 @@ impl ToRuby for u32 { } impl UncheckedValue for VALUE { - fn to_checked(self) -> CheckResult { + type ToRust = CheckedValue; + + fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, sys::T_FIXNUM) || sys::RB_TYPE_P(self, sys::T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -82,7 +91,7 @@ impl UncheckedValue for VALUE { } } -impl ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue { fn to_rust(self) -> i32 { unsafe { sys::NUM2I32(self.inner) } } diff --git a/src/coercions/mod.rs b/src/coercions/mod.rs index 23bbac12..95c37abc 100644 --- a/src/coercions/mod.rs +++ b/src/coercions/mod.rs @@ -9,23 +9,36 @@ mod option; use sys::{VALUE}; use std::marker::PhantomData; +#[derive(Debug, Clone, Copy)] +pub struct CallFrame<'a> { + lifetime: &'a () +} + +impl<'a> CallFrame<'a> { + pub unsafe fn new<'lt>(lifetime: &'lt ()) -> CallFrame<'lt> { + CallFrame { lifetime } + } +} + pub struct CheckedValue { pub inner: VALUE, - marker: PhantomData + target: PhantomData } impl CheckedValue { // This is unsafe because it's the primary way that the coercion // protocol asserts that subsequent operations are safe - pub unsafe fn new(inner: VALUE) -> CheckedValue { - CheckedValue { inner: inner, marker: PhantomData } + pub unsafe fn new(inner: VALUE) -> CheckedValue { + CheckedValue { inner, target: PhantomData } } } -pub type CheckResult = Result, String>; +pub type CheckResult = Result; pub trait UncheckedValue { - fn to_checked(self) -> CheckResult; + type ToRust: ToRust; + + fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult; } pub trait ToRust { diff --git a/src/coercions/option.rs b/src/coercions/option.rs index a8205f6e..1907162d 100644 --- a/src/coercions/option.rs +++ b/src/coercions/option.rs @@ -1,26 +1,40 @@ +use coercions::*; use sys::{VALUE, Qnil}; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; +use ruby; -impl UncheckedValue> for VALUE where VALUE: UncheckedValue { - fn to_checked(self) -> CheckResult> { - if unsafe { self == Qnil } { - Ok(unsafe { CheckedValue::new(self) }) +impl ToRust for Box> { + fn to_rust(self) -> T { + self.to_rust() + } +} + +struct CheckedOption<'a> { + inner: ruby::Value<'a>, + present: bool +} + +impl<'a, T, U: ToRust> UncheckedValue> for ruby::Value<'a> where ruby::Value<'a>: UncheckedValue { + type ToRust = CheckedOption<'a>; + + fn to_checked<'frame>(self, frame: CallFrame<'frame>) -> CheckResult { + if unsafe { self.inner() == Qnil } { + Ok(CheckedOption { inner: self, present: false }) } else { - match UncheckedValue::::to_checked(self) { - Ok(_) => Ok(unsafe { CheckedValue::new(self) }), + match UncheckedValue::::to_checked(self, frame) { + Ok(checked) => Ok(CheckedOption { inner: self, present: true }), Err(e) => Err(e) } } } } -impl ToRust> for CheckedValue> where CheckedValue: ToRust { - fn to_rust(self) -> Option { - if unsafe { self.inner == Qnil } { - None +impl<'a, T, U> ToRust> for CheckedOption<'a> where U: ToRust { + fn to_rust(self) -> Option { + if self.present { + ToRust::>::to_rust(self.inner) } else { - let checked: CheckedValue = unsafe { CheckedValue::new(self.inner) }; - Some(checked.to_rust()) + None } } } diff --git a/src/coercions/slice.rs b/src/coercions/slice.rs index f3331af3..3a6be03c 100644 --- a/src/coercions/slice.rs +++ b/src/coercions/slice.rs @@ -3,10 +3,13 @@ use sys; use sys::{VALUE}; use ::inspect; +use coercions::*; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust}; impl<'a> UncheckedValue<&'a[usize]> for VALUE { - fn to_checked(self) -> CheckResult<&'a[usize]> { + type ToRust = CheckedValue<&'a[usize]>; + + fn to_checked<'b>(self, frame: CallFrame<'b>) -> CheckResult<&'a[usize]> { if unsafe { sys::RB_TYPE_P(self, sys::T_ARRAY) } { Ok(unsafe { CheckedValue::new(self) }) } else { diff --git a/src/coercions/string.rs b/src/coercions/string.rs index 8b6222f7..83641f67 100644 --- a/src/coercions/string.rs +++ b/src/coercions/string.rs @@ -3,12 +3,15 @@ use std; use sys; use sys::{VALUE}; +use coercions::*; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; // VALUE -> to_coercible_rust -> CheckResult -> unwrap() -> Coercible -> to_rust() -> String impl UncheckedValue for VALUE { - fn to_checked(self) -> CheckResult { + type ToRust = CheckedValue; + + fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, sys::T_STRING) } { Ok(unsafe { CheckedValue::::new(self) }) } else { @@ -18,7 +21,7 @@ impl UncheckedValue for VALUE { } } -impl ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue { fn to_rust(self) -> String { let size = unsafe { sys::RSTRING_LEN(self.inner) }; let ptr = unsafe { sys::RSTRING_PTR(self.inner) }; diff --git a/src/macros/coercions.rs b/src/macros/coercions.rs index 4e3a3e09..b87e23c3 100644 --- a/src/macros/coercions.rs +++ b/src/macros/coercions.rs @@ -77,7 +77,7 @@ macro_rules! impl_struct_to_rust { } impl<'a> $crate::UncheckedValue<$cls> for $crate::sys::VALUE { - fn to_checked(self) -> $crate::CheckResult<$cls> { + fn to_checked<'lt>(self) -> $crate::CheckResult<'lt, $cls> { use $crate::{CheckedValue, sys}; use ::std::ffi::{CStr}; diff --git a/src/macros/init.rs b/src/macros/init.rs index c39d809f..dd76f532 100644 --- a/src/macros/init.rs +++ b/src/macros/init.rs @@ -194,6 +194,8 @@ macro_rules! codegen_define_method { #[allow(unused_imports)] use $crate::{ToRust}; + let lifetime = &(); + let rust_self = match $crate::UncheckedValue::::to_checked(rb_self) { Ok(v) => v, Err(e) => return Err($crate::ExceptionInfo::with_message(e)) diff --git a/src/macros/mod.rs b/src/macros/mod.rs index 85d306f3..67d65bcc 100644 --- a/src/macros/mod.rs +++ b/src/macros/mod.rs @@ -79,4 +79,3 @@ macro_rules! throw { panic!($crate::ExceptionInfo::with_message(String::from($msg))) } } - diff --git a/src/ruby/array.rs b/src/ruby/array.rs new file mode 100644 index 00000000..aa88511e --- /dev/null +++ b/src/ruby/array.rs @@ -0,0 +1,44 @@ +use libc; +use std; +use std::ops::{Deref, Index}; +use sys::{self, VALUE}; +use super::{Value}; +use coercions::*; + +#[derive(Debug, Copy, Clone)] +pub struct Array<'a> { + inner: Value<'a> +} + +impl<'a> Deref for Array<'a> { + type Target = Value<'a>; + + fn deref(&self) -> &Value<'a> { + &self.inner + } +} + +impl<'a> Index for Array<'a> { + type Output = Value<'a>; + + fn index(&self, offset: usize) -> &Value<'a> { + let val = unsafe { sys::rb_ary_aref(self.to_ruby(), offset as libc::c_long) }; + unsafe { std::mem::transmute(val) } + } +} + +struct CheckedArray<'a> { + inner: Value<'a> +} + +impl<'a> UncheckedValue> for VALUE { + fn to_checked(self, frame: CallFrame<'a>) -> CheckResult, CheckedArray<'a>> { + Ok(CheckedArray { inner: unsafe { Value::new(self, frame) } }) + } +} + +impl<'a> ToRust> for Value<'a> { + fn to_rust(self) -> Array<'a> { + Array { inner: self } + } +} diff --git a/src/ruby/mod.rs b/src/ruby/mod.rs index e591f39e..433b6920 100644 --- a/src/ruby/mod.rs +++ b/src/ruby/mod.rs @@ -1,5 +1,7 @@ mod value; mod types; +mod array; pub use self::value::{Value}; pub use self::types::{Type}; +pub use self::array::{Array}; diff --git a/src/ruby/value.rs b/src/ruby/value.rs index a1f9353d..52665e44 100644 --- a/src/ruby/value.rs +++ b/src/ruby/value.rs @@ -1,11 +1,23 @@ +use std; use sys::VALUE; use super::Type; +use coercions::*; -pub struct Value { - inner: VALUE +#[derive(Debug, Clone, Copy)] +pub struct Value<'a> { + inner: VALUE, + frame: CallFrame<'a> } -impl Value { +impl<'a> Value<'a> { + pub unsafe fn new<'b>(value: VALUE, frame: CallFrame<'b>) -> Value<'b> { + Value { inner: value, frame } + } + + pub unsafe fn inner(&self) -> VALUE { + self.inner + } + pub fn is_type(&self, ty: Type) -> bool { ty.matches(self) } @@ -13,24 +25,28 @@ impl Value { pub fn ruby_type(&self) -> Type { Type::of(self) } -} -use coercions::*; + pub fn to_rust(&self) -> T where VALUE: UncheckedValue, CheckedValue: ToRust { + self.inner.to_checked(self.frame).unwrap().to_rust() + } +} -impl ToRuby for Value { +impl<'a> ToRuby for Value<'a> { fn to_ruby(&self) -> VALUE { self.inner } } -impl UncheckedValue for VALUE { - fn to_checked(self) -> CheckResult { - Ok(unsafe { CheckedValue::new(self) }) +impl<'a> UncheckedValue> for VALUE { + type ToRust = Value<'a>; + + fn to_checked<'b>(self, frame: CallFrame<'b>) -> CheckResult, Value<'b>> { + Ok(unsafe { Value::new(self, frame) }) } } -impl ToRust for CheckedValue { - fn to_rust(self) -> Value { - Value { inner: self.inner } +impl<'a> ToRust> for Value<'a> { + fn to_rust(self) -> Value<'a> { + self } } From 8b8a635b1940c4f3ef761369b5a231fa98b0df16 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Thu, 4 May 2017 15:15:34 -0700 Subject: [PATCH 03/10] More work --- Dockerfile | 34 ++++++++++++ src/coercions/bool.rs | 9 ++-- src/coercions/float.rs | 9 ++-- src/coercions/integers.rs | 33 ++++++------ src/coercions/mod.rs | 11 ++-- src/coercions/option.rs | 110 +++++++++++++++++++++----------------- src/coercions/slice.rs | 9 ++-- src/coercions/string.rs | 9 ++-- src/ruby/array.rs | 6 +-- src/ruby/value.rs | 4 +- 10 files changed, 143 insertions(+), 91 deletions(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c8bae72e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM bitnami/minideb:jessie + +RUN apt-get update +RUN apt-get install curl -y --no-install-recommends +RUN apt-get install ca-certificates -y --no-install-recommends +RUN apt-get install wget tar -y --no-install-recommends +RUN apt-get install build-essential make -y --no-install-recommends +RUN apt-get install build-essential git -y --no-install-recommends + +RUN adduser --disabled-password --gecos '' helix +USER helix + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y + +ENV PATH="/home/helix/.cargo/bin:${PATH}" + +RUN rustup target add x86_64-unknown-linux-musl + +RUN cd ~ && wget -O ruby-install-0.6.1.tar.gz https://github.com/postmodern/ruby-install/archive/v0.6.1.tar.gz +RUN cd ~ && tar -xzvf ruby-install-0.6.1.tar.gz + +USER root + +RUN cd /home/helix/ruby-install-0.6.1 && make install +RUN ruby-install ruby 2.4.1 --system + +USER helix +ENV GEM_HOME=/home/helix/.gem +ENV GEM_PATH=/home/helix/.gem +ENV PATH="/home/helix/.gem/bin:${PATH}" +RUN gem install bundler rake --no-ri --no-rdoc + +VOLUME "app" +WORKDIR "/app" diff --git a/src/coercions/bool.rs b/src/coercions/bool.rs index 7b23e3e9..1e2967bf 100644 --- a/src/coercions/bool.rs +++ b/src/coercions/bool.rs @@ -1,11 +1,12 @@ use sys::{self, VALUE, Qtrue, Qfalse}; use coercions::*; +use ruby::Value; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; -impl UncheckedValue for VALUE { - type ToRust = CheckedValue; +impl<'a> UncheckedValue for Value<'a> { + type ToRust = CheckedValue<'a, bool>; - fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { + fn to_checked(self) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, sys::T_TRUE) || sys::RB_TYPE_P(self, sys::T_FALSE) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -14,7 +15,7 @@ impl UncheckedValue for VALUE { } } -impl<'a> ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue<'a, bool> { fn to_rust(self) -> bool { if self.inner == unsafe { Qtrue } { true diff --git a/src/coercions/float.rs b/src/coercions/float.rs index 9e086bc4..37e43503 100644 --- a/src/coercions/float.rs +++ b/src/coercions/float.rs @@ -1,12 +1,13 @@ use sys::{self, VALUE, T_FLOAT, T_FIXNUM, T_BIGNUM}; use coercions::*; +use ruby::Value; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; -impl UncheckedValue for VALUE { - type ToRust = CheckedValue; +impl<'a> UncheckedValue for Value<'a> { + type ToRust = CheckedValue<'a, f64>; - fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { + fn to_checked(self) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, T_FLOAT) || sys::RB_TYPE_P(self, T_FIXNUM) || sys::RB_TYPE_P(self, T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -16,7 +17,7 @@ impl UncheckedValue for VALUE { } } -impl<'a> ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue<'a, f64> { fn to_rust(self) -> f64 { unsafe { sys::NUM2F64(self.inner) } } diff --git a/src/coercions/integers.rs b/src/coercions/integers.rs index 69f84a28..2595b9fc 100644 --- a/src/coercions/integers.rs +++ b/src/coercions/integers.rs @@ -1,12 +1,13 @@ use sys::{self, VALUE, T_FIXNUM, T_BIGNUM}; use coercions::*; +use ruby::Value; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; -impl UncheckedValue for VALUE { - type ToRust = CheckedValue; +impl<'a> UncheckedValue for Value<'a> { + type ToRust = CheckedValue<'a, u64>; - fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { + fn to_checked(self) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, T_FIXNUM) || sys::RB_TYPE_P(self, T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -16,7 +17,7 @@ impl UncheckedValue for VALUE { } } -impl<'a> ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue<'a, u64> { fn to_rust(self) -> u64 { unsafe { sys::NUM2U64(self.inner) } } @@ -28,10 +29,10 @@ impl ToRuby for u64 { } } -impl UncheckedValue for VALUE { - type ToRust = CheckedValue; +impl<'a> UncheckedValue for Value<'a> { + type ToRust = CheckedValue<'a, i64>; - fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { + fn to_checked(self) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, sys::T_FIXNUM) || sys::RB_TYPE_P(self, sys::T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -41,7 +42,7 @@ impl UncheckedValue for VALUE { } } -impl<'a> ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue<'a, i64> { fn to_rust(self) -> i64 { unsafe { sys::NUM2I64(self.inner) } } @@ -53,10 +54,10 @@ impl ToRuby for i64 { } } -impl UncheckedValue for VALUE { - type ToRust = CheckedValue; +impl<'a> UncheckedValue for Value<'a> { + type ToRust = CheckedValue<'a, u32>; - fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { + fn to_checked(self) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, T_FIXNUM) || sys::RB_TYPE_P(self, T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -66,7 +67,7 @@ impl UncheckedValue for VALUE { } } -impl<'a> ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue<'a, u32> { fn to_rust(self) -> u32 { unsafe { sys::NUM2U32(self.inner) } } @@ -78,10 +79,10 @@ impl ToRuby for u32 { } } -impl UncheckedValue for VALUE { - type ToRust = CheckedValue; +impl<'a> UncheckedValue for Value<'a> { + type ToRust = CheckedValue<'a, i32>; - fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { + fn to_checked(self) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, sys::T_FIXNUM) || sys::RB_TYPE_P(self, sys::T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -91,7 +92,7 @@ impl UncheckedValue for VALUE { } } -impl<'a> ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue<'a, i32> { fn to_rust(self) -> i32 { unsafe { sys::NUM2I32(self.inner) } } diff --git a/src/coercions/mod.rs b/src/coercions/mod.rs index 95c37abc..aaf37c7c 100644 --- a/src/coercions/mod.rs +++ b/src/coercions/mod.rs @@ -6,6 +6,7 @@ mod integers; mod float; mod option; +use ruby; use sys::{VALUE}; use std::marker::PhantomData; @@ -20,15 +21,15 @@ impl<'a> CallFrame<'a> { } } -pub struct CheckedValue { - pub inner: VALUE, +pub struct CheckedValue<'a, T> { + pub inner: ruby::Value<'a>, target: PhantomData } -impl CheckedValue { +impl<'a, T> CheckedValue<'a, T> { // This is unsafe because it's the primary way that the coercion // protocol asserts that subsequent operations are safe - pub unsafe fn new(inner: VALUE) -> CheckedValue { + pub unsafe fn new<'lt, M>(inner: ruby::Value<'lt>) -> CheckedValue<'lt, T> { CheckedValue { inner, target: PhantomData } } } @@ -38,7 +39,7 @@ pub type CheckResult = Result; pub trait UncheckedValue { type ToRust: ToRust; - fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult; + fn to_checked(self) -> CheckResult; } pub trait ToRust { diff --git a/src/coercions/option.rs b/src/coercions/option.rs index 1907162d..7f7972a8 100644 --- a/src/coercions/option.rs +++ b/src/coercions/option.rs @@ -1,49 +1,61 @@ -use coercions::*; -use sys::{VALUE, Qnil}; -use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; -use ruby; - -impl ToRust for Box> { - fn to_rust(self) -> T { - self.to_rust() - } -} - -struct CheckedOption<'a> { - inner: ruby::Value<'a>, - present: bool -} - -impl<'a, T, U: ToRust> UncheckedValue> for ruby::Value<'a> where ruby::Value<'a>: UncheckedValue { - type ToRust = CheckedOption<'a>; - - fn to_checked<'frame>(self, frame: CallFrame<'frame>) -> CheckResult { - if unsafe { self.inner() == Qnil } { - Ok(CheckedOption { inner: self, present: false }) - } else { - match UncheckedValue::::to_checked(self, frame) { - Ok(checked) => Ok(CheckedOption { inner: self, present: true }), - Err(e) => Err(e) - } - } - } -} - -impl<'a, T, U> ToRust> for CheckedOption<'a> where U: ToRust { - fn to_rust(self) -> Option { - if self.present { - ToRust::>::to_rust(self.inner) - } else { - None - } - } -} - -impl ToRuby for Option where T: ToRuby { - fn to_ruby(&self) -> VALUE { - match *self { - Some(ref value) => value.to_ruby(), - None => unsafe { Qnil } - } - } -} +// use coercions::*; +// use sys::{VALUE, Qnil}; +// use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; +// use ruby; + +// impl ToRust for Box> { +// fn to_rust(self) -> T { +// self.to_rust() +// } +// } + +// struct CheckedOption<'a, T> { +// inner: VALUE, +// target: PhantomData, +// frame: CallFrame<'a>, +// present: bool, +// } + +// impl UncheckedValue> for VALUE where VALUE: UncheckedValue { +// type ToRust = CheckedOption; + +// fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { +// CheckedOption { inner: self, target: PhantomData, frame: frame, present: false } +// } +// } + +// // impl<'a, TARGET, U> ToRust> for CheckedOption<'a, U> where U: ToRust + +// // impl<'a, T, U: ToRust> UncheckedValue> for ruby::Value<'a> where ruby::Value<'a>: UncheckedValue { +// // type ToRust = CheckedOption<'a>; + +// // fn to_checked<'frame>(self, frame: CallFrame<'frame>) -> CheckResult { +// // if unsafe { self.inner() == Qnil } { +// // Ok(CheckedOption { inner: self, present: false }) +// // } else { +// // match UncheckedValue::::to_checked(self, frame) { +// // Ok(checked) => Ok(CheckedOption { inner: self, present: true }), +// // Err(e) => Err(e) +// // } +// // } +// // } +// // } + +// // impl<'a, T, U> ToRust> for CheckedOption<'a> where U: ToRust { +// // fn to_rust(self) -> Option { +// // if self.present { +// // ToRust::>::to_rust(self.inner) +// // } else { +// // None +// // } +// // } +// // } + +// impl ToRuby for Option where T: ToRuby { +// fn to_ruby(&self) -> VALUE { +// match *self { +// Some(ref value) => value.to_ruby(), +// None => unsafe { Qnil } +// } +// } +// } diff --git a/src/coercions/slice.rs b/src/coercions/slice.rs index 3a6be03c..cba0157e 100644 --- a/src/coercions/slice.rs +++ b/src/coercions/slice.rs @@ -4,12 +4,13 @@ use sys::{VALUE}; use ::inspect; use coercions::*; +use ruby::Value; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust}; -impl<'a> UncheckedValue<&'a[usize]> for VALUE { - type ToRust = CheckedValue<&'a[usize]>; +impl<'a> UncheckedValue<&'a[usize]> for Value<'a> { + type ToRust = CheckedValue<'a, &'a[usize]>; - fn to_checked<'b>(self, frame: CallFrame<'b>) -> CheckResult<&'a[usize]> { + fn to_checked(self) -> CheckResult<&'a[usize]> { if unsafe { sys::RB_TYPE_P(self, sys::T_ARRAY) } { Ok(unsafe { CheckedValue::new(self) }) } else { @@ -18,7 +19,7 @@ impl<'a> UncheckedValue<&'a[usize]> for VALUE { } } -impl<'a> ToRust<&'a[usize]> for CheckedValue<&'a[usize]> { +impl<'a> ToRust<&'a[usize]> for CheckedValue<'a, &'a[usize]> { fn to_rust(self) -> &'a[usize] { let size = unsafe { sys::RARRAY_LEN(self.inner) }; let ptr = unsafe { sys::RARRAY_PTR(self.inner) }; diff --git a/src/coercions/string.rs b/src/coercions/string.rs index 83641f67..833653e3 100644 --- a/src/coercions/string.rs +++ b/src/coercions/string.rs @@ -4,14 +4,15 @@ use sys; use sys::{VALUE}; use coercions::*; +use ruby::Value; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; // VALUE -> to_coercible_rust -> CheckResult -> unwrap() -> Coercible -> to_rust() -> String -impl UncheckedValue for VALUE { - type ToRust = CheckedValue; +impl<'a> UncheckedValue for Value<'a> { + type ToRust = CheckedValue<'a, String>; - fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { + fn to_checked(self) -> CheckResult { if unsafe { sys::RB_TYPE_P(self, sys::T_STRING) } { Ok(unsafe { CheckedValue::::new(self) }) } else { @@ -21,7 +22,7 @@ impl UncheckedValue for VALUE { } } -impl<'a> ToRust for CheckedValue { +impl<'a> ToRust for CheckedValue<'a, String> { fn to_rust(self) -> String { let size = unsafe { sys::RSTRING_LEN(self.inner) }; let ptr = unsafe { sys::RSTRING_PTR(self.inner) }; diff --git a/src/ruby/array.rs b/src/ruby/array.rs index aa88511e..df25e54d 100644 --- a/src/ruby/array.rs +++ b/src/ruby/array.rs @@ -28,11 +28,11 @@ impl<'a> Index for Array<'a> { } struct CheckedArray<'a> { - inner: Value<'a> + inner: Value<'a> } -impl<'a> UncheckedValue> for VALUE { - fn to_checked(self, frame: CallFrame<'a>) -> CheckResult, CheckedArray<'a>> { +impl<'a> UncheckedValue> for Value<'a> { + fn to_checked(self) -> CheckResult> { Ok(CheckedArray { inner: unsafe { Value::new(self, frame) } }) } } diff --git a/src/ruby/value.rs b/src/ruby/value.rs index 52665e44..2d9149fb 100644 --- a/src/ruby/value.rs +++ b/src/ruby/value.rs @@ -26,7 +26,7 @@ impl<'a> Value<'a> { Type::of(self) } - pub fn to_rust(&self) -> T where VALUE: UncheckedValue, CheckedValue: ToRust { + pub fn to_rust<'lt, T>(&self) -> T where Value<'lt>: UncheckedValue, CheckedValue<'lt, T>: ToRust { self.inner.to_checked(self.frame).unwrap().to_rust() } } @@ -40,7 +40,7 @@ impl<'a> ToRuby for Value<'a> { impl<'a> UncheckedValue> for VALUE { type ToRust = Value<'a>; - fn to_checked<'b>(self, frame: CallFrame<'b>) -> CheckResult, Value<'b>> { + fn to_checked(self) -> CheckResult> { Ok(unsafe { Value::new(self, frame) }) } } From ad6b692418cf0acf82965cce3f0cf5a5969d00b6 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Wed, 3 May 2017 11:41:22 -0700 Subject: [PATCH 04/10] Bump Version --- CHANGELOG.md | 10 ++++++++++ Cargo.toml | 4 ++-- crates/libcruby-sys/Cargo.toml | 6 +++--- examples/calculator/Gemfile.lock | 2 +- examples/console/Gemfile.lock | 2 +- examples/duration/Gemfile.lock | 2 +- examples/membership/Gemfile.lock | 2 +- examples/text_transform/Gemfile.lock | 2 +- examples/turbo_blank/Gemfile.lock | 2 +- ruby/ext/helix_runtime/native/helix_runtime.c | 2 +- ruby/lib/helix_runtime/version.rb | 2 +- 11 files changed, 23 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41b90e39..3d0162e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.6.0 (May 3, 2017) + +* [BUGFIX] Raise TypeError on bad initialize +* [BUGFIX] Fix Windows release +* [IMPROVEMENT] Fewer allocations by making function signatures take `&CStr`s. +* [IMPROVEMENT] Improve installation process when running examples +* [IMPROVEMENT] Better handling of unbuilt helix_runtime/native during development +* [IMPROVEMENT] Fix var shadowing and ambiguous args in ruby warnings +* [IMPROVEMENT] Bump cslice crate: 0.2 -> 0.3. + ## 0.5.0 (April 26, 2017) * First official release diff --git a/Cargo.toml b/Cargo.toml index 5e55f43b..4a924907 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "helix" -version = "0.5.0" +version = "0.6.0" authors = ["Godhuda "] description = "Embed Rust in your Ruby" documentation = "https://usehelix.com/documentation" @@ -31,7 +31,7 @@ version = "0.3" [dependencies.libcruby-sys] path = "crates/libcruby-sys" -version = "0.5.0" +version = "0.6.0" [dependencies.cstr-macro] path = "crates/cstr-macro" diff --git a/crates/libcruby-sys/Cargo.toml b/crates/libcruby-sys/Cargo.toml index e8a681e9..9f268bc0 100644 --- a/crates/libcruby-sys/Cargo.toml +++ b/crates/libcruby-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcruby-sys" -version = "0.5.0" +version = "0.6.0" authors = ["Godhuda "] description = "Ruby bindings" repository = "https://github.com/tildeio/helix" @@ -11,8 +11,8 @@ include = [ "Cargo.toml", "ruby_info.rb", # Keep in sync with version - "helix-runtime-0-5-0-alpha-4.i386.lib", - "helix-runtime-0-5-0-alpha-4.x86_64.lib" + "helix-runtime-0-6-0.i386.lib", + "helix-runtime-0-6-0.x86_64.lib" ] [dependencies] diff --git a/examples/calculator/Gemfile.lock b/examples/calculator/Gemfile.lock index 6f425f4e..82ef40da 100644 --- a/examples/calculator/Gemfile.lock +++ b/examples/calculator/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../../ruby specs: - helix_runtime (0.5.0) + helix_runtime (0.6.0) rake (>= 10.0) thor (~> 0.19.4) diff --git a/examples/console/Gemfile.lock b/examples/console/Gemfile.lock index 73b46144..0ebaf7eb 100644 --- a/examples/console/Gemfile.lock +++ b/examples/console/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../../ruby specs: - helix_runtime (0.5.0) + helix_runtime (0.6.0) rake (>= 10.0) thor (~> 0.19.4) diff --git a/examples/duration/Gemfile.lock b/examples/duration/Gemfile.lock index 6b26ccc7..1c44c060 100644 --- a/examples/duration/Gemfile.lock +++ b/examples/duration/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../../ruby specs: - helix_runtime (0.5.0) + helix_runtime (0.6.0) rake (>= 10.0) thor (~> 0.19.4) diff --git a/examples/membership/Gemfile.lock b/examples/membership/Gemfile.lock index 73dc6559..8ed7a62f 100644 --- a/examples/membership/Gemfile.lock +++ b/examples/membership/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../../ruby specs: - helix_runtime (0.5.0) + helix_runtime (0.6.0) rake (>= 10.0) thor (~> 0.19.4) diff --git a/examples/text_transform/Gemfile.lock b/examples/text_transform/Gemfile.lock index fd58b3a2..d5affc55 100644 --- a/examples/text_transform/Gemfile.lock +++ b/examples/text_transform/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../../ruby specs: - helix_runtime (0.5.0) + helix_runtime (0.6.0) rake (>= 10.0) thor (~> 0.19.4) diff --git a/examples/turbo_blank/Gemfile.lock b/examples/turbo_blank/Gemfile.lock index a2c2f202..d89dd60f 100644 --- a/examples/turbo_blank/Gemfile.lock +++ b/examples/turbo_blank/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../../ruby specs: - helix_runtime (0.5.0) + helix_runtime (0.6.0) rake (>= 10.0) thor (~> 0.19.4) diff --git a/ruby/ext/helix_runtime/native/helix_runtime.c b/ruby/ext/helix_runtime/native/helix_runtime.c index 6f4112c4..4db3bcb1 100644 --- a/ruby/ext/helix_runtime/native/helix_runtime.c +++ b/ruby/ext/helix_runtime/native/helix_runtime.c @@ -6,7 +6,7 @@ #include // Update with version.rb -const char* HELIX_RUNTIME_VERSION = "0.5.0"; +const char* HELIX_RUNTIME_VERSION = "0.6.0"; const char* HELIX_PRIsVALUE = PRIsVALUE; const char* HELIX_SPRINTF_TO_S = "%" PRIsVALUE; diff --git a/ruby/lib/helix_runtime/version.rb b/ruby/lib/helix_runtime/version.rb index 9c7a62f6..2a550b17 100644 --- a/ruby/lib/helix_runtime/version.rb +++ b/ruby/lib/helix_runtime/version.rb @@ -1,5 +1,5 @@ module HelixRuntime # Also update helix_runtime.c - VERSION = "0.5.0" + VERSION = "0.6.0" GEM_VERSION = VERSION.gsub("-", ".") end From 6d3a6b7e54eaf9d011979e518c76e2fdea2387b9 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Wed, 3 May 2017 11:51:06 -0700 Subject: [PATCH 05/10] CI against stable Rust instead of beta --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fd517d08..147cca47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ env: - EXAMPLES="calculator console membership turbo_blank duration" - VERBOSE=true - RUST_BACKTRACE=1 - - RUST_VERSION=beta + - RUST_VERSION=stable matrix: include: From de233dcc20d295d00826ff2cbea786c73925fe24 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Wed, 3 May 2017 11:51:15 -0700 Subject: [PATCH 06/10] Don't try to run auto-release on nightly --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 147cca47..6567faa3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,9 @@ before_install: - if [ ! -z "$TRAVIS_TAG" ]; then export TEST_RELEASE=1; fi # This only works on Linux - if [[ "$TRAVIS_OS_NAME" != "linux" ]]; then unset TEST_RELEASE; fi + # Only try to release on stable + # FIXME: There should be a better way of doing this + - if [[ "$RUST_VERSION" != "stable" ]]; then unset TEST_RELEASE; fi # Install Rust - if [ ! -e "$HOME/.cargo/bin" ]; then curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain $RUST_VERSION -y; fi - export PATH="$HOME/.cargo/bin:$PATH" From 612ec615b84889989e566ad20441b25608d5ff3f Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Fri, 5 May 2017 12:56:09 -0700 Subject: [PATCH 07/10] More updates --- crates/libcruby-sys/build.rs | 106 ++++++++++++++++++----------------- src/coercions/bool.rs | 12 ++-- src/coercions/float.rs | 13 ++--- src/coercions/integers.rs | 49 ++++++++-------- src/coercions/mod.rs | 18 +++--- src/coercions/option.rs | 29 +++++++++- src/coercions/slice.rs | 10 ++-- src/coercions/string.rs | 16 +++--- src/coercions/unit.rs | 2 +- src/lib.rs | 6 +- src/macros/coercions.rs | 39 +++++++------ src/macros/init.rs | 15 ++++- src/ruby/array.rs | 14 +++-- src/ruby/value.rs | 17 +++--- 14 files changed, 196 insertions(+), 150 deletions(-) diff --git a/crates/libcruby-sys/build.rs b/crates/libcruby-sys/build.rs index f4b58bad..f836bdd0 100644 --- a/crates/libcruby-sys/build.rs +++ b/crates/libcruby-sys/build.rs @@ -1,65 +1,67 @@ -use std::{env,fs}; -use std::path::Path; -use std::process::Command; +// use std::{env,fs}; +// use std::path::Path; +// use std::process::Command; -fn main() { - // TODO: Clean this all up. There has to be a prettier way. - let target = env::var("TARGET").expect("TARGET required"); - let manifest_dir_str = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR required"); - let version_str = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION required").replace(".", "-"); +fn main() {} - let root = Path::new(manifest_dir_str.as_str()); +// fn main() { +// // TODO: Clean this all up. There has to be a prettier way. +// let target = env::var("TARGET").expect("TARGET required"); +// let manifest_dir_str = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR required"); +// let version_str = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION required").replace(".", "-"); - let lib_root_str = env::var("HELIX_LIB_DIR").unwrap_or(manifest_dir_str.clone()); - let lib_root = Path::new(lib_root_str.as_str()); +// let root = Path::new(manifest_dir_str.as_str()); - // Best way I could find to tell if we're packaging vs just building - let is_packaging = root.parent().expect("root has no parent").ends_with("target/package"); - let libname32 = format!("helix-runtime-{}.i386", version_str); - let libname64 = format!("helix-runtime-{}.x86_64", version_str); - let libname = if target.starts_with("x86_64") { libname64.clone() } else { libname32.clone() }; +// let lib_root_str = env::var("HELIX_LIB_DIR").unwrap_or(manifest_dir_str.clone()); +// let lib_root = Path::new(lib_root_str.as_str()); - // Not required for non-Windows, but it needs to be part of the package - if is_packaging && (!lib_root.join(format!("{}.lib", libname32)).exists() || - !lib_root.join(format!("{}.lib", libname64)).exists()) { - panic!("{}.lib and {}.lib must exist when packaging. Please run ./prepackage.sh and set HELIX_LIB_DIR.", libname32, libname64); - } +// // Best way I could find to tell if we're packaging vs just building +// let is_packaging = root.parent().expect("root has no parent").ends_with("target/package"); +// let libname32 = format!("helix-runtime-{}.i386", version_str); +// let libname64 = format!("helix-runtime-{}.x86_64", version_str); +// let libname = if target.starts_with("x86_64") { libname64.clone() } else { libname32.clone() }; - if target.contains("windows") && !lib_root.join(format!("{}.lib", libname)).exists() { - panic!("{}.lib must exist when running. Set HELIX_LIB_DIR to ruby/windows_build for development.", libname); - } +// // Not required for non-Windows, but it needs to be part of the package +// if is_packaging && (!lib_root.join(format!("{}.lib", libname32)).exists() || +// !lib_root.join(format!("{}.lib", libname64)).exists()) { +// panic!("{}.lib and {}.lib must exist when packaging. Please run ./prepackage.sh and set HELIX_LIB_DIR.", libname32, libname64); +// } - if target.contains("windows") { - let out_dir_str = env::var("OUT_DIR").expect("OUT_DIR required"); +// if target.contains("windows") && !lib_root.join(format!("{}.lib", libname)).exists() { +// panic!("{}.lib must exist when running. Set HELIX_LIB_DIR to ruby/windows_build for development.", libname); +// } - let out_dir = Path::new(out_dir_str.as_str()); +// if target.contains("windows") { +// let out_dir_str = env::var("OUT_DIR").expect("OUT_DIR required"); - // Read info about current Ruby install - let raw_ruby_info = Command::new("ruby") - .arg(root.join("ruby_info.rb")) - .output() - .expect("failed to get Ruby info"); - let raw_ruby_output = String::from_utf8_lossy(&raw_ruby_info.stdout); - let mut raw_ruby_lines = raw_ruby_output.lines(); - let ruby_libdir = Path::new(raw_ruby_lines.next().expect("Ruby info has no libdir")); - let libruby = raw_ruby_lines.next().expect("Ruby info has no LIBRUBY"); - let libruby_so = raw_ruby_lines.next().expect("Ruby info has no LIBRUBY_SO"); - if raw_ruby_lines.next() != None { - panic!("Unexpected information returned in Ruby info"); - } +// let out_dir = Path::new(out_dir_str.as_str()); - let ruby_libname = libruby_so.split('.').next().expect("can't extract Ruby lib name"); +// // Read info about current Ruby install +// let raw_ruby_info = Command::new("ruby") +// .arg(root.join("ruby_info.rb")) +// .output() +// .expect("failed to get Ruby info"); +// let raw_ruby_output = String::from_utf8_lossy(&raw_ruby_info.stdout); +// let mut raw_ruby_lines = raw_ruby_output.lines(); +// let ruby_libdir = Path::new(raw_ruby_lines.next().expect("Ruby info has no libdir")); +// let libruby = raw_ruby_lines.next().expect("Ruby info has no LIBRUBY"); +// let libruby_so = raw_ruby_lines.next().expect("Ruby info has no LIBRUBY_SO"); +// if raw_ruby_lines.next() != None { +// panic!("Unexpected information returned in Ruby info"); +// } - // Copy .dll.a file to .lib since Rust msvc looks for .lib files only - fs::copy(ruby_libdir.join(libruby), out_dir.join(ruby_libname).with_extension("lib")) - .expect("unable to copy libruby"); +// let ruby_libname = libruby_so.split('.').next().expect("can't extract Ruby lib name"); - // Set up linker - println!("cargo:rustc-flags=-L {libpath} -l dylib={libruby} -L {root} -l helix-runtime:{libname}", - libpath=out_dir.to_str().expect("can't get str from out_dir"), - libruby=ruby_libname, - root=lib_root.to_str().expect("can't get str from root dir"), - libname=libname); - } -} +// // Copy .dll.a file to .lib since Rust msvc looks for .lib files only +// fs::copy(ruby_libdir.join(libruby), out_dir.join(ruby_libname).with_extension("lib")) +// .expect("unable to copy libruby"); + +// // Set up linker +// println!("cargo:rustc-flags=-L {libpath} -l dylib={libruby} -L {root} -l helix-runtime:{libname}", +// libpath=out_dir.to_str().expect("can't get str from out_dir"), +// libruby=ruby_libname, +// root=lib_root.to_str().expect("can't get str from root dir"), +// libname=libname); +// } +// } diff --git a/src/coercions/bool.rs b/src/coercions/bool.rs index 1e2967bf..94a223c8 100644 --- a/src/coercions/bool.rs +++ b/src/coercions/bool.rs @@ -6,9 +6,9 @@ use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; impl<'a> UncheckedValue for Value<'a> { type ToRust = CheckedValue<'a, bool>; - fn to_checked(self) -> CheckResult { - if unsafe { sys::RB_TYPE_P(self, sys::T_TRUE) || sys::RB_TYPE_P(self, sys::T_FALSE) } { - Ok(unsafe { CheckedValue::new(self) }) + fn to_checked(self) -> CheckResult { + if unsafe { sys::RB_TYPE_P(self.inner(), sys::T_TRUE) || sys::RB_TYPE_P(self.inner(), sys::T_FALSE) } { + Ok(unsafe { CheckedValue::<'a, bool>::new(self) }) } else { Err(format!("No implicit conversion of {} into Rust bool", "?")) } @@ -17,7 +17,7 @@ impl<'a> UncheckedValue for Value<'a> { impl<'a> ToRust for CheckedValue<'a, bool> { fn to_rust(self) -> bool { - if self.inner == unsafe { Qtrue } { + if unsafe { self.inner.inner() == Qtrue } { true } else { false @@ -26,8 +26,8 @@ impl<'a> ToRust for CheckedValue<'a, bool> { } impl ToRuby for bool { - fn to_ruby(&self) -> VALUE { - if *self { + fn to_ruby(self) -> VALUE { + if self { unsafe { Qtrue } } else { unsafe { Qfalse } diff --git a/src/coercions/float.rs b/src/coercions/float.rs index 37e43503..279315d1 100644 --- a/src/coercions/float.rs +++ b/src/coercions/float.rs @@ -1,17 +1,16 @@ use sys::{self, VALUE, T_FLOAT, T_FIXNUM, T_BIGNUM}; -use coercions::*; use ruby::Value; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; impl<'a> UncheckedValue for Value<'a> { type ToRust = CheckedValue<'a, f64>; - fn to_checked(self) -> CheckResult { - if unsafe { sys::RB_TYPE_P(self, T_FLOAT) || sys::RB_TYPE_P(self, T_FIXNUM) || sys::RB_TYPE_P(self, T_BIGNUM) } { + fn to_checked(self) -> CheckResult { + if unsafe { sys::RB_TYPE_P(self.inner(), T_FLOAT) || sys::RB_TYPE_P(self.inner(), T_FIXNUM) || sys::RB_TYPE_P(self.inner(), T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { - let val = unsafe { CheckedValue::::new(sys::rb_inspect(self)) }; + let val = unsafe { CheckedValue::::from_value(sys::rb_inspect(self.inner()), self.frame()) }; Err(format!("No implicit conversion of {} into Rust f64", val.to_rust())) } } @@ -19,12 +18,12 @@ impl<'a> UncheckedValue for Value<'a> { impl<'a> ToRust for CheckedValue<'a, f64> { fn to_rust(self) -> f64 { - unsafe { sys::NUM2F64(self.inner) } + unsafe { sys::NUM2F64(self.inner.inner()) } } } impl ToRuby for f64 { - fn to_ruby(&self) -> VALUE { - unsafe { sys::F642NUM(*self) } + fn to_ruby(self) -> VALUE { + unsafe { sys::F642NUM(self) } } } diff --git a/src/coercions/integers.rs b/src/coercions/integers.rs index 2595b9fc..2fba9183 100644 --- a/src/coercions/integers.rs +++ b/src/coercions/integers.rs @@ -1,17 +1,16 @@ use sys::{self, VALUE, T_FIXNUM, T_BIGNUM}; -use coercions::*; use ruby::Value; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; impl<'a> UncheckedValue for Value<'a> { type ToRust = CheckedValue<'a, u64>; - fn to_checked(self) -> CheckResult { - if unsafe { sys::RB_TYPE_P(self, T_FIXNUM) || sys::RB_TYPE_P(self, T_BIGNUM) } { + fn to_checked(self) -> CheckResult { + if unsafe { sys::RB_TYPE_P(self.inner(), T_FIXNUM) || sys::RB_TYPE_P(self.inner(), T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { - let val = unsafe { CheckedValue::::new(sys::rb_inspect(self)) }; + let val = unsafe { CheckedValue::::from_value(sys::rb_inspect(self.inner()), self.frame()) }; Err(format!("No implicit conversion of {} into Rust u64", val.to_rust())) } } @@ -19,24 +18,24 @@ impl<'a> UncheckedValue for Value<'a> { impl<'a> ToRust for CheckedValue<'a, u64> { fn to_rust(self) -> u64 { - unsafe { sys::NUM2U64(self.inner) } + unsafe { sys::NUM2U64(self.inner.inner()) } } } impl ToRuby for u64 { - fn to_ruby(&self) -> VALUE { - unsafe { sys::U642NUM(*self) } + fn to_ruby(self) -> VALUE { + unsafe { sys::U642NUM(self) } } } impl<'a> UncheckedValue for Value<'a> { type ToRust = CheckedValue<'a, i64>; - fn to_checked(self) -> CheckResult { - if unsafe { sys::RB_TYPE_P(self, sys::T_FIXNUM) || sys::RB_TYPE_P(self, sys::T_BIGNUM) } { + fn to_checked(self) -> CheckResult { + if unsafe { sys::RB_TYPE_P(self.inner(), sys::T_FIXNUM) || sys::RB_TYPE_P(self.inner(), sys::T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { - let val = unsafe { CheckedValue::::new(sys::rb_inspect(self)) }; + let val = unsafe { CheckedValue::::from_value(sys::rb_inspect(self.inner()), self.frame()) }; Err(format!("No implicit conversion of {} into Rust i64", val.to_rust())) } } @@ -44,24 +43,24 @@ impl<'a> UncheckedValue for Value<'a> { impl<'a> ToRust for CheckedValue<'a, i64> { fn to_rust(self) -> i64 { - unsafe { sys::NUM2I64(self.inner) } + unsafe { sys::NUM2I64(self.inner.inner()) } } } impl ToRuby for i64 { - fn to_ruby(&self) -> VALUE { - unsafe { sys::I642NUM(*self) } + fn to_ruby(self) -> VALUE { + unsafe { sys::I642NUM(self) } } } impl<'a> UncheckedValue for Value<'a> { type ToRust = CheckedValue<'a, u32>; - fn to_checked(self) -> CheckResult { - if unsafe { sys::RB_TYPE_P(self, T_FIXNUM) || sys::RB_TYPE_P(self, T_BIGNUM) } { + fn to_checked(self) -> CheckResult { + if unsafe { sys::RB_TYPE_P(self.inner(), T_FIXNUM) || sys::RB_TYPE_P(self.inner(), T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { - let val = unsafe { CheckedValue::::new(sys::rb_inspect(self)) }; + let val = unsafe { CheckedValue::::from_value(sys::rb_inspect(self.inner()), self.frame()) }; Err(format!("No implicit conversion of {} into Rust u32", val.to_rust())) } } @@ -69,24 +68,24 @@ impl<'a> UncheckedValue for Value<'a> { impl<'a> ToRust for CheckedValue<'a, u32> { fn to_rust(self) -> u32 { - unsafe { sys::NUM2U32(self.inner) } + unsafe { sys::NUM2U32(self.inner.inner()) } } } impl ToRuby for u32 { - fn to_ruby(&self) -> VALUE { - unsafe { sys::U322NUM(*self) } + fn to_ruby(self) -> VALUE { + unsafe { sys::U322NUM(self) } } } impl<'a> UncheckedValue for Value<'a> { type ToRust = CheckedValue<'a, i32>; - fn to_checked(self) -> CheckResult { - if unsafe { sys::RB_TYPE_P(self, sys::T_FIXNUM) || sys::RB_TYPE_P(self, sys::T_BIGNUM) } { + fn to_checked(self) -> CheckResult { + if unsafe { sys::RB_TYPE_P(self.inner(), sys::T_FIXNUM) || sys::RB_TYPE_P(self.inner(), sys::T_BIGNUM) } { Ok(unsafe { CheckedValue::new(self) }) } else { - let val = unsafe { CheckedValue::::new(sys::rb_inspect(self)) }; + let val = unsafe { CheckedValue::::from_value(sys::rb_inspect(self.inner()), self.frame()) }; Err(format!("No implicit conversion of {} into Rust i32", val.to_rust())) } } @@ -94,12 +93,12 @@ impl<'a> UncheckedValue for Value<'a> { impl<'a> ToRust for CheckedValue<'a, i32> { fn to_rust(self) -> i32 { - unsafe { sys::NUM2I32(self.inner) } + unsafe { sys::NUM2I32(self.inner.inner()) } } } impl ToRuby for i32 { - fn to_ruby(&self) -> VALUE { - unsafe { sys::I322NUM(*self) } + fn to_ruby(self) -> VALUE { + unsafe { sys::I322NUM(self) } } } diff --git a/src/coercions/mod.rs b/src/coercions/mod.rs index aaf37c7c..e52449a0 100644 --- a/src/coercions/mod.rs +++ b/src/coercions/mod.rs @@ -10,7 +10,7 @@ use ruby; use sys::{VALUE}; use std::marker::PhantomData; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct CallFrame<'a> { lifetime: &'a () } @@ -29,15 +29,19 @@ pub struct CheckedValue<'a, T> { impl<'a, T> CheckedValue<'a, T> { // This is unsafe because it's the primary way that the coercion // protocol asserts that subsequent operations are safe - pub unsafe fn new<'lt, M>(inner: ruby::Value<'lt>) -> CheckedValue<'lt, T> { + pub unsafe fn new<'lt>(inner: ruby::Value<'lt>) -> CheckedValue<'lt, T> { CheckedValue { inner, target: PhantomData } } + + pub unsafe fn from_value<'lt>(inner: VALUE, frame: CallFrame<'lt>) -> CheckedValue<'lt, T> { + CheckedValue { inner: ruby::Value::new(inner, frame), target: PhantomData } + } } pub type CheckResult = Result; -pub trait UncheckedValue { - type ToRust: ToRust; +pub trait UncheckedValue { + type ToRust: ToRust; fn to_checked(self) -> CheckResult; } @@ -47,11 +51,11 @@ pub trait ToRust { } pub trait ToRuby { - fn to_ruby(&self) -> VALUE; + fn to_ruby(self) -> VALUE; } impl ToRuby for VALUE { - fn to_ruby(&self) -> VALUE { - *self + fn to_ruby(self) -> VALUE { + self } } diff --git a/src/coercions/option.rs b/src/coercions/option.rs index 7f7972a8..bcb943ee 100644 --- a/src/coercions/option.rs +++ b/src/coercions/option.rs @@ -1,7 +1,30 @@ // use coercions::*; -// use sys::{VALUE, Qnil}; -// use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; -// use ruby; +use sys::{VALUE, Qnil}; +use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; +use ruby::Value; + +impl<'a, T> UncheckedValue> for Value<'a> where Value<'a>: UncheckedValue, CheckedValue<'a, T>: ToRust { + type ToRust = Option>; + + fn to_checked(self) -> CheckResult>> { + if unsafe { self.inner() } == unsafe { Qnil } { + Ok(None) + } else { + let val = UncheckedValue::::to_checked(self); + match val { + // TODO: This transmute is caused by a compiler issue I think? + Ok(v) => Ok(Some(unsafe { CheckedValue::::new(v) })), + Err(e) => Err(e) + } + } + } +} + +impl<'a, T> ToRust> for Option> where CheckedValue<'a, T>: ToRust { + fn to_rust(self) -> Option { + self.map(|val| val.to_rust()) + } +} // impl ToRust for Box> { // fn to_rust(self) -> T { diff --git a/src/coercions/slice.rs b/src/coercions/slice.rs index cba0157e..6b45bf05 100644 --- a/src/coercions/slice.rs +++ b/src/coercions/slice.rs @@ -1,17 +1,15 @@ use std; use sys; -use sys::{VALUE}; use ::inspect; -use coercions::*; use ruby::Value; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust}; impl<'a> UncheckedValue<&'a[usize]> for Value<'a> { type ToRust = CheckedValue<'a, &'a[usize]>; - fn to_checked(self) -> CheckResult<&'a[usize]> { - if unsafe { sys::RB_TYPE_P(self, sys::T_ARRAY) } { + fn to_checked(self) -> CheckResult { + if unsafe { sys::RB_TYPE_P(self.inner(), sys::T_ARRAY) } { Ok(unsafe { CheckedValue::new(self) }) } else { Err(format!("No implicit conversion of {} into Slice", inspect(self))) @@ -21,8 +19,8 @@ impl<'a> UncheckedValue<&'a[usize]> for Value<'a> { impl<'a> ToRust<&'a[usize]> for CheckedValue<'a, &'a[usize]> { fn to_rust(self) -> &'a[usize] { - let size = unsafe { sys::RARRAY_LEN(self.inner) }; - let ptr = unsafe { sys::RARRAY_PTR(self.inner) }; + let size = unsafe { sys::RARRAY_LEN(self.inner.inner()) }; + let ptr = unsafe { sys::RARRAY_PTR(self.inner.inner()) }; unsafe { std::slice::from_raw_parts(ptr as *const usize, size as usize) } } } diff --git a/src/coercions/string.rs b/src/coercions/string.rs index 833653e3..97f02347 100644 --- a/src/coercions/string.rs +++ b/src/coercions/string.rs @@ -3,7 +3,6 @@ use std; use sys; use sys::{VALUE}; -use coercions::*; use ruby::Value; use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; @@ -12,27 +11,26 @@ use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; impl<'a> UncheckedValue for Value<'a> { type ToRust = CheckedValue<'a, String>; - fn to_checked(self) -> CheckResult { - if unsafe { sys::RB_TYPE_P(self, sys::T_STRING) } { + fn to_checked(self) -> CheckResult { + if unsafe { sys::RB_TYPE_P(self.inner(), sys::T_STRING) } { Ok(unsafe { CheckedValue::::new(self) }) } else { - let val = unsafe { CheckedValue::::new(sys::rb_inspect(self)) }; - Err(format!("No implicit conversion of {} into String", val.to_rust())) + Err(format!("No implicit conversion of {} into String", ::inspect(self))) } } } impl<'a> ToRust for CheckedValue<'a, String> { fn to_rust(self) -> String { - let size = unsafe { sys::RSTRING_LEN(self.inner) }; - let ptr = unsafe { sys::RSTRING_PTR(self.inner) }; + let size = unsafe { sys::RSTRING_LEN(self.inner.inner()) }; + let ptr = unsafe { sys::RSTRING_PTR(self.inner.inner()) }; let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, size as usize) }; unsafe { std::str::from_utf8_unchecked(slice) }.to_string() } } impl ToRuby for String { - fn to_ruby(&self) -> VALUE { + fn to_ruby(self) -> VALUE { let ptr = self.as_ptr(); let len = self.len(); unsafe { sys::rb_utf8_str_new(ptr as *const libc::c_char, len as libc::c_long) } @@ -40,7 +38,7 @@ impl ToRuby for String { } impl<'a> ToRuby for &'a str { - fn to_ruby(&self) -> VALUE { + fn to_ruby(self) -> VALUE { let ptr = self.as_ptr(); let len = self.len(); unsafe { sys::rb_utf8_str_new(ptr as *const libc::c_char, len as libc::c_long) } diff --git a/src/coercions/unit.rs b/src/coercions/unit.rs index 1c7b00d5..d7c8a65e 100644 --- a/src/coercions/unit.rs +++ b/src/coercions/unit.rs @@ -2,7 +2,7 @@ use sys::{self, VALUE}; use ToRuby; impl ToRuby for () { - fn to_ruby(&self) -> VALUE { + fn to_ruby(self) -> VALUE { unsafe { sys::Qnil } } } diff --git a/src/lib.rs b/src/lib.rs index ab6d533b..e2e705a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ use sys::VALUE; mod macros; mod class_definition; -mod coercions; +pub mod coercions; pub mod ruby; pub use coercions::*; @@ -81,8 +81,8 @@ impl Class { } } -pub fn inspect(val: VALUE) -> String { - unsafe { CheckedValue::::new(sys::rb_inspect(val)).to_rust() } +pub fn inspect<'a>(val: ruby::Value<'a>) -> String { + unsafe { CheckedValue::::from_value(sys::rb_inspect(val.inner()), val.frame()).to_rust() } } pub type Metadata = ::VALUE; diff --git a/src/macros/coercions.rs b/src/macros/coercions.rs index b87e23c3..1758d374 100644 --- a/src/macros/coercions.rs +++ b/src/macros/coercions.rs @@ -7,23 +7,25 @@ macro_rules! codegen_coercions { struct: (), methods: $methods:tt }) => ( - impl $crate::UncheckedValue<$cls> for $crate::sys::VALUE { - fn to_checked(self) -> $crate::CheckResult<$cls> { + impl<'a> $crate::UncheckedValue<$cls> for $crate::ruby::Value<'a> { + type ToRust = $crate::CheckedValue<'a, $cls>; + + fn to_checked(self) -> $crate::CheckResult { use $crate::{CheckedValue, sys}; use ::std::ffi::{CStr}; - if unsafe { $cls == ::std::mem::transmute(sys::rb_obj_class(self)) } { + if unsafe { $cls == ::std::mem::transmute(sys::rb_obj_class(self.inner())) } { Ok(unsafe { CheckedValue::new(self) }) } else { - let val = unsafe { CStr::from_ptr(sys::rb_obj_classname(self)).to_string_lossy() }; + let val = unsafe { CStr::from_ptr(sys::rb_obj_classname(self.inner())).to_string_lossy() }; Err(format!("No implicit conversion of {} into {}", val, stringify!($cls))) } } } - impl $crate::ToRust<$cls> for $crate::CheckedValue<$cls> { + impl<'a> $crate::ToRust<$cls> for $crate::CheckedValue<'a, $cls> { fn to_rust(self) -> $cls { - $cls { helix: self.inner } + $cls { helix: unsafe { self.inner.inner() } } } } @@ -59,7 +61,7 @@ macro_rules! impl_to_ruby { ($cls:ty) => { item! { impl<'a> $crate::ToRuby for $cls { - fn to_ruby(&self) -> $crate::sys::VALUE { + fn to_ruby(self) -> $crate::sys::VALUE { self.helix } } @@ -70,26 +72,31 @@ macro_rules! impl_to_ruby { #[macro_export] macro_rules! impl_struct_to_rust { ($cls:ty, $helix_id:tt) => { - impl<'a> $crate::ToRust<$cls> for $crate::CheckedValue<$cls> { + impl<'a> $crate::ToRust<$cls> for $crate::CheckedValue<'a, $cls> { fn to_rust(self) -> $cls { - unsafe { ::std::mem::transmute($crate::sys::Data_Get_Struct_Value(self.inner)) } + unsafe { ::std::mem::transmute($crate::sys::Data_Get_Struct_Value(self.inner.inner())) } } } - impl<'a> $crate::UncheckedValue<$cls> for $crate::sys::VALUE { - fn to_checked<'lt>(self) -> $crate::CheckResult<'lt, $cls> { + impl<'a> $crate::UncheckedValue<$cls> for $crate::ruby::Value<'a> { + type ToRust = $crate::CheckedValue<'a, $cls>; + + fn to_checked<'lt>(self) -> $crate::CheckResult { use $crate::{CheckedValue, sys}; use ::std::ffi::{CStr}; - if unsafe { $helix_id == ::std::mem::transmute(sys::rb_obj_class(self)) } { - if unsafe { $crate::sys::Data_Get_Struct_Value(self) == ::std::ptr::null_mut() } { - Err(format!("Uninitialized {}", $crate::inspect(unsafe { sys::rb_obj_class(self) }))) + if unsafe { $helix_id == ::std::mem::transmute(sys::rb_obj_class(self.inner())) } { + if unsafe { $crate::sys::Data_Get_Struct_Value(self.inner()) == ::std::ptr::null_mut() } { + let val = unsafe { sys::rb_obj_class(self.inner()) }; + let inspect = $crate::inspect(unsafe { $crate::ruby::Value::new(val, self.frame()) }); + Err(format!("Uninitialized {}", inspect)) } else { Ok(unsafe { CheckedValue::new(self) }) } } else { - let val = unsafe { CStr::from_ptr(sys::rb_obj_classname(self)).to_string_lossy() }; - Err(format!("No implicit conversion of {} into {}", val, $crate::inspect(unsafe { sys::rb_obj_class(self) }))) + let val = unsafe { CStr::from_ptr(sys::rb_obj_classname(self.inner())).to_string_lossy() }; + let target = unsafe { $crate::ruby::Value::new(sys::rb_obj_class(self.inner()), self.frame()) }; + Err(format!("No implicit conversion of {} into {}", val, $crate::inspect(target))) } } } diff --git a/src/macros/init.rs b/src/macros/init.rs index dd76f532..f98859ed 100644 --- a/src/macros/init.rs +++ b/src/macros/init.rs @@ -113,7 +113,7 @@ macro_rules! codegen_define_method { let checked = __checked_call__($($arg),*); match checked { - Ok(ref val) => CallResult { error_klass: unsafe { Qnil }, value: $crate::ToRuby::to_ruby(val) }, + Ok(val) => CallResult { error_klass: unsafe { Qnil }, value: $crate::ToRuby::to_ruby(val) }, Err(err) => CallResult { error_klass: err.exception.inner(), value: err.message } } } @@ -123,7 +123,11 @@ macro_rules! codegen_define_method { #[allow(unused_imports)] use $crate::{ToRust}; + let lifetime = &(); + let frame = unsafe { $crate::coercions::CallFrame::new(lifetime) }; + $( + let $arg = unsafe { $crate::ruby::Value::new($arg, frame) }; let $arg = match $crate::UncheckedValue::<$argty>::to_checked($arg) { Ok(v) => v, Err(e) => return Err($crate::ExceptionInfo::type_error(e)) @@ -184,7 +188,7 @@ macro_rules! codegen_define_method { let checked = __checked_call__(rb_self, $($arg),*); match checked { - Ok(ref val) => CallResult { error_klass: unsafe { Qnil }, value: $crate::ToRuby::to_ruby(val) }, + Ok(val) => CallResult { error_klass: unsafe { Qnil }, value: $crate::ToRuby::to_ruby(val) }, Err(err) => CallResult { error_klass: err.exception.inner(), value: err.message } } } @@ -195,13 +199,16 @@ macro_rules! codegen_define_method { use $crate::{ToRust}; let lifetime = &(); + let frame = unsafe { $crate::coercions::CallFrame::new(lifetime) }; + let rb_self = unsafe { $crate::ruby::Value::new(rb_self, frame) }; let rust_self = match $crate::UncheckedValue::::to_checked(rb_self) { Ok(v) => v, Err(e) => return Err($crate::ExceptionInfo::with_message(e)) }; $( + let $arg = unsafe { $crate::ruby::Value::new($arg, frame) }; let $arg = match $crate::UncheckedValue::<$argty>::to_checked($arg) { Ok(v) => v, Err(e) => return Err($crate::ExceptionInfo::type_error(e)) @@ -265,7 +272,11 @@ macro_rules! codegen_define_method { #[allow(unused_imports)] use $crate::{ToRust}; + let lifetime = &(); + let frame = unsafe { $crate::coercions::CallFrame::new(lifetime) }; + $( + let $arg = unsafe { $crate::ruby::Value::new($arg, frame) }; let $arg = try!($crate::UncheckedValue::<$argty>::to_checked($arg)); )* diff --git a/src/ruby/array.rs b/src/ruby/array.rs index df25e54d..9e883f26 100644 --- a/src/ruby/array.rs +++ b/src/ruby/array.rs @@ -1,7 +1,7 @@ use libc; use std; use std::ops::{Deref, Index}; -use sys::{self, VALUE}; +use sys::{self}; use super::{Value}; use coercions::*; @@ -27,18 +27,20 @@ impl<'a> Index for Array<'a> { } } -struct CheckedArray<'a> { +pub struct CheckedArray<'a> { inner: Value<'a> } impl<'a> UncheckedValue> for Value<'a> { - fn to_checked(self) -> CheckResult> { - Ok(CheckedArray { inner: unsafe { Value::new(self, frame) } }) + type ToRust = CheckedArray<'a>; + + fn to_checked(self) -> CheckResult { + Ok(CheckedArray { inner: self }) } } -impl<'a> ToRust> for Value<'a> { +impl<'a> ToRust> for CheckedArray<'a> { fn to_rust(self) -> Array<'a> { - Array { inner: self } + Array { inner: self.inner } } } diff --git a/src/ruby/value.rs b/src/ruby/value.rs index 2d9149fb..86f56d38 100644 --- a/src/ruby/value.rs +++ b/src/ruby/value.rs @@ -1,9 +1,8 @@ -use std; use sys::VALUE; use super::Type; use coercions::*; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct Value<'a> { inner: VALUE, frame: CallFrame<'a> @@ -18,6 +17,10 @@ impl<'a> Value<'a> { self.inner } + pub unsafe fn frame(&self) -> CallFrame<'a> { + self.frame + } + pub fn is_type(&self, ty: Type) -> bool { ty.matches(self) } @@ -26,22 +29,22 @@ impl<'a> Value<'a> { Type::of(self) } - pub fn to_rust<'lt, T>(&self) -> T where Value<'lt>: UncheckedValue, CheckedValue<'lt, T>: ToRust { - self.inner.to_checked(self.frame).unwrap().to_rust() + pub fn to_rust(&self) -> T where Value<'a>: UncheckedValue, CheckedValue<'a, T>: ToRust { + self.to_checked().unwrap().to_rust() } } impl<'a> ToRuby for Value<'a> { - fn to_ruby(&self) -> VALUE { + fn to_ruby(self) -> VALUE { self.inner } } -impl<'a> UncheckedValue> for VALUE { +impl<'a> UncheckedValue> for Value<'a> { type ToRust = Value<'a>; fn to_checked(self) -> CheckResult> { - Ok(unsafe { Value::new(self, frame) }) + Ok(self) } } From 6cd888e2a4bdd81e7c13b7a241dbb3ed677ebed2 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Fri, 5 May 2017 13:39:26 -0700 Subject: [PATCH 08/10] Trying box --- src/coercions/option.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coercions/option.rs b/src/coercions/option.rs index bcb943ee..d4e2a757 100644 --- a/src/coercions/option.rs +++ b/src/coercions/option.rs @@ -4,23 +4,23 @@ use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; use ruby::Value; impl<'a, T> UncheckedValue> for Value<'a> where Value<'a>: UncheckedValue, CheckedValue<'a, T>: ToRust { - type ToRust = Option>; + type ToRust = Option>>; - fn to_checked(self) -> CheckResult>> { + fn to_checked(self) -> CheckResult { if unsafe { self.inner() } == unsafe { Qnil } { Ok(None) } else { let val = UncheckedValue::::to_checked(self); match val { // TODO: This transmute is caused by a compiler issue I think? - Ok(v) => Ok(Some(unsafe { CheckedValue::::new(v) })), + Ok(v) => Ok(Some(Box::new(v) as Box>)), Err(e) => Err(e) } } } } -impl<'a, T> ToRust> for Option> where CheckedValue<'a, T>: ToRust { +impl<'a, T> ToRust> for Option>> where CheckedValue<'a, T>: ToRust { fn to_rust(self) -> Option { self.map(|val| val.to_rust()) } From 8d345d2a51fa9107ae1dfbcd52f2f0c91d0f9aeb Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Fri, 5 May 2017 14:00:08 -0700 Subject: [PATCH 09/10] Option compiles --- src/coercions/option.rs | 87 ++++++++--------------------------------- 1 file changed, 16 insertions(+), 71 deletions(-) diff --git a/src/coercions/option.rs b/src/coercions/option.rs index d4e2a757..9790d166 100644 --- a/src/coercions/option.rs +++ b/src/coercions/option.rs @@ -1,84 +1,29 @@ -// use coercions::*; -use sys::{VALUE, Qnil}; -use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby}; +use sys::{Qnil}; +use super::{UncheckedValue, CheckResult, CheckedValue, ToRust}; use ruby::Value; -impl<'a, T> UncheckedValue> for Value<'a> where Value<'a>: UncheckedValue, CheckedValue<'a, T>: ToRust { - type ToRust = Option>>; +pub struct CheckedOption<'a, T> { + checked: Option> +} + +impl<'a, T> UncheckedValue> for Value<'a> + where Value<'a>: UncheckedValue, + CheckedOption<'a, T>: ToRust> +{ + type ToRust = CheckedOption<'a, T>; fn to_checked(self) -> CheckResult { if unsafe { self.inner() } == unsafe { Qnil } { - Ok(None) + Ok(CheckedOption { checked: None }) } else { - let val = UncheckedValue::::to_checked(self); - match val { - // TODO: This transmute is caused by a compiler issue I think? - Ok(v) => Ok(Some(Box::new(v) as Box>)), - Err(e) => Err(e) - } + UncheckedValue::::to_checked(self) + .map(|_| CheckedOption { checked: Some(unsafe { CheckedValue::::new(self) }) }) } } } -impl<'a, T> ToRust> for Option>> where CheckedValue<'a, T>: ToRust { +impl<'a, T> ToRust> for CheckedOption<'a, T> where CheckedValue<'a, T>: ToRust { fn to_rust(self) -> Option { - self.map(|val| val.to_rust()) + self.checked.map(|c| c.to_rust()) } } - -// impl ToRust for Box> { -// fn to_rust(self) -> T { -// self.to_rust() -// } -// } - -// struct CheckedOption<'a, T> { -// inner: VALUE, -// target: PhantomData, -// frame: CallFrame<'a>, -// present: bool, -// } - -// impl UncheckedValue> for VALUE where VALUE: UncheckedValue { -// type ToRust = CheckedOption; - -// fn to_checked<'a>(self, frame: CallFrame<'a>) -> CheckResult { -// CheckedOption { inner: self, target: PhantomData, frame: frame, present: false } -// } -// } - -// // impl<'a, TARGET, U> ToRust> for CheckedOption<'a, U> where U: ToRust - -// // impl<'a, T, U: ToRust> UncheckedValue> for ruby::Value<'a> where ruby::Value<'a>: UncheckedValue { -// // type ToRust = CheckedOption<'a>; - -// // fn to_checked<'frame>(self, frame: CallFrame<'frame>) -> CheckResult { -// // if unsafe { self.inner() == Qnil } { -// // Ok(CheckedOption { inner: self, present: false }) -// // } else { -// // match UncheckedValue::::to_checked(self, frame) { -// // Ok(checked) => Ok(CheckedOption { inner: self, present: true }), -// // Err(e) => Err(e) -// // } -// // } -// // } -// // } - -// // impl<'a, T, U> ToRust> for CheckedOption<'a> where U: ToRust { -// // fn to_rust(self) -> Option { -// // if self.present { -// // ToRust::>::to_rust(self.inner) -// // } else { -// // None -// // } -// // } -// // } - -// impl ToRuby for Option where T: ToRuby { -// fn to_ruby(&self) -> VALUE { -// match *self { -// Some(ref value) => value.to_ruby(), -// None => unsafe { Qnil } -// } -// } -// } From 268669dc5c67db84b200b18fa6c6a0b20fb3bb2d Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Fri, 2 Jun 2017 17:05:21 -0700 Subject: [PATCH 10/10] All tests pass --- examples/membership/src/lib.rs | 14 ++++++++++---- examples/turbo_blank/src/lib.rs | 9 ++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/examples/membership/src/lib.rs b/examples/membership/src/lib.rs index 1db19e94..4f459f3d 100644 --- a/examples/membership/src/lib.rs +++ b/examples/membership/src/lib.rs @@ -27,13 +27,19 @@ ruby! { } } -// Delete me: +// This is incredibly terrible and illustrates an increasingly bad problem +// with the current factoring around reopen. TLDR: reopen really doesn't +// work at the moment and you shouldn't use it. -use helix::{UncheckedValue, ToRust}; +use helix::{UncheckedValue, ToRust, ruby}; +use helix::coercions::CallFrame; impl AsRef<[usize]> for Array { fn as_ref(&self) -> &[usize] { - let checked = self.helix.to_checked().unwrap(); - checked.to_rust() + let lt: &'static () = unsafe { std::mem::transmute(&()) }; + let frame = unsafe { CallFrame::new(lt) }; + let val = unsafe { ruby::Value::new(self.helix, frame) }; + let checked = UncheckedValue::<&[usize]>::to_checked(val).unwrap(); + ToRust::<&[usize]>::to_rust(checked) } } diff --git a/examples/turbo_blank/src/lib.rs b/examples/turbo_blank/src/lib.rs index 7bb0fe5a..5298c660 100644 --- a/examples/turbo_blank/src/lib.rs +++ b/examples/turbo_blank/src/lib.rs @@ -13,11 +13,14 @@ ruby! { // Delete me: -use helix::{UncheckedValue, ToRust}; +use helix::{UncheckedValue, ToRust, ruby}; +use helix::coercions::CallFrame; impl ToString for RubyString { fn to_string(&self) -> String { - let checked = self.helix.to_checked().unwrap(); - checked.to_rust() + let lt = &(); + let val = unsafe { ruby::Value::new(self.helix, CallFrame::new(lt)) }; + let checked = UncheckedValue::::to_checked(val).unwrap(); + ToRust::::to_rust(checked) } }