therubyracer-discourse 0.12.0

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 (91) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.travis.yml +11 -0
  4. data/Changelog.md +247 -0
  5. data/Gemfile +9 -0
  6. data/README.md +176 -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/v8.rb +30 -0
  40. data/lib/v8/access.rb +5 -0
  41. data/lib/v8/access/indices.rb +40 -0
  42. data/lib/v8/access/invocation.rb +47 -0
  43. data/lib/v8/access/names.rb +65 -0
  44. data/lib/v8/array.rb +26 -0
  45. data/lib/v8/context.rb +256 -0
  46. data/lib/v8/conversion.rb +36 -0
  47. data/lib/v8/conversion/array.rb +11 -0
  48. data/lib/v8/conversion/class.rb +119 -0
  49. data/lib/v8/conversion/code.rb +38 -0
  50. data/lib/v8/conversion/fixnum.rb +11 -0
  51. data/lib/v8/conversion/fundamental.rb +11 -0
  52. data/lib/v8/conversion/hash.rb +11 -0
  53. data/lib/v8/conversion/indentity.rb +31 -0
  54. data/lib/v8/conversion/method.rb +26 -0
  55. data/lib/v8/conversion/object.rb +28 -0
  56. data/lib/v8/conversion/primitive.rb +7 -0
  57. data/lib/v8/conversion/proc.rb +5 -0
  58. data/lib/v8/conversion/reference.rb +16 -0
  59. data/lib/v8/conversion/string.rb +12 -0
  60. data/lib/v8/conversion/symbol.rb +7 -0
  61. data/lib/v8/conversion/time.rb +13 -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 +73 -0
  68. data/spec/c/array_spec.rb +17 -0
  69. data/spec/c/constants_spec.rb +20 -0
  70. data/spec/c/exception_spec.rb +26 -0
  71. data/spec/c/external_spec.rb +9 -0
  72. data/spec/c/function_spec.rb +46 -0
  73. data/spec/c/handles_spec.rb +35 -0
  74. data/spec/c/locker_spec.rb +38 -0
  75. data/spec/c/object_spec.rb +46 -0
  76. data/spec/c/script_spec.rb +28 -0
  77. data/spec/c/string_spec.rb +16 -0
  78. data/spec/c/template_spec.rb +30 -0
  79. data/spec/c/trycatch_spec.rb +51 -0
  80. data/spec/mem/blunt_spec.rb +42 -0
  81. data/spec/redjs_spec.rb +10 -0
  82. data/spec/spec_helper.rb +45 -0
  83. data/spec/threading_spec.rb +64 -0
  84. data/spec/v8/context_spec.rb +19 -0
  85. data/spec/v8/conversion_spec.rb +52 -0
  86. data/spec/v8/error_spec.rb +165 -0
  87. data/spec/v8/function_spec.rb +9 -0
  88. data/spec/v8/object_spec.rb +15 -0
  89. data/thefrontside.png +0 -0
  90. data/therubyracer.gemspec +21 -0
  91. metadata +163 -0
@@ -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
@@ -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
@@ -0,0 +1,256 @@
1
+ require 'stringio'
2
+ module V8
3
+ # All JavaScript must be executed in a context. This context consists of a global scope containing the
4
+ # standard JavaScript objects¨and functions like Object, String, Array, as well as any objects or
5
+ # functions from Ruby which have been embedded into it from the containing enviroment. E.g.
6
+ #
7
+ # V8::Context.new do |cxt|
8
+ # cxt['num'] = 5
9
+ # cxt.eval('num + 5') #=> 10
10
+ # end
11
+ #
12
+ # The same object may appear in any number of contexts, but only one context may be executing JavaScript code
13
+ # in any given thread. If a new context is opened in a thread in which a context is already opened, the second
14
+ # context will "mask" the old context e.g.
15
+ #
16
+ # six = 6
17
+ # Context.new do |cxt|
18
+ # cxt['num'] = 5
19
+ # cxt.eval('num') # => 5
20
+ # Context.new do |cxt|
21
+ # cxt['num'] = 10
22
+ # cxt.eval('num') # => 10
23
+ # cxt.eval('++num') # => 11
24
+ # end
25
+ # cxt.eval('num') # => 5
26
+ # end
27
+ class Context
28
+ include V8::Error::Try
29
+
30
+ # @!attribute [r] conversion
31
+ # @return [V8::Conversion] conversion behavior for this context
32
+ attr_reader :conversion
33
+
34
+ # @!attrribute [r] access
35
+ # @return [V8::Access] Ruby access behavior for this context
36
+ attr_reader :access
37
+
38
+ # @!attribute [r] native
39
+ # @return [V8::C::Context] the underlying C++ object
40
+ attr_reader :native
41
+
42
+ # maximum execution time for script in milliseconds
43
+ attr_reader :timeout
44
+
45
+ # Creates a new context.
46
+ #
47
+ # If passed the `:with` option, that object will be used as
48
+ # the global scope of the newly creating context. e.g.
49
+ #
50
+ # scope = Object.new
51
+ # def scope.hello; "Hi"; end
52
+ # V8::Context.new(:with => scope) do |cxt|
53
+ # cxt['hello'] #=> 'Hi'
54
+ # end
55
+ #
56
+ # If passed the `:timeout` option, every eval will timeout once
57
+ # N milliseconds elapse
58
+ #
59
+ # @param [Hash<Symbol, Object>] options initial context configuration
60
+ # * :with scope serves as the global scope of the new context
61
+ # @yield [V8::Context] the newly created context
62
+ def initialize(options = {})
63
+ @conversion = Conversion.new
64
+ @access = Access.new
65
+ @timeout = options[:timeout]
66
+ if global = options[:with]
67
+ Context.new.enter do
68
+ global_template = global.class.to_template.InstanceTemplate()
69
+ @native = V8::C::Context::New(nil, global_template)
70
+ end
71
+ enter {link global, @native.Global()}
72
+ else
73
+ V8::C::Locker() do
74
+ @native = V8::C::Context::New()
75
+ end
76
+ end
77
+ yield self if block_given?
78
+ end
79
+
80
+ # Compile and execute a string of JavaScript source.
81
+ #
82
+ # If `source` is an IO object it will be read fully before being evaluated
83
+ #
84
+ # @param [String,IO] source the source code to compile and execute
85
+ # @param [String] filename the name to use for this code when generating stack traces
86
+ # @param [Integer] line the line number to start with
87
+ # @return [Object] the result of the evaluation
88
+ def eval(source, filename = '<eval>', line = 1)
89
+ if IO === source || StringIO === source
90
+ source = source.read
91
+ end
92
+ enter do
93
+ script = try { V8::C::Script::New(source.to_s, filename.to_s) }
94
+ if @timeout
95
+ to_ruby try {script.RunWithTimeout(@timeout)}
96
+ else
97
+ to_ruby try {script.Run()}
98
+ end
99
+ end
100
+ end
101
+
102
+ # Read a value from the global scope of this context
103
+ #
104
+ # @param [Object] key the name of the value to read
105
+ # @return [Object] value the value at `key`
106
+ def [](key)
107
+ enter do
108
+ to_ruby(@native.Global().Get(to_v8(key)))
109
+ end
110
+ end
111
+
112
+ # Binds `value` to the name `key` in the global scope of this context.
113
+ #
114
+ # @param [Object] key the name to bind to
115
+ # @param [Object] value the value to bind
116
+ def []=(key, value)
117
+ enter do
118
+ @native.Global().Set(to_v8(key), to_v8(value))
119
+ end
120
+ return value
121
+ end
122
+
123
+ # Destroy this context and release any internal references it may
124
+ # contain to embedded Ruby objects.
125
+ #
126
+ # A disposed context may never again be used for anything, and all
127
+ # objects created with it will become unusable.
128
+ def dispose
129
+ return unless @native
130
+ @native.Dispose()
131
+ @native = nil
132
+ V8::C::V8::ContextDisposedNotification()
133
+ def self.enter
134
+ fail "cannot enter a context which has already been disposed"
135
+ end
136
+ end
137
+
138
+ # Returns this context's global object. This will be a `V8::Object`
139
+ # if no scope was provided or just an `Object` if a Ruby object
140
+ # is serving as the global scope.
141
+ #
142
+ # @return [Object] scope the context's global scope.
143
+ def scope
144
+ enter { to_ruby @native.Global() }
145
+ end
146
+
147
+ # Converts a v8 C++ object into its ruby counterpart. This is method
148
+ # is used to translate all values passed to Ruby from JavaScript, either
149
+ # as return values or as callback parameters.
150
+ #
151
+ # @param [V8::C::Object] v8_object the native c++ object to convert.
152
+ # @return [Object] to pass to Ruby
153
+ # @see V8::Conversion for how to customize and extend this mechanism
154
+ def to_ruby(v8_object)
155
+ @conversion.to_ruby(v8_object)
156
+ end
157
+
158
+ # Converts a Ruby object into a native v8 C++ object. This method is
159
+ # used to translate all values passed to JavaScript from Ruby, either
160
+ # as return value or as callback parameters.
161
+ #
162
+ # @param [Object] ruby_object the Ruby object to convert
163
+ # @return [V8::C::Object] to pass to V8
164
+ # @see V8::Conversion for customizing and extending this mechanism
165
+ def to_v8(ruby_object)
166
+ @conversion.to_v8(ruby_object)
167
+ end
168
+
169
+ # Marks a Ruby object and a v8 C++ Object as being the same. In other
170
+ # words whenever `ruby_object` is passed to v8, the result of the
171
+ # conversion should be `v8_object`. Conversely, whenever `v8_object`
172
+ # is passed to Ruby, the result of the conversion should be `ruby_object`.
173
+ # The Ruby Racer uses this mechanism to maintain referential integrity
174
+ # between Ruby and JavaScript peers
175
+ #
176
+ # @param [Object] ruby_object the Ruby half of the object identity
177
+ # @param [V8::C::Object] v8_object the V8 half of the object identity.
178
+ # @see V8::Conversion::Identity
179
+ def link(ruby_object, v8_object)
180
+ @conversion.equate ruby_object, v8_object
181
+ end
182
+
183
+ # Links `ruby_object` and `v8_object` inside the currently entered
184
+ # context. This is an error if no context has been entered.
185
+ #
186
+ # @param [Object] ruby_object the Ruby half of the object identity
187
+ # @param [V8::C::Object] v8_object the V8 half of the object identity.
188
+ def self.link(ruby_object, v8_object)
189
+ current.link ruby_object, v8_object
190
+ end
191
+
192
+ # Run some Ruby code in the context of this context.
193
+ #
194
+ # This will acquire the V8 interpreter lock (possibly blocking
195
+ # until it is available), and prepare V8 for JavaScript execution.
196
+ #
197
+ # Only one context may be running at a time per thread.
198
+ #
199
+ # @return [Object] the result of executing `block`
200
+ def enter(&block)
201
+ if !entered?
202
+ lock_scope_and_enter(&block)
203
+ else
204
+ yield
205
+ end
206
+ end
207
+
208
+ # Indicates if this context is the currently entered context
209
+ #
210
+ # @return true if this context is currently entered
211
+ def entered?
212
+ Context.current == self
213
+ end
214
+
215
+ # Get the currently entered context.
216
+ #
217
+ # @return [V8::Context] currently entered context, nil if none entered.
218
+ def self.current
219
+ Thread.current[:v8_context]
220
+ end
221
+
222
+ # Compile and execute the contents of the file with path `filename`
223
+ # as JavaScript code.
224
+ #
225
+ # @param [String] filename path to the file to execute.
226
+ # @return [Object] the result of the evaluation.
227
+ def load(filename)
228
+ File.open(filename) do |file|
229
+ self.eval file, filename
230
+ end
231
+ end
232
+
233
+ private
234
+
235
+ def self.current=(context)
236
+ Thread.current[:v8_context] = context
237
+ end
238
+
239
+ def lock_scope_and_enter
240
+ current = Context.current
241
+ Context.current = self
242
+ V8::C::Locker() do
243
+ V8::C::HandleScope() do
244
+ begin
245
+ @native.Enter()
246
+ yield if block_given?
247
+ ensure
248
+ @native.Exit()
249
+ end
250
+ end
251
+ end
252
+ ensure
253
+ Context.current = current
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,36 @@
1
+
2
+ class V8::Conversion
3
+ include Fundamental
4
+ include Identity
5
+
6
+ def to_ruby(v8_object)
7
+ super v8_object
8
+ end
9
+
10
+ def to_v8(ruby_object)
11
+ super ruby_object
12
+ end
13
+ end
14
+
15
+ for type in [TrueClass, FalseClass, NilClass, Float] do
16
+ type.class_eval do
17
+ include V8::Conversion::Primitive
18
+ end
19
+ end
20
+
21
+ for type in [Class, Object, Array, Hash, String, Symbol, Time, Proc, Method, Fixnum] do
22
+ type.class_eval do
23
+ include V8::Conversion.const_get(type.name)
24
+ end
25
+ end
26
+
27
+ class UnboundMethod
28
+ include V8::Conversion::Method
29
+ end
30
+
31
+ for type in [:Object, :String, :Date] do
32
+ V8::C::const_get(type).class_eval do
33
+ include V8::Conversion::const_get("Native#{type}")
34
+ end
35
+ end
36
+
@@ -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
@@ -0,0 +1,119 @@
1
+ class V8::Conversion
2
+ module Class
3
+ include V8::Conversion::Code
4
+
5
+ def to_template
6
+ weakcell(:constructor) do
7
+ template = V8::C::FunctionTemplate::New(V8::Conversion::Constructor.new(self))
8
+ prototype = template.InstanceTemplate()
9
+ prototype.SetNamedPropertyHandler(V8::Conversion::Get, V8::Conversion::Set)
10
+ prototype.SetIndexedPropertyHandler(V8::Conversion::IGet, V8::Conversion::ISet)
11
+ if self != ::Object && superclass != ::Object && superclass != ::Class
12
+ template.Inherit(superclass.to_template)
13
+ end
14
+ template
15
+ end
16
+ end
17
+ end
18
+
19
+ class Constructor
20
+ include V8::Error::Protect
21
+
22
+ def initialize(cls)
23
+ @class = cls
24
+ end
25
+
26
+ def call(arguments)
27
+ arguments.extend Args
28
+ protect do
29
+ if arguments.linkage_call?
30
+ arguments.link
31
+ else
32
+ arguments.construct @class
33
+ end
34
+ end
35
+ return arguments.This()
36
+ end
37
+
38
+ module Args
39
+ def linkage_call?
40
+ self.Length() == 1 && self[0].IsExternal()
41
+ end
42
+
43
+ def link
44
+ external = self[0]
45
+ This().SetHiddenValue("rr::implementation", external)
46
+ context.link external.Value(), This()
47
+ end
48
+
49
+ def construct(cls)
50
+ context.link cls.new(*to_args), This()
51
+ end
52
+
53
+ def context
54
+ V8::Context.current
55
+ end
56
+
57
+ def to_args
58
+ args = ::Array.new(Length())
59
+ 0.upto(args.length - 1) do |i|
60
+ args[i] = self[i]
61
+ end
62
+ return args
63
+ end
64
+ end
65
+ end
66
+
67
+ module Accessor
68
+ include V8::Error::Protect
69
+ def intercept(info, key, &block)
70
+ context = V8::Context.current
71
+ access = context.access
72
+ object = context.to_ruby(info.This())
73
+ handles_property = true
74
+ dontintercept = proc do
75
+ handles_property = false
76
+ end
77
+ protect do
78
+ result = block.call(context, access, object, context.to_ruby(key), dontintercept)
79
+ handles_property ? context.to_v8(result) : V8::C::Value::Empty
80
+ end
81
+ end
82
+ end
83
+
84
+ class Get
85
+ extend Accessor
86
+ def self.call(property, info)
87
+ intercept(info, property) do |context, access, object, key, dontintercept|
88
+ access.get(object, key, &dontintercept)
89
+ end
90
+ end
91
+ end
92
+
93
+ class Set
94
+ extend Accessor
95
+ def self.call(property, value, info)
96
+ intercept(info, property) do |context, access, object, key, dontintercept|
97
+ access.set(object, key, context.to_ruby(value), &dontintercept)
98
+ end
99
+ end
100
+ end
101
+
102
+ class IGet
103
+ extend Accessor
104
+ def self.call(property, info)
105
+ intercept(info, property) do |context, access, object, key, dontintercept|
106
+ access.iget(object, key, &dontintercept)
107
+ end
108
+ end
109
+ end
110
+
111
+ class ISet
112
+ extend Accessor
113
+ def self.call(property, value, info)
114
+ intercept(info, property) do |context, access, object, key, dontintercept|
115
+ access.iset(object, key, context.to_ruby(value), &dontintercept)
116
+ end
117
+ end
118
+ end
119
+ end