therubyracer-xcode 0.12.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.travis.yml +14 -0
  4. data/Changelog.md +263 -0
  5. data/Gemfile +12 -0
  6. data/README.md +227 -0
  7. data/Rakefile +42 -0
  8. data/benchmarks.rb +218 -0
  9. data/ext/v8/accessor.cc +181 -0
  10. data/ext/v8/array.cc +26 -0
  11. data/ext/v8/backref.cc +45 -0
  12. data/ext/v8/constants.cc +34 -0
  13. data/ext/v8/constraints.cc +52 -0
  14. data/ext/v8/context.cc +130 -0
  15. data/ext/v8/date.cc +18 -0
  16. data/ext/v8/exception.cc +38 -0
  17. data/ext/v8/extconf.rb +34 -0
  18. data/ext/v8/external.cc +43 -0
  19. data/ext/v8/function.cc +58 -0
  20. data/ext/v8/gc.cc +43 -0
  21. data/ext/v8/handles.cc +34 -0
  22. data/ext/v8/heap.cc +35 -0
  23. data/ext/v8/init.cc +39 -0
  24. data/ext/v8/invocation.cc +86 -0
  25. data/ext/v8/locker.cc +77 -0
  26. data/ext/v8/message.cc +51 -0
  27. data/ext/v8/object.cc +335 -0
  28. data/ext/v8/primitive.cc +8 -0
  29. data/ext/v8/rr.cc +83 -0
  30. data/ext/v8/rr.h +934 -0
  31. data/ext/v8/script.cc +115 -0
  32. data/ext/v8/signature.cc +18 -0
  33. data/ext/v8/stack.cc +76 -0
  34. data/ext/v8/string.cc +47 -0
  35. data/ext/v8/template.cc +175 -0
  36. data/ext/v8/trycatch.cc +87 -0
  37. data/ext/v8/v8.cc +87 -0
  38. data/ext/v8/value.cc +239 -0
  39. data/lib/therubyracer.rb +1 -0
  40. data/lib/v8/access/indices.rb +40 -0
  41. data/lib/v8/access/invocation.rb +47 -0
  42. data/lib/v8/access/names.rb +65 -0
  43. data/lib/v8/access.rb +5 -0
  44. data/lib/v8/array.rb +26 -0
  45. data/lib/v8/context.rb +258 -0
  46. data/lib/v8/conversion/array.rb +11 -0
  47. data/lib/v8/conversion/class.rb +119 -0
  48. data/lib/v8/conversion/code.rb +38 -0
  49. data/lib/v8/conversion/fixnum.rb +11 -0
  50. data/lib/v8/conversion/fundamental.rb +11 -0
  51. data/lib/v8/conversion/hash.rb +11 -0
  52. data/lib/v8/conversion/indentity.rb +31 -0
  53. data/lib/v8/conversion/method.rb +26 -0
  54. data/lib/v8/conversion/object.rb +28 -0
  55. data/lib/v8/conversion/primitive.rb +7 -0
  56. data/lib/v8/conversion/proc.rb +5 -0
  57. data/lib/v8/conversion/reference.rb +16 -0
  58. data/lib/v8/conversion/string.rb +12 -0
  59. data/lib/v8/conversion/symbol.rb +7 -0
  60. data/lib/v8/conversion/time.rb +13 -0
  61. data/lib/v8/conversion.rb +36 -0
  62. data/lib/v8/error.rb +169 -0
  63. data/lib/v8/function.rb +28 -0
  64. data/lib/v8/object.rb +79 -0
  65. data/lib/v8/stack.rb +85 -0
  66. data/lib/v8/version.rb +3 -0
  67. data/lib/v8/weak.rb +82 -0
  68. data/lib/v8.rb +30 -0
  69. data/spec/c/array_spec.rb +19 -0
  70. data/spec/c/constants_spec.rb +22 -0
  71. data/spec/c/exception_spec.rb +28 -0
  72. data/spec/c/external_spec.rb +11 -0
  73. data/spec/c/function_spec.rb +48 -0
  74. data/spec/c/handles_spec.rb +31 -0
  75. data/spec/c/locker_spec.rb +36 -0
  76. data/spec/c/object_spec.rb +47 -0
  77. data/spec/c/script_spec.rb +30 -0
  78. data/spec/c/string_spec.rb +18 -0
  79. data/spec/c/template_spec.rb +31 -0
  80. data/spec/c/trycatch_spec.rb +52 -0
  81. data/spec/mem/blunt_spec.rb +42 -0
  82. data/spec/redjs_spec.rb +10 -0
  83. data/spec/spec_helper.rb +41 -0
  84. data/spec/threading_spec.rb +64 -0
  85. data/spec/v8/context_spec.rb +19 -0
  86. data/spec/v8/conversion_spec.rb +52 -0
  87. data/spec/v8/error_spec.rb +167 -0
  88. data/spec/v8/function_spec.rb +9 -0
  89. data/spec/v8/object_spec.rb +15 -0
  90. data/thefrontside.png +0 -0
  91. data/therubyracer.gemspec +22 -0
  92. metadata +186 -0
data/ext/v8/value.cc ADDED
@@ -0,0 +1,239 @@
1
+ #include "rr.h"
2
+
3
+ namespace rr {
4
+
5
+ VALUE Value::Empty;
6
+
7
+ void Value::Init() {
8
+ Empty = rb_eval_string("Object.new");
9
+ ClassBuilder("Value").
10
+ defineConst("Empty", Empty).
11
+ defineMethod("IsUndefined", &IsUndefined).
12
+ defineMethod("IsNull", &IsNull).
13
+ defineMethod("IsTrue", &IsTrue).
14
+ defineMethod("IsFalse", &IsFalse).
15
+ defineMethod("IsString", &IsString).
16
+ defineMethod("IsFunction", &IsFunction).
17
+ defineMethod("IsArray", &IsArray).
18
+ defineMethod("IsObject", &IsObject).
19
+ defineMethod("IsBoolean", &IsBoolean).
20
+ defineMethod("IsNumber", &IsNumber).
21
+ defineMethod("IsExternal", &IsExternal).
22
+ defineMethod("IsInt32", &IsInt32).
23
+ defineMethod("IsUint32", &IsUint32).
24
+ defineMethod("IsDate", &IsDate).
25
+ defineMethod("IsBooleanObject", &IsBooleanObject).
26
+ defineMethod("IsNumberObject", &IsNumberObject).
27
+ defineMethod("IsStringObject", &IsStringObject).
28
+ defineMethod("IsNativeError", &IsNativeError).
29
+ defineMethod("IsRegExp", &IsRegExp).
30
+ defineMethod("ToString", &ToString).
31
+ defineMethod("ToDetailString", &ToDetailString).
32
+ defineMethod("ToObject", &ToObject).
33
+ defineMethod("BooleanValue", &BooleanValue).
34
+ defineMethod("NumberValue", &NumberValue).
35
+ defineMethod("IntegerValue", &IntegerValue).
36
+ defineMethod("Uint32Value", &Uint32Value).
37
+ defineMethod("IntegerValue", &IntegerValue).
38
+ defineMethod("Equals", &Equals).
39
+ defineMethod("StrictEquals", &StrictEquals)
40
+ .store(&Class);
41
+ rb_gc_register_address(&Empty);
42
+ }
43
+
44
+ VALUE Value::IsUndefined(VALUE self) {
45
+ return Bool(Value(self)->IsUndefined());
46
+ }
47
+ VALUE Value::IsNull(VALUE self) {
48
+ return Bool(Value(self)->IsNull());
49
+ }
50
+ VALUE Value::IsTrue(VALUE self) {
51
+ return Bool(Value(self)->IsTrue());
52
+ }
53
+ VALUE Value::IsFalse(VALUE self) {
54
+ return Bool(Value(self)->IsFalse());
55
+ }
56
+ VALUE Value::IsString(VALUE self) {
57
+ return Bool(Value(self)->IsString());
58
+ }
59
+ VALUE Value::IsFunction(VALUE self) {
60
+ return Bool(Value(self)->IsFunction());
61
+ }
62
+ VALUE Value::IsArray(VALUE self) {
63
+ return Bool(Value(self)->IsArray());
64
+ }
65
+ VALUE Value::IsObject(VALUE self) {
66
+ return Bool(Value(self)->IsObject());
67
+ }
68
+ VALUE Value::IsBoolean(VALUE self) {
69
+ return Bool(Value(self)->IsBoolean());
70
+ }
71
+ VALUE Value::IsNumber(VALUE self) {
72
+ return Bool(Value(self)->IsNumber());
73
+ }
74
+ VALUE Value::IsExternal(VALUE self) {
75
+ return Bool(Value(self)->IsExternal());
76
+ }
77
+ VALUE Value::IsInt32(VALUE self) {
78
+ return Bool(Value(self)->IsInt32());
79
+ }
80
+ VALUE Value::IsUint32(VALUE self) {
81
+ return Bool(Value(self)->IsUint32());
82
+ }
83
+ VALUE Value::IsDate(VALUE self) {
84
+ return Bool(Value(self)->IsDate());
85
+ }
86
+ VALUE Value::IsBooleanObject(VALUE self) {
87
+ return Bool(Value(self)->IsBooleanObject());
88
+ }
89
+ VALUE Value::IsNumberObject(VALUE self) {
90
+ return Bool(Value(self)->IsNumberObject());
91
+ }
92
+ VALUE Value::IsStringObject(VALUE self) {
93
+ return Bool(Value(self)->IsStringObject());
94
+ }
95
+ VALUE Value::IsNativeError(VALUE self) {
96
+ return Bool(Value(self)->IsNativeError());
97
+ }
98
+ VALUE Value::IsRegExp(VALUE self) {
99
+ return Bool(Value(self)->IsRegExp());
100
+ }
101
+
102
+ // VALUE Value::ToBoolean(VALUE self) {
103
+ // return Boolean(Value(self)->ToBoolean());
104
+ // }
105
+
106
+ // VALUE Value::ToNumber(VALUE self) {
107
+ // return Number(Value(self)->ToNumber());
108
+ // }
109
+ VALUE Value::ToString(VALUE self) {
110
+ return String(Value(self)->ToString());
111
+ }
112
+
113
+ VALUE Value::ToDetailString(VALUE self) {
114
+ return String(Value(self)->ToDetailString());
115
+ }
116
+
117
+ VALUE Value::ToObject(VALUE self) {
118
+ return Object(Value(self)->ToObject());
119
+ }
120
+
121
+ // VALUE Value::ToInteger(VALUE self) {
122
+ // return Integer(Value(self)->ToInteger());
123
+ // }
124
+
125
+ // VALUE Value::ToUint32(VALUE self) {
126
+ // return Uint32(Value(self)->ToUint32());
127
+ // }
128
+
129
+ // VALUE Value::ToInt32(VALUE self) {
130
+ // return Int32(Value(self)->ToInt32());
131
+ // }
132
+
133
+
134
+ // VALUE Value::ToArrayIndex(VALUE self) {
135
+ // return Uint32(Value(self)->ToArrayIndex());
136
+ // }
137
+
138
+ VALUE Value::BooleanValue(VALUE self) {
139
+ return Bool(Value(self)->BooleanValue());
140
+ }
141
+ VALUE Value::NumberValue(VALUE self) {
142
+ return rb_float_new(Value(self)->NumberValue());
143
+ }
144
+ VALUE Value::IntegerValue(VALUE self) {
145
+ return INT2NUM(Value(self)->IntegerValue());
146
+ }
147
+ VALUE Value::Uint32Value(VALUE self) {
148
+ return UINT2NUM(Value(self)->Uint32Value());
149
+ }
150
+ VALUE Value::Int32Value(VALUE self) {
151
+ return INT2FIX(Value(self)->Int32Value());
152
+ }
153
+
154
+ VALUE Value::Equals(VALUE self, VALUE other) {
155
+ return Bool(Value(self)->Equals(Value(other)));
156
+ }
157
+
158
+ VALUE Value::StrictEquals(VALUE self, VALUE other) {
159
+ return Bool(Value(self)->StrictEquals(Value(other)));
160
+ }
161
+
162
+ Value::operator VALUE() {
163
+ if (handle.IsEmpty() || handle->IsUndefined() || handle->IsNull()) {
164
+ return Qnil;
165
+ }
166
+ if (handle->IsTrue()) {
167
+ return Qtrue;
168
+ }
169
+ if (handle->IsFalse()) {
170
+ return Qfalse;
171
+ }
172
+ if (handle->IsExternal()) {
173
+ return External((v8::Handle<v8::External>)v8::External::Cast(*handle));
174
+ }
175
+ if (handle->IsUint32()) {
176
+ return UInt32(handle->Uint32Value());
177
+ }
178
+ if (handle->IsInt32()) {
179
+ return INT2FIX(handle->Int32Value());
180
+ }
181
+ if (handle->IsBoolean()) {
182
+ return handle->BooleanValue() ? Qtrue : Qfalse;
183
+ }
184
+ if (handle->IsNumber()) {
185
+ return rb_float_new(handle->NumberValue());
186
+ }
187
+ if (handle->IsString()) {
188
+ return String(handle->ToString());
189
+ }
190
+ if (handle->IsDate()) {
191
+ return Date((v8::Handle<v8::Date>)v8::Date::Cast(*handle));
192
+ }
193
+ if (handle->IsObject()) {
194
+ return Object(handle->ToObject());
195
+ }
196
+ return Ref<v8::Value>::operator VALUE();
197
+ }
198
+
199
+ Value::operator v8::Handle<v8::Value>() const {
200
+ if (rb_equal(value,Empty)) {
201
+ return v8::Handle<v8::Value>();
202
+ }
203
+ switch (TYPE(value)) {
204
+ case T_FIXNUM:
205
+ return v8::Integer::New(NUM2INT(value));
206
+ case T_FLOAT:
207
+ return v8::Number::New(NUM2DBL(value));
208
+ case T_STRING:
209
+ return v8::String::New(RSTRING_PTR(value), (int)RSTRING_LEN(value));
210
+ case T_NIL:
211
+ return v8::Null();
212
+ case T_TRUE:
213
+ return v8::True();
214
+ case T_FALSE:
215
+ return v8::False();
216
+ case T_DATA:
217
+ return Ref<v8::Value>::operator v8::Handle<v8::Value>();
218
+ case T_OBJECT:
219
+ case T_CLASS:
220
+ case T_ICLASS:
221
+ case T_MODULE:
222
+ case T_REGEXP:
223
+ case T_MATCH:
224
+ case T_ARRAY:
225
+ case T_HASH:
226
+ case T_STRUCT:
227
+ case T_BIGNUM:
228
+ case T_FILE:
229
+ case T_SYMBOL:
230
+ case T_UNDEF:
231
+ case T_NODE:
232
+ default:
233
+ rb_warn("unknown conversion to V8 for: %s", RSTRING_PTR(rb_inspect(value)));
234
+ return v8::String::New("Undefined Conversion");
235
+ }
236
+
237
+ return v8::Undefined();
238
+ }
239
+ }
@@ -0,0 +1 @@
1
+ require "v8"
@@ -0,0 +1,40 @@
1
+ class V8::Access
2
+ module Indices
3
+
4
+ def indices(obj)
5
+ obj.respond_to?(:length) ? (0..obj.length).to_a : []
6
+ end
7
+
8
+ def iget(obj, index, &dontintercept)
9
+ if obj.respond_to?(:[])
10
+ obj.send(:[], index, &dontintercept)
11
+ else
12
+ yield
13
+ end
14
+ end
15
+
16
+ def iset(obj, index, value, &dontintercept)
17
+ if obj.respond_to?(:[]=)
18
+ obj.send(:[]=, index, value, &dontintercept)
19
+ else
20
+ yield
21
+ end
22
+ end
23
+
24
+ def iquery(obj, index, attributes, &dontintercept)
25
+ if obj.respond_to?(:[])
26
+ attributes.dont_delete
27
+ unless obj.respond_to?(:[]=)
28
+ attributes.read_only
29
+ end
30
+ else
31
+ yield
32
+ end
33
+ end
34
+
35
+ def idelete(obj, index, &dontintercept)
36
+ yield
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,47 @@
1
+ class V8::Access
2
+ module Invocation
3
+ def methodcall(code, this, args)
4
+ code.methodcall this, args
5
+ end
6
+
7
+ module Aritize
8
+ def aritize(args)
9
+ arity < 0 ? args : Array.new(arity).to_enum(:each_with_index).map {|item, i| args[i]}
10
+ end
11
+ end
12
+
13
+ module Proc
14
+ include Aritize
15
+ def methodcall(this, args)
16
+ call *aritize([this].concat(args))
17
+ end
18
+ ::Proc.send :include, self
19
+ end
20
+
21
+ module Method
22
+ include Aritize
23
+ def methodcall(this, args)
24
+ context = V8::Context.current
25
+ access = context.access
26
+ if this.equal? self.receiver
27
+ call *aritize(args)
28
+ elsif this.class <= self.receiver.class
29
+ access.methodcall(unbind, this, args)
30
+ elsif this.equal? context.scope
31
+ call *aritize(args)
32
+ else
33
+ fail TypeError, "cannot invoke #{self} on #{this}"
34
+ end
35
+ end
36
+ ::Method.send :include, self
37
+ end
38
+
39
+ module UnboundMethod
40
+ def methodcall(this, args)
41
+ access = V8::Context.current.access
42
+ access.methodcall bind(this), this, args
43
+ end
44
+ ::UnboundMethod.send :include, self
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,65 @@
1
+ require 'set'
2
+ class V8::Access
3
+ module Names
4
+ def names(obj)
5
+ accessible_names(obj)
6
+ end
7
+
8
+ def get(obj, name, &dontintercept)
9
+ methods = accessible_names(obj)
10
+ if methods.include?(name)
11
+ method = obj.method(name)
12
+ method.arity == 0 ? method.call : method.unbind
13
+ elsif obj.respond_to?(:[]) && !special?(name)
14
+ obj.send(:[], name, &dontintercept)
15
+ else
16
+ yield
17
+ end
18
+ end
19
+
20
+ def set(obj, name, value, &dontintercept)
21
+ setter = name + "="
22
+ methods = accessible_names(obj, true)
23
+ if methods.include?(setter)
24
+ obj.send(setter, value)
25
+ elsif obj.respond_to?(:[]=) && !special?(name)
26
+ obj.send(:[]=, name, value, &dontintercept)
27
+ else
28
+ yield
29
+ end
30
+ end
31
+
32
+ def query(obj, name, attributes, &dontintercept)
33
+ if obj.respond_to?(name)
34
+ attributes.dont_delete
35
+ unless obj.respond_to?(name + "=")
36
+ attributes.read_only
37
+ end
38
+ else
39
+ yield
40
+ end
41
+ end
42
+
43
+ def delete(obj, name, &dontintercept)
44
+ yield
45
+ end
46
+
47
+ def accessible_names(obj, special_methods = false)
48
+ obj.public_methods(false).map {|m| m.to_s}.to_set.tap do |methods|
49
+ ancestors = obj.class.ancestors.dup
50
+ while ancestor = ancestors.shift
51
+ break if ancestor == ::Object
52
+ methods.merge(ancestor.public_instance_methods(false).map {|m| m.to_s})
53
+ end
54
+ methods.reject!(&special?) unless special_methods
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def special?(name = nil)
61
+ @special ||= lambda {|m| m == "[]" || m == "[]=" || m =~ /=$/}
62
+ name.nil? ? @special : @special[name]
63
+ end
64
+ end
65
+ end
data/lib/v8/access.rb ADDED
@@ -0,0 +1,5 @@
1
+ class V8::Access
2
+ include Names
3
+ include Indices
4
+ include Invocation
5
+ end
data/lib/v8/array.rb ADDED
@@ -0,0 +1,26 @@
1
+ class V8::Array < V8::Object
2
+
3
+ def initialize(native_or_length = nil)
4
+ super do
5
+ if native_or_length.is_a?(Numeric)
6
+ V8::C::Array::New(native_or_length)
7
+ elsif native_or_length.is_a?(V8::C::Array)
8
+ native_or_length
9
+ else
10
+ V8::C::Array::New()
11
+ end
12
+ end
13
+ end
14
+
15
+ def each
16
+ @context.enter do
17
+ 0.upto(@native.Length() - 1) do |i|
18
+ yield @context.to_ruby(@native.Get(i))
19
+ end
20
+ end
21
+ end
22
+
23
+ def length
24
+ @native.Length()
25
+ end
26
+ end
data/lib/v8/context.rb ADDED
@@ -0,0 +1,258 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'stringio'
3
+ module V8
4
+ # All JavaScript must be executed in a context. This context consists of a global scope containing the
5
+ # standard JavaScript objects¨and functions like Object, String, Array, as well as any objects or
6
+ # functions from Ruby which have been embedded into it from the containing enviroment. E.g.
7
+ #
8
+ # V8::Context.new do |cxt|
9
+ # cxt['num'] = 5
10
+ # cxt.eval('num + 5') #=> 10
11
+ # end
12
+ #
13
+ # The same object may appear in any number of contexts, but only one context may be executing JavaScript code
14
+ # in any given thread. If a new context is opened in a thread in which a context is already opened, the second
15
+ # context will "mask" the old context e.g.
16
+ #
17
+ # six = 6
18
+ # Context.new do |cxt|
19
+ # cxt['num'] = 5
20
+ # cxt.eval('num') # => 5
21
+ # Context.new do |cxt|
22
+ # cxt['num'] = 10
23
+ # cxt.eval('num') # => 10
24
+ # cxt.eval('++num') # => 11
25
+ # end
26
+ # cxt.eval('num') # => 5
27
+ # end
28
+ class Context
29
+ include V8::Error::Try
30
+
31
+ # @!attribute [r] conversion
32
+ # @return [V8::Conversion] conversion behavior for this context
33
+ attr_reader :conversion
34
+
35
+ # @!attrribute [r] access
36
+ # @return [V8::Access] Ruby access behavior for this context
37
+ attr_reader :access
38
+
39
+ # @!attribute [r] native
40
+ # @return [V8::C::Context] the underlying C++ object
41
+ attr_reader :native
42
+
43
+ # @!attribute [r] timeout
44
+ # @return [Number] maximum execution time in milliseconds for scripts executed in this context
45
+ attr_reader :timeout
46
+
47
+ # Creates a new context.
48
+ #
49
+ # If passed the `:with` option, that object will be used as
50
+ # the global scope of the newly creating context. e.g.
51
+ #
52
+ # scope = Object.new
53
+ # def scope.hello; "Hi"; end
54
+ # V8::Context.new(:with => scope) do |cxt|
55
+ # cxt['hello'] #=> 'Hi'
56
+ # end
57
+ #
58
+ # If passed the `:timeout` option, every eval will timeout once
59
+ # N milliseconds elapse
60
+ #
61
+ # @param [Hash<Symbol, Object>] options initial context configuration
62
+ # * :with scope serves as the global scope of the new context
63
+ # @yield [V8::Context] the newly created context
64
+ def initialize(options = {})
65
+ @conversion = Conversion.new
66
+ @access = Access.new
67
+ @timeout = options[:timeout]
68
+ if global = options[:with]
69
+ Context.new.enter do
70
+ global_template = global.class.to_template.InstanceTemplate()
71
+ @native = V8::C::Context::New(nil, global_template)
72
+ end
73
+ enter {link global, @native.Global()}
74
+ else
75
+ V8::C::Locker() do
76
+ @native = V8::C::Context::New()
77
+ end
78
+ end
79
+ yield self if block_given?
80
+ end
81
+
82
+ # Compile and execute a string of JavaScript source.
83
+ #
84
+ # If `source` is an IO object it will be read fully before being evaluated
85
+ #
86
+ # @param [String,IO] source the source code to compile and execute
87
+ # @param [String] filename the name to use for this code when generating stack traces
88
+ # @param [Integer] line the line number to start with
89
+ # @return [Object] the result of the evaluation
90
+ def eval(source, filename = '<eval>', line = 1)
91
+ if IO === source || StringIO === source
92
+ source = source.read
93
+ end
94
+ enter do
95
+ script = try { V8::C::Script::New(source.to_s, filename.to_s) }
96
+ if @timeout
97
+ to_ruby try {script.RunWithTimeout(@timeout)}
98
+ else
99
+ to_ruby try {script.Run()}
100
+ end
101
+ end
102
+ end
103
+
104
+ # Read a value from the global scope of this context
105
+ #
106
+ # @param [Object] key the name of the value to read
107
+ # @return [Object] value the value at `key`
108
+ def [](key)
109
+ enter do
110
+ to_ruby(@native.Global().Get(to_v8(key)))
111
+ end
112
+ end
113
+
114
+ # Binds `value` to the name `key` in the global scope of this context.
115
+ #
116
+ # @param [Object] key the name to bind to
117
+ # @param [Object] value the value to bind
118
+ def []=(key, value)
119
+ enter do
120
+ @native.Global().Set(to_v8(key), to_v8(value))
121
+ end
122
+ return value
123
+ end
124
+
125
+ # Destroy this context and release any internal references it may
126
+ # contain to embedded Ruby objects.
127
+ #
128
+ # A disposed context may never again be used for anything, and all
129
+ # objects created with it will become unusable.
130
+ def dispose
131
+ return unless @native
132
+ @native.Dispose()
133
+ @native = nil
134
+ V8::C::V8::ContextDisposedNotification()
135
+ def self.enter
136
+ fail "cannot enter a context which has already been disposed"
137
+ end
138
+ end
139
+
140
+ # Returns this context's global object. This will be a `V8::Object`
141
+ # if no scope was provided or just an `Object` if a Ruby object
142
+ # is serving as the global scope.
143
+ #
144
+ # @return [Object] scope the context's global scope.
145
+ def scope
146
+ enter { to_ruby @native.Global() }
147
+ end
148
+
149
+ # Converts a v8 C++ object into its ruby counterpart. This is method
150
+ # is used to translate all values passed to Ruby from JavaScript, either
151
+ # as return values or as callback parameters.
152
+ #
153
+ # @param [V8::C::Object] v8_object the native c++ object to convert.
154
+ # @return [Object] to pass to Ruby
155
+ # @see V8::Conversion for how to customize and extend this mechanism
156
+ def to_ruby(v8_object)
157
+ @conversion.to_ruby(v8_object)
158
+ end
159
+
160
+ # Converts a Ruby object into a native v8 C++ object. This method is
161
+ # used to translate all values passed to JavaScript from Ruby, either
162
+ # as return value or as callback parameters.
163
+ #
164
+ # @param [Object] ruby_object the Ruby object to convert
165
+ # @return [V8::C::Object] to pass to V8
166
+ # @see V8::Conversion for customizing and extending this mechanism
167
+ def to_v8(ruby_object)
168
+ @conversion.to_v8(ruby_object)
169
+ end
170
+
171
+ # Marks a Ruby object and a v8 C++ Object as being the same. In other
172
+ # words whenever `ruby_object` is passed to v8, the result of the
173
+ # conversion should be `v8_object`. Conversely, whenever `v8_object`
174
+ # is passed to Ruby, the result of the conversion should be `ruby_object`.
175
+ # The Ruby Racer uses this mechanism to maintain referential integrity
176
+ # between Ruby and JavaScript peers
177
+ #
178
+ # @param [Object] ruby_object the Ruby half of the object identity
179
+ # @param [V8::C::Object] v8_object the V8 half of the object identity.
180
+ # @see V8::Conversion::Identity
181
+ def link(ruby_object, v8_object)
182
+ @conversion.equate ruby_object, v8_object
183
+ end
184
+
185
+ # Links `ruby_object` and `v8_object` inside the currently entered
186
+ # context. This is an error if no context has been entered.
187
+ #
188
+ # @param [Object] ruby_object the Ruby half of the object identity
189
+ # @param [V8::C::Object] v8_object the V8 half of the object identity.
190
+ def self.link(ruby_object, v8_object)
191
+ current.link ruby_object, v8_object
192
+ end
193
+
194
+ # Run some Ruby code in the context of this context.
195
+ #
196
+ # This will acquire the V8 interpreter lock (possibly blocking
197
+ # until it is available), and prepare V8 for JavaScript execution.
198
+ #
199
+ # Only one context may be running at a time per thread.
200
+ #
201
+ # @return [Object] the result of executing `block`
202
+ def enter(&block)
203
+ if !entered?
204
+ lock_scope_and_enter(&block)
205
+ else
206
+ yield
207
+ end
208
+ end
209
+
210
+ # Indicates if this context is the currently entered context
211
+ #
212
+ # @return true if this context is currently entered
213
+ def entered?
214
+ Context.current == self
215
+ end
216
+
217
+ # Get the currently entered context.
218
+ #
219
+ # @return [V8::Context] currently entered context, nil if none entered.
220
+ def self.current
221
+ Thread.current[:v8_context]
222
+ end
223
+
224
+ # Compile and execute the contents of the file with path `filename`
225
+ # as JavaScript code.
226
+ #
227
+ # @param [String] filename path to the file to execute.
228
+ # @return [Object] the result of the evaluation.
229
+ def load(filename)
230
+ File.open(filename) do |file|
231
+ self.eval file, filename
232
+ end
233
+ end
234
+
235
+ private
236
+
237
+ def self.current=(context)
238
+ Thread.current[:v8_context] = context
239
+ end
240
+
241
+ def lock_scope_and_enter
242
+ current = Context.current
243
+ Context.current = self
244
+ V8::C::Locker() do
245
+ V8::C::HandleScope() do
246
+ begin
247
+ @native.Enter()
248
+ yield if block_given?
249
+ ensure
250
+ @native.Exit()
251
+ end
252
+ end
253
+ end
254
+ ensure
255
+ Context.current = current
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,11 @@
1
+ class V8::Conversion
2
+ module Array
3
+ def to_v8
4
+ array = V8::Array.new(length)
5
+ each_with_index do |item, i|
6
+ array[i] = item
7
+ end
8
+ return array.to_v8
9
+ end
10
+ end
11
+ end