therubyracer-xcode 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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