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:
parent
52e52c1a04
commit
acf9be1873
14 changed files with 343 additions and 8 deletions
|
@ -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);
|
||||
|
|
|
@ -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
95
ext/v8/function.cc
Normal 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
26
ext/v8/function.h
Normal 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
|
|
@ -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();
|
||||
|
|
20
ext/v8/ref.h
20
ext/v8/ref.h
|
@ -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>
|
||||
|
|
|
@ -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
48
ext/v8/script.cc
Normal 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
19
ext/v8/script.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
53
spec/c/function_spec.rb
Normal 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
32
spec/c/script_spec.rb
Normal 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
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue