1
0
Fork 0
mirror of https://github.com/rubyjs/therubyracer synced 2023-03-27 23:21:42 -04:00

Implement Function and Script

This commit is contained in:
Georgy Angelov 2015-04-04 18:23:23 +00:00
parent 52e52c1a04
commit acf9be1873
14 changed files with 343 additions and 8 deletions

View file

@ -10,6 +10,8 @@ namespace rr {
defineMethod("Enter", &Enter).
defineMethod("Exit", &Exit).
defineMethod("Global", &Global).
store(&Class);
// TODO
@ -58,6 +60,13 @@ namespace rr {
return Qnil;
}
VALUE Context::Global(VALUE self) {
Context context(self);
Locker lock(context.getIsolate());
return Object(context->GetIsolate(), context->Global());
}
// TODO
// template <> void Pointer<v8::ExtensionConfiguration>::unwrap(VALUE value) {
// Data_Get_Struct(value, class v8::ExtensionConfiguration, pointer);

View file

@ -13,8 +13,9 @@ namespace rr {
static VALUE Enter(VALUE self);
static VALUE Exit(VALUE self);
static VALUE Global(VALUE self);
// TODO
// static VALUE Global(VALUE self);
// static VALUE DetachGlobal(VALUE self);
// static VALUE ReattachGlobal(VALUE self, VALUE global);
// static VALUE GetEntered(VALUE self);

95
ext/v8/function.cc Normal file
View file

@ -0,0 +1,95 @@
#include "rr.h"
namespace rr {
void Function::Init() {
ClassBuilder("Function", Object::Class).
defineMethod("NewInstance", &NewInstance).
defineMethod("Call", &Call).
defineMethod("SetName", &SetName).
defineMethod("GetName", &GetName).
defineMethod("GetInferredName", &GetInferredName).
defineMethod("GetScriptLineNumber", &GetScriptLineNumber).
defineMethod("GetScriptColumnNumber", &GetScriptColumnNumber).
defineMethod("GetScriptId", &GetScriptId).
defineMethod("GetScriptOrigin", &GetScriptOrigin).
store(&Class);
}
VALUE Function::NewInstance(int argc, VALUE argv[], VALUE self) {
VALUE args;
rb_scan_args(argc, argv, "01", &args);
Function function(self);
v8::Isolate* isolate = function.getIsolate();
Locker lock(isolate);
if (RTEST(args)) {
std::vector< v8::Handle<v8::Value> > vector(Value::convertRubyArray(isolate, args));
return Object(isolate, function->NewInstance(RARRAY_LENINT(args), &vector[0]));
} else {
return Object(isolate, function->NewInstance());
}
}
VALUE Function::Call(VALUE self, VALUE receiver, VALUE argv) {
Function function(self);
v8::Isolate* isolate = function.getIsolate();
Locker lock(isolate);
std::vector< v8::Handle<v8::Value> > vector(Value::convertRubyArray(isolate, argv));
return Value::handleToRubyObject(isolate, function->Call(Value(receiver), RARRAY_LENINT(argv), &vector[0]));
}
VALUE Function::SetName(VALUE self, VALUE name) {
Function function(self);
Locker lock(function.getIsolate());
function->SetName(String(name));
return Qnil;
}
VALUE Function::GetName(VALUE self) {
Function function(self);
Locker lock(function.getIsolate());
return Value::handleToRubyObject(function.getIsolate(), function->GetName());
}
VALUE Function::GetInferredName(VALUE self) {
Function function(self);
Locker lock(function.getIsolate());
return Value::handleToRubyObject(function.getIsolate(), function->GetInferredName());
}
VALUE Function::GetScriptLineNumber(VALUE self) {
Function function(self);
Locker lock(function.getIsolate());
return INT2FIX(function->GetScriptLineNumber());
}
VALUE Function::GetScriptColumnNumber(VALUE self) {
Function function(self);
Locker lock(function.getIsolate());
return INT2FIX(function->GetScriptColumnNumber());
}
VALUE Function::GetScriptId(VALUE self) {
Function function(self);
Locker lock(function.getIsolate());
return INT2FIX(function->ScriptId());
}
VALUE Function::GetScriptOrigin(VALUE self) {
return not_implemented("GetScriptOrigin");
}
}

26
ext/v8/function.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef RR_FUNCTION
#define RR_FUNCTION
namespace rr {
class Function : public Ref<v8::Function> {
public:
static void Init();
static VALUE NewInstance(int argc, VALUE argv[], VALUE self);
static VALUE Call(VALUE self, VALUE receiver, VALUE argv);
static VALUE SetName(VALUE self, VALUE name);
static VALUE GetName(VALUE self);
static VALUE GetInferredName(VALUE self);
static VALUE GetScriptLineNumber(VALUE self);
static VALUE GetScriptColumnNumber(VALUE self);
static VALUE GetScriptId(VALUE self);
static VALUE GetScriptOrigin(VALUE self);
inline Function(VALUE value) : Ref<v8::Function>(value) {}
inline Function(v8::Isolate* isolate, v8::Handle<v8::Function> function) : Ref<v8::Function>(isolate, function) {}
};
}
#endif

View file

@ -17,16 +17,16 @@ extern "C" {
Object::Init();
Primitive::Init();
String::Init();
Function::Init();
Script::Init();
// Accessor::Init();
// Invocation::Init();
// Signature::Init();
// Array::Init();
// Function::Init();
// Date::Init();
// Constants::Init();
// External::Init();
// Script::Init();
// Template::Init();
// Stack::Init();
// Message::Init();

View file

@ -138,6 +138,26 @@ namespace rr {
v8::Isolate* isolate;
v8::Handle<T> handle;
public:
template <class C>
class array {
public:
inline array(VALUE ary) : argv(ary), vector(RARRAY_LENINT(argv)) { }
inline operator v8::Handle<T>*() {
for (uint32_t i = 0; i < vector.size(); i++) {
vector[i] = C(rb_ary_entry(argv, i));
}
return &vector[0];
}
private:
VALUE argv;
std::vector< v8::Handle<T> > vector;
};
};
template <class T>

View file

@ -10,6 +10,12 @@
#include "ruby/encoding.h"
#endif
inline VALUE not_implemented(const char* message) {
rb_raise(rb_eStandardError, "not yet implemented %s", message);
return Qnil;
}
#include "class_builder.h"
#include "equiv.h"
@ -33,4 +39,7 @@
// This one is named v8_string to avoid name collisions with C's string.h
#include "rr_string.h"
#include "function.h"
#include "script.h"
#endif

48
ext/v8/script.cc Normal file
View file

@ -0,0 +1,48 @@
#include "rr.h"
namespace rr {
void Script::Init() {
ClassBuilder("Script").
defineSingletonMethod("Compile", &Compile).
defineMethod("Run", &Run).
// TODO
// defineMethod("RunWithTimeout", &RunWithTimeout).
store(&Class);
// TODO
// ClassBuilder("ScriptOrigin").
// defineSingletonMethod("new", &ScriptOrigin::initialize).
// store(&ScriptOrigin::Class);
// TODO
// 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::Compile(int argc, VALUE argv[], VALUE self) {
VALUE source, rb_context, origin;
rb_scan_args(argc, argv, "21", &source, &rb_context, &origin);
Context context(rb_context);
Locker lock(context.getIsolate());
// TODO: ScriptOrigin
return Script(context.getIsolate(), v8::Script::Compile(String(source)));
}
VALUE Script::Run(VALUE self, VALUE rb_context) {
Context context(rb_context);
Locker lock(context->GetIsolate());
return Value::handleToRubyObject(context->GetIsolate(), Script(self)->Run());
}
}

19
ext/v8/script.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef RR_SCRIPT
#define RR_SCRIPT
namespace rr {
class Script : public Ref<v8::Script> {
public:
static void Init();
static VALUE Compile(int argc, VALUE argv[], VALUE self);
static VALUE Run(VALUE self, VALUE context);
// static VALUE RunWithTimeout(VALUE self, VALUE timeout);
inline Script(VALUE value) : Ref<v8::Script>(value) {}
inline Script(v8::Isolate* isolate, v8::Handle<v8::Script> script) : Ref<v8::Script>(isolate, script) {}
};
}
#endif

View file

@ -15,7 +15,7 @@ namespace rr {
defineMethod("IsTrue", &IsTrue).
defineMethod("IsFalse", &IsFalse).
defineMethod("IsString", &IsString).
// defineMethod("IsFunction", &IsFunction).
defineMethod("IsFunction", &IsFunction).
// defineMethod("IsArray", &IsArray).
defineMethod("IsObject", &IsObject).
// defineMethod("IsBoolean", &IsBoolean).
@ -83,6 +83,13 @@ namespace rr {
return Bool(value->IsString());
}
VALUE Value::IsFunction(VALUE self) {
Value value(self);
Locker lock(value.getIsolate());
return Bool(value->IsFunction());
}
VALUE Value::IsObject(VALUE self) {
Value value(self);
Locker lock(value.getIsolate());
@ -190,6 +197,10 @@ namespace rr {
// return Date((v8::Handle<v8::Date>)v8::Date::Cast(*handle));
// }
if (handle->IsFunction()) {
return Function(isolate, v8::Handle<v8::Function>::Cast(handle));
}
if (handle->IsObject()) {
return Object(isolate, handle->ToObject());
}
@ -239,4 +250,14 @@ namespace rr {
return v8::Undefined(isolate);
}
std::vector< v8::Handle<v8::Value> > Value::convertRubyArray(v8::Isolate* isolate, VALUE value) {
std::vector< v8::Handle<v8::Value> > vector(RARRAY_LENINT(value));
for (uint32_t i = 0; i < vector.size(); i++) {
vector[i] = Value::rubyObjectToHandle(isolate, rb_ary_entry(value, i));
}
return vector;
}
}

View file

@ -12,7 +12,7 @@ namespace rr {
static VALUE IsTrue(VALUE self);
static VALUE IsFalse(VALUE self);
static VALUE IsString(VALUE self);
// static VALUE IsFunction(VALUE self);
static VALUE IsFunction(VALUE self);
// static VALUE IsArray(VALUE self);
static VALUE IsObject(VALUE self);
static VALUE IsBoolean(VALUE self);
@ -47,6 +47,8 @@ namespace rr {
static VALUE handleToRubyObject(v8::Isolate* isolate, v8::Handle<v8::Value> value);
static v8::Handle<v8::Value> rubyObjectToHandle(v8::Isolate* isolate, VALUE value);
static std::vector< v8::Handle<v8::Value> > convertRubyArray(v8::Isolate* isolate, VALUE value);
static VALUE Empty;
};

53
spec/c/function_spec.rb Normal file
View file

@ -0,0 +1,53 @@
require 'c_spec_helper'
describe V8::C::Function do
requires_v8_context
it 'can be called' do
fn = run '(function() { return "foo" })'
expect(fn.Call(@ctx.Global, []).Utf8Value).to eq 'foo'
end
it 'can be called with arguments and context' do
fn = run '(function(one, two, three) {this.one = one; this.two = two; this.three = three})'
one = V8::C::Object.New(@isolate)
two = V8::C::Object.New(@isolate)
fn.Call(@ctx.Global, [one, two, 3])
expect(@ctx.Global.Get(V8::C::String.NewFromUtf8(@isolate, 'one'))).to eq one
expect(@ctx.Global.Get(V8::C::String.NewFromUtf8(@isolate, 'two'))).to eq two
expect(@ctx.Global.Get(V8::C::String.NewFromUtf8(@isolate, 'three'))).to eq 3
end
it 'can be called as a constructor' do
fn = run '(function() {this.foo = "foo"})'
expect(fn.NewInstance.Get(V8::C::String.NewFromUtf8(@isolate, 'foo')).Utf8Value).to eq 'foo'
end
# it 'can be called as a constructor with arguments' do
# fn = run '(function(foo) {this.foo = foo})'
# object = fn.NewInstance([V8::C::String.NewFromUtf8(@isolate, 'bar')])
# expect(object.Get(V8::C::String.NewFromUtf8(@isolate, 'foo')).Utf8Value).to eq 'bar'
# end
# TODO
# it 'doesn\'t kill the world if invoking it throws a javascript exception' do
# V8::C::TryCatch do
# fn = run '(function() { throw new Error("boom!")})'
# fn.Call(@ctx.Global(), [])
# fn.NewInstance([])
# end
# end
def run(source)
source = V8::C::String.NewFromUtf8(@isolate, source.to_s)
filename = V8::C::String.NewFromUtf8(@isolate, "<eval>")
script = V8::C::Script.Compile(source, filename)
result = script.Run(@ctx)
result.kind_of?(V8::C::String) ? result.Utf8Value : result
end
end

32
spec/c/script_spec.rb Normal file
View file

@ -0,0 +1,32 @@
require 'c_spec_helper'
describe V8::C::Script do
requires_v8_context
# TODO
# it 'can run a script and return a polymorphic result' do
# source = V8::C::String::New("(new Array())")
# script = V8::C::Script::New(source)
#
# result = script.Run()
# expect(result).to be_an V8::C::Array
# end
# TODO
# it 'can accept precompiled script data' do
# source = "7 * 6"
# name = V8::C::String::New("<spec>")
# 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
# TODO
# 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

View file

@ -14,12 +14,12 @@ module V8ContextHelpers
@isolate = V8::C::Isolate.New
V8::C::HandleScope(@isolate) do
@cxt = V8::C::Context::New(@isolate)
@ctx = V8::C::Context::New(@isolate)
begin
@cxt.Enter
@ctx.Enter
yield
ensure
@cxt.Exit
@ctx.Exit
end
end
end