begin internal rewrite.

This commit is contained in:
Charles Lowell 2012-05-09 14:44:55 -05:00
parent 490002aad7
commit 9cb4f1c7d2
87 changed files with 0 additions and 4613 deletions

13
.gitignore vendored
View File

@ -1,13 +0,0 @@
.bundle
.rvmrc
Gemfile.lock
v8.bundle
v8.so
*.o
*.gem
*.rbc
*.log
*~
pkg/
tmp/
.yardoc/

1
.rspec
View File

@ -1 +0,0 @@
--colour

View File

@ -1 +0,0 @@
lib/**/*.rb ext/**/*.cpp

View File

@ -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
View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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')

View File

@ -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));
// }

View File

@ -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

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -1,6 +0,0 @@
#ifndef _RUBY_V8_CXT_
#define _RUBY_V8_CXT_
void rr_init_context();
#endif

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -1,6 +0,0 @@
#ifndef _RR_V8_DEBUG_
#define _RR_V8_DEBUG_
void rr_init_v8_debug();
#endif

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -1,6 +0,0 @@
#ifndef _RUBY_V8_LOCKER
#define _RUBY_V8_LOCKER
void rr_init_v8_locker();
#endif

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -1,8 +0,0 @@
#ifndef _RUBY_V8_SCRIPT_
#define _RUBY_V8_SCRIPT_
#include "rr.h"
void rr_init_script();
#endif

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -1,8 +0,0 @@
#ifndef _RUBY_V8_TEMPLATE_
#define _RUBY_V8_TEMPLATE_
#include "rr.h"
void rr_init_template();
#endif

View File

@ -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);
}

View File

@ -1,5 +0,0 @@
#ifndef _RR_V8_TRY_CATCH_
#define _RR_V8_TRY_CATCH_
void rr_init_v8_try_catch();
#endif

View File

@ -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);
}

View File

@ -1,6 +0,0 @@
#ifndef _RR_V8_V8_
#define _RR_V8_V8_
void rr_init_v8_v8();
#endif

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,9 +0,0 @@
unless Object.method_defined?(:tap)
class Object
def tap
yield self
return self
end
end
end

View File

@ -1,3 +0,0 @@
module V8
VERSION = "0.10.1"
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,2 +0,0 @@
require Pathname(__FILE__).dirname.join('../spec/spec_helper')

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -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