diff --git a/ext/v8/rr.h b/ext/v8/rr.h index 08ec282..9a9640a 100644 --- a/ext/v8/rr.h +++ b/ext/v8/rr.h @@ -4,6 +4,9 @@ #include #include #include +#ifdef HAVE_RUBY_ENCODING_H +#include "ruby/encoding.h" +#endif namespace rr { @@ -70,6 +73,28 @@ private: T defaultValue; }; +/** +* A pointer to V8 object managed by Ruby +*/ + +template class Pointer { +public: + inline Pointer(T* t) : pointer(t) {}; + inline Pointer() {}; + inline operator T*() {return pointer;} + inline T* operator ->() {return pointer;} + inline operator VALUE() { + return Data_Wrap_Struct(Class, 0, &release, pointer); + } + static void release(T* pointer) { + delete pointer; + } + static VALUE Class; +protected: + T* pointer; +}; +template VALUE Pointer::Class; + /** * A Reference to a V8 managed object */ @@ -196,10 +221,34 @@ private: }; }; +class ScriptOrigin : public Pointer { +public: + inline ScriptOrigin(v8::ScriptOrigin* o) : Pointer(o) {}; + inline ScriptOrigin(VALUE value) { + Data_Get_Struct(value, class v8::ScriptOrigin, pointer); + } + + static VALUE initialize(int argc, VALUE argv[], VALUE self); +}; + +class ScriptData : public Pointer { +public: + inline ScriptData(v8::ScriptData* d) : Pointer(d) {}; + inline ScriptData(VALUE value) { + Data_Get_Struct(value, class v8::ScriptData, pointer); + } + + static VALUE PreCompile(VALUE self, VALUE input, VALUE length); + static VALUE New(VALUE self, VALUE data, VALUE length); + static VALUE Length(VALUE self); + static VALUE Data(VALUE self); + static VALUE HasError(VALUE self); +}; + class Script : public Ref { public: static void Init(); - static VALUE New(VALUE klass, VALUE source, VALUE filename); + static VALUE New(int argc, VALUE argv[], VALUE self); static VALUE Run(VALUE self); inline Script(VALUE value) : Ref(value) {} diff --git a/ext/v8/script.cc b/ext/v8/script.cc index 2a7301b..1ce5b5e 100644 --- a/ext/v8/script.cc +++ b/ext/v8/script.cc @@ -7,10 +7,62 @@ void Script::Init() { defineSingletonMethod("New", &New). defineMethod("Run", &Run). store(&Class); + ClassBuilder("ScriptOrigin"). + defineSingletonMethod("new", &ScriptOrigin::initialize). + store(&ScriptOrigin::Class); + ClassBuilder("ScriptData"). + defineSingletonMethod("PreCompile", &ScriptData::PreCompile). + defineSingletonMethod("New", &ScriptData::New). + defineMethod("Length", &ScriptData::Length). + defineMethod("Data", &ScriptData::Data). + defineMethod("HasError", &ScriptData::HasError). + store(&ScriptData::Class); } -VALUE Script::New(VALUE klass, VALUE source, VALUE filename) { - return Script(v8::Script::New(String(source), Value(filename))); +VALUE ScriptOrigin::initialize(int argc, VALUE argv[], VALUE self) { + VALUE name; VALUE line_offset; VALUE column_offset; + rb_scan_args(argc, argv, "12", &name, &line_offset, &column_offset); + v8::Handle loff = v8::Integer::New(RTEST(line_offset) ? NUM2INT(line_offset) : 0); + v8::Handle coff = v8::Integer::New(RTEST(column_offset) ? NUM2INT(column_offset) : 0); + return ScriptOrigin(new v8::ScriptOrigin(*String(name), loff, coff)); +} + +VALUE ScriptData::PreCompile(VALUE self, VALUE input, VALUE length) { +#ifdef HAVE_RUBY_ENCODING_H + if (!rb_equal(rb_enc_from_encoding(rb_utf8_encoding()), rb_obj_encoding(input))) { + rb_warn("ScriptData::Precompile only accepts UTF-8 encoded source, not: %s", RSTRING_PTR(rb_inspect(rb_obj_encoding(input)))); + } +#endif + return ScriptData(v8::ScriptData::PreCompile(RSTRING_PTR(input), NUM2INT(length))); +} +VALUE ScriptData::New(VALUE self, VALUE data, VALUE length) { + return ScriptData(v8::ScriptData::New(RSTRING_PTR(data), NUM2INT(length))); +} +VALUE ScriptData::Length(VALUE self) { + return ScriptData(self)->Length(); +} +VALUE ScriptData::Data(VALUE self) { + ScriptData data(self); +#ifdef HAVE_RUBY_ENCODING_H + return rb_enc_str_new(data->Data(), data->Length(), rb_enc_find("BINARY")); +#else + return rb_str_new(data->Data(), data->Length()); +#endif +} + +VALUE ScriptData::HasError(VALUE self) { + return ScriptData(self)->HasError(); +} + +VALUE Script::New(int argc, VALUE argv[], VALUE self) { + VALUE source; VALUE origin; VALUE pre_data; VALUE script_data; + rb_scan_args(argc, argv, "13", &source, &origin, &pre_data, &script_data); + if (argc == 2) { + VALUE filename = origin; + return Script(v8::Script::New(String(source), Value(filename))); + } else { + return Script(v8::Script::New(String(source), ScriptOrigin(origin), ScriptData(pre_data), String(script_data))); + } } VALUE Script::Run(VALUE self) { diff --git a/ext/v8/string.cc b/ext/v8/string.cc index 159cf6b..b0db7ac 100644 --- a/ext/v8/string.cc +++ b/ext/v8/string.cc @@ -1,7 +1,4 @@ #include "rr.h" -#ifdef HAVE_RUBY_ENCODING_H -#include "ruby/encoding.h" -#endif namespace rr { diff --git a/spec/c/script_spec.rb b/spec/c/script_spec.rb index ec07f36..8ba8aee 100644 --- a/spec/c/script_spec.rb +++ b/spec/c/script_spec.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 require 'spec_helper' describe V8::C::Script do @@ -8,4 +9,20 @@ describe V8::C::Script do result = script.Run() result.should be_kind_of V8::C::Array end + + it "can accept precompiled script data" do + source = "7 * 6" + name = V8::C::String::New("") + origin = V8::C::ScriptOrigin.new(name) + data = V8::C::ScriptData::PreCompile(source, source.length) + data.HasError().should be_false + script = V8::C::Script::New(V8::C::String::New(source), origin, data) + script.Run().should eql 42 + end + + it "can detect errors in the script data" do + source = "^ = ;" + data = V8::C::ScriptData::PreCompile(source, source.length) + data.HasError().should be_true + end end \ No newline at end of file