begin internal rewrite.
This commit is contained in:
parent
490002aad7
commit
9cb4f1c7d2
|
@ -1,13 +0,0 @@
|
|||
.bundle
|
||||
.rvmrc
|
||||
Gemfile.lock
|
||||
v8.bundle
|
||||
v8.so
|
||||
*.o
|
||||
*.gem
|
||||
*.rbc
|
||||
*.log
|
||||
*~
|
||||
pkg/
|
||||
tmp/
|
||||
.yardoc/
|
3
Gemfile
3
Gemfile
|
@ -1,3 +0,0 @@
|
|||
source 'http://rubygems.org'
|
||||
gemspec
|
||||
gem 'redjs', :git => 'git://github.com/cowboyd/redjs.git', :tag => "v0.6.0", :group => :test
|
167
README.md
167
README.md
|
@ -1,167 +0,0 @@
|
|||
# therubyracer
|
||||
|
||||
* [http://github.com/cowboyd/therubyracer](http://github.com/cowboyd/therubyracer)
|
||||
* [http://groups.google.com/group/therubyracer](http://groups.google.com/group/therubyracer)
|
||||
* [irc://irc.freenode.net/therubyracer](http://groups.google.com/group/therubyracer)
|
||||
* [Documentation](https://github.com/cowboyd/therubyracer/wiki)
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Embed the V8 Javascript interpreter into Ruby.
|
||||
|
||||
|
||||
## FEATURES
|
||||
|
||||
* Evaluate Javascript from with in Ruby
|
||||
* Embed your Ruby objects into the Javascript world
|
||||
* Manipulate JavaScript objects and call JavaScript functions from Ruby
|
||||
* API compatible with the The Ruby Rhino (for JRuby: http://github.com/cowboyd/therubyrhino)
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
gem install therubyracer ;: stable
|
||||
gem install therubyracer --pre ;: bleeding edge
|
||||
|
||||
then in your ruby code
|
||||
|
||||
require 'v8'
|
||||
|
||||
evaluate some simple javascript
|
||||
|
||||
cxt = V8::Context.new
|
||||
cxt.eval('7 * 6') #=> 42
|
||||
|
||||
embed values into the scope of your context
|
||||
|
||||
cxt['foo'] = "bar"
|
||||
cxt.eval('foo') # => "bar"
|
||||
|
||||
embed ruby code into your scope and call it from javascript
|
||||
|
||||
cxt["say"] = lambda {|word, times| word * times}
|
||||
cxt.eval("say('Hello', 3)") #=> HelloHelloHello
|
||||
|
||||
embed a ruby object into your scope and access its properties/methods from javascript
|
||||
|
||||
class MyMath
|
||||
def plus(lhs, rhs)
|
||||
lhs + rhs
|
||||
end
|
||||
end
|
||||
|
||||
cxt['math'] = MyMath.new
|
||||
cxt.eval("math.plus(20,22)") #=> 42
|
||||
|
||||
make a ruby object *be* your global javascript scope.
|
||||
|
||||
math = MyMath.new
|
||||
V8::Context.new(:with => math) do |cxt|
|
||||
cxt.eval("plus(20,22)") #=> 42
|
||||
end
|
||||
|
||||
you can do the same thing with Object#eval_js
|
||||
|
||||
math.eval_js("plus(20,22)")
|
||||
|
||||
## Different ways of loading javascript source
|
||||
|
||||
In addition to just evaluating strings, you can also use streams such as files.
|
||||
|
||||
evaluate bytes read from any File/IO object:
|
||||
|
||||
File.open("mysource.js") do |file|
|
||||
cxt.eval(file, "mysource.js")
|
||||
end
|
||||
|
||||
or load it by filename
|
||||
|
||||
cxt.load("mysource.js")
|
||||
|
||||
|
||||
## Safe by default, dangerous by demand
|
||||
|
||||
The Ruby Racer is designed to let you evaluate javascript as safely as possible unless you tell it to do something more
|
||||
dangerous. The default context is a hermetically sealed javascript environment with only the standard javascript objects
|
||||
and functions. Nothing from the ruby world is accessible at all.
|
||||
|
||||
For ruby objects that you explicitly embed into javascript, by default only the _public_ methods _below_ `Object` are
|
||||
exposed by default. E.g.
|
||||
|
||||
class A
|
||||
def a
|
||||
"a"
|
||||
end
|
||||
|
||||
def to_s
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
class B < A
|
||||
def b
|
||||
"b"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
V8::Context.new do |cxt|
|
||||
cxt['a'] = A.new
|
||||
cxt['b'] = B.new
|
||||
cxt.eval("a.a") # => 'a'
|
||||
cxt.eval("b.b") # => 'b'
|
||||
cxt.eval("b.a") # => 'a'
|
||||
cxt.eval("b.to_s") # => #<B:0x101776be8> (because A explicitly defined it)
|
||||
cxt.eval("b.object_id") #=> undefined, object_id is on Object
|
||||
end
|
||||
|
||||
If needed, you can override the [Ruby Access](https://github.com/cowboyd/therubyracer/blob/master/lib/v8/access.rb)
|
||||
to allow whatever behavior you'd like
|
||||
|
||||
More documentation can be found on the [github wiki](https://github.com/cowboyd/therubyracer/wiki)
|
||||
|
||||
## REQUIREMENTS:
|
||||
|
||||
* python >= 2.5 (required to compile v8)
|
||||
* C++ compiler
|
||||
|
||||
## Rails/Bundler
|
||||
|
||||
To use the ruby racer in rails, or any application using Bundler to manage gems, add the following to your Gemfile
|
||||
|
||||
gem "therubyracer", :require => 'v8'
|
||||
gem "therubyracer", "~> 0.8.2.pre" #bleeding edge.
|
||||
|
||||
## DEVELOP
|
||||
git clone git://github.com/cowboyd/therubyracer.git
|
||||
cd therubyracer
|
||||
git submodule update --init
|
||||
bundle install
|
||||
rake compile
|
||||
|
||||
## Sponsored by
|
||||
<a href="http://thefrontside.net">![The Frontside](http://github.com/cowboyd/therubyracer/raw/master/thefrontside.png)</a>
|
||||
|
||||
## LICENSE:
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009,2010,2011 Charles Lowell
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
Rakefile
26
Rakefile
|
@ -1,26 +0,0 @@
|
|||
require 'bundler'
|
||||
require 'bundler/setup'
|
||||
require "rake/extensiontask"
|
||||
require "rspec/core/rake_task"
|
||||
Bundler::GemHelper.install_tasks
|
||||
|
||||
task :default => :spec
|
||||
|
||||
desc "remove all generated artifacts except built v8 objects"
|
||||
task :clean do
|
||||
sh "rm -rf pkg"
|
||||
sh "rm -rf ext/v8/*.bundle ext/v8/*.so"
|
||||
sh "rm -rf lib/v8/*.bundle lib/v8/*.so"
|
||||
end
|
||||
|
||||
Rake::ExtensionTask.new("v8", eval(File.read("therubyracer.gemspec"))) do |ext|
|
||||
ext.lib_dir = "lib/v8"
|
||||
ext.source_pattern = "*.{cpp,h}"
|
||||
end
|
||||
|
||||
RSpec::Core::RakeTask.new(:spec) do |spec|
|
||||
spec.rspec_opts = ['--color', "--format documentation"]
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
begin
|
||||
require 'v8'
|
||||
rescue LoadError
|
||||
require 'rubygems'
|
||||
require 'v8'
|
||||
end
|
||||
require 'v8/cli'
|
||||
|
||||
V8::CLI.run(File.basename(__FILE__), ARGV)
|
|
@ -1,26 +0,0 @@
|
|||
require 'mkmf'
|
||||
require 'set'
|
||||
begin
|
||||
require 'libv8'
|
||||
rescue LoadError
|
||||
require 'rubygems'
|
||||
require 'libv8'
|
||||
end
|
||||
|
||||
have_library('objc') if RUBY_PLATFORM =~ /darwin/
|
||||
|
||||
#we have to manually prepend the libv8 include path to INCFLAGS
|
||||
#since find_header() does not actually work as advertized.
|
||||
#see https://github.com/cowboyd/therubyracer/issues/91
|
||||
$INCFLAGS.insert 0, "-I#{Libv8.include_path} "
|
||||
|
||||
$CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
|
||||
$CPPFLAGS += " -g" unless $CPPFLAGS.split.include? "-g"
|
||||
$CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
|
||||
|
||||
$LDFLAGS.insert 0, "#{Libv8.library_path}/libv8.#{$LIBEXT} "
|
||||
$LIBS << ' -lpthread'
|
||||
|
||||
CONFIG['LDSHARED'] = '$(CXX) -shared' unless RUBY_PLATFORM =~ /darwin/
|
||||
|
||||
create_makefile('v8')
|
189
ext/v8/rr.cpp
189
ext/v8/rr.cpp
|
@ -1,189 +0,0 @@
|
|||
#include "rr.h"
|
||||
#include "v8_context.h"
|
||||
#include "v8_handle.h"
|
||||
#include "v8_value.h"
|
||||
#include "v8_object.h"
|
||||
#include "v8_function.h"
|
||||
#include "v8_array.h"
|
||||
#include "v8_string.h"
|
||||
#include "v8_date.h"
|
||||
#include "v8_message.h"
|
||||
#include "v8_external.h"
|
||||
#include "v8_exception.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
VALUE rr_define_class(const char *name, VALUE superclass) {
|
||||
VALUE V8 = rb_define_module("V8");
|
||||
VALUE V8_C = rb_define_module_under(V8, "C");
|
||||
VALUE klass = rb_define_class_under(V8_C, name, superclass);
|
||||
rb_funcall(klass, rb_intern("private_class_method"), 1, rb_str_new2("new"));
|
||||
return klass;
|
||||
}
|
||||
|
||||
VALUE rr_define_module(const char *name) {
|
||||
VALUE V8 = rb_define_module("V8");
|
||||
VALUE V8_C = rb_define_module_under(V8, "C");
|
||||
return rb_define_module_under(V8_C, name);
|
||||
}
|
||||
|
||||
VALUE rr_define_const(const char *name, VALUE value) {
|
||||
VALUE V8 = rb_define_module("V8");
|
||||
VALUE V8_C = rb_define_module_under(V8, "C");
|
||||
rb_define_const(V8_C, name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
VALUE rr_const_get(const char *name) {
|
||||
VALUE V8 = rb_define_module("V8");
|
||||
VALUE V8_C = rb_define_module_under(V8, "C");
|
||||
return rb_const_get(V8_C, rb_intern(name));
|
||||
}
|
||||
|
||||
VALUE rr_define_finalizer(VALUE object, void* finalizer, VALUE data) {
|
||||
VALUE finalizer_proc = rb_proc_new((VALUE (*)(...))finalizer, data);
|
||||
rb_iv_set(finalizer_proc, "data", data);
|
||||
VALUE ospace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
|
||||
rb_funcall(ospace, rb_intern("define_finalizer"), 2, object, finalizer_proc);
|
||||
}
|
||||
|
||||
VALUE rr_v82rb(Handle<Value> value) {
|
||||
if (value.IsEmpty()) {
|
||||
return rr_v8_value_empty();
|
||||
}
|
||||
if (value->IsUndefined() || value->IsNull()) {
|
||||
return Qnil;
|
||||
}
|
||||
if (value->IsExternal()) {
|
||||
return rr_reflect_v8_external(value);
|
||||
}
|
||||
if (value->IsUint32()) {
|
||||
return UINT2NUM(value->Uint32Value());
|
||||
}
|
||||
if (value->IsInt32()) {
|
||||
return INT2FIX(value->Int32Value());
|
||||
}
|
||||
if (value->IsBoolean()) {
|
||||
return value->BooleanValue() ? Qtrue : Qfalse;
|
||||
}
|
||||
if (value->IsNumber()) {
|
||||
return rb_float_new(value->NumberValue());
|
||||
}
|
||||
if (value->IsString()) {
|
||||
return rr_reflect_v8_string(value);
|
||||
}
|
||||
if (value->IsFunction()) {
|
||||
return rr_reflect_v8_function(value);
|
||||
}
|
||||
if (value->IsArray()) {
|
||||
return rr_reflect_v8_array(value);
|
||||
}
|
||||
if (value->IsDate()) {
|
||||
return rr_reflect_v8_date(value);
|
||||
}
|
||||
if (value->IsObject()) {
|
||||
return rr_reflect_v8_object(value);
|
||||
}
|
||||
return rr_wrap_v8_value(value);
|
||||
}
|
||||
|
||||
VALUE rr_v82rb(Handle<Message> value) {
|
||||
return rr_reflect_v8_message(value);
|
||||
}
|
||||
VALUE rr_v82rb(Handle<StackTrace> value) {
|
||||
return rr_reflect_v8_stacktrace(value);
|
||||
}
|
||||
VALUE rr_v82rb(Handle<StackFrame> value) {
|
||||
return rr_reflect_v8_stackframe(value);
|
||||
}
|
||||
|
||||
VALUE rr_v82rb(Handle<Boolean> value) {
|
||||
return rr_v82rb((Handle<Value>)value);
|
||||
}
|
||||
VALUE rr_v82rb(Handle<Number> value) {
|
||||
return rr_v82rb((Handle<Value>)value);
|
||||
}
|
||||
VALUE rr_v82rb(Handle<String> value) {
|
||||
return rr_v82rb((Handle<Value>)value);
|
||||
}
|
||||
VALUE rr_v82rb(Handle<Object> value) {
|
||||
return rr_v82rb((Handle<Value>)value);
|
||||
}
|
||||
VALUE rr_v82rb(Handle<Array> value) {
|
||||
return rr_v82rb((Handle<Value>)value);
|
||||
}
|
||||
VALUE rr_v82rb(v8::Handle<v8::Function> value) {
|
||||
return rr_v82rb((Handle<Value>)value);
|
||||
}
|
||||
VALUE rr_v82rb(Handle<Integer> value) {
|
||||
return rr_v82rb((Handle<Value>)value);
|
||||
}
|
||||
VALUE rr_v82rb(Handle<Uint32> value) {
|
||||
return rr_v82rb((Handle<Value>)value);
|
||||
}
|
||||
VALUE rr_v82rb(Handle<Int32> value) {
|
||||
return rr_v82rb((Handle<Value>)value);
|
||||
}
|
||||
VALUE rr_v82rb(bool value) {
|
||||
return value ? Qtrue : Qfalse;
|
||||
}
|
||||
VALUE rr_v82rb(double value) {
|
||||
return rb_float_new(value);
|
||||
}
|
||||
VALUE rr_v82rb(int64_t value) {
|
||||
return LONG2NUM(value);
|
||||
}
|
||||
VALUE rr_v82rb(uint32_t value) {
|
||||
return UINT2NUM(value);
|
||||
}
|
||||
VALUE rr_v82rb(int32_t value) {
|
||||
return INT2FIX(value);
|
||||
}
|
||||
|
||||
Handle<Value> rr_rb2v8(VALUE value) {
|
||||
switch (TYPE(value)) {
|
||||
case T_FIXNUM:
|
||||
// TODO: use this conversion if value will fit in 32 bits.
|
||||
// return Integer::New(FIX2LONG(value));
|
||||
case T_FLOAT:
|
||||
return Number::New(NUM2DBL(value));
|
||||
case T_STRING:
|
||||
return String::New(RSTRING_PTR(value), RSTRING_LEN(value));
|
||||
case T_NIL:
|
||||
return Null();
|
||||
case T_TRUE:
|
||||
return True();
|
||||
case T_FALSE:
|
||||
return False();
|
||||
case T_DATA:
|
||||
return rr_v8_handle<Value>(value);
|
||||
case T_OBJECT:
|
||||
case T_CLASS:
|
||||
case T_ICLASS:
|
||||
case T_MODULE:
|
||||
case T_REGEXP:
|
||||
case T_MATCH:
|
||||
case T_ARRAY:
|
||||
case T_HASH:
|
||||
case T_STRUCT:
|
||||
case T_BIGNUM:
|
||||
case T_FILE:
|
||||
case T_SYMBOL:
|
||||
// case T_BLKTAG: (not in 1.9)
|
||||
case T_UNDEF:
|
||||
// case T_VARMAP: (not in 1.9)
|
||||
// case T_SCOPE: (not in 1.9)
|
||||
case T_NODE:
|
||||
default:
|
||||
rb_warn("unknown conversion to V8 for: %s", RSTRING_PTR(rb_inspect(value)));
|
||||
return String::New("Undefined Conversion");
|
||||
}
|
||||
|
||||
return Undefined();
|
||||
}
|
||||
// VALUE rr_v82rb(v8::ScriptData *data) {
|
||||
// return rr_thunk(rr_wrap_script_data(data));
|
||||
// }
|
||||
|
||||
|
||||
|
41
ext/v8/rr.h
41
ext/v8/rr.h
|
@ -1,41 +0,0 @@
|
|||
#ifndef _THE_RUBY_RACER_
|
||||
#define _THE_RUBY_RACER_
|
||||
|
||||
#include <ruby.h>
|
||||
#include <v8.h>
|
||||
|
||||
#define rr_define_method(klass, name, impl, argc) rb_define_method(klass, name, (VALUE(*)(...))impl, argc)
|
||||
#define rr_define_singleton_method(object, name, impl, argc) rb_define_singleton_method(object, name, (VALUE(*)(...))impl, argc)
|
||||
|
||||
VALUE rr_define_class(const char *name, VALUE superclass = rb_cObject);
|
||||
VALUE rr_define_module(const char *name);
|
||||
VALUE rr_define_const(const char *name, VALUE value);
|
||||
VALUE rr_const_get(const char *name);
|
||||
VALUE rr_define_finalizer(VALUE object, void* finalizer, VALUE data);
|
||||
|
||||
extern "C" VALUE rb_proc_new(VALUE (*)(ANYARGS/* VALUE yieldarg[, VALUE procarg] */), VALUE);
|
||||
|
||||
|
||||
VALUE rr_v82rb(v8::Handle<v8::Value> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::Boolean> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::Number> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::String> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::Object> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::Array> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::Function> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::Integer> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::Uint32> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::Int32> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::Message> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::StackTrace> value);
|
||||
VALUE rr_v82rb(v8::Handle<v8::StackFrame> value);
|
||||
|
||||
VALUE rr_v82rb(bool value);
|
||||
VALUE rr_v82rb(double value);
|
||||
VALUE rr_v82rb(int64_t value);
|
||||
VALUE rr_v82rb(uint32_t value);
|
||||
VALUE rr_v82rb(int32_t value);
|
||||
|
||||
v8::Handle<v8::Value> rr_rb2v8(VALUE value);
|
||||
|
||||
#endif
|
|
@ -1,48 +0,0 @@
|
|||
#include "v8_handle.h"
|
||||
#include "v8_context.h"
|
||||
#include "v8_value.h"
|
||||
#include "v8_string.h"
|
||||
#include "v8_object.h"
|
||||
#include "v8_array.h"
|
||||
#include "v8_message.h"
|
||||
#include "v8_function.h"
|
||||
#include "v8_date.h"
|
||||
#include "v8_script.h"
|
||||
#include "v8_template.h"
|
||||
#include "v8_try_catch.h"
|
||||
#include "v8_callbacks.h"
|
||||
#include "v8_external.h"
|
||||
#include "v8_exception.h"
|
||||
#include "v8_locker.h"
|
||||
#include "v8_debug.h"
|
||||
#include "v8_v8.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
void Init_v8();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void Init_v8() {
|
||||
v8::Locker locker;
|
||||
rr_init_handle();
|
||||
rr_init_context();
|
||||
rr_init_value();
|
||||
rr_init_string();
|
||||
rr_init_script();
|
||||
rr_init_template();
|
||||
rr_init_object();
|
||||
rr_init_function();
|
||||
rr_init_v8_array();
|
||||
rr_init_v8_date();
|
||||
rr_init_message();
|
||||
rr_init_v8_try_catch();
|
||||
rr_init_v8_callbacks();
|
||||
rr_init_v8_external();
|
||||
rr_init_v8_exception();
|
||||
rr_init_v8_locker();
|
||||
rr_init_v8_debug();
|
||||
rr_init_v8_v8();
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
#include "v8_handle.h"
|
||||
#include "v8_array.h"
|
||||
#include "v8_object.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
VALUE ArrayClass;
|
||||
|
||||
Persistent<Array>& unwrap(VALUE self) {
|
||||
return rr_v8_handle<Array>(self);
|
||||
}
|
||||
|
||||
VALUE New(int argc, VALUE *argv, VALUE self) {
|
||||
if (!Context::InContext()) {
|
||||
rb_raise(rb_eScriptError, "must be in a context to call Array::New()");
|
||||
return Qnil;
|
||||
}
|
||||
VALUE length;
|
||||
rb_scan_args(argc, argv, "01", &length);
|
||||
if (NIL_P(length)) {
|
||||
length = INT2FIX(0);
|
||||
}
|
||||
HandleScope scope;
|
||||
return rr_v8_handle_new(self, Array::New(NUM2INT(length)));
|
||||
}
|
||||
|
||||
VALUE Length(VALUE self) {
|
||||
return rr_v82rb(unwrap(self)->Length());
|
||||
}
|
||||
|
||||
VALUE CloneElementAt(VALUE self, VALUE index) {
|
||||
return rr_v82rb(unwrap(self)->CloneElementAt(NUM2UINT(index)));
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_v8_array() {
|
||||
ArrayClass = rr_define_class("Array", rr_v8_object_class());
|
||||
rr_define_singleton_method(ArrayClass, "New", New, -1);
|
||||
rr_define_method(ArrayClass, "Length", Length, 0);
|
||||
rr_define_method(ArrayClass, "CloneElementAt", CloneElementAt, 1);
|
||||
}
|
||||
|
||||
VALUE rr_reflect_v8_array(Handle<Value> value) {
|
||||
return rr_reflect_v8_object_as(value, ArrayClass);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef _RR_V8_ARRAY_
|
||||
#define _RR_V8_ARRAY_
|
||||
|
||||
#include "rr.h"
|
||||
|
||||
void rr_init_v8_array();
|
||||
VALUE rr_reflect_v8_array(v8::Handle<v8::Value> value);
|
||||
#endif
|
|
@ -1,81 +0,0 @@
|
|||
#include "v8_callbacks.h"
|
||||
#include "rr.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
|
||||
namespace {
|
||||
VALUE ArgumentsClass;
|
||||
VALUE AccessorInfoClass;
|
||||
|
||||
VALUE _Data(VALUE self) {
|
||||
return rb_iv_get(self, "data");
|
||||
}
|
||||
|
||||
namespace Accessor {
|
||||
AccessorInfo *info(VALUE value) {
|
||||
AccessorInfo* i = 0;
|
||||
Data_Get_Struct(value, class AccessorInfo, i);
|
||||
return i;
|
||||
}
|
||||
VALUE This(VALUE self) {
|
||||
return rr_v82rb(info(self)->This());
|
||||
}
|
||||
VALUE Holder(VALUE self) {
|
||||
return rr_v82rb(info(self)->Holder());
|
||||
}
|
||||
}
|
||||
|
||||
namespace Args {
|
||||
Arguments* args(VALUE value) {
|
||||
Arguments *arguments = 0;
|
||||
Data_Get_Struct(value, class Arguments, arguments);
|
||||
return arguments;
|
||||
}
|
||||
VALUE This(VALUE self) {
|
||||
return rr_v82rb(args(self)->This());
|
||||
}
|
||||
|
||||
VALUE Holder(VALUE self) {
|
||||
return rr_v82rb(args(self)->Holder());
|
||||
}
|
||||
|
||||
VALUE Length(VALUE self) {
|
||||
return rr_v82rb(args(self)->Length());
|
||||
}
|
||||
VALUE Get(VALUE self, VALUE index) {
|
||||
int i = NUM2INT(index);
|
||||
return rr_v82rb((*args(self))[i]);
|
||||
}
|
||||
VALUE Callee(VALUE self) {
|
||||
return rr_v82rb(args(self)->Callee());
|
||||
}
|
||||
VALUE IsConstructCall(VALUE self) {
|
||||
return rr_v82rb(args(self)->IsConstructCall());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void rr_init_v8_callbacks() {
|
||||
AccessorInfoClass = rr_define_class("AccessorInfo");
|
||||
rr_define_method(AccessorInfoClass, "This", Accessor::This, 0);
|
||||
rr_define_method(AccessorInfoClass, "Holder", Accessor::Holder, 0);
|
||||
rr_define_method(AccessorInfoClass, "Data", _Data, 0);
|
||||
|
||||
ArgumentsClass = rr_define_class("Arguments");
|
||||
rr_define_method(ArgumentsClass, "This", Args::This, 0);
|
||||
rr_define_method(ArgumentsClass, "Holder", Args::Holder, 0);
|
||||
rr_define_method(ArgumentsClass, "Data", _Data, 0);
|
||||
rr_define_method(ArgumentsClass, "Length", Args::Length, 0);
|
||||
rr_define_method(ArgumentsClass, "Callee", Args::Callee, 0);
|
||||
rr_define_method(ArgumentsClass, "IsConstructCall", Args::IsConstructCall, 0);
|
||||
rr_define_method(ArgumentsClass, "[]", Args::Get, 1);
|
||||
}
|
||||
|
||||
VALUE rr_v82rb(const AccessorInfo& info) {
|
||||
return Data_Wrap_Struct(AccessorInfoClass, 0, 0, (void*)&info);
|
||||
}
|
||||
VALUE rr_v82rb(const Arguments& arguments) {
|
||||
return Data_Wrap_Struct(ArgumentsClass, 0, 0, (void*)&arguments);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef _RR_V8_CALLBACKS_
|
||||
#define _RR_V8_CALLBACKS_
|
||||
#include "rr.h"
|
||||
void rr_init_v8_callbacks();
|
||||
VALUE rr_v82rb(const v8::AccessorInfo& info);
|
||||
VALUE rr_v82rb(const v8::Arguments& arguments);
|
||||
|
||||
#endif
|
|
@ -1,92 +0,0 @@
|
|||
#include "rr.h"
|
||||
#include "v8_handle.h"
|
||||
#include "v8_context.h"
|
||||
#include "v8_message.h"
|
||||
#include "v8_template.h"
|
||||
#include "v8_external.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
|
||||
VALUE ContextClass;
|
||||
|
||||
Persistent<Context>& unwrap(VALUE value) {
|
||||
return rr_v8_handle<Context>(value);
|
||||
}
|
||||
|
||||
VALUE New(int argc, VALUE *argv, VALUE self) {
|
||||
HandleScope handles;
|
||||
VALUE global_template; VALUE global_object;
|
||||
rb_scan_args(argc,argv, "02", &global_template, &global_object);
|
||||
Handle<ObjectTemplate> v8_global_template(NIL_P(global_template) ? Handle<ObjectTemplate>() : rr_v8_handle<ObjectTemplate>(global_template));
|
||||
Handle<Value> v8_global_object(NIL_P(global_object) ? Handle<Value>() : rr_v8_handle<Value>(global_object));
|
||||
Persistent<Context> cxt(Context::New(0, v8_global_template, v8_global_object));
|
||||
VALUE ref = rr_v8_handle_new(self, cxt);
|
||||
cxt.Dispose();
|
||||
return ref;
|
||||
}
|
||||
|
||||
VALUE InContext(VALUE self) {
|
||||
return Context::InContext() ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
VALUE GetEntered(VALUE self) {
|
||||
HandleScope handles;
|
||||
if (Context::InContext()) {
|
||||
Local<Context> current = Context::GetEntered();
|
||||
return rr_v8_handle_new(self, current);
|
||||
} else {
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
VALUE Global(VALUE self) {
|
||||
HandleScope handles;
|
||||
return rr_v82rb(unwrap(self)->Global());
|
||||
}
|
||||
|
||||
VALUE Enter(VALUE self) {
|
||||
HandleScope handles;
|
||||
unwrap(self)->Enter();
|
||||
return self;
|
||||
}
|
||||
|
||||
VALUE Exit(VALUE self) {
|
||||
HandleScope handles;
|
||||
unwrap(self)->Exit();
|
||||
return self;
|
||||
}
|
||||
|
||||
VALUE IsEntered(VALUE self) {
|
||||
HandleScope handles;
|
||||
if (Context::InContext()) {
|
||||
return rr_v82rb(unwrap(self) == Context::GetEntered());
|
||||
} else {
|
||||
return Qfalse;
|
||||
}
|
||||
}
|
||||
VALUE GetData(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->GetData());
|
||||
}
|
||||
VALUE SetData(VALUE self, VALUE data) {
|
||||
HandleScope scope;
|
||||
unwrap(self)->SetData(rr_rb2v8(data)->ToString());
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_context() {
|
||||
ContextClass = rr_define_class("Context", rr_v8_handle_class());
|
||||
rr_define_singleton_method(ContextClass, "New", New, -1);
|
||||
rr_define_singleton_method(ContextClass, "InContext", InContext, 0);
|
||||
rr_define_singleton_method(ContextClass, "GetEntered", GetEntered, 0);
|
||||
rr_define_method(ContextClass, "Global", Global, 0);
|
||||
rr_define_method(ContextClass, "Enter", Enter, 0);
|
||||
rr_define_method(ContextClass, "Exit", Exit, 0);
|
||||
rr_define_method(ContextClass, "IsEntered", IsEntered, 0);
|
||||
rr_define_method(ContextClass, "GetData", GetData, 0);
|
||||
rr_define_method(ContextClass, "SetData", SetData, 1);
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef _RUBY_V8_CXT_
|
||||
#define _RUBY_V8_CXT_
|
||||
|
||||
void rr_init_context();
|
||||
|
||||
#endif
|
|
@ -1,34 +0,0 @@
|
|||
#include "rr.h"
|
||||
#include "v8_date.h"
|
||||
#include "v8_value.h"
|
||||
#include "v8_handle.h"
|
||||
#include "v8_object.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
|
||||
VALUE DateClass;
|
||||
|
||||
VALUE New(VALUE self, VALUE time) {
|
||||
HandleScope scope;
|
||||
return rr_v8_handle_new(self, Date::New(NUM2DBL(time)));
|
||||
}
|
||||
|
||||
// Override Value::NumberValue in order to ensure that we call the more specific and optimized
|
||||
// Number Value in v8::Date
|
||||
VALUE NumberValue(VALUE self) {
|
||||
Persistent<Date> date = rr_v8_handle<Date>(self);
|
||||
return rr_v82rb(date->NumberValue());
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_v8_date() {
|
||||
DateClass = rr_define_class("Date", rr_v8_value_class());
|
||||
rr_define_singleton_method(DateClass, "New", New, 1);
|
||||
rr_define_method(DateClass, "NumberValue", NumberValue, 0);
|
||||
}
|
||||
|
||||
VALUE rr_reflect_v8_date(Handle<Value> value) {
|
||||
return rr_reflect_v8_object_as(Handle<Object>::Cast(value), DateClass);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef _RR_V8_DATE_
|
||||
#define _RR_V8_DATE_
|
||||
|
||||
void rr_init_v8_date();
|
||||
VALUE rr_reflect_v8_date(v8::Handle<v8::Value> value);
|
||||
#endif
|
|
@ -1,17 +0,0 @@
|
|||
#include "rr.h"
|
||||
#include "v8_debug.h"
|
||||
#include "v8-debug.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
|
||||
VALUE EnableAgent(VALUE self, VALUE application_name, VALUE port) {
|
||||
return rr_v82rb(v8::Debug::EnableAgent(RSTRING_PTR(application_name), NUM2INT(port), false));
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_v8_debug() {
|
||||
VALUE DebugModule = rr_define_module("Debug");
|
||||
rr_define_singleton_method(DebugModule, "EnableAgent", EnableAgent, 2);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef _RR_V8_DEBUG_
|
||||
#define _RR_V8_DEBUG_
|
||||
|
||||
void rr_init_v8_debug();
|
||||
|
||||
#endif
|
|
@ -1,133 +0,0 @@
|
|||
#include "v8_exception.h"
|
||||
#include "rr.h"
|
||||
#include "v8_handle.h"
|
||||
// #include "execinfo.h"
|
||||
// #include "signal.h"
|
||||
|
||||
using namespace v8;
|
||||
namespace {
|
||||
|
||||
static void* stack[20];
|
||||
|
||||
void fatal(const char* location, const char* message) {
|
||||
rb_raise(rb_eFatal, "%s: %s", location, message);
|
||||
}
|
||||
// void segfault(int sig) {
|
||||
// fprintf(stderr, "segfault: game over.\n");
|
||||
// int size = backtrace(stack, 20);
|
||||
// backtrace_symbols_fd(stack, size, 2);
|
||||
// exit(1);
|
||||
// }
|
||||
VALUE _ThrowException(VALUE rbmod, VALUE value) {
|
||||
HandleScope scope;
|
||||
Handle<Value> err = rr_rb2v8(value);
|
||||
return rr_v82rb(ThrowException(err));
|
||||
}
|
||||
VALUE RangeError(VALUE rbclass, VALUE value) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(Exception::RangeError(rr_rb2v8(value)->ToString()));
|
||||
}
|
||||
VALUE ReferenceError(VALUE rbclass, VALUE value) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(Exception::ReferenceError(rr_rb2v8(value)->ToString()));
|
||||
}
|
||||
VALUE SyntaxError(VALUE rbclass, VALUE value) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(Exception::SyntaxError(rr_rb2v8(value)->ToString()));
|
||||
}
|
||||
VALUE Error(VALUE rbclass, VALUE value) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(Exception::Error(rr_rb2v8(value)->ToString()));
|
||||
}
|
||||
|
||||
VALUE StackTraceClass;
|
||||
VALUE StackFrameClass;
|
||||
namespace Trace {
|
||||
|
||||
Persistent<StackTrace>& trace(VALUE value) {
|
||||
return rr_v8_handle<StackTrace>(value);
|
||||
}
|
||||
VALUE GetFrame(VALUE self, VALUE index) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(trace(self)->GetFrame(NUM2UINT(index)));
|
||||
}
|
||||
VALUE GetFrameCount(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(trace(self)->GetFrameCount());
|
||||
}
|
||||
VALUE AsArray(VALUE self) {
|
||||
return rr_v82rb(trace(self)->AsArray());
|
||||
}
|
||||
VALUE CurrentStackTrace(VALUE self, VALUE frame_limit) {
|
||||
return rr_v82rb(StackTrace::CurrentStackTrace(NUM2INT(frame_limit)));
|
||||
}
|
||||
}
|
||||
|
||||
namespace Frame {
|
||||
Persistent<StackFrame>& frame(VALUE value) {
|
||||
return rr_v8_handle<StackFrame>(value);
|
||||
}
|
||||
VALUE GetLineNumber(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(frame(self)->GetLineNumber());
|
||||
}
|
||||
VALUE GetColumn(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(frame(self)->GetColumn());
|
||||
}
|
||||
VALUE GetScriptName(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(frame(self)->GetScriptName());
|
||||
}
|
||||
VALUE GetFunctionName(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(frame(self)->GetFunctionName());
|
||||
}
|
||||
VALUE IsEval(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(frame(self)->IsEval());
|
||||
}
|
||||
VALUE IsConstructor(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(frame(self)->IsConstructor());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void rr_init_v8_exception() {
|
||||
VALUE V8 = rb_define_module("V8");
|
||||
VALUE V8_C = rb_define_module_under(V8, "C");
|
||||
rr_define_singleton_method(V8_C, "ThrowException", _ThrowException, 1);
|
||||
|
||||
VALUE ExceptionClass = rr_define_class("Exception");
|
||||
rr_define_singleton_method(ExceptionClass, "RangeError", RangeError, 1);
|
||||
rr_define_singleton_method(ExceptionClass, "ReferenceError", ReferenceError, 1);
|
||||
rr_define_singleton_method(ExceptionClass, "SyntaxError", SyntaxError, 1);
|
||||
rr_define_singleton_method(ExceptionClass, "Error", Error, 1);
|
||||
|
||||
StackTraceClass = rr_define_class("StackTrace", rr_v8_handle_class());
|
||||
rr_define_singleton_method(StackTraceClass, "CurrentStackTrace", Trace::CurrentStackTrace, 1);
|
||||
rr_define_method(StackTraceClass, "GetFrame", Trace::GetFrame, 1);
|
||||
rr_define_method(StackTraceClass, "GetFrameCount", Trace::GetFrameCount, 0);
|
||||
rr_define_method(StackTraceClass, "AsArray", Trace::AsArray, 0);
|
||||
|
||||
StackFrameClass = rr_define_class("StackFrame", rr_v8_handle_class());
|
||||
rr_define_method(StackFrameClass, "GetLineNumber", Frame::GetLineNumber, 0);
|
||||
rr_define_method(StackFrameClass, "GetColumn", Frame::GetColumn, 0);
|
||||
rr_define_method(StackFrameClass, "GetScriptName", Frame::GetScriptName, 0);
|
||||
rr_define_method(StackFrameClass, "GetFunctionName", Frame::GetFunctionName, 0);
|
||||
rr_define_method(StackFrameClass, "IsEval", Frame::IsEval, 0);
|
||||
rr_define_method(StackFrameClass, "IsConstructor", Frame::IsConstructor, 0);
|
||||
|
||||
v8::V8::SetFatalErrorHandler(fatal);
|
||||
//comment this in for debugging.
|
||||
// signal(SIGSEGV, segfault);
|
||||
}
|
||||
|
||||
VALUE rr_reflect_v8_stacktrace(Handle<StackTrace> value) {
|
||||
return rr_v8_handle_new(StackTraceClass, value);
|
||||
}
|
||||
VALUE rr_reflect_v8_stackframe(Handle<StackFrame> value) {
|
||||
return rr_v8_handle_new(StackFrameClass, value);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#ifndef _RR_V8_EXCEPTION_
|
||||
#define _RR_V8_EXCEPTION_
|
||||
|
||||
#include "v8.h"
|
||||
#include "ruby.h"
|
||||
|
||||
void rr_init_v8_exception();
|
||||
VALUE rr_reflect_v8_stacktrace(v8::Handle<v8::StackTrace> value);
|
||||
VALUE rr_reflect_v8_stackframe(v8::Handle<v8::StackFrame> value);
|
||||
|
||||
#endif
|
|
@ -1,70 +0,0 @@
|
|||
#include "rr.h"
|
||||
#include "v8_external.h"
|
||||
|
||||
#include "v8_handle.h"
|
||||
#include "v8_value.h"
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
VALUE ExternalClass;
|
||||
VALUE references;
|
||||
|
||||
struct Weaklink {
|
||||
bool finalized_from_rb;
|
||||
bool finalized_from_v8;
|
||||
VALUE target;
|
||||
};
|
||||
|
||||
VALUE Weaklink_finalized_from_ruby(VALUE object_id, VALUE data) {
|
||||
Weaklink* link = 0;
|
||||
Data_Get_Struct(data, struct Weaklink, link);
|
||||
link->finalized_from_rb = true;
|
||||
if (link->finalized_from_v8) {
|
||||
delete link;
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
void Weaklink_finalized_from_v8(Persistent<Value> value, void* data) {
|
||||
Weaklink* link = (Weaklink*)data;
|
||||
link->finalized_from_v8 = true;
|
||||
if (link->finalized_from_rb) {
|
||||
delete link;
|
||||
}
|
||||
value.Dispose();
|
||||
}
|
||||
|
||||
VALUE New(VALUE self, VALUE value) {
|
||||
HandleScope scope;
|
||||
Weaklink* link = new Weaklink();
|
||||
link->finalized_from_v8 = false;
|
||||
link->finalized_from_rb = false;
|
||||
link->target = value;
|
||||
Persistent<External> external = Persistent<External>::New(External::New((void*)link));
|
||||
external.MakeWeak(link,Weaklink_finalized_from_v8);
|
||||
VALUE finalizer_data = Data_Wrap_Struct(rb_cObject, 0, 0, link);
|
||||
rr_define_finalizer(value, (void*)Weaklink_finalized_from_ruby, finalizer_data);
|
||||
return rr_v8_handle_new(self, external);
|
||||
}
|
||||
|
||||
VALUE _Value(VALUE self) {
|
||||
HandleScope scope;
|
||||
Weaklink* link = (Weaklink*)rr_v8_handle<External>(self)->Value();
|
||||
if (link->finalized_from_rb) {
|
||||
rb_warn("an active v8::External wraps a VALUE that has already been finalized! This is not good.\n");
|
||||
return Qnil;
|
||||
} else {
|
||||
return link->target;
|
||||
}
|
||||
return (VALUE)rr_v8_handle<External>(self)->Value();
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_v8_external() {
|
||||
ExternalClass = rr_define_class("External", rr_v8_value_class());
|
||||
rr_define_singleton_method(ExternalClass, "New", New, 1);
|
||||
rr_define_method(ExternalClass, "Value", _Value, 0);
|
||||
}
|
||||
|
||||
VALUE rr_reflect_v8_external(Handle<Value> external) {
|
||||
return rr_v8_handle_new(ExternalClass, external);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef _RR_V8_EXTERNAL_
|
||||
#define _RR_V8_EXTERNAL_
|
||||
|
||||
#include "rr.h"
|
||||
|
||||
void rr_init_v8_external();
|
||||
VALUE rr_reflect_v8_external(v8::Handle<v8::Value> value);
|
||||
#endif
|
|
@ -1,69 +0,0 @@
|
|||
|
||||
#include <vector>
|
||||
#include "v8_function.h"
|
||||
#include "v8_object.h"
|
||||
#include "v8_handle.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
VALUE FunctionClass;
|
||||
|
||||
Persistent<Function>& unwrap(VALUE value) {
|
||||
return rr_v8_handle<Function>(value);
|
||||
}
|
||||
VALUE Call(VALUE self, VALUE recv, VALUE arguments) {
|
||||
HandleScope handles;
|
||||
if (!Context::InContext()) {
|
||||
rb_raise(rb_eScriptError, "no open V8 Context in V8::C::Function::Call()");
|
||||
return Qnil;
|
||||
}
|
||||
Handle<Function> function = unwrap(self);
|
||||
Local<Object> thisObj = rr_rb2v8(recv)->ToObject();
|
||||
Handle<Array> args = rr_v8_handle<Array>(arguments);
|
||||
int argc = args->Length();
|
||||
std::vector< Handle<Value> > argv (argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
argv[i] = args->Get(i);
|
||||
}
|
||||
return rr_v82rb(function->Call(thisObj, argc, &argv[0]));
|
||||
}
|
||||
|
||||
VALUE NewInstance(VALUE self, VALUE arguments) {
|
||||
HandleScope scope;
|
||||
Handle<Function> function = unwrap(self);
|
||||
Handle<Array> args = rr_v8_handle<Array>(arguments);
|
||||
int argc = args->Length();
|
||||
std::vector< Handle<Value> > argv (argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
argv[i] = args->Get(i);
|
||||
}
|
||||
return rr_v82rb(function->NewInstance(argc, &argv[0]));
|
||||
}
|
||||
VALUE GetName(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->GetName());
|
||||
}
|
||||
VALUE SetName(VALUE self, VALUE name) {
|
||||
HandleScope scope;
|
||||
Handle<String> str = rr_v8_handle<String>(name);
|
||||
unwrap(self)->SetName(str);
|
||||
return Qnil;
|
||||
}
|
||||
// VALUE GetScriptOrigin(VALUE self) {
|
||||
// return rr_v82rb(unwrap(self)->GetScriptOrigin());
|
||||
// }
|
||||
}
|
||||
|
||||
void rr_init_function() {
|
||||
FunctionClass = rr_define_class("Function", rr_v8_object_class());
|
||||
rr_define_method(FunctionClass, "Call", Call, 2);
|
||||
rr_define_method(FunctionClass, "NewInstance", NewInstance, 1);
|
||||
rr_define_method(FunctionClass, "GetName", GetName, 0);
|
||||
rr_define_method(FunctionClass, "SetName", SetName, 1);
|
||||
// rr_define_method(FunctionClass, "GetScriptOrigin", GetScriptOrigin, 0);
|
||||
}
|
||||
|
||||
VALUE rr_reflect_v8_function(Handle<Value> value) {
|
||||
return rr_reflect_v8_object_as(value, FunctionClass);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#ifndef _RUBY_V8_FUNCTION_
|
||||
#define _RUBY_V8_FUNCTION_
|
||||
|
||||
#include "rr.h"
|
||||
#include "v8.h"
|
||||
|
||||
void rr_init_function();
|
||||
|
||||
VALUE rr_reflect_v8_function(v8::Handle<v8::Value> value);
|
||||
|
||||
#endif
|
|
@ -1,186 +0,0 @@
|
|||
|
||||
#include "rr.h"
|
||||
#include "v8_handle.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
/**
|
||||
* Creates a new Persistent storage cell for `handle`
|
||||
* so that we can reference it from Ruby. Ruby metadat
|
||||
* is contained on the handle object, and then the actual
|
||||
* v8 references are contain in an instance of `Payload`
|
||||
*/
|
||||
v8_handle::v8_handle(Handle<void> object) {
|
||||
this->weakref_callback = Qnil;
|
||||
this->weakref_callback_parameters = Qnil;
|
||||
this->dead = false;
|
||||
this->payload = new Payload(object);
|
||||
}
|
||||
|
||||
v8_handle::~v8_handle() {}
|
||||
|
||||
/**
|
||||
* Construct a new handle payload.
|
||||
*
|
||||
* Each payload contains a Ruby object wrapper so that it
|
||||
* can be enqueued for V8 GC (the GC queue is a ruby Array)
|
||||
* the wrapper is pre-allocated at payload creation time
|
||||
* so that no Ruby objects are allocated during Ruby GC.
|
||||
*/
|
||||
v8_handle::Payload::Payload(Handle<void> object) {
|
||||
rb_gc_register_address(&wrapper);
|
||||
handle = Persistent<void>::New(object);
|
||||
wrapper = Data_Wrap_Struct(rb_cObject, 0, destroy, this);
|
||||
}
|
||||
|
||||
v8_handle::Payload::~Payload() {
|
||||
rb_gc_unregister_address(&wrapper);
|
||||
}
|
||||
|
||||
void v8_handle::Payload::release() {
|
||||
handle.Dispose();
|
||||
handle.Clear();
|
||||
}
|
||||
|
||||
void v8_handle::Payload::destroy(v8_handle::Payload* payload) {
|
||||
delete payload;
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* Holds dead references, that are no longer being held in Ruby, so that they can be garbage collected
|
||||
* inside of V8
|
||||
*/
|
||||
VALUE handle_queue;
|
||||
|
||||
/**
|
||||
* Invoked by the Ruby garbage collector whenever it determines that this handle is
|
||||
* still reachable. We in turn, mark our weak callback parameters, so that it knows
|
||||
* they are reachable too.
|
||||
*/
|
||||
void v8_handle_mark(v8_handle* handle) {
|
||||
rb_gc_mark(handle->weakref_callback);
|
||||
rb_gc_mark(handle->weakref_callback_parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whenver a V8::C::Handle becomes garbage collected, we do not free it immediately.
|
||||
* instead, we put them into a "zombie" queue, where its corresponding V8 storage cell
|
||||
* can be released safely while the V8 engine is running. A zombie Ruby object is
|
||||
* created to wrap it so that it can be stored in the queue.
|
||||
*/
|
||||
void v8_handle_enqueue(v8_handle* handle) {
|
||||
handle->dead = true;
|
||||
rb_ary_unshift(handle_queue, handle->payload->wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drains the dead handle queue, and releases them from V8
|
||||
*
|
||||
* This implements the V8 `GCPrologueCallback` and is registered to run before
|
||||
* each invocation of the V8 garbage collector. It empties the queue of dead handles
|
||||
* and disposes of them. It is important to do this operations inside V8 so that
|
||||
* Ruby garbage collection is never locked, and never touches V8.
|
||||
*/
|
||||
void v8_handle_dequeue(GCType type, GCCallbackFlags flags) {
|
||||
for (VALUE handle = rb_ary_pop(handle_queue); RTEST(handle); handle = rb_ary_pop(handle_queue)) {
|
||||
v8_handle::Payload* payload = NULL;
|
||||
Data_Get_Struct(handle, struct v8_handle::Payload, payload);
|
||||
payload->release();
|
||||
}
|
||||
}
|
||||
|
||||
VALUE New(VALUE self, VALUE handle) {
|
||||
if (RTEST(handle)) {
|
||||
Persistent<void> that = rr_v8_handle<void>(handle);
|
||||
return rr_v8_handle_new(self, that);
|
||||
} else {
|
||||
return rr_v8_handle_new(self, Handle<void>());
|
||||
}
|
||||
}
|
||||
|
||||
VALUE IsEmpty(VALUE self) {
|
||||
return rr_v82rb(rr_v8_handle<void>(self).IsEmpty());
|
||||
}
|
||||
|
||||
VALUE Clear(VALUE self) {
|
||||
rr_v8_handle<void>(self).Clear();
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE Dispose(VALUE self) {
|
||||
rr_v8_handle<void>(self).Dispose();
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
void RubyWeakReferenceCallback(Persistent<Value> value, void* parameter) {
|
||||
VALUE self = (VALUE)parameter;
|
||||
v8_handle* handle = rr_v8_handle_raw(self);
|
||||
VALUE callback = handle->weakref_callback;
|
||||
VALUE parameters = handle->weakref_callback_parameters;
|
||||
if (RTEST(callback)) {
|
||||
rb_funcall(callback, rb_intern("call"), 2, self, parameters);
|
||||
}
|
||||
value.Dispose();
|
||||
handle->payload->release();
|
||||
handle->dead = true;
|
||||
|
||||
}
|
||||
|
||||
VALUE MakeWeak(VALUE self, VALUE parameters, VALUE callback) {
|
||||
v8_handle* handle = rr_v8_handle_raw(self);
|
||||
handle->weakref_callback = callback;
|
||||
handle->weakref_callback_parameters = parameters;
|
||||
rr_v8_handle<void>(self).MakeWeak((void*)self, RubyWeakReferenceCallback);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE ClearWeak(VALUE self) {
|
||||
rr_v8_handle<void>(self).ClearWeak();
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE IsNearDeath(VALUE self) {
|
||||
return rr_v82rb(rr_v8_handle<void>(self).IsNearDeath());
|
||||
}
|
||||
|
||||
VALUE IsWeak(VALUE self) {
|
||||
return rr_v82rb(rr_v8_handle<void>(self).IsWeak());
|
||||
}
|
||||
|
||||
VALUE dead_p(VALUE self) {
|
||||
return rr_v8_handle_raw(self)->dead ? Qtrue : Qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_handle() {
|
||||
VALUE HandleClass = rr_define_class("Handle");
|
||||
rr_define_method(HandleClass, "dead?", dead_p, 0);
|
||||
rr_define_singleton_method(HandleClass, "New", New, 1);
|
||||
rr_define_method(HandleClass, "IsEmpty", IsEmpty, 0);
|
||||
rr_define_method(HandleClass, "Clear", Clear, 0);
|
||||
rr_define_method(HandleClass, "Dispose", Dispose, 0);
|
||||
rr_define_method(HandleClass, "MakeWeak", MakeWeak, 2);
|
||||
rr_define_method(HandleClass, "ClearWeak", ClearWeak, 0);
|
||||
rr_define_method(HandleClass, "IsNearDeath", IsNearDeath, 0);
|
||||
rr_define_method(HandleClass, "IsWeak", IsWeak, 0);
|
||||
|
||||
rb_gc_register_address(&handle_queue);
|
||||
handle_queue = rb_ary_new();
|
||||
V8::AddGCPrologueCallback(v8_handle_dequeue);
|
||||
}
|
||||
|
||||
VALUE rr_v8_handle_new(VALUE klass, v8::Handle<void> handle) {
|
||||
v8_handle* new_handle = new v8_handle(handle);
|
||||
return Data_Wrap_Struct(klass, v8_handle_mark, v8_handle_enqueue, new_handle);
|
||||
}
|
||||
|
||||
VALUE rr_v8_handle_class() {
|
||||
return rr_define_class("Handle");
|
||||
}
|
||||
|
||||
v8_handle* rr_v8_handle_raw(VALUE value) {
|
||||
v8_handle* handle = 0;
|
||||
Data_Get_Struct(value, struct v8_handle, handle);
|
||||
return handle;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
#ifndef _RR_V8_HANDLE_
|
||||
#define _RR_V8_HANDLE_
|
||||
|
||||
#include <v8.h>
|
||||
#include "ruby.h"
|
||||
|
||||
/**
|
||||
* Holds a reference to a V8 heap object. This serves as the base
|
||||
* class for all of the low-level proxies that reference into V8.
|
||||
*/
|
||||
struct v8_handle {
|
||||
|
||||
/**
|
||||
* Contains the *actual* V8 references. This object is kept
|
||||
* separate so that it can be "detached" from the handle when
|
||||
* it is garbage collected, and enqueued separately and
|
||||
* collected in the context of a V8 thread.
|
||||
*/
|
||||
|
||||
struct Payload {
|
||||
Payload(v8::Handle<void> object);
|
||||
virtual ~Payload();
|
||||
void release();
|
||||
static void destroy(v8_handle::Payload* payload);
|
||||
v8::Persistent<void> handle;
|
||||
VALUE wrapper;
|
||||
};
|
||||
|
||||
v8_handle(v8::Handle<void> object);
|
||||
virtual ~v8_handle();
|
||||
|
||||
Payload* payload;
|
||||
bool dead;
|
||||
VALUE weakref_callback;
|
||||
VALUE weakref_callback_parameters;
|
||||
};
|
||||
|
||||
void rr_init_handle();
|
||||
|
||||
v8_handle* rr_v8_handle_raw(VALUE value);
|
||||
|
||||
template <class T> v8::Persistent<T>& rr_v8_handle(VALUE value) {
|
||||
return (v8::Persistent<T>&)(rr_v8_handle_raw(value)->payload->handle);
|
||||
}
|
||||
VALUE rr_v8_handle_new(VALUE rbclass, v8::Handle<void> handle);
|
||||
VALUE rr_v8_handle_class();
|
||||
|
||||
#endif
|
|
@ -1,139 +0,0 @@
|
|||
#include "rr.h"
|
||||
#include "v8_locker.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
namespace Lock {
|
||||
|
||||
/**
|
||||
* Document-method: V8::C::Locker#new
|
||||
*
|
||||
* Allocates and returns a new `v8::Locker` object. The thread that instantiated
|
||||
* this object will hold the V8 interpreter lock until it is released with a
|
||||
* corresponding call to {#delete}.
|
||||
*
|
||||
* It critical that you call {#delete} to deallocate it, preferably within the same method.
|
||||
* If you don't, two bad things will happen:
|
||||
*
|
||||
* 1. You'll leak the underlying C++ object
|
||||
* 1. Worse, you'll leave the V8 vm locked to this thread forever
|
||||
*
|
||||
* It's dangerous! Be sure to `ensure`.
|
||||
*
|
||||
* for detailed semantics see the locking {API http://izs.me/v8-docs/classv8_1_1Unlocker.html}
|
||||
*
|
||||
* @return [V8::C::Locker] the new locker
|
||||
*/
|
||||
|
||||
VALUE New(VALUE LockerClass) {
|
||||
Locker* locker = new Locker();
|
||||
return Data_Wrap_Struct(LockerClass, 0, 0, (void*)locker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Document-method: V8::C::Locker#delete
|
||||
*
|
||||
* Pop this lock off the stack for this thread. For a full run down of V8 locking
|
||||
* semantics see the locking {API http://izs.me/v8-docs/classv8_1_1Unlocker.html}
|
||||
* @return nil
|
||||
*/
|
||||
VALUE Delete(VALUE self) {
|
||||
Locker* locker = 0;
|
||||
Data_Get_Struct(self, class Locker, locker);
|
||||
delete locker;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Unlock {
|
||||
/**
|
||||
* Document-method: V8::C::Unlocker#new
|
||||
*
|
||||
* Allocates and returns a new `v8::UnLocker` object, temporarily releasing any locks that
|
||||
* this thread is holding. It will reaquire all of the locksto {#delete}.
|
||||
*
|
||||
* This is a great thing to do when you want to call out to some code that might do some
|
||||
* waiting, sleeping, and you want to politely let other threads use this VM.
|
||||
*
|
||||
* It critical that you call {#delete} to deallocate it, preferably within the same method.
|
||||
* If you don't, two bad things will happen:
|
||||
*
|
||||
* 1. You'll leak the underlying C++ object
|
||||
* 1. You won't restore the locks to your current thread, and will mess things up horribly
|
||||
*
|
||||
* It's dangerous! Be sure to `ensure`.
|
||||
*
|
||||
* For details on V8 locking semantics, see the locking {API http://izs.me/v8-docs/classv8_1_1Unlocker.html}
|
||||
* @return [V8::C::Unocker] the new locker
|
||||
*/
|
||||
VALUE New(VALUE UnlockerClass) {
|
||||
Unlocker* unlocker = new Unlocker();
|
||||
return Data_Wrap_Struct(UnlockerClass, 0, 0, (void*)unlocker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Document-method: V8::C::Unlocker#delete
|
||||
*
|
||||
* Restore any locks to the stack that were temporarily removed by this `Unlocker`.
|
||||
* For a full run down, see semantics see the locking {API http://izs.me/v8-docs/classv8_1_1Unlocker.html}
|
||||
* @return nil
|
||||
*/
|
||||
VALUE Delete(VALUE self) {
|
||||
Unlocker* unlocker;
|
||||
Data_Get_Struct(self, class Unlocker, unlocker);
|
||||
delete unlocker;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Document-method: V8::C::Locker#StartPreemption
|
||||
* Start preemption.
|
||||
* When preemption is started, a timer is fired every n milli seconds that will switch between
|
||||
* multiple threads that are in contention for the V8 lock.
|
||||
*
|
||||
* @param [Integer] every_n_ms
|
||||
* @return nil
|
||||
*/
|
||||
VALUE StartPreemption(VALUE self, VALUE every_n_ms) {
|
||||
Locker::StartPreemption(NUM2INT(rb_to_int(every_n_ms)));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document-method: V8::C::Locker#StartPreemption
|
||||
* Stop preemption
|
||||
*/
|
||||
VALUE StopPreemption(VALUE self) {
|
||||
Locker::StopPreemption();
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document-method: V8::C::Locker#IsLocked
|
||||
* Returns whether or not the locker is locked by the current thread.
|
||||
*/
|
||||
VALUE IsLocked(VALUE self) {
|
||||
return rr_v82rb(Locker::IsLocked());
|
||||
}
|
||||
|
||||
/**
|
||||
* Document-method: V8::C::Locker#IsActive
|
||||
* Returns whether v8::Locker is being used by this V8 instance.
|
||||
*/
|
||||
VALUE IsActive(VALUE self) {
|
||||
return rr_v82rb(Locker::IsActive());
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_v8_locker() {
|
||||
VALUE LockerClass = rr_define_class("Locker");
|
||||
VALUE UnlockerClass = rr_define_class("Unlocker");
|
||||
rr_define_singleton_method(LockerClass, "new", Lock::New, 0);
|
||||
rr_define_method(LockerClass, "delete", Lock::Delete, 0);
|
||||
rr_define_singleton_method(UnlockerClass, "new", Unlock::New, 0);
|
||||
rr_define_method(UnlockerClass, "delete", Unlock::Delete, 0);
|
||||
rr_define_singleton_method(LockerClass, "StartPreemption", StartPreemption, 1);
|
||||
rr_define_singleton_method(LockerClass, "StopPreemption", StopPreemption, 0);
|
||||
rr_define_singleton_method(LockerClass, "IsLocked", IsLocked, 0);
|
||||
rr_define_singleton_method(LockerClass, "IsActive", IsActive, 0);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef _RUBY_V8_LOCKER
|
||||
#define _RUBY_V8_LOCKER
|
||||
|
||||
void rr_init_v8_locker();
|
||||
|
||||
#endif
|
|
@ -1,67 +0,0 @@
|
|||
#include "v8_message.h"
|
||||
#include "v8_handle.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
VALUE MessageClass;
|
||||
|
||||
Persistent<Message>& unwrap(VALUE self) {
|
||||
return rr_v8_handle<Message>(self);
|
||||
}
|
||||
|
||||
VALUE Get(VALUE self) {
|
||||
return rr_v82rb(unwrap(self)->Get());
|
||||
}
|
||||
|
||||
VALUE GetSourceLine(VALUE self) {
|
||||
return rr_v82rb(unwrap(self)->GetSourceLine());
|
||||
}
|
||||
|
||||
VALUE GetScriptResourceName(VALUE self) {
|
||||
return rr_v82rb(unwrap(self)->GetScriptResourceName());
|
||||
}
|
||||
|
||||
VALUE GetStackTrace(VALUE self) {
|
||||
Handle<StackTrace> trace = unwrap(self)->GetStackTrace();
|
||||
return trace.IsEmpty() ? Qnil : rr_v82rb(trace);
|
||||
}
|
||||
|
||||
VALUE GetLineNumber(VALUE self) {
|
||||
return rr_v82rb(unwrap(self)->GetLineNumber());
|
||||
}
|
||||
|
||||
VALUE GetStartPosition(VALUE self) {
|
||||
return rr_v82rb(unwrap(self)->GetStartPosition());
|
||||
}
|
||||
|
||||
VALUE GetEndPosition(VALUE self) {
|
||||
return rr_v82rb(unwrap(self)->GetEndPosition());
|
||||
}
|
||||
|
||||
VALUE GetStartColumn(VALUE self) {
|
||||
return rr_v82rb(unwrap(self)->GetStartColumn());
|
||||
}
|
||||
|
||||
VALUE GetEndColumn(VALUE self) {
|
||||
return rr_v82rb(unwrap(self)->GetEndColumn());
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_message() {
|
||||
MessageClass = rr_define_class("Message", rr_v8_handle_class());
|
||||
rr_define_method(MessageClass, "Get", Get, 0);
|
||||
rr_define_method(MessageClass, "GetSourceLine", GetSourceLine, 0);
|
||||
rr_define_method(MessageClass, "GetScriptResourceName", GetScriptResourceName, 0);
|
||||
rr_define_method(MessageClass, "GetStackTrace", GetStackTrace, 0);
|
||||
rr_define_method(MessageClass, "GetLineNumber", GetLineNumber, 0);
|
||||
rr_define_method(MessageClass, "GetStartPosition", GetStartPosition, 0);
|
||||
rr_define_method(MessageClass, "GetEndPosition", GetEndPosition, 0);
|
||||
rr_define_method(MessageClass, "GetStartColumn", GetStartColumn, 0);
|
||||
rr_define_method(MessageClass, "GetEndColumn", GetEndColumn, 0);
|
||||
}
|
||||
|
||||
VALUE rr_reflect_v8_message(Handle<Message> value) {
|
||||
return rr_v8_handle_new(MessageClass, value);
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef _RUBY_V8_MESSAGE_
|
||||
#define _RUBY_V8_MESSAGE_
|
||||
|
||||
#include "v8.h"
|
||||
#include "rr.h"
|
||||
|
||||
void rr_init_message();
|
||||
VALUE rr_reflect_v8_message(v8::Handle<v8::Message> value);
|
||||
|
||||
#endif
|
|
@ -1,122 +0,0 @@
|
|||
#include "v8_handle.h"
|
||||
#include "v8_weakref.h"
|
||||
#include "v8_object.h"
|
||||
#include "v8_value.h"
|
||||
#include "v8_template.h"
|
||||
#include "v8_external.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace {
|
||||
|
||||
VALUE ObjectClass;
|
||||
|
||||
Persistent<Object>& unwrap(VALUE object) {
|
||||
return rr_v8_handle<Object>(object);
|
||||
}
|
||||
|
||||
VALUE Get(VALUE self, VALUE key) {
|
||||
HandleScope handles;
|
||||
Persistent<Object> obj(unwrap(self));
|
||||
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
|
||||
return rr_v82rb(obj->Get(NUM2UINT(key)));
|
||||
} else {
|
||||
return rr_v82rb(obj->Get(rr_rb2v8(key)->ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
VALUE New(VALUE rbclass) {
|
||||
HandleScope handles;
|
||||
if (!Context::InContext()) {
|
||||
rb_raise(rb_eScriptError, "Object::New() called without an entered Context");
|
||||
return Qnil;
|
||||
}
|
||||
return rr_v8_handle_new(rbclass, Object::New());
|
||||
}
|
||||
|
||||
VALUE Set(VALUE self, VALUE key, VALUE value) {
|
||||
HandleScope handles;
|
||||
Persistent<Object> obj = unwrap(self);
|
||||
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
|
||||
return rr_v82rb(obj->Set(NUM2UINT(key), rr_rb2v8(value)));
|
||||
} else {
|
||||
return rr_v82rb(obj->Set(rr_rb2v8(key), rr_rb2v8(value)));
|
||||
}
|
||||
}
|
||||
|
||||
VALUE GetPropertyNames(VALUE self) {
|
||||
HandleScope handles;
|
||||
Persistent<Object> object = unwrap(self);
|
||||
Local<Value> names = object->GetPropertyNames();
|
||||
return rr_v82rb(names);
|
||||
}
|
||||
VALUE GetIdentityHash(VALUE self) {
|
||||
return rr_v82rb(unwrap(self)->GetIdentityHash());
|
||||
}
|
||||
VALUE SetHiddenValue(VALUE self, VALUE key, VALUE value) {
|
||||
HandleScope scope;
|
||||
if (Context::InContext()) {
|
||||
unwrap(self)->SetHiddenValue(rr_rb2v8(key)->ToString(), rr_rb2v8(value));
|
||||
} else {
|
||||
rb_raise(rb_eScriptError, "Object::SetHiddenValue() called without an entered Context");
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
VALUE GetHiddenValue(VALUE self, VALUE key) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->GetHiddenValue(rr_rb2v8(key)->ToString()));
|
||||
}
|
||||
VALUE GetPrototype(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->GetPrototype());
|
||||
}
|
||||
VALUE SetPrototype(VALUE self, VALUE prototype) {
|
||||
HandleScope scope;
|
||||
Handle<Value> proto(rr_rb2v8(prototype));
|
||||
return rr_v82rb(unwrap(self)->SetPrototype(rr_rb2v8(prototype)));
|
||||
}
|
||||
}
|
||||
|
||||
VALUE rr_v8_object_class() {
|
||||
return ObjectClass;
|
||||
}
|
||||
|
||||
void rr_init_object() {
|
||||
ObjectClass = rr_define_class("Object", rr_v8_value_class());
|
||||
rr_define_singleton_method(ObjectClass, "New", New, 0);
|
||||
rr_define_method(ObjectClass, "Get", Get, 1);
|
||||
rr_define_method(ObjectClass, "Set", Set, 2);
|
||||
rr_define_method(ObjectClass, "GetPropertyNames", GetPropertyNames, 0);
|
||||
rr_define_method(ObjectClass, "GetIdentityHash", GetIdentityHash, 0);
|
||||
rr_define_method(ObjectClass, "GetHiddenValue", GetHiddenValue, 1);
|
||||
rr_define_method(ObjectClass, "SetHiddenValue", SetHiddenValue, 2);
|
||||
rr_define_method(ObjectClass, "GetPrototype", GetPrototype, 0);
|
||||
rr_define_method(ObjectClass, "SetPrototype", SetPrototype, 1);
|
||||
}
|
||||
|
||||
VALUE rr_reflect_v8_object_as(Handle<Value> value, VALUE ruby_class) {
|
||||
Handle<Object> object = Handle<Object>::Cast(value);
|
||||
VALUE handle;
|
||||
v8_weakref* backref;
|
||||
Local<Value> holder = object->GetHiddenValue(String::NewSymbol("TheRubyRacer::Backref"));
|
||||
if (holder.IsEmpty()) {
|
||||
handle = rr_v8_handle_new(ruby_class, object);
|
||||
backref = new v8_weakref(handle);
|
||||
object->SetHiddenValue(String::NewSymbol("TheRubyRacer::Backref"), backref->external);
|
||||
} else {
|
||||
backref = (v8_weakref*)External::Unwrap(holder);
|
||||
handle = backref->get();
|
||||
if (!RTEST(handle)) {
|
||||
handle = rr_v8_handle_new(ruby_class, object);
|
||||
backref->set(handle);
|
||||
}
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
VALUE rr_reflect_v8_object(Handle<Value> value) {
|
||||
return rr_reflect_v8_object_as(value, ObjectClass);
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef _RUBY_V8_OBJECT_
|
||||
#define _RUBY_V8_OBJECT_
|
||||
|
||||
#include "rr.h"
|
||||
|
||||
void rr_init_object();
|
||||
VALUE rr_v8_object_class();
|
||||
VALUE rr_reflect_v8_object(v8::Handle<v8::Value> value);
|
||||
VALUE rr_reflect_v8_object_as(v8::Handle<v8::Value> object, VALUE ruby_class);
|
||||
#endif
|
|
@ -1,36 +0,0 @@
|
|||
#include "v8.h"
|
||||
#include "v8_handle.h"
|
||||
#include "v8_script.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
|
||||
VALUE New(VALUE self, VALUE source, VALUE source_name) {
|
||||
HandleScope scope;
|
||||
Local<String> src(rr_rb2v8(source)->ToString());
|
||||
Local<String> src_name(rr_rb2v8(source_name)->ToString());
|
||||
return rr_v8_handle_new(self, Script::Compile(src, src_name));
|
||||
}
|
||||
|
||||
VALUE Compile(VALUE self, VALUE source, VALUE source_name) {
|
||||
HandleScope scope;
|
||||
Local<String> src(rr_rb2v8(source)->ToString());
|
||||
Local<String> src_name(rr_rb2v8(source_name)->ToString());
|
||||
return rr_v8_handle_new(self, Script::Compile(src, src_name));
|
||||
}
|
||||
|
||||
VALUE Run(VALUE self) {
|
||||
HandleScope scope;
|
||||
Persistent<Script> script(rr_v8_handle<Script>(self));
|
||||
Local<Value> result(script->Run());
|
||||
return result.IsEmpty() ? Qnil : rr_v82rb(result);
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_script() {
|
||||
VALUE ScriptClass = rr_define_class("Script", rr_v8_handle_class());
|
||||
rr_define_singleton_method(ScriptClass, "New", New, 2);
|
||||
rr_define_singleton_method(ScriptClass, "Compile", Compile, 2);
|
||||
rr_define_method(ScriptClass, "Run", Run, 0);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef _RUBY_V8_SCRIPT_
|
||||
#define _RUBY_V8_SCRIPT_
|
||||
|
||||
#include "rr.h"
|
||||
|
||||
void rr_init_script();
|
||||
|
||||
#endif
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
#include "v8.h"
|
||||
#include "v8_handle.h"
|
||||
#include "v8_value.h"
|
||||
#include "v8_string.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
VALUE StringClass;
|
||||
|
||||
Persistent<String>& unwrap(VALUE value) {
|
||||
return rr_v8_handle<String>(value);
|
||||
}
|
||||
VALUE New(VALUE string_class, VALUE data) {
|
||||
HandleScope handles;
|
||||
VALUE str = rb_funcall(data, rb_intern("to_s"), 0);
|
||||
return rr_v8_handle_new(string_class, String::New(RSTRING_PTR(str), RSTRING_LEN(str)));
|
||||
}
|
||||
VALUE NewSymbol(VALUE string_class, VALUE data) {
|
||||
HandleScope scope;
|
||||
VALUE str = rb_funcall(data, rb_intern("to_s"), 0);
|
||||
return rr_v8_handle_new(string_class, String::NewSymbol(RSTRING_PTR(str), RSTRING_LEN(str)));
|
||||
}
|
||||
VALUE Utf8Value(VALUE self) {
|
||||
HandleScope handles;
|
||||
Handle<String> str = unwrap(self);
|
||||
return rb_str_new(*String::Utf8Value(str), str->Utf8Length());
|
||||
}
|
||||
VALUE Utf16Value(VALUE self) {
|
||||
//How are UTF16 strings represented in ruby 1.8, 1.9
|
||||
return Qnil;
|
||||
}
|
||||
VALUE AsciiValue(VALUE self) {
|
||||
HandleScope handles;
|
||||
Handle<String> str = unwrap(self);
|
||||
return rb_str_new(*String::AsciiValue(str), str->Length());
|
||||
}
|
||||
}
|
||||
|
||||
VALUE rr_reflect_v8_string(Handle<Value> value) {
|
||||
return rr_v8_handle_new(StringClass, Handle<String>::Cast(value));
|
||||
}
|
||||
|
||||
void rr_init_string() {
|
||||
StringClass = rr_define_class("String", rr_v8_value_class());
|
||||
rr_define_singleton_method(StringClass, "New", New, 1);
|
||||
rr_define_singleton_method(StringClass, "NewSymbol", NewSymbol, 1);
|
||||
rr_define_method(StringClass, "Utf8Value", Utf8Value, 0);
|
||||
rr_define_method(StringClass, "Utf16Value", Utf16Value, 0);
|
||||
rr_define_method(StringClass, "AsciiValue", AsciiValue, 0);
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#ifndef _RUBY_V8_STR_
|
||||
#define _RUBY_V8_STR_
|
||||
|
||||
#include "rr.h"
|
||||
|
||||
void rr_init_string();
|
||||
|
||||
VALUE rr_reflect_v8_string(v8::Handle<v8::Value> value);
|
||||
#endif
|
|
@ -1,344 +0,0 @@
|
|||
#include "rr.h"
|
||||
#include "v8_handle.h"
|
||||
#include "v8_function.h"
|
||||
#include "v8_template.h"
|
||||
#include "v8_external.h"
|
||||
#include "v8_callbacks.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
|
||||
VALUE ObjectTemplateClass;
|
||||
VALUE FunctionTemplateClass;
|
||||
|
||||
struct v8_callback_data {
|
||||
VALUE handler;
|
||||
VALUE getter;
|
||||
VALUE setter;
|
||||
VALUE query;
|
||||
VALUE deleter;
|
||||
VALUE enumerator;
|
||||
VALUE data;
|
||||
};
|
||||
|
||||
void delete_v8_data(Persistent<Value> value, void* parameter) {
|
||||
value.Dispose();
|
||||
delete (v8_callback_data*)parameter;
|
||||
}
|
||||
|
||||
Local<External> make_v8_data(int argc, VALUE *argv, const char* argf) {
|
||||
VALUE handler; VALUE data;
|
||||
rb_scan_args(argc, argv, argf, &handler, &data);
|
||||
v8_callback_data* v8_data = new v8_callback_data();
|
||||
v8_data->handler = handler;
|
||||
v8_data->data = data;
|
||||
Local<External> external = External::New((void*)v8_data);
|
||||
Persistent<External>::New(external).MakeWeak((void*)v8_data, delete_v8_data);
|
||||
return external;
|
||||
}
|
||||
|
||||
Persistent<Template> tmpl(VALUE self) {
|
||||
return rr_v8_handle<Template>(self);
|
||||
}
|
||||
Persistent<ObjectTemplate> obj(VALUE self) {
|
||||
return rr_v8_handle<ObjectTemplate>(self);
|
||||
}
|
||||
Persistent<FunctionTemplate> func(VALUE self) {
|
||||
return rr_v8_handle<FunctionTemplate>(self);
|
||||
}
|
||||
|
||||
VALUE Set(VALUE self, VALUE name, VALUE value) {
|
||||
HandleScope handles;
|
||||
Local<String> key = rr_rb2v8(name)->ToString();
|
||||
Persistent<Data> data = rr_v8_handle<Data>(value);
|
||||
tmpl(self)->Set(key, data);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
Handle<Value> RubyInvocationCallback(const Arguments& args) {
|
||||
Handle<External> v8_data_wrapper = Handle<External>::Cast(args.Data());
|
||||
v8_callback_data* v8_data = (v8_callback_data*)v8_data_wrapper->Value();
|
||||
if (RTEST(v8_data->handler)) {
|
||||
VALUE rb_args = rr_v82rb(args);
|
||||
rb_iv_set(rb_args, "data", v8_data->data);
|
||||
VALUE result = rb_funcall(v8_data->handler, rb_intern("call"), 1, rb_args);
|
||||
return rr_rb2v8(result);
|
||||
} else {
|
||||
return Handle<Value>();
|
||||
}
|
||||
}
|
||||
|
||||
namespace Obj {
|
||||
|
||||
|
||||
v8_callback_data* accessor_info_data(const AccessorInfo& info) {
|
||||
Handle<External> v8_data_wrapper = Handle<External>::Cast(info.Data());
|
||||
return (v8_callback_data*)v8_data_wrapper->Value();
|
||||
}
|
||||
|
||||
VALUE accessor_info_rb(const AccessorInfo& info) {
|
||||
VALUE rb_data = accessor_info_data(info)->data;
|
||||
VALUE rb_info = rr_v82rb(info);
|
||||
rb_iv_set(rb_info, "data", rb_data);
|
||||
return rb_info;
|
||||
}
|
||||
|
||||
Local<External> accessor_info_data(VALUE getter, VALUE setter, VALUE query, VALUE deleter, VALUE enumerator, VALUE data) {
|
||||
v8_callback_data* v8_data = new v8_callback_data();
|
||||
v8_data->getter = getter;
|
||||
v8_data->setter = setter;
|
||||
v8_data->query = query;
|
||||
v8_data->deleter = deleter;
|
||||
v8_data->enumerator = enumerator;
|
||||
v8_data->data = data;
|
||||
Local<External> external = External::New((void*)v8_data);
|
||||
Persistent<External>::New(external).MakeWeak((void*)v8_data, delete_v8_data);
|
||||
return external;
|
||||
}
|
||||
|
||||
/**
|
||||
* NamedProperty[Getter|Setter] are used as interceptors on object.
|
||||
* See ObjectTemplate::SetNamedPropertyHandler.
|
||||
*/
|
||||
Handle<Value> RubyNamedPropertyGetter(Local<String> property, const AccessorInfo& info) {
|
||||
VALUE getter = accessor_info_data(info)->getter;
|
||||
return rr_rb2v8(rb_funcall(getter, rb_intern("call"), 2, rr_v82rb(property), accessor_info_rb(info)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value if the setter intercepts the request.
|
||||
* Otherwise, returns an empty handle.
|
||||
*/
|
||||
Handle<Value> RubyNamedPropertySetter(Local<String> property, Local<Value> value, const AccessorInfo& info) {
|
||||
VALUE setter = accessor_info_data(info)->setter;
|
||||
VALUE result = rb_funcall(setter, rb_intern("call"), 3, rr_v82rb(property), rr_v82rb(value), accessor_info_rb(info));
|
||||
return rr_rb2v8(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a non-empty handle if the interceptor intercepts the request.
|
||||
* The result is true if the property exists and false otherwise.
|
||||
*/
|
||||
Handle<Integer> RubyNamedPropertyQuery(Local<String> property, const AccessorInfo& info) {
|
||||
VALUE query = accessor_info_data(info)->query;
|
||||
VALUE result = rb_funcall(query, rb_intern("call"), 2, rr_v82rb(property), accessor_info_rb(info));
|
||||
Handle<Value> intercepts = rr_rb2v8(result);
|
||||
return intercepts.IsEmpty() ? Handle<Integer>() : Integer::New(None);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a non-empty handle if the deleter intercepts the request.
|
||||
* The return value is true if the property could be deleted and false
|
||||
* otherwise.
|
||||
*/
|
||||
Handle<Boolean> RubyNamedPropertyDeleter(Local<String> property, const AccessorInfo& info) {
|
||||
VALUE deleter = accessor_info_data(info)->deleter;
|
||||
VALUE result = rb_funcall(deleter, rb_intern("call"), 2, rr_v82rb(property), accessor_info_rb(info));
|
||||
Handle<Value> intercepts = rr_rb2v8(result);
|
||||
return intercepts.IsEmpty() ? Handle<Boolean>() : intercepts->ToBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the names of the properties the named
|
||||
* property getter intercepts.
|
||||
*/
|
||||
Handle<Array> RubyNamedPropertyEnumerator(const AccessorInfo& info) {
|
||||
VALUE enumerator = accessor_info_data(info)->enumerator;
|
||||
VALUE result = rb_funcall(enumerator, rb_intern("call"), 1, accessor_info_rb(info));
|
||||
Handle<Value> v(rr_rb2v8(result));
|
||||
if (v.IsEmpty()) {
|
||||
return Array::New();
|
||||
} else if (!v->IsArray()) {
|
||||
Local<Array> a = Array::New();
|
||||
a->Set(Integer::New(0), v->ToString());
|
||||
return a;
|
||||
} else {
|
||||
return (Handle<Array>)Array::Cast(*v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the property if the getter intercepts the
|
||||
* request. Otherwise, returns an empty handle.
|
||||
*/
|
||||
Handle<Value> RubyIndexedPropertyGetter(uint32_t index, const AccessorInfo& info) {
|
||||
VALUE getter = accessor_info_data(info)->getter;
|
||||
VALUE result = rb_funcall(getter, rb_intern("call"), 2, UINT2NUM(index), accessor_info_rb(info));
|
||||
return rr_rb2v8(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value if the setter intercepts the request.
|
||||
* Otherwise, returns an empty handle.
|
||||
*/
|
||||
Handle<Value> RubyIndexedPropertySetter(uint32_t index, Local<Value> value, const AccessorInfo& info) {
|
||||
VALUE setter = accessor_info_data(info)->setter;
|
||||
VALUE result = rb_funcall(setter, rb_intern("call"), 3, UINT2NUM(index), rr_v82rb(value), accessor_info_rb(info));
|
||||
return rr_rb2v8(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a non-empty handle if the interceptor intercepts the request.
|
||||
* The result is true if the property exists and false otherwise.
|
||||
*/
|
||||
Handle<Integer> RubyIndexedPropertyQuery(uint32_t index, const AccessorInfo& info) {
|
||||
VALUE query = accessor_info_data(info)->query;
|
||||
VALUE result = rb_funcall(query, rb_intern("call"), 2, UINT2NUM(index), accessor_info_rb(info));
|
||||
Handle<Value> intercepts = rr_rb2v8(result);
|
||||
return intercepts.IsEmpty() ? Handle<Integer>() : Integer::New(None);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a non-empty handle if the deleter intercepts the request.
|
||||
* The return value is true if the property could be deleted and false
|
||||
* otherwise.
|
||||
*/
|
||||
Handle<Boolean> RubyIndexedPropertyDeleter(uint32_t index, const AccessorInfo& info) {
|
||||
VALUE deleter = accessor_info_data(info)->deleter;
|
||||
VALUE result = rb_funcall(deleter, rb_intern("call"), 2, UINT2NUM(index), accessor_info_rb(info));
|
||||
Handle<Value> intercepts = rr_rb2v8(result);
|
||||
return intercepts.IsEmpty() ? Handle<Boolean>() : intercepts->ToBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the indices of the properties the
|
||||
* indexed property getter intercepts.
|
||||
*/
|
||||
Handle<Array> RubyIndexedPropertyEnumerator(const AccessorInfo& info) {
|
||||
VALUE enumerator = accessor_info_data(info)->enumerator;
|
||||
VALUE result = rb_funcall(enumerator, rb_intern("call"), 1, accessor_info_rb(info));
|
||||
Handle<Value> v(rr_rb2v8(result));
|
||||
if (v.IsEmpty()) {
|
||||
return Array::New();
|
||||
} else if (!v->IsArray()) {
|
||||
Local<Array> a = Array::New();
|
||||
a->Set(Integer::New(0), v->ToString());
|
||||
return a;
|
||||
} else {
|
||||
return (Handle<Array>)Array::Cast(*v);
|
||||
}
|
||||
}
|
||||
|
||||
VALUE New(VALUE rbclass) {
|
||||
HandleScope handles;
|
||||
return rr_v8_handle_new(rbclass, ObjectTemplate::New());
|
||||
}
|
||||
VALUE NewInstance(VALUE self) {
|
||||
HandleScope scope;
|
||||
if (!Context::InContext()) {
|
||||
rb_raise(rb_eScriptError, "ObjectTemplate::NewInstance() called without an entered Context");
|
||||
return Qnil;
|
||||
}
|
||||
Local<Object> object(obj(self)->NewInstance());
|
||||
if (object.IsEmpty()) {
|
||||
rb_raise(rb_eFatal, "V8 returned empty handle on call to ObjectTemplate::NewInstance()");
|
||||
return Qnil;
|
||||
}
|
||||
return rr_v82rb(object);
|
||||
}
|
||||
VALUE SetNamedPropertyHandler(VALUE self, VALUE getter, VALUE setter, VALUE query, VALUE deleter, VALUE enumerator, VALUE data) {
|
||||
HandleScope handles;
|
||||
if (!RTEST(getter)) {
|
||||
rb_raise(rb_eArgError, "you must supply at least a getter to V8::C::ObjectTemplate#SetNamedPropertyHandler()");
|
||||
return Qnil;
|
||||
}
|
||||
obj(self)->SetNamedPropertyHandler(
|
||||
RubyNamedPropertyGetter,
|
||||
RTEST(setter) ? RubyNamedPropertySetter : 0,
|
||||
RTEST(query) ? RubyNamedPropertyQuery : 0,
|
||||
RTEST(deleter) ? RubyNamedPropertyDeleter : 0,
|
||||
RTEST(enumerator) ? RubyNamedPropertyEnumerator : 0,
|
||||
accessor_info_data(getter, setter, query, deleter, enumerator, data)
|
||||
);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE SetIndexedPropertyHandler(VALUE self, VALUE getter, VALUE setter, VALUE query, VALUE deleter, VALUE enumerator, VALUE data) {
|
||||
HandleScope scope;
|
||||
if (!RTEST(getter)) {
|
||||
rb_raise(rb_eArgError, "you must supply at least a getter to V8::C::ObjectTemplate#SetNamedPropertyHandler()");
|
||||
return Qnil;
|
||||
}
|
||||
obj(self)->SetIndexedPropertyHandler(
|
||||
RubyIndexedPropertyGetter,
|
||||
RTEST(setter) ? RubyIndexedPropertySetter : 0,
|
||||
RTEST(query) ? RubyIndexedPropertyQuery : 0,
|
||||
RTEST(deleter) ? RubyIndexedPropertyDeleter : 0,
|
||||
RTEST(enumerator) ? RubyIndexedPropertyEnumerator : 0,
|
||||
accessor_info_data(getter, setter, query, deleter, enumerator, data)
|
||||
);
|
||||
return Qnil;
|
||||
}
|
||||
VALUE SetCallAsFunctionHandler(int argc, VALUE *argv, VALUE self) {
|
||||
Handle<Value> v8_data = make_v8_data(argc, argv, "11");
|
||||
obj(self)->SetCallAsFunctionHandler(RubyInvocationCallback, v8_data);
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Func {
|
||||
|
||||
VALUE New(int argc, VALUE *argv, VALUE self) {
|
||||
HandleScope h;
|
||||
Handle<External> v8_data = make_v8_data(argc, argv, "02");
|
||||
Local<FunctionTemplate> t = FunctionTemplate::New(RubyInvocationCallback, v8_data);
|
||||
VALUE handle = rr_v8_handle_new(self,t);
|
||||
return handle;
|
||||
}
|
||||
VALUE SetCallHandler(int argc, VALUE *argv, VALUE self) {
|
||||
HandleScope h;
|
||||
Handle<Value> v8_data = make_v8_data(argc, argv, "11");
|
||||
func(self)->SetCallHandler(RubyInvocationCallback, v8_data);
|
||||
return Qnil;
|
||||
}
|
||||
VALUE PrototypeTemplate(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v8_handle_new(ObjectTemplateClass, func(self)->PrototypeTemplate());
|
||||
}
|
||||
VALUE InstanceTemplate(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v8_handle_new(ObjectTemplateClass, func(self)->InstanceTemplate());
|
||||
}
|
||||
VALUE Inherit(VALUE self, VALUE function_template) {
|
||||
HandleScope scope;
|
||||
func(self)->Inherit(func(function_template));
|
||||
return Qnil;
|
||||
}
|
||||
VALUE SetClassName(VALUE self, VALUE name) {
|
||||
HandleScope scope;
|
||||
func(self)->SetClassName(rr_rb2v8(name)->ToString());
|
||||
return Qnil;
|
||||
}
|
||||
VALUE GetFunction(VALUE self) {
|
||||
HandleScope handles;
|
||||
if (!Context::InContext()) {
|
||||
rb_raise(rb_eScriptError, "calls to FunctionTemplate::GetFunction() require a Context to be entered");
|
||||
return Qnil;
|
||||
}
|
||||
return rr_v82rb(func(self)->GetFunction());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_template() {
|
||||
VALUE Template = rr_define_class("Template", rr_v8_handle_class());
|
||||
rr_define_method(Template, "Set", Set, 2);
|
||||
|
||||
ObjectTemplateClass = rr_define_class("ObjectTemplate", Template);
|
||||
rr_define_singleton_method(ObjectTemplateClass, "New", Obj::New, 0);
|
||||
rr_define_method(ObjectTemplateClass, "NewInstance", Obj::NewInstance, 0);
|
||||
rr_define_method(ObjectTemplateClass, "SetNamedPropertyHandler", Obj::SetNamedPropertyHandler, 6);
|
||||
rr_define_method(ObjectTemplateClass, "SetIndexedPropertyHandler", Obj::SetIndexedPropertyHandler, 6);
|
||||
rr_define_method(ObjectTemplateClass, "SetCallAsFunctionHandler", Obj::SetCallAsFunctionHandler, -1);
|
||||
|
||||
FunctionTemplateClass = rr_define_class("FunctionTemplate", Template);
|
||||
rr_define_singleton_method(FunctionTemplateClass, "New", Func::New, -1);
|
||||
rr_define_method(FunctionTemplateClass, "SetCallHandler", Func::SetCallHandler, -1);
|
||||
rr_define_method(FunctionTemplateClass, "PrototypeTemplate", Func::PrototypeTemplate, 0);
|
||||
rr_define_method(FunctionTemplateClass, "InstanceTemplate", Func::InstanceTemplate, 0);
|
||||
rr_define_method(FunctionTemplateClass, "Inherit", Func::Inherit, 1);
|
||||
rr_define_method(FunctionTemplateClass, "SetClassName", Func::SetClassName, 1);
|
||||
rr_define_method(FunctionTemplateClass, "GetFunction", Func::GetFunction, 0);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef _RUBY_V8_TEMPLATE_
|
||||
#define _RUBY_V8_TEMPLATE_
|
||||
|
||||
#include "rr.h"
|
||||
|
||||
void rr_init_template();
|
||||
|
||||
#endif
|
|
@ -1,70 +0,0 @@
|
|||
#include "rr.h"
|
||||
#include "v8_try_catch.h"
|
||||
#include "v8_message.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
VALUE TryCatchClass;
|
||||
|
||||
TryCatch *unwrap(VALUE self) {
|
||||
TryCatch *tc = 0;
|
||||
Data_Get_Struct(self, class TryCatch, tc);
|
||||
if (RTEST(rb_iv_get(self, "dead"))) {
|
||||
rb_raise(rb_eScriptError, "out of scope access of %s", RSTRING_PTR(rb_inspect(self)));
|
||||
return false;
|
||||
} else {
|
||||
return tc;
|
||||
}
|
||||
}
|
||||
|
||||
VALUE Try(int argc, VALUE *argv, VALUE self) {
|
||||
if (rb_block_given_p()) {
|
||||
HandleScope scope;
|
||||
TryCatch tc;
|
||||
VALUE try_catch = Data_Wrap_Struct(TryCatchClass, 0, 0, &tc);
|
||||
rb_iv_set(try_catch, "dead", Qfalse);
|
||||
VALUE result = rb_yield(try_catch);
|
||||
rb_iv_set(try_catch, "dead", Qtrue);
|
||||
tc.Reset();
|
||||
return result;
|
||||
} else {
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
VALUE HasCaught(VALUE self) {
|
||||
TryCatch *tc = unwrap(self);
|
||||
return tc ? rr_v82rb(tc->HasCaught()) : Qnil;
|
||||
}
|
||||
|
||||
VALUE _Exception(VALUE self) {
|
||||
TryCatch *tc = unwrap(self);
|
||||
return tc ? rr_v82rb(tc->Exception()) : Qnil;
|
||||
}
|
||||
|
||||
VALUE _StackTrace(VALUE self) {
|
||||
TryCatch *tc = unwrap(self);
|
||||
return tc ? rr_v82rb(tc->StackTrace()) : Qnil;
|
||||
}
|
||||
|
||||
VALUE _Message(VALUE self) {
|
||||
TryCatch *tc = unwrap(self);
|
||||
return tc ? rr_v82rb(tc->Message()) : Qnil;
|
||||
}
|
||||
|
||||
VALUE CanContinue(VALUE self) {
|
||||
TryCatch *tc = unwrap(self);
|
||||
return tc ? rr_v82rb(tc->CanContinue()) : Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_v8_try_catch() {
|
||||
TryCatchClass = rr_define_class("TryCatch");
|
||||
rr_define_singleton_method(TryCatchClass, "try", Try, -1);
|
||||
rr_define_method(TryCatchClass, "HasCaught", HasCaught, 0);
|
||||
rr_define_method(TryCatchClass, "Exception", _Exception, 0);
|
||||
rr_define_method(TryCatchClass, "StackTrace", _StackTrace, 0);
|
||||
rr_define_method(TryCatchClass, "Message", _Message, 0);
|
||||
rr_define_method(TryCatchClass, "CanContinue", CanContinue, 0);
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#ifndef _RR_V8_TRY_CATCH_
|
||||
#define _RR_V8_TRY_CATCH_
|
||||
|
||||
void rr_init_v8_try_catch();
|
||||
#endif
|
|
@ -1,35 +0,0 @@
|
|||
|
||||
#include "rr.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
|
||||
VALUE IsDead(VALUE self) {
|
||||
return rr_v82rb(V8::IsDead());
|
||||
}
|
||||
|
||||
VALUE AdjustAmountOfExternalAllocatedMemory(VALUE self, VALUE bytes) {
|
||||
V8::AdjustAmountOfExternalAllocatedMemory(NUM2INT(bytes));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE IdleNotification(VALUE self) {
|
||||
return rr_v82rb(V8::IdleNotification());
|
||||
}
|
||||
|
||||
VALUE SetFlagsFromString(VALUE self, VALUE string) {
|
||||
V8::SetFlagsFromString(RSTRING_PTR(string), RSTRING_LEN(string));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void rr_init_v8_v8() {
|
||||
VALUE V8Module = rr_define_module("V8");
|
||||
rr_define_singleton_method(V8Module, "IsDead", IsDead, 0);
|
||||
rr_define_singleton_method(V8Module, "AdjustAmountOfExternalAllocatedMemory", AdjustAmountOfExternalAllocatedMemory, 1);
|
||||
rr_define_singleton_method(V8Module, "IdleNotification", IdleNotification, 0);
|
||||
rr_define_singleton_method(V8Module, "SetFlagsFromString", SetFlagsFromString, 1);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef _RR_V8_V8_
|
||||
#define _RR_V8_V8_
|
||||
|
||||
void rr_init_v8_v8();
|
||||
|
||||
#endif
|
|
@ -1,175 +0,0 @@
|
|||
#include "v8_handle.h"
|
||||
#include "v8_value.h"
|
||||
|
||||
using namespace v8;
|
||||
namespace {
|
||||
|
||||
VALUE ValueClass;
|
||||
|
||||
Persistent<Value>& unwrap(VALUE value) {
|
||||
return rr_v8_handle<Value>(value);
|
||||
}
|
||||
VALUE IsEmpty(VALUE value) {
|
||||
return value == rr_v8_value_empty() ? Qtrue : Qfalse;
|
||||
}
|
||||
VALUE IsUndefined(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsUndefined());
|
||||
}
|
||||
VALUE IsNull(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsNull());
|
||||
}
|
||||
VALUE IsTrue(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsTrue());
|
||||
}
|
||||
VALUE IsFalse(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsFalse());
|
||||
}
|
||||
VALUE IsString(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsString());
|
||||
}
|
||||
VALUE IsFunction(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsFunction());
|
||||
}
|
||||
VALUE IsArray(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsArray());
|
||||
}
|
||||
VALUE IsObject(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsObject());
|
||||
}
|
||||
VALUE IsBoolean(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsBoolean());
|
||||
}
|
||||
VALUE IsNumber(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsNumber());
|
||||
}
|
||||
VALUE IsExternal(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsExternal());
|
||||
}
|
||||
VALUE IsInt32(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsInt32());
|
||||
}
|
||||
VALUE IsUint32(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsUint32());
|
||||
}
|
||||
VALUE IsDate(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->IsDate());
|
||||
}
|
||||
|
||||
VALUE ToBoolean(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->ToBoolean());
|
||||
}
|
||||
VALUE ToNumber(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->ToNumber());
|
||||
}
|
||||
VALUE ToString(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->ToString());
|
||||
}
|
||||
VALUE ToDetailString(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->ToDetailString());
|
||||
}
|
||||
VALUE ToObject(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->ToObject());
|
||||
}
|
||||
VALUE ToInteger(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->ToInteger());
|
||||
}
|
||||
VALUE ToUint32(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->ToUint32());
|
||||
}
|
||||
VALUE ToInt32(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->ToInt32());
|
||||
}
|
||||
VALUE ToArrayIndex(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->ToArrayIndex());
|
||||
}
|
||||
|
||||
VALUE Equals(VALUE self, VALUE that) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->Equals(unwrap(that)));
|
||||
}
|
||||
VALUE StrictEquals(VALUE self, VALUE that) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->StrictEquals(unwrap(that)));
|
||||
}
|
||||
|
||||
VALUE BooleanValue(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->BooleanValue());
|
||||
}
|
||||
|
||||
VALUE NumberValue(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->NumberValue());
|
||||
}
|
||||
}
|
||||
|
||||
VALUE rr_v8_value_class() {
|
||||
return ValueClass;
|
||||
}
|
||||
|
||||
VALUE rr_v8_value_empty() {
|
||||
return rr_const_get("Empty");
|
||||
}
|
||||
|
||||
void rr_init_value() {
|
||||
ValueClass = rr_define_class("Value", rr_v8_handle_class());
|
||||
rr_define_const("Empty", rr_v8_handle_new(ValueClass, Handle<Value>()));
|
||||
|
||||
rr_define_method(ValueClass, "IsEmpty", IsEmpty, 0);
|
||||
rr_define_method(ValueClass, "IsUndefined", IsUndefined, 0);
|
||||
rr_define_method(ValueClass, "IsNull", IsNull, 0);
|
||||
rr_define_method(ValueClass, "IsTrue", IsTrue, 0);
|
||||
rr_define_method(ValueClass, "IsFalse", IsFalse, 0);
|
||||
rr_define_method(ValueClass, "IsString", IsString, 0);
|
||||
rr_define_method(ValueClass, "IsFunction", IsFunction, 0);
|
||||
rr_define_method(ValueClass, "IsArray", IsArray, 0);
|
||||
rr_define_method(ValueClass, "IsObject", IsObject, 0);
|
||||
rr_define_method(ValueClass, "IsBoolean", IsBoolean, 0);
|
||||
rr_define_method(ValueClass, "IsNumber", IsNumber, 0);
|
||||
rr_define_method(ValueClass, "IsExternal", IsExternal, 0);
|
||||
rr_define_method(ValueClass, "IsInt32", IsInt32, 0);
|
||||
rr_define_method(ValueClass, "IsUint32", IsUint32, 0);
|
||||
rr_define_method(ValueClass, "IsDate", IsDate, 0);
|
||||
|
||||
rr_define_method(ValueClass, "ToBoolean", ToBoolean, 0);
|
||||
rr_define_method(ValueClass, "ToNumber", ToNumber, 0);
|
||||
rr_define_method(ValueClass, "ToString", ToString, 0);
|
||||
rr_define_method(ValueClass, "ToDetailString", ToDetailString, 0);
|
||||
rr_define_method(ValueClass, "ToObject", ToObject, 0);
|
||||
rr_define_method(ValueClass, "ToInteger", ToInteger, 0);
|
||||
rr_define_method(ValueClass, "ToUint32", ToUint32, 0);
|
||||
rr_define_method(ValueClass, "ToArrayIndex", ToArrayIndex, 0);
|
||||
|
||||
rr_define_method(ValueClass, "Equals", Equals, 1);
|
||||
rr_define_method(ValueClass, "StrictEquals", StrictEquals, 1);
|
||||
|
||||
rr_define_method(ValueClass, "BooleanValue", BooleanValue, 0);
|
||||
rr_define_method(ValueClass, "NumberValue", NumberValue, 0);
|
||||
}
|
||||
|
||||
VALUE rr_wrap_v8_value(Handle<Value>& value) {
|
||||
return rr_v8_handle_new(ValueClass, value);
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef _RR_V8_VALUE_
|
||||
#define _RR_V8_VALUE_
|
||||
|
||||
#include "rr.h"
|
||||
|
||||
void rr_init_value();
|
||||
VALUE rr_wrap_v8_value(v8::Handle<v8::Value>& value);
|
||||
VALUE rr_v8_value_class();
|
||||
VALUE rr_v8_value_empty();
|
||||
#endif
|
|
@ -1,61 +0,0 @@
|
|||
#include "v8.h"
|
||||
#include "v8_weakref.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
v8_weakref::v8_weakref(VALUE object) {
|
||||
this->v8_active = true;
|
||||
this->rb_active = true;
|
||||
this->external = Persistent<External>::New(External::New((void *)this));
|
||||
this->external.MakeWeak(this, v8_weakref_dispose);
|
||||
this->set(object);
|
||||
}
|
||||
|
||||
void v8_weakref::set(VALUE value) {
|
||||
this->object_id = rb_obj_id(value);
|
||||
this->rb_active = true;
|
||||
VALUE data = Data_Wrap_Struct(rb_cObject, 0, 0, this);
|
||||
rr_define_finalizer(value,(void*)v8_weakref_finalize, data);
|
||||
}
|
||||
|
||||
VALUE v8_weakref::get() {
|
||||
if (this->object_id) {
|
||||
return rb_rescue((VALUE (*)(...))v8_weakref_id2ref, this->object_id, (VALUE (*)(...))v8_weakref_nil, Qnil);
|
||||
} else {
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
VALUE v8_weakref_finalize(VALUE object_id, VALUE data) {
|
||||
v8_weakref* weakref = 0;
|
||||
Data_Get_Struct(data, struct v8_weakref, weakref);
|
||||
weakref->object_id = Qnil;
|
||||
weakref->rb_active = false;
|
||||
if (!weakref->v8_active) {
|
||||
delete weakref;
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
void v8_weakref_dispose(Persistent<Value> value, void* data) {
|
||||
value.Dispose();
|
||||
value.Clear();
|
||||
v8_weakref* weakref = (v8_weakref*)data;
|
||||
weakref->v8_active = false;
|
||||
if (!weakref->rb_active) {
|
||||
delete weakref;
|
||||
}
|
||||
}
|
||||
|
||||
VALUE v8_weakref_nil(VALUE nil, VALUE exception) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
VALUE v8_weakref_objectspace() {
|
||||
return rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
|
||||
}
|
||||
|
||||
VALUE v8_weakref_id2ref(VALUE id) {
|
||||
return rb_funcall(v8_weakref_objectspace(), rb_intern("_id2ref"), 1, id);
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#ifndef _RR_V8_WEAKREF_
|
||||
#define _RR_V8_WEAKREF_
|
||||
|
||||
#include <v8.h>
|
||||
#include "ruby.h"
|
||||
#include "rr.h"
|
||||
|
||||
struct v8_weakref {
|
||||
v8_weakref(VALUE object);
|
||||
VALUE get();
|
||||
void set(VALUE object);
|
||||
void retain();
|
||||
void release();
|
||||
|
||||
VALUE object_id;
|
||||
bool v8_active;
|
||||
bool rb_active;
|
||||
v8::Persistent<v8::External> external;
|
||||
};
|
||||
|
||||
void v8_weakref_dispose(v8::Persistent<v8::Value> value, void* weakref);
|
||||
VALUE v8_weakref_finalize(VALUE self, VALUE object_id);
|
||||
VALUE v8_weakref_objectspace();
|
||||
VALUE v8_weakref_nil(VALUE nil, VALUE exception);
|
||||
VALUE v8_weakref_id2ref(VALUE id);
|
||||
|
||||
// extern "C" VALUE rb_proc_new(VALUE (*)(ANYARGS/* VALUE yieldarg[, VALUE procarg] */), VALUE);
|
||||
|
||||
#endif
|
23
lib/v8.rb
23
lib/v8.rb
|
@ -1,23 +0,0 @@
|
|||
$:.unshift(File.dirname(__FILE__)) unless
|
||||
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
||||
|
||||
module V8
|
||||
require 'v8/version'
|
||||
require 'v8/v8' #native glue
|
||||
require 'v8/c/locker'
|
||||
require 'v8/portal'
|
||||
require 'v8/portal/caller'
|
||||
require 'v8/portal/proxies'
|
||||
require 'v8/portal/templates'
|
||||
require 'v8/portal/function'
|
||||
require 'v8/portal/constructor'
|
||||
require 'v8/portal/interceptors'
|
||||
require 'v8/context'
|
||||
require 'v8/object'
|
||||
require 'v8/array'
|
||||
require 'v8/function'
|
||||
require 'v8/tap'
|
||||
require 'v8/access'
|
||||
require 'v8/error'
|
||||
require 'v8/stack'
|
||||
end
|
|
@ -1,92 +0,0 @@
|
|||
require 'set'
|
||||
module V8
|
||||
class Access
|
||||
def get(obj, name, &dontintercept)
|
||||
methods = accessible_methods(obj)
|
||||
if methods.include?(name)
|
||||
method = obj.method(name)
|
||||
method.arity == 0 ? method.call : method.unbind
|
||||
elsif obj.respond_to?(:[]) && !special?(name)
|
||||
obj.send(:[], name, &dontintercept)
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def iget(obj, index, &dontintercept)
|
||||
if obj.respond_to?(:[])
|
||||
obj.send(:[], index, &dontintercept)
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def set(obj, name, value, &dontintercept)
|
||||
setter = name + "="
|
||||
methods = accessible_methods(obj, true)
|
||||
if methods.include?(setter)
|
||||
obj.send(setter, value)
|
||||
elsif obj.respond_to?(:[]=) && !special?(name)
|
||||
obj.send(:[]=, name, value, &dontintercept)
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def iset(obj, index, value, &dontintercept)
|
||||
if obj.respond_to?(:[]=)
|
||||
obj.send(:[]=, index, value, &dontintercept)
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def query(obj, name, attributes)
|
||||
if obj.respond_to?(name)
|
||||
attributes.dont_delete
|
||||
unless obj.respond_to?(name + "=")
|
||||
attributes.read_only
|
||||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def iquery(obj, index, attributes)
|
||||
if obj.respond_to?(:[])
|
||||
attributes.dont_delete
|
||||
unless obj.respond_to?(:[]=)
|
||||
attributes.read_only
|
||||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def names(obj)
|
||||
accessible_methods(obj)
|
||||
end
|
||||
|
||||
def indices(obj)
|
||||
obj.respond_to?(:length) ? (0..obj.length).to_a : yield
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def accessible_methods(obj, special_methods = false)
|
||||
obj.public_methods(false).map {|m| m.to_s}.to_set.tap do |methods|
|
||||
ancestors = obj.class.ancestors.dup
|
||||
while ancestor = ancestors.shift
|
||||
break if ancestor == ::Object
|
||||
methods.merge(ancestor.public_instance_methods(false).map {|m| m.to_s})
|
||||
end
|
||||
methods.reject!(&special?) unless special_methods
|
||||
end
|
||||
end
|
||||
|
||||
def special?(name = nil)
|
||||
@special ||= lambda {|m| m == "[]" || m == "[]=" || m =~ /=$/}
|
||||
name.nil? ? @special : @special[name]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
module V8
|
||||
class Array < V8::Object
|
||||
|
||||
def each
|
||||
@portal.open do |to|
|
||||
for i in 0..(@native.Length() - 1)
|
||||
yield to.rb(@native.Get(i))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def length
|
||||
@native.Length()
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,18 +0,0 @@
|
|||
module V8
|
||||
module C
|
||||
# Shim to support the old style locking syntax. We don't currently
|
||||
# deprecate this because it might make a comeback at some point.
|
||||
#
|
||||
# to synchronize access to V8, and to associate V8 with this thread:
|
||||
#
|
||||
# Locker() do
|
||||
# #... interact with v8
|
||||
# end
|
||||
def self.Locker
|
||||
lock = Locker.new
|
||||
yield
|
||||
ensure
|
||||
lock.delete
|
||||
end
|
||||
end
|
||||
end
|
133
lib/v8/cli.rb
133
lib/v8/cli.rb
|
@ -1,133 +0,0 @@
|
|||
require 'optparse'
|
||||
require 'ostruct'
|
||||
|
||||
module V8
|
||||
module CLI
|
||||
def self.run(exename = 'v8', args = [])
|
||||
options = OpenStruct.new
|
||||
options.libs = []
|
||||
options.libdirs = []
|
||||
parser = OptionParser.new
|
||||
parser.banner = "Usage: #{exename} [options]"
|
||||
parser.on("-r", "--require FILE", "load and execute FILE as JavaScript source") {|r| options.libs << r}
|
||||
parser.on("-i", "--interactive", "interactive mode") {options.interactive = true}
|
||||
parser.on("-e", "--execute JAVASCRIPT", String, "evaluate JAVASCRIPT and exit") {|src| options.execute = src}
|
||||
parser.on("--selftest", "check that therubyracer is functioning properly") {options.selftest = true}
|
||||
parser.on_tail("-v", "--version", "Show version and exit") {options.version_info = true}
|
||||
parser.on_tail("-h", "--help", "Show this message") do
|
||||
puts parser
|
||||
exit
|
||||
end
|
||||
begin
|
||||
parser.parse!(args.dup)
|
||||
rescue OptionParser::InvalidOption => e
|
||||
puts "#{exename}: #{e.message}"
|
||||
exit(1)
|
||||
end
|
||||
if options.version_info
|
||||
require 'libv8'
|
||||
puts "The Ruby Racer #{V8::VERSION}"
|
||||
puts "V8 Version #{Libv8::V8_VERSION}"
|
||||
exit
|
||||
elsif options.selftest
|
||||
self.test
|
||||
end
|
||||
Context.new(:with => Shell.new) do |cxt|
|
||||
for libfile in options.libs do
|
||||
load(cxt,libfile)
|
||||
end
|
||||
if options.interactive
|
||||
repl(cxt, exename)
|
||||
elsif options.execute
|
||||
cxt.eval(options.execute, '<execute>')
|
||||
else
|
||||
begin
|
||||
cxt.eval($stdin, '<stdin>')
|
||||
rescue Interrupt => e
|
||||
puts; exit
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.load(cxt, libfile)
|
||||
begin
|
||||
cxt.load(libfile)
|
||||
rescue V8::JSError => e
|
||||
puts e.message
|
||||
puts e.backtrace(:javascript)
|
||||
rescue StandardError => e
|
||||
puts e
|
||||
end
|
||||
end
|
||||
|
||||
def self.test
|
||||
begin
|
||||
require 'rspec'
|
||||
specs = File.expand_path('../../../spec', __FILE__)
|
||||
$:.unshift specs
|
||||
ARGV.clear
|
||||
ARGV << specs
|
||||
::RSpec::Core::Runner.autorun
|
||||
exit(0)
|
||||
rescue LoadError => e
|
||||
puts "selftest requires rspec to be installed (gem install rspec)"
|
||||
exit(1)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.repl(cxt, exename)
|
||||
require 'readline'
|
||||
puts "help() for help. quit() to quit."
|
||||
puts "The Ruby Racer #{V8::VERSION}"
|
||||
puts "Vroom Vroom!"
|
||||
trap("SIGINT") do
|
||||
puts "^C"
|
||||
end
|
||||
loop do
|
||||
line = Readline.readline("#{exename}> ", true)
|
||||
begin
|
||||
result = cxt.eval(line, '<shell>')
|
||||
puts(result) unless result.nil?
|
||||
rescue V8::JSError => e
|
||||
puts e.message
|
||||
puts e.backtrace(:javascript)
|
||||
rescue StandardError => e
|
||||
puts e
|
||||
puts e.backtrace.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Shell
|
||||
def to_s
|
||||
"[object Shell]"
|
||||
end
|
||||
|
||||
def print(string)
|
||||
puts string
|
||||
end
|
||||
|
||||
def exit(status = 0)
|
||||
Kernel.exit(status)
|
||||
end
|
||||
|
||||
alias_method :quit, :exit
|
||||
|
||||
def help(*args)
|
||||
<<-HELP
|
||||
print(msg)
|
||||
print msg to STDOUT
|
||||
|
||||
exit(status = 0)
|
||||
exit the shell
|
||||
also: quit()
|
||||
|
||||
evalrb(source)
|
||||
evaluate some ruby source
|
||||
HELP
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,111 +0,0 @@
|
|||
require 'stringio'
|
||||
|
||||
module V8
|
||||
class Context
|
||||
attr_reader :native, :scope, :access
|
||||
|
||||
def initialize(opts = {})
|
||||
@access = Access.new
|
||||
@to = Portal.new(self, @access)
|
||||
@to.lock do
|
||||
with = opts[:with]
|
||||
constructor = nil
|
||||
template = if with
|
||||
constructor = @to.templates.to_constructor(with.class)
|
||||
constructor.disable()
|
||||
constructor.template.InstanceTemplate()
|
||||
else
|
||||
C::ObjectTemplate::New()
|
||||
end
|
||||
@native = opts[:with] ? C::Context::New(template) : C::Context::New()
|
||||
@native.enter do
|
||||
@global = @native.Global()
|
||||
@to.proxies.register_javascript_proxy @global, :for => with if with
|
||||
constructor.enable() if constructor
|
||||
@scope = @to.rb(@global)
|
||||
@global.SetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyContext"), C::External::New(self))
|
||||
end
|
||||
yield(self) if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
def eval(javascript, filename = "<eval>", line = 1)
|
||||
if IO === javascript || StringIO === javascript
|
||||
javascript = javascript.read()
|
||||
end
|
||||
err = nil
|
||||
value = nil
|
||||
@to.lock do
|
||||
C::TryCatch.try do |try|
|
||||
@native.enter do
|
||||
script = C::Script::Compile(@to.v8(javascript.to_s), @to.v8(filename.to_s))
|
||||
if try.HasCaught()
|
||||
err = JSError.new(try, @to)
|
||||
else
|
||||
result = script.Run()
|
||||
if try.HasCaught()
|
||||
err = JSError.new(try, @to)
|
||||
else
|
||||
value = @to.rb(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
raise err if err
|
||||
return value
|
||||
end
|
||||
|
||||
def load(filename)
|
||||
File.open(filename) do |file|
|
||||
self.eval file, filename, 1
|
||||
end
|
||||
end
|
||||
|
||||
def [](key)
|
||||
@to.open do
|
||||
@to.rb @global.Get(@to.v8(key))
|
||||
end
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
@to.open do
|
||||
@global.Set(@to.v8(key), @to.v8(value))
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
def self.stack(limit = 99)
|
||||
if native = C::Context::GetEntered()
|
||||
global = native.Global()
|
||||
cxt = global.GetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyContext")).Value()
|
||||
cxt.instance_eval {@to.rb(C::StackTrace::CurrentStackTrace(limit))}
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def self.passes_this_argument?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
module C
|
||||
class Context
|
||||
def enter
|
||||
if block_given?
|
||||
if IsEntered()
|
||||
yield(self)
|
||||
else
|
||||
Enter()
|
||||
begin
|
||||
yield(self)
|
||||
ensure
|
||||
Exit()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
130
lib/v8/error.rb
130
lib/v8/error.rb
|
@ -1,130 +0,0 @@
|
|||
|
||||
module V8
|
||||
class JSError < StandardError
|
||||
attr_reader :value, :boundaries
|
||||
|
||||
def initialize(try, to)
|
||||
@to = to
|
||||
super(initialize_unsafe(try))
|
||||
rescue Exception => e
|
||||
@boundaries = [Boundary.new(:rbframes => e.backtrace)]
|
||||
@value = e
|
||||
super(<<-EOMSG)
|
||||
You have uncovered what is probably a BUG in therubyracer exception code. An error report would be very helpful.
|
||||
JSError#initialize failed!: #{e.message}"
|
||||
EOMSG
|
||||
end
|
||||
|
||||
def initialize_unsafe(try)
|
||||
message = nil
|
||||
ex = @to.rb(try.Exception())
|
||||
@boundaries = [Boundary.new(:rbframes => caller(3), :jsframes => parse_js_frames(try))]
|
||||
if V8::Object === ex
|
||||
if msg = ex['message']
|
||||
message = msg
|
||||
else
|
||||
message = ex.to_s
|
||||
end
|
||||
if cause = ex.instance_variable_get(:@native).GetHiddenValue("TheRubyRacer::Cause")
|
||||
if !cause.IsEmpty()
|
||||
prev = cause.Value()
|
||||
if prev.kind_of?(JSError)
|
||||
@boundaries.concat prev.boundaries
|
||||
@value = prev.value
|
||||
else
|
||||
@value = prev
|
||||
@boundaries.concat [Boundary.new(:rbframes => prev.backtrace)]
|
||||
end
|
||||
else
|
||||
@value = ex
|
||||
end
|
||||
end
|
||||
else
|
||||
@value = ex
|
||||
message = ex.to_s
|
||||
end
|
||||
return message
|
||||
end
|
||||
|
||||
def in_ruby?
|
||||
@value.kind_of?(Exception)
|
||||
end
|
||||
|
||||
def in_javascript?
|
||||
!in_ruby?
|
||||
end
|
||||
|
||||
def backtrace(*modifiers)
|
||||
trace_framework = modifiers.include?(:framework)
|
||||
trace_ruby = modifiers.length == 0 || modifiers.include?(:ruby)
|
||||
trace_javascript = modifiers.length == 0 || modifiers.include?(:javascript)
|
||||
mixed = []
|
||||
rbcontext = []
|
||||
jscontext = []
|
||||
@boundaries.each do |b|
|
||||
rbframes = b.rbframes.dup
|
||||
rbcontext.reverse_each do |frame|
|
||||
if frame == rbframes.last
|
||||
rbframes.pop
|
||||
else
|
||||
break
|
||||
end
|
||||
end if trace_ruby
|
||||
jsframes = b.jsframes.dup
|
||||
jscontext.reverse_each do |frame|
|
||||
if frame == jsframes.last
|
||||
jsframes.pop
|
||||
else
|
||||
break
|
||||
end
|
||||
end if trace_javascript
|
||||
rbcontext = b.rbframes
|
||||
jscontext = b.jsframes
|
||||
rbframes.reject! {|f| f =~ /lib\/v8\/.*\.rb/} unless trace_framework
|
||||
mixed.unshift(*rbframes) if trace_ruby
|
||||
mixed.unshift(*jsframes) if trace_javascript
|
||||
end
|
||||
return mixed
|
||||
end
|
||||
|
||||
def parse_js_frames(try)
|
||||
#I can't figure out why V8 is not capturing the stacktrace here
|
||||
#in terms of StackTrace and StackFrame objects, so we have to
|
||||
#parse the string.
|
||||
raw = @to.rb(try.StackTrace())
|
||||
if raw && !raw.empty? && !syntax_error?(try)
|
||||
raw.split("\n")[1..-1].tap do |frames|
|
||||
frames.each {|frame| frame.strip!; frame.chomp!(",")}
|
||||
end.reject(&:empty?)
|
||||
else
|
||||
msg = try.Message()
|
||||
["at #{@to.rb(msg.GetScriptResourceName())}:#{msg.GetLineNumber()}:#{msg.GetStartColumn() + 1}"]
|
||||
end
|
||||
end
|
||||
|
||||
#Syntax errors are weird in that they have a non-empty stack trace
|
||||
#but it does not contain any source location information, so
|
||||
#in these instances, we have to pull it out of the Message object
|
||||
#in the TryCatch. Is there a better way to detect a syntax error
|
||||
def syntax_error?(try)
|
||||
ex = @to.rb(try.Exception())
|
||||
if ex && ex.kind_of?(V8::Object)
|
||||
type = ex["constructor"]
|
||||
type && type.kind_of?(V8::Function) && type.name == "SyntaxError"
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
class Boundary
|
||||
attr_reader :rbframes, :jsframes
|
||||
|
||||
def initialize(frames = {})
|
||||
@rbframes = frames[:rbframes] || []
|
||||
@jsframes = frames[:jsframes] || []
|
||||
end
|
||||
end
|
||||
end
|
||||
#deprecated -- use JSError
|
||||
JavasriptError = JSError
|
||||
end
|
|
@ -1,44 +0,0 @@
|
|||
module V8
|
||||
class Function < V8::Object
|
||||
|
||||
def methodcall(thisObject, *args)
|
||||
err = nil
|
||||
return_value = nil
|
||||
@portal.open do |to|
|
||||
C::TryCatch.try do |try|
|
||||
this = to.v8(thisObject)
|
||||
return_value = to.rb(@native.Call(this, to.v8(args)))
|
||||
err = JSError.new(try, to) if try.HasCaught()
|
||||
end
|
||||
end
|
||||
raise err if err
|
||||
return return_value
|
||||
end
|
||||
|
||||
def call(*args)
|
||||
@portal.open do
|
||||
self.methodcall(@portal.context.native.Global(), *args)
|
||||
end
|
||||
end
|
||||
|
||||
def new(*args)
|
||||
@portal.open do |to|
|
||||
to.rb(@native.NewInstance(to.v8(args)))
|
||||
end
|
||||
end
|
||||
|
||||
def name
|
||||
@portal.open do |to|
|
||||
to.rb(@native.GetName())
|
||||
end
|
||||
end
|
||||
|
||||
def name=(name)
|
||||
name.tap do
|
||||
@portal.open do |to|
|
||||
@native.SetName(to.v8(name))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,69 +0,0 @@
|
|||
|
||||
module V8
|
||||
class Object
|
||||
include Enumerable
|
||||
|
||||
def initialize(native, portal)
|
||||
@native, @portal = native, portal
|
||||
end
|
||||
|
||||
def [](key)
|
||||
@portal.open do |to|
|
||||
to.rb(@native.Get(to.v8(key)))
|
||||
end
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
value.tap do
|
||||
@portal.open do |to|
|
||||
@native.Set(to.v8(key), to.v8(value))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
@portal.open do |to|
|
||||
to.rb(@native.ToString())
|
||||
end
|
||||
end
|
||||
|
||||
def each
|
||||
@portal.open do |to|
|
||||
for prop in to.rb(@native.GetPropertyNames())
|
||||
yield prop, self[prop]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def respond_to?(method)
|
||||
super or self[method] != nil
|
||||
end
|
||||
|
||||
def method_missing(name, *args, &block)
|
||||
if name.to_s =~ /(.*)=$/
|
||||
if args.length > 1
|
||||
self[$1] = args
|
||||
return args
|
||||
else
|
||||
self[$1] = args.first
|
||||
return args
|
||||
end
|
||||
end
|
||||
return super(name, *args, &block) unless self.respond_to?(name)
|
||||
property = self[name]
|
||||
if property.kind_of?(V8::Function)
|
||||
property.methodcall(self, *args)
|
||||
elsif args.empty?
|
||||
property
|
||||
else
|
||||
raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Object
|
||||
def eval_js(javascript)
|
||||
V8::Context.new(:with => self).eval(javascript)
|
||||
end
|
||||
end
|
|
@ -1,86 +0,0 @@
|
|||
|
||||
module V8
|
||||
class Portal
|
||||
attr_reader :context, :access, :proxies, :templates, :interceptors, :caller
|
||||
|
||||
def initialize(context, access)
|
||||
@context, @access = context, access
|
||||
@proxies = Proxies.new
|
||||
@templates = Templates.new(self)
|
||||
@interceptors = Interceptors.new(self)
|
||||
@caller = Caller.new(self)
|
||||
end
|
||||
|
||||
def lock
|
||||
lock = V8::C::Locker.new
|
||||
yield
|
||||
ensure
|
||||
lock.delete
|
||||
end
|
||||
|
||||
def open
|
||||
lock do
|
||||
@context.native.enter do
|
||||
yield(self)
|
||||
end if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
def rb(value)
|
||||
@proxies.js2rb(value) do
|
||||
case value
|
||||
when V8::C::Function then V8::Function.new(value, self)
|
||||
when V8::C::Array then V8::Array.new(value, self)
|
||||
when V8::C::Object then V8::Object.new(value, self)
|
||||
when V8::C::String then value.Utf8Value.tap {|s| return s.respond_to?(:force_encoding) ? s.force_encoding("UTF-8") : s}
|
||||
when V8::C::Date then Time.at(value.NumberValue() / 1000)
|
||||
when V8::C::StackTrace then V8::StackTrace.new(self, value)
|
||||
when V8::C::Value then nil if value.IsEmpty()
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def v8(value)
|
||||
case value
|
||||
when V8::Object
|
||||
value.instance_eval {@native}
|
||||
when String
|
||||
C::String::New(value)
|
||||
when Symbol
|
||||
C::String::NewSymbol(value.to_s)
|
||||
when Proc,Method,UnboundMethod
|
||||
@proxies.rb2js(value) do
|
||||
@templates.to_function(value).function
|
||||
end
|
||||
when ::Array
|
||||
C::Array::New(value.length).tap do |a|
|
||||
value.each_with_index do |item, i|
|
||||
a.Set(i, v8(item))
|
||||
end
|
||||
end
|
||||
when ::Hash
|
||||
C::Object::New().tap do |o|
|
||||
value.each do |key, val|
|
||||
o.Set(v8(key), v8(val))
|
||||
end
|
||||
end
|
||||
when ::Time
|
||||
C::Date::New(value.to_f * 1000)
|
||||
when ::Class
|
||||
@proxies.rb2js(value) do
|
||||
constructor = @templates.to_constructor(value)
|
||||
constructor.exposed = true
|
||||
constructor.function
|
||||
end
|
||||
when nil,Numeric,TrueClass,FalseClass, C::Value
|
||||
value
|
||||
else
|
||||
@proxies.rb2js(value) do
|
||||
@templates.to_constructor(value.class).allocate(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,37 +0,0 @@
|
|||
|
||||
module V8
|
||||
class Portal
|
||||
class Caller
|
||||
|
||||
def initialize(portal)
|
||||
@portal = portal
|
||||
end
|
||||
|
||||
def raw
|
||||
yield
|
||||
rescue Exception => e
|
||||
case e
|
||||
when SystemExit, NoMemoryError
|
||||
raise e
|
||||
else
|
||||
error = V8::C::Exception::Error(V8::C::String::New(e.message))
|
||||
#TODO: This is almost certainly a crash here.
|
||||
#we need to hold onto `error` while it bubbles up the javascript stack.
|
||||
error.SetHiddenValue("TheRubyRacer::Cause", C::External::New(e))
|
||||
V8::C::ThrowException(error)
|
||||
end
|
||||
end
|
||||
|
||||
def protect(*args, &block)
|
||||
@portal.v8 raw(*args, &block)
|
||||
end
|
||||
|
||||
def invoke(code, *args, &block)
|
||||
protect do
|
||||
args = args.slice(0, code.arity) if code.arity >= 0
|
||||
code.call(*args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,98 +0,0 @@
|
|||
|
||||
module V8
|
||||
class Portal
|
||||
class ConstructorAdapter
|
||||
attr_reader :template, :exposed
|
||||
alias_method :exposed?, :exposed
|
||||
|
||||
def initialize(templates, class_id)
|
||||
@exposed = false
|
||||
@class_id = class_id
|
||||
@templates = templates
|
||||
@invoke = method(:invoke)
|
||||
@template = C::FunctionTemplate::New(@invoke)
|
||||
portal.interceptors.setup(@template.InstanceTemplate())
|
||||
cls = self.ruby_class
|
||||
superclass = cls.superclass
|
||||
if cls != ::Object && superclass != ::Object && superclass != ::Class
|
||||
@template.Inherit(templates.to_constructor(superclass).template)
|
||||
end
|
||||
if cls.name && cls.name =~ /(::)?(\w+?)$/
|
||||
template.SetClassName(C::String::NewSymbol("rb::" + $2))
|
||||
else
|
||||
template.SetClassName("Ruby")
|
||||
end
|
||||
end
|
||||
|
||||
def function
|
||||
@template.GetFunction()
|
||||
end
|
||||
|
||||
def allocate(object)
|
||||
arguments = C::Array::New(1)
|
||||
arguments.Set(0, C::External::New(object))
|
||||
function.NewInstance(arguments)
|
||||
end
|
||||
|
||||
def disable
|
||||
@disabled = true
|
||||
end
|
||||
|
||||
def enable
|
||||
@disabled = nil
|
||||
end
|
||||
|
||||
def invoke(arguments)
|
||||
return if @disabled
|
||||
if !@exposed
|
||||
unless arguments.Length() == 1 && arguments[0].kind_of?(C::External)
|
||||
C::ThrowException(C::Exception::Error(C::String::New("cannot call native constructor from javascript")))
|
||||
else
|
||||
object = arguments[0].Value()
|
||||
proxies.register_javascript_proxy arguments.This(), :for => object
|
||||
end
|
||||
else
|
||||
instance = nil
|
||||
if arguments.Length() > 0 && arguments[0].kind_of?(C::External)
|
||||
instance = arguments[0].Value()
|
||||
else
|
||||
rbargs = []
|
||||
for i in 0..arguments.Length() - 1
|
||||
rbargs << @templates.portal.rb(arguments[i])
|
||||
end
|
||||
instance = portal.caller.raw do
|
||||
self.ruby_class.new(*rbargs)
|
||||
end
|
||||
end
|
||||
proxies.register_javascript_proxy arguments.This(), :for => instance
|
||||
end
|
||||
end
|
||||
|
||||
def exposed=(exposed)
|
||||
if exposed && !@augmented
|
||||
#create a prototype so that this constructor also acts like a ruby object
|
||||
prototype_template = C::ObjectTemplate::New()
|
||||
portal.interceptors.setup(prototype_template)
|
||||
prototype = prototype_template.NewInstance()
|
||||
#set *that* object's prototype to an empty function so that it will look and behave like a function.
|
||||
prototype.SetPrototype(C::FunctionTemplate::New().GetFunction())
|
||||
template.GetFunction().SetPrototype(prototype)
|
||||
@augmented = true
|
||||
end
|
||||
@exposed = exposed
|
||||
end
|
||||
|
||||
def ruby_class
|
||||
ObjectSpace._id2ref(@class_id)
|
||||
end
|
||||
|
||||
def proxies
|
||||
@templates.proxies
|
||||
end
|
||||
|
||||
def portal
|
||||
@templates.portal
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,63 +0,0 @@
|
|||
module V8
|
||||
class Portal
|
||||
class FunctionAdapter
|
||||
|
||||
attr_reader :template
|
||||
|
||||
def initialize(portal, code)
|
||||
@portal = portal
|
||||
@caller = case code
|
||||
when Method then BoundCall.new(portal)
|
||||
when UnboundMethod then BindAndCall.new(portal)
|
||||
else Call.new(portal)
|
||||
end
|
||||
@code = code
|
||||
@template = V8::C::FunctionTemplate::New(@caller, @code)
|
||||
end
|
||||
|
||||
def function
|
||||
@template.GetFunction()
|
||||
end
|
||||
|
||||
class Call
|
||||
def initialize(portal)
|
||||
@portal = portal
|
||||
end
|
||||
|
||||
def call(arguments)
|
||||
proc = arguments.Data()
|
||||
rbargs = [@portal.rb(arguments.This())]
|
||||
for i in 0..arguments.Length() - 1
|
||||
rbargs << @portal.rb(arguments[i])
|
||||
end
|
||||
@portal.caller.invoke(proc, *rbargs)
|
||||
end
|
||||
end
|
||||
|
||||
class BoundCall < Call
|
||||
def call(arguments)
|
||||
proc = arguments.Data()
|
||||
rbargs = []
|
||||
for i in 0..arguments.Length() - 1
|
||||
rbargs << @portal.rb(arguments[i])
|
||||
end
|
||||
@portal.caller.invoke(proc, *rbargs)
|
||||
end
|
||||
end
|
||||
|
||||
class BindAndCall < Call
|
||||
def call(arguments)
|
||||
method = arguments.Data()
|
||||
rbargs = []
|
||||
for i in 0..arguments.Length() - 1
|
||||
rbargs << @portal.rb(arguments[i])
|
||||
end
|
||||
this = @portal.rb(arguments.This())
|
||||
@portal.caller.protect do
|
||||
method.bind(this).call(*rbargs)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,152 +0,0 @@
|
|||
module V8
|
||||
class Portal
|
||||
class Interceptors
|
||||
def initialize(portal)
|
||||
@getter = NamedPropertyGetter.new(portal)
|
||||
@setter = NamedPropertySetter.new(portal)
|
||||
@query = nil
|
||||
@deleter = nil
|
||||
@enumerator = NamedPropertyEnumerator.new(portal)
|
||||
@igetter = IndexedPropertyGetter.new(portal)
|
||||
@isetter = IndexedPropertySetter.new(portal)
|
||||
@iquery = nil
|
||||
@ideleter = nil
|
||||
@ienumerator = IndexedPropertyEnumerator.new(portal)
|
||||
end
|
||||
|
||||
def setup(template)
|
||||
template.SetNamedPropertyHandler(@getter,@setter,@query,@deleter,@enumerator, nil)
|
||||
template.SetIndexedPropertyHandler(@igetter,@isetter,@iquery,@ideleter,@ienumerator, nil)
|
||||
end
|
||||
|
||||
class PropertyAttributes
|
||||
attr_reader :flags
|
||||
def initialize
|
||||
@flags = 0
|
||||
end
|
||||
|
||||
def read_only
|
||||
tap do
|
||||
@flags |= V8::C::ReadOnly
|
||||
end
|
||||
end
|
||||
|
||||
def dont_enum
|
||||
tap do
|
||||
@flags |= V8::C::DontEnum
|
||||
end
|
||||
end
|
||||
|
||||
def dont_delete
|
||||
tap do
|
||||
@flags |= V8::C::DontDelete
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Interceptor
|
||||
|
||||
def initialize(portal)
|
||||
@to, @access = portal, portal.access
|
||||
end
|
||||
|
||||
def intercept(info, retval = nil, &code)
|
||||
obj = @to.rb(info.This())
|
||||
intercepts = true
|
||||
result = @to.caller.protect do
|
||||
dontintercept = proc do
|
||||
intercepts = false
|
||||
end
|
||||
code.call(obj, dontintercept)
|
||||
end
|
||||
intercepts ? (retval || result) : C::Empty
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertyGetter < Interceptor
|
||||
def call(property, info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.get(obj, @to.rb(property), &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertySetter < Interceptor
|
||||
def call(property, value, info)
|
||||
intercept(info, value) do |obj, dontintercept|
|
||||
@access.set(obj, @to.rb(property), @to.rb(value), &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertyQuery
|
||||
def call(property, info)
|
||||
attributes = PropertyAttributes.new
|
||||
result = intercept(info) do |obj, dontintercept|
|
||||
@access.query(obj, @to.rb(property), attributes, &dontintercept)
|
||||
end
|
||||
return result == C::Empty ? result : C::Integer::New(attributes.flags)
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertyEnumerator < Interceptor
|
||||
def call(info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.names(obj, &dontintercept).to_a
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertyDeleter < Interceptor
|
||||
def call(property, info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.delete(obj, property, &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class IndexedPropertyGetter < Interceptor
|
||||
def call(index, info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.iget(obj, index, &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class IndexedPropertySetter < Interceptor
|
||||
def call(index, value, info)
|
||||
intercept(info, value) do |obj, dontintercept|
|
||||
@access.iset(obj, index, @to.rb(value), &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class IndexedPropertyQuery < Interceptor
|
||||
def call(property, info)
|
||||
attributes = PropertyAttributes.new
|
||||
result = intercept(info) do |obj, dontintercept|
|
||||
@access.indices(obj, &dontintercept)
|
||||
end
|
||||
result == C::Empty ? C::Empty : C::Integer::New(attributes.flags)
|
||||
end
|
||||
end
|
||||
|
||||
class IndexedPropertyDeleter < Interceptor
|
||||
def call(index, info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.idelete(obj, index, &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class IndexedPropertyEnumerator < Interceptor
|
||||
def call(info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.indices(obj, &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
module V8
|
||||
class Portal
|
||||
class Proxies
|
||||
|
||||
def initialize
|
||||
@js_proxies_rb2js = {}
|
||||
@js_proxies_js2rb = {}
|
||||
@rb_proxies_rb2js = {}
|
||||
@rb_proxies_js2rb = {}
|
||||
@clear_js_proxy = ClearJSProxy.new(@js_proxies_rb2js, @js_proxies_js2rb)
|
||||
@clear_rb_proxy = ClearRubyProxy.new(@rb_proxies_rb2js, @rb_proxies_js2rb)
|
||||
end
|
||||
|
||||
def js2rb(js)
|
||||
if rb = js_proxy_2_rb_object(js)
|
||||
return rb
|
||||
elsif rb = js_object_2_rb_proxy(js)
|
||||
return rb
|
||||
else
|
||||
proxy = block_given? ? yield(js) : Object.new
|
||||
register_ruby_proxy proxy, :for => js if proxy && js && js.kind_of?(V8::C::Handle)
|
||||
return proxy
|
||||
end
|
||||
end
|
||||
|
||||
def rb2js(rb)
|
||||
if js = rb_proxy_2_js_object(rb)
|
||||
return js
|
||||
elsif js = rb_object_2_js_proxy(rb)
|
||||
return js
|
||||
else
|
||||
proxy = block_given? ? yield(rb) : V8::C::Object::New()
|
||||
register_javascript_proxy proxy, :for => rb unless @js_proxies_rb2js[rb]
|
||||
return proxy
|
||||
end
|
||||
end
|
||||
|
||||
def register_javascript_proxy(proxy, options = {})
|
||||
target = options[:for] or fail ArgumentError, "must specify the object that you're proxying with the :for => param"
|
||||
fail ArgumentError, "javascript proxy must be a V8::C::Handle, not #{proxy.class}" unless proxy.kind_of?(V8::C::Handle)
|
||||
fail DoubleProxyError, "target already has a registered proxy" if @js_proxies_rb2js[target]
|
||||
|
||||
@js_proxies_js2rb[proxy] = target
|
||||
@js_proxies_rb2js[target] = proxy
|
||||
proxy.MakeWeak(nil, @clear_js_proxy)
|
||||
end
|
||||
|
||||
# Lookup the JavaScript proxy for a natively Ruby object
|
||||
# @param [Object] object the Ruby object
|
||||
# @return [V8::C::Handle] the JavaScript proxy representing `object`
|
||||
def rb_object_2_js_proxy(object)
|
||||
@js_proxies_rb2js[object]
|
||||
end
|
||||
|
||||
# Look up a natively Ruby object given its JavaScript proxy
|
||||
# @param [V8::C::Handle] proxy the JavaScript proxy
|
||||
# @return [Object] the Ruby object represented by `proxy`
|
||||
def js_proxy_2_rb_object(proxy)
|
||||
@js_proxies_js2rb[proxy]
|
||||
end
|
||||
|
||||
def register_ruby_proxy(proxy, options = {})
|
||||
target = options[:for] or fail ArgumentError, "must specify the object that you're proxying with the :for => param"
|
||||
fail ArgumentError, "'#{proxy.inspect}' is not a Handle to an actual V8 object" unless target.kind_of?(V8::C::Handle)
|
||||
@rb_proxies_rb2js[proxy.object_id] = target
|
||||
@rb_proxies_js2rb[target] = proxy.object_id
|
||||
ObjectSpace.define_finalizer(proxy, @clear_rb_proxy)
|
||||
end
|
||||
|
||||
# Looks up the Ruby proxy for an object that is natively JavaScript
|
||||
# @param [V8::C::Handle] object the JavaScript whose proxy we want
|
||||
# @return [Object] the Ruby proxy for `object`
|
||||
def js_object_2_rb_proxy(object)
|
||||
if id = @rb_proxies_js2rb[object]
|
||||
ObjectSpace._id2ref id
|
||||
end
|
||||
rescue RangeError
|
||||
# sometimes, the Ruby proxy has been garbage collected, but
|
||||
# the finalizer which runs has not been called. That's OK
|
||||
# we just clear out the entry, and return nil so that a new
|
||||
# proxy can be created.
|
||||
@clear_rb_proxy.call(id)
|
||||
return nil
|
||||
end
|
||||
|
||||
# Looks up a native JavaScript object by its Ruby proxy
|
||||
# @param [Object] proxy the Ruby proxy
|
||||
# @return [V8::C::Handle] the native JavaScript object
|
||||
def rb_proxy_2_js_object(proxy)
|
||||
@rb_proxies_rb2js[proxy.object_id]
|
||||
end
|
||||
|
||||
def js_empty?
|
||||
@js_proxies_rb2js.empty? && @js_proxies_js2rb.empty?
|
||||
end
|
||||
|
||||
def rb_empty?
|
||||
@rb_proxies_rb2js.empty? && @rb_proxies_js2rb.empty?
|
||||
end
|
||||
|
||||
def empty?
|
||||
js_empty? && rb_empty?
|
||||
end
|
||||
DoubleProxyError = Class.new(StandardError)
|
||||
|
||||
class ClearJSProxy
|
||||
|
||||
def initialize(rb2js, js2rb)
|
||||
@rb2js, @js2rb = rb2js, js2rb
|
||||
end
|
||||
|
||||
def call(proxy, parameter)
|
||||
rb = @js2rb[proxy]
|
||||
@js2rb.delete(proxy)
|
||||
@rb2js.delete(rb)
|
||||
end
|
||||
end
|
||||
|
||||
# @Private
|
||||
# Remove the linkage between a Ruby proxy and a native
|
||||
# JavaScript object. In general, this object is registered
|
||||
# as a finalizer on the Ruby proxy itself, so that when it is
|
||||
# garbage collected, it releases the back reference to the
|
||||
# native JavaScript object.
|
||||
#
|
||||
# It is important to do this as soon as is reasonably possible
|
||||
# so that the native JavaScript object can itself be garbage
|
||||
# collected (provided there are no other references to it)
|
||||
class ClearRubyProxy
|
||||
def initialize(rb2js, js2rb)
|
||||
@rb2js, @js2rb = rb2js, js2rb
|
||||
end
|
||||
|
||||
# takes the object id of a Ruby proxy that has been garbage collected
|
||||
# and releases the reference to the native JavaScript object that
|
||||
# it was bound to.
|
||||
# @param[Fixnum] proxy_id the proxy id of the garbage collected Ruby proxy
|
||||
def call(proxy_id)
|
||||
# TODO: this if-check should be synchronized, so that if called manually
|
||||
# it will not conflict with the finalization thread. It's not so heinous
|
||||
# if the refererence gets cleared twice, but we definiteily dont't want
|
||||
# to double-decrement the v8 GC hint.
|
||||
if js = @rb2js[proxy_id]
|
||||
@rb2js.delete(proxy_id)
|
||||
@js2rb.delete(js)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,73 +0,0 @@
|
|||
|
||||
module V8
|
||||
class Portal
|
||||
class Templates
|
||||
|
||||
attr_reader :portal
|
||||
|
||||
def initialize(portal)
|
||||
@portal = portal
|
||||
@constructors = {}
|
||||
@methods = {}
|
||||
@procs = {}
|
||||
@releases = {}
|
||||
end
|
||||
|
||||
def to_constructor(ruby_class)
|
||||
class_id = ruby_class.object_id
|
||||
if constructor = @constructors[class_id]
|
||||
return constructor
|
||||
else
|
||||
constructor = @constructors[class_id] = ConstructorAdapter.new(self, class_id)
|
||||
ObjectSpace.define_finalizer(ruby_class, release(@constructors, class_id))
|
||||
return constructor
|
||||
end
|
||||
end
|
||||
|
||||
def to_function(code)
|
||||
case code
|
||||
when Method, UnboundMethod
|
||||
if fn = @methods[code.to_s]
|
||||
return fn
|
||||
else
|
||||
function = @methods[code.to_s] = FunctionAdapter.new(@portal, code)
|
||||
#TODO: test this weak behavior
|
||||
function.template.MakeWeak(0, release(@methods, code.to_s))
|
||||
return function
|
||||
end
|
||||
else
|
||||
if fn = @procs[code]
|
||||
return fn
|
||||
else
|
||||
function = @procs[code] = FunctionAdapter.new(@portal, code)
|
||||
#TODO: test this weak behavior
|
||||
function.template.MakeWeak(0, release(@procs, code))
|
||||
return function
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def proxies
|
||||
@portal.proxies
|
||||
end
|
||||
|
||||
def release(refs, id)
|
||||
release = Release.new(@releases, refs, id)
|
||||
@releases[release] = true
|
||||
return release
|
||||
end
|
||||
|
||||
class Release
|
||||
def initialize(releases, refs, id)
|
||||
@releases, @refs, @id = releases, refs, id
|
||||
end
|
||||
|
||||
def call(*args)
|
||||
@refs.delete(@id)
|
||||
@releases.delete(self)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,66 +0,0 @@
|
|||
|
||||
module V8
|
||||
|
||||
class StackTrace
|
||||
include Enumerable
|
||||
|
||||
def initialize(to, native)
|
||||
@to = to
|
||||
@native = native
|
||||
end
|
||||
|
||||
def length
|
||||
@native.GetFrameCount()
|
||||
end
|
||||
|
||||
def each
|
||||
for i in 0..length - 1
|
||||
yield V8::StackFrame.new(@to, @native.GetFrame(i))
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
map {|f|"at #{f}"}.join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
class StackFrame
|
||||
|
||||
def initialize(portal, native)
|
||||
@to = portal
|
||||
@native = native
|
||||
end
|
||||
|
||||
def script_name
|
||||
@to.rb(@native.GetScriptName())
|
||||
end
|
||||
|
||||
def function_name
|
||||
@to.rb(@native.GetFunctionName())
|
||||
end
|
||||
|
||||
def line_number
|
||||
@native.GetLineNumber()
|
||||
end
|
||||
|
||||
def column
|
||||
@native.GetColumn()
|
||||
end
|
||||
|
||||
def eval?
|
||||
@native.IsEval()
|
||||
end
|
||||
|
||||
def constructor?
|
||||
@native.IsConstructor()
|
||||
end
|
||||
|
||||
def to_s
|
||||
if @native.GetFunctionName()
|
||||
"#{function_name} (#{script_name}:#{line_number}:#{column})"
|
||||
else
|
||||
"#{script_name}:#{line_number}:#{column}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
unless Object.method_defined?(:tap)
|
||||
class Object
|
||||
def tap
|
||||
yield self
|
||||
return self
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
module V8
|
||||
VERSION = "0.10.1"
|
||||
end
|
|
@ -1,15 +0,0 @@
|
|||
require "spec_helper"
|
||||
|
||||
describe V8::C::Array do
|
||||
include V8::ExtSpec
|
||||
|
||||
it "can be instantiated" do
|
||||
a = c::Array::New()
|
||||
a.Length().should eql(0)
|
||||
end
|
||||
|
||||
it "maintains referential integrity" do
|
||||
v8_eval('a = []')
|
||||
v8_eval('a').should be(v8_eval('a'))
|
||||
end
|
||||
end
|
|
@ -1,57 +0,0 @@
|
|||
|
||||
require "#{File.dirname(__FILE__)}/../spec_helper.rb"
|
||||
|
||||
include V8
|
||||
|
||||
describe C::Context do
|
||||
|
||||
before {@lock = C::Locker.new}
|
||||
after {@lock.delete}
|
||||
|
||||
it "should not have a current context if no context is open" do
|
||||
C::Context::GetEntered().should be_nil
|
||||
end
|
||||
|
||||
it "can javascript properties on the global scope via ruby when the default scope is a ruby object" do
|
||||
V8::Context.new(:with => Object.new) do |cxt|
|
||||
cxt['foo'] = 'bar'
|
||||
cxt.eval('foo').should eql('bar')
|
||||
end
|
||||
end
|
||||
|
||||
it "can get the current javascript execution stack" do
|
||||
V8::Context.new do |cxt|
|
||||
trace = nil
|
||||
cxt['getTrace'] = lambda do
|
||||
trace = V8::Context.stack
|
||||
end
|
||||
cxt.eval(<<-JS, 'trace.js')
|
||||
function one() {
|
||||
return two();
|
||||
}
|
||||
|
||||
function two() {
|
||||
return three();
|
||||
}
|
||||
|
||||
function three() {
|
||||
return getTrace()
|
||||
}
|
||||
one();
|
||||
JS
|
||||
trace.length.should be(4)
|
||||
trace.to_a[0].tap do |frame|
|
||||
frame.line_number.should == 10
|
||||
frame.column.should == 16
|
||||
frame.script_name.should == 'trace.js'
|
||||
frame.function_name.should == 'three'
|
||||
frame.should_not be_eval
|
||||
frame.should_not be_constructor
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "has an empty stack if there is no enterned context" do
|
||||
V8::Context.stack.should be_empty
|
||||
end
|
||||
end
|
|
@ -1,27 +0,0 @@
|
|||
|
||||
module V8::ExtSpec
|
||||
|
||||
def self.included(object)
|
||||
object.class_eval do
|
||||
before do
|
||||
@lock = c::Locker.new
|
||||
@cxt = c::Context::New()
|
||||
@cxt.Enter()
|
||||
end
|
||||
after do
|
||||
@cxt.Exit()
|
||||
@cxt.Dispose()
|
||||
@lock.delete
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def v8_eval(script, sourcename = "<eval>")
|
||||
c::Script::New(c::String::New(script), c::String::New(sourcename)).Run()
|
||||
end
|
||||
|
||||
def c
|
||||
V8::C
|
||||
end
|
||||
|
||||
end
|
|
@ -1,64 +0,0 @@
|
|||
require "#{File.dirname(__FILE__)}/../spec_helper.rb"
|
||||
|
||||
include V8
|
||||
|
||||
describe C::Function do
|
||||
it "is callable" do
|
||||
Context.new do |cxt|
|
||||
f = cxt.eval('(function() {return "Hello World"})', '<eval>');
|
||||
f.call().should == "Hello World"
|
||||
end
|
||||
end
|
||||
|
||||
it "receives proper argument length from ruby" do
|
||||
Context.new do |cxt|
|
||||
f = cxt.eval('(function() {return arguments.length})', 'eval')
|
||||
f.call(1, 2, 3).should == 3
|
||||
end
|
||||
end
|
||||
|
||||
it "maps all arguments from ruby" do
|
||||
Context.new do |cxt|
|
||||
f = cxt.eval('(function(one, two, three) {return one + two + three})', 'eval')
|
||||
f.call(1,2,3).should == 6
|
||||
end
|
||||
end
|
||||
|
||||
it "properly maps ruby objects back and forth from arguments to return value" do
|
||||
Context.new do |cxt|
|
||||
Object.new.tap do |this|
|
||||
f = cxt.eval('(function() {return this})', 'eval')
|
||||
f.methodcall(this).should be(this)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "can be called outside of a context" do
|
||||
Context.new do |cxt|
|
||||
@f = cxt.eval('(function() {return "Call Me"})', 'eval')
|
||||
end
|
||||
@f.call().should == "Call Me"
|
||||
end
|
||||
|
||||
it "is reflected properly" do
|
||||
Context.new do |cxt|
|
||||
cxt['say'] = lambda {|this, word, times| word * times}
|
||||
cxt.eval('say("Hello", 3)').should == "HelloHelloHello"
|
||||
end
|
||||
end
|
||||
|
||||
it "has a name" do
|
||||
Context.new do |cxt|
|
||||
f = cxt.eval('(function hi() {return "Hello World"})', '<eval>')
|
||||
f.name.should == "hi"
|
||||
end
|
||||
end
|
||||
|
||||
it "can have its name set" do
|
||||
Context.new do |cxt|
|
||||
f = cxt.eval('(function () {return "Goodbye World"})', '<eval>')
|
||||
f.name = 'bye'
|
||||
f.name.should == 'bye'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe V8::C::Object do
|
||||
include V8::ExtSpec
|
||||
|
||||
it "always returns a copy of the same object if it is the same object" do
|
||||
v8_eval('var o = new Object()')
|
||||
v8_eval('o').should be(v8_eval('o'))
|
||||
end
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe V8::C::String do
|
||||
include V8::ExtSpec
|
||||
|
||||
describe "a string with null bytes" do
|
||||
subject {c::String::New("foo\0bar")}
|
||||
its(:Utf8Value) {should eql "foo\0bar"}
|
||||
end
|
||||
end
|
|
@ -1,60 +0,0 @@
|
|||
require "#{File.dirname(__FILE__)}/../spec_helper.rb"
|
||||
|
||||
include V8
|
||||
|
||||
describe C::TryCatch do
|
||||
|
||||
before {@lock = C::Locker.new;@cxt = C::Context::New();}
|
||||
after {@cxt.Dispose(); @lock.delete}
|
||||
|
||||
it "does not allow instance creation by default" do
|
||||
lambda {
|
||||
C::TryCatch.new
|
||||
}.should raise_error
|
||||
end
|
||||
|
||||
it "will do nothing if not passed a block" do
|
||||
C::TryCatch.try.should == nil
|
||||
end
|
||||
|
||||
it "executes a block in the context of a C++ stack frame" do
|
||||
C::TryCatch.try do |catch|
|
||||
catch.HasCaught().should be(false)
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an erro if you try to access it outside of its scope" do
|
||||
tc = C::TryCatch.try do |catch|
|
||||
catch.tap {}
|
||||
end
|
||||
lambda {
|
||||
tc.HasCaught()
|
||||
}.should raise_error(ScriptError)
|
||||
end
|
||||
|
||||
it "doesn't segfault when an error is raised in a javascript function on a native prototype" do
|
||||
constructor = Class.new
|
||||
constructor.class_eval do
|
||||
def detonate(*a)
|
||||
raise "BOOM!"
|
||||
end
|
||||
end
|
||||
V8::Context.new do |cxt|
|
||||
cxt['Boom'] = constructor
|
||||
cxt['puts'] = method(:puts)
|
||||
danger = <<-JS
|
||||
Boom.prototype.boom = function() {
|
||||
this.detonate()
|
||||
}
|
||||
var go = new(Boom)()
|
||||
try {
|
||||
go.boom()
|
||||
} catch (e) {
|
||||
}
|
||||
go.boom()
|
||||
JS
|
||||
expect {cxt.eval(danger, 'danger.js')}.should raise_error(V8::JSError)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'redjs/load_specs'
|
||||
module RedJS
|
||||
Context = V8::Context
|
||||
Error = V8::JSError
|
||||
end
|
||||
describe V8::Context do
|
||||
it_behaves_like 'RedJS::Context'
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
require 'v8'
|
||||
require 'erb'
|
||||
require Pathname(__FILE__).dirname.join('ext/ext_spec_helper')
|
||||
require Pathname(__FILE__).dirname.join('../specmem/spec_helper')
|
||||
def rputs(msg)
|
||||
puts "<pre>#{ERB::Util.h(msg)}</pre>"
|
||||
$stdout.flush
|
||||
end
|
|
@ -1,131 +0,0 @@
|
|||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
|
||||
describe V8::JSError do
|
||||
|
||||
before(:each) do
|
||||
@cxt = V8::Context.new
|
||||
@cxt['one'] = lambda do
|
||||
@cxt.eval('two()', 'one.js')
|
||||
end
|
||||
@cxt['two'] = lambda do
|
||||
@cxt.eval('three()', 'two.js')
|
||||
end
|
||||
end
|
||||
|
||||
it "captures a message without over nesting when the error is an error" do
|
||||
throw! do |e|
|
||||
e.message.should == "BOOM!"
|
||||
end
|
||||
end
|
||||
|
||||
it "captures the js message without over nesting when the error is a normal object" do
|
||||
throw!('{foo: "bar"}') do |e|
|
||||
e.message.should == "[object Object]"
|
||||
end
|
||||
throw!('{message: "bar"}') do |e|
|
||||
e.message.should == "bar"
|
||||
end
|
||||
end
|
||||
|
||||
it "captures a thrown value as the message" do
|
||||
throw!('"BOOM!"') do |e|
|
||||
e.message.should == "BOOM!"
|
||||
end
|
||||
throw!('6') do |e|
|
||||
e.message.should == '6'
|
||||
end
|
||||
end
|
||||
|
||||
it "has a reference to the root javascript cause" do
|
||||
throw!('"I am a String"') do |e|
|
||||
e.should_not be_in_ruby
|
||||
e.should be_in_javascript
|
||||
e.value.should == "I am a String"
|
||||
end
|
||||
end
|
||||
|
||||
it "has a reference to the root ruby cause if one exists" do
|
||||
StandardError.new("BOOM!").tap do |bomb|
|
||||
@cxt['boom'] = lambda do
|
||||
raise bomb
|
||||
end
|
||||
lambda {
|
||||
@cxt.eval('boom()', 'boom.js')
|
||||
}.should(raise_error do |raised|
|
||||
raised.should be_in_ruby
|
||||
raised.should_not be_in_javascript
|
||||
raised.value.should be(bomb)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "backtrace" do
|
||||
|
||||
it "is mixed with ruby and javascript" do
|
||||
throw! do |e|
|
||||
e.backtrace.first.should == "at three.js:1:7"
|
||||
e.backtrace[1].should =~ /error_spec.rb/
|
||||
e.backtrace[2].should == "at two.js:1:1"
|
||||
e.backtrace[3].should =~ /error_spec.rb/
|
||||
e.backtrace[4].should == "at one.js:1:1"
|
||||
end
|
||||
end
|
||||
|
||||
it "can be set to show only ruby frames" do
|
||||
throw! do |e|
|
||||
e.backtrace(:ruby).each do |frame|
|
||||
frame.should =~ /(\.rb|):\d+/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "can be set to show only javascript frames" do
|
||||
throw! do |e|
|
||||
e.backtrace(:javascript).each do |frame|
|
||||
frame.should =~ /\.js:\d:\d/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "includes a mystery marker when the original frame is unavailable because what got thrown wasn't an error" do
|
||||
throw!("6") do |e|
|
||||
e.backtrace.first.should == 'at three.js:1:1'
|
||||
end
|
||||
end
|
||||
|
||||
it "has a source name and line number when there is a javascript SyntaxError" do
|
||||
lambda do
|
||||
@cxt.eval(<<-INVALID, 'source.js')
|
||||
"this line is okay";
|
||||
"this line has a syntax error because it ends with a colon":
|
||||
"this line is also okay";
|
||||
"how do I find out that line 2 has the syntax error?";
|
||||
INVALID
|
||||
end.should raise_error(V8::JSError) {|error|
|
||||
error.backtrace.first.should == 'at source.js:2:60'
|
||||
}
|
||||
end
|
||||
|
||||
it "can start with ruby at the bottom" do
|
||||
@cxt['boom'] = lambda do
|
||||
raise StandardError, "Bif!"
|
||||
end
|
||||
lambda {
|
||||
@cxt.eval('boom()', "boom.js")
|
||||
}.should(raise_error {|e|
|
||||
e.backtrace.first.should =~ /error_spec\.rb/
|
||||
e.backtrace[1].should =~ /boom.js/
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def throw!(js = "new Error('BOOM!')", &block)
|
||||
@cxt['three'] = lambda do
|
||||
@cxt.eval("throw #{js}", 'three.js')
|
||||
end
|
||||
lambda do
|
||||
@cxt['one'].call()
|
||||
end.should(raise_error(V8::JSError, &block))
|
||||
end
|
||||
end
|
|
@ -1,106 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe V8::Portal::Proxies do
|
||||
include V8::ExtSpec
|
||||
|
||||
|
||||
context "for Ruby objects which are embedded into javascript" do
|
||||
|
||||
it "allows you to resolve the Ruby object's JavaScript proxy" do
|
||||
js_proxy = c::Object::New()
|
||||
rb_object = Object.new
|
||||
subject.register_javascript_proxy js_proxy, :for => rb_object
|
||||
subject.rb_object_2_js_proxy(rb_object).should be(js_proxy)
|
||||
subject.js_proxy_2_rb_object(js_proxy).should be(rb_object)
|
||||
end
|
||||
|
||||
it "requires a Ruby object which is the actual object that is proxied" do
|
||||
expect {subject.register_javascript_proxy c::Object::New()}.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "can only register proxies which are low-level JavaScript objects" do
|
||||
expect {subject.register_javascript_proxy Object.new, :for => Object.new}.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "is only allowed to have a single JavaScript proxy" do
|
||||
rb_object = Object.new
|
||||
subject.register_javascript_proxy c::Object::New(), :for => rb_object
|
||||
expect {subject.register_javascript_proxy c::Object::New(), :for => rb_object}.should raise_error(V8::Portal::Proxies::DoubleProxyError)
|
||||
end
|
||||
end
|
||||
|
||||
context "for a JavaScript objects which are embedded into Ruby" do
|
||||
|
||||
it "allows you to resolve the JavaScript object's Ruby proxy" do
|
||||
rb_proxy = Object.new
|
||||
js_object = c::Object::New()
|
||||
subject.register_ruby_proxy rb_proxy, :for => js_object
|
||||
subject.js_object_2_rb_proxy(js_object).should be(rb_proxy)
|
||||
subject.rb_proxy_2_js_object(rb_proxy).should be(js_object)
|
||||
end
|
||||
|
||||
it "requires a JavaScript low level javascript object as the actual object that is proxied" do
|
||||
expect {subject.register_javascript_proxy Object.new, :for => c::Object::New()}.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "will not a proxy twice if the proxy creator block actually registers the proxy inside it" do
|
||||
target = Object.new
|
||||
proxy = c::Object::New()
|
||||
expect {subject.rb2js(target) do |object|
|
||||
subject.register_javascript_proxy(proxy, :for => object)
|
||||
c::Object::New()
|
||||
end}.should_not raise_error
|
||||
subject.rb2js(target).should be(proxy)
|
||||
end
|
||||
|
||||
context "looking up a Ruby object from a random JavaScript object" do
|
||||
it "checks first if it's a native Ruby object with a javascript proxy" do
|
||||
target = Object.new
|
||||
proxy = c::Object::New()
|
||||
subject.register_javascript_proxy proxy, :for => target
|
||||
subject.js2rb(proxy).should be(target)
|
||||
end
|
||||
it "then sees if maybe it's a native JavaScript that has a Ruby proxy" do
|
||||
target = c::Object::New()
|
||||
proxy = Object.new
|
||||
subject.register_ruby_proxy proxy, :for => target
|
||||
subject.js2rb(target).should be(proxy)
|
||||
end
|
||||
it "will assume that it is a native JavaScript object that needs a Ruby proxy if no corresponding Ruby object can be found" do
|
||||
js = c::Object::New()
|
||||
proxy = subject.js2rb(js) do |target|
|
||||
{:target => target}
|
||||
end
|
||||
subject.js2rb(js).should be(proxy)
|
||||
end
|
||||
end
|
||||
|
||||
context "looking up a JavaScript object from a random Ruby object" do
|
||||
it "checks first if it's a native JavaScript object with a Ruby proxy" do
|
||||
target = c::Object::New()
|
||||
proxy = Object.new
|
||||
subject.register_ruby_proxy proxy, :for => target
|
||||
subject.rb2js(proxy).should be(target)
|
||||
end
|
||||
|
||||
it "then sees if maybe it's a native Ruby object that has a JavaScript proxy" do
|
||||
target = Object.new
|
||||
proxy = c::Object::New()
|
||||
subject.register_javascript_proxy proxy, :for => target
|
||||
subject.rb2js(target).should be(proxy)
|
||||
end
|
||||
|
||||
it "assumes that it is a native Ruby object that needs a JavaScript proxy if no corresponding JavaScript object can be found" do
|
||||
rb = Object.new
|
||||
proxy = nil
|
||||
js = subject.rb2js(rb) do |target|
|
||||
target.should be(rb)
|
||||
proxy = c::Object::New()
|
||||
end
|
||||
proxy.should_not be_nil
|
||||
js.should be(proxy)
|
||||
subject.rb2js(rb).should be(proxy)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,41 +0,0 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe V8::C::Handle do
|
||||
include V8::MemSpec
|
||||
context "A JavaScript Object reflected into Ruby" do
|
||||
|
||||
before do
|
||||
@weakref_callback = WeakrefCallback.new
|
||||
end
|
||||
|
||||
it "has a strong reference from the ruby side, which is not released until the Ruby reference goes away" do
|
||||
handle = c::Handle::New(object = c::Object::New())
|
||||
handle.MakeWeak(nil, @weakref_callback)
|
||||
ruby_gc do
|
||||
v8_gc
|
||||
@weakref_callback.should_not have_been_invoked
|
||||
handle.should_not be_dead
|
||||
end
|
||||
ruby_gc do
|
||||
object = nil
|
||||
v8_gc
|
||||
@weakref_callback.should have_been_invoked
|
||||
handle.should be_dead
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
class WeakrefCallback
|
||||
|
||||
def call(value, parameters)
|
||||
@invoked = true
|
||||
end
|
||||
|
||||
def has_been_invoked?
|
||||
@invoked
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe V8::C::Object do
|
||||
include V8::MemSpec
|
||||
it "will return a new peer and not barf if the old peer has been garbage collected" do
|
||||
v8_eval('var o = {foo: "bar"}')
|
||||
old_id = v8_eval('o').object_id
|
||||
ruby_gc do
|
||||
v8_eval('o').Get(c::String::New("foo")).Utf8Value().should == "bar"
|
||||
v8_eval('o').object_id.should_not be(old_id)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,49 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe V8::Portal::Proxies do
|
||||
include V8::MemSpec
|
||||
|
||||
context "A Ruby object embedded into JavaScript" do
|
||||
it "holds a hard reference to any ruby object which is linked to a javascript proxy" do
|
||||
subject.register_javascript_proxy c::Object::New(), :for => Object.new
|
||||
ruby_gc do
|
||||
subject.should_not be_empty
|
||||
end
|
||||
end
|
||||
|
||||
it "releases the hard reference if its corresponding javascript object has been garbage collected" do
|
||||
ruby_gc do
|
||||
rb_object = Object.new
|
||||
js_proxy = c::Object::New()
|
||||
subject.should be_empty
|
||||
subject.register_javascript_proxy js_proxy, :for => rb_object
|
||||
rb_object = nil
|
||||
subject.should_not be_empty
|
||||
v8_gc()
|
||||
end
|
||||
subject.should be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "A JavaScript object embedded into Ruby" do
|
||||
it "holds a hard reference to any JavaScript object which is linked to a Ruby proxy" do
|
||||
proxy = Object.new
|
||||
subject.register_ruby_proxy proxy, :for => c::Object::New()
|
||||
ruby_gc do
|
||||
subject.should_not be_empty
|
||||
end
|
||||
end
|
||||
|
||||
it "clears any strong references to the JavaScript object when it's Ruby proxy is garbage collected" do
|
||||
subject.register_ruby_proxy Object.new, :for => c::Object::New()
|
||||
subject.should_not be_empty
|
||||
ruby_gc do
|
||||
v8_gc
|
||||
GC.start
|
||||
v8_gc
|
||||
end
|
||||
subject.should be_empty
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,24 +0,0 @@
|
|||
require Pathname(__FILE__).dirname.join('../spec/spec_helper')
|
||||
|
||||
module V8::MemSpec
|
||||
|
||||
def self.included(cls)
|
||||
cls.class_eval do
|
||||
include V8::ExtSpec
|
||||
before(:all) {V8::C::V8::SetFlagsFromString("--expose-gc")}
|
||||
end
|
||||
end
|
||||
|
||||
def ruby_gc
|
||||
current = GC.stress
|
||||
GC.stress = true
|
||||
yield
|
||||
ensure
|
||||
GC.stress = current
|
||||
end
|
||||
|
||||
def v8_gc
|
||||
while !c::V8::IdleNotification();end
|
||||
v8_eval('gc()', 'gc.js')
|
||||
end
|
||||
end
|
|
@ -1,2 +0,0 @@
|
|||
require Pathname(__FILE__).dirname.join('../spec/spec_helper')
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe "using v8 from multiple threads" do
|
||||
|
||||
it "is possible" do
|
||||
Thread.new do
|
||||
require 'v8'
|
||||
V8::Context.new
|
||||
end.join
|
||||
V8::Context.new
|
||||
end
|
||||
end
|
BIN
thefrontside.png
BIN
thefrontside.png
Binary file not shown.
Before Width: | Height: | Size: 7.5 KiB |
|
@ -1,26 +0,0 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
$:.push File.expand_path("../lib", __FILE__)
|
||||
require "v8/version"
|
||||
require 'pathname'
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = s.rubyforge_project = "therubyracer"
|
||||
s.summary = "Embed the V8 Javascript interpreter into Ruby"
|
||||
s.version = V8::VERSION
|
||||
s.authors = ["Charles Lowell", "Bill Robertson"]
|
||||
s.description = "Call javascript code and manipulate javascript objects from ruby. Call ruby code and manipulate ruby objects from javascript."
|
||||
s.homepage = "http://github.com/cowboyd/therubyracer"
|
||||
s.email = "cowboyd@thefrontside.net"
|
||||
|
||||
root = Pathname(__FILE__).dirname
|
||||
s.files = `git ls-files`.split("\n")
|
||||
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
||||
s.extensions = ["ext/v8/extconf.rb"]
|
||||
s.require_paths = ["lib", "ext"]
|
||||
|
||||
s.add_dependency "libv8", "~> 3.3.10"
|
||||
|
||||
s.add_development_dependency "rake"
|
||||
s.add_development_dependency "rspec", "~> 2.0"
|
||||
s.add_development_dependency "rake-compiler"
|
||||
end
|
Loading…
Reference in New Issue