therubyracer 0.11.0beta7-x86-linux → 0.11.0beta8-x86-linux
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of therubyracer might be problematic. Click here for more details.
- data/ext/v8/backref.cc +1 -1
- data/ext/v8/init.so +0 -0
- data/ext/v8/stack.cc +1 -0
- data/lib/v8.rb +2 -2
- data/lib/v8/conversion/code.rb +1 -1
- data/lib/v8/conversion/indentity.rb +2 -2
- data/lib/v8/conversion/method.rb +1 -1
- data/lib/v8/error.rb +96 -21
- data/lib/v8/stack.rb +85 -0
- data/lib/v8/version.rb +1 -1
- data/lib/v8/weak.rb +70 -0
- data/spec/v8/error_spec.rb +155 -11
- metadata +4 -3
- data/lib/v8/util/weakcell.rb +0 -29
data/ext/v8/backref.cc
CHANGED
data/ext/v8/init.so
CHANGED
Binary file
|
data/ext/v8/stack.cc
CHANGED
@@ -22,6 +22,7 @@ namespace rr {
|
|
22
22
|
defineMethod("GetColumn", &Frame::GetColumn).
|
23
23
|
defineMethod("GetScriptName", &Frame::GetScriptName).
|
24
24
|
defineMethod("GetScriptNameOrSourceURL", &Frame::GetScriptNameOrSourceURL).
|
25
|
+
defineMethod("GetFunctionName", &Frame::GetFunctionName).
|
25
26
|
defineMethod("IsEval", &Frame::IsEval).
|
26
27
|
defineMethod("IsConstructor", &Frame::IsConstructor).
|
27
28
|
store(&Frame::Class);
|
data/lib/v8.rb
CHANGED
data/lib/v8/conversion/code.rb
CHANGED
@@ -21,11 +21,11 @@ class V8::Conversion
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def v8_idmap
|
24
|
-
@v8_idmap ||=
|
24
|
+
@v8_idmap ||= V8::Weak::WeakValueMap.new
|
25
25
|
end
|
26
26
|
|
27
27
|
def rb_idmap
|
28
|
-
@ruby_idmap ||=
|
28
|
+
@ruby_idmap ||= V8::Weak::WeakValueMap.new
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
data/lib/v8/conversion/method.rb
CHANGED
data/lib/v8/error.rb
CHANGED
@@ -1,18 +1,89 @@
|
|
1
1
|
module V8
|
2
|
+
# capture 99 stack frames on exception with normal details.
|
3
|
+
# You can adjust these values for performance or turn of stack capture entirely
|
4
|
+
V8::C::V8::SetCaptureStackTraceForUncaughtExceptions(true, 99, V8::C::StackTrace::kOverview)
|
2
5
|
class Error < StandardError
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# @!attribute [r] value
|
9
|
+
# @return [Object] the JavaScript value passed to the `throw` statement
|
3
10
|
attr_reader :value
|
4
|
-
|
11
|
+
|
12
|
+
# @!attribute [r] cause
|
13
|
+
# @return [Exception] the underlying error (if any) that triggered this error to be raised
|
14
|
+
attr_reader :cause
|
15
|
+
|
16
|
+
# @!attribute [V8::StackTrace] javascript_backtrace
|
17
|
+
# @return the complete JavaScript stack at the point this error was thrown
|
18
|
+
attr_reader :javascript_backtrace
|
19
|
+
|
20
|
+
# keep an alias to the StandardError#backtrace method so that we can capture
|
21
|
+
# just ruby backtrace frames
|
22
|
+
alias_method :standard_error_backtrace, :backtrace
|
23
|
+
|
24
|
+
def initialize(message, value, javascript_backtrace, cause = nil)
|
5
25
|
super(message)
|
6
26
|
@value = value
|
27
|
+
@cause = cause
|
28
|
+
@javascript_backtrace = javascript_backtrace
|
29
|
+
end
|
30
|
+
|
31
|
+
def causes
|
32
|
+
[].tap do |causes|
|
33
|
+
current = self
|
34
|
+
until current.nil? do
|
35
|
+
causes.push current
|
36
|
+
current = current.respond_to?(:cause) ? current.cause : nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def backtrace(*modifiers)
|
42
|
+
return unless super()
|
43
|
+
trace_framework = modifiers.include?(:framework)
|
44
|
+
trace_ruby = modifiers.length == 0 || modifiers.include?(:ruby)
|
45
|
+
trace_javascript = modifiers.length == 0 || modifiers.include?(:javascript)
|
46
|
+
bilingual_backtrace(trace_ruby, trace_javascript).tap do |trace|
|
47
|
+
trace.reject! {|frame| frame =~ %r{(lib/v8/.*\.rb|ext/v8/.*\.cc)}} unless modifiers.include?(:framework)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def root_cause
|
52
|
+
causes.last
|
53
|
+
end
|
54
|
+
|
55
|
+
def in_javascript?
|
56
|
+
causes.last.is_a? self.class
|
57
|
+
end
|
58
|
+
|
59
|
+
def in_ruby?
|
60
|
+
!in_javascript?
|
61
|
+
end
|
62
|
+
|
63
|
+
def bilingual_backtrace(trace_ruby = true, trace_javascript = true)
|
64
|
+
backtrace = causes.reduce(:backtrace => [], :ruby => -1, :javascript => -1) { |accumulator, cause|
|
65
|
+
accumulator.tap do
|
66
|
+
if trace_ruby
|
67
|
+
backtrace_selector = cause.respond_to?(:standard_error_backtrace) ? :standard_error_backtrace : :backtrace
|
68
|
+
ruby_frames = cause.send(backtrace_selector)[0..accumulator[:ruby]]
|
69
|
+
accumulator[:backtrace].unshift *ruby_frames
|
70
|
+
accumulator[:ruby] -= ruby_frames.length
|
71
|
+
end
|
72
|
+
if trace_javascript && cause.respond_to?(:javascript_backtrace)
|
73
|
+
javascript_frames = cause.javascript_backtrace.to_a[0..accumulator[:javascript]].map(&:to_s)
|
74
|
+
accumulator[:backtrace].unshift *javascript_frames
|
75
|
+
accumulator[:javascript] -= javascript_frames.length
|
76
|
+
end
|
77
|
+
end
|
78
|
+
}[:backtrace]
|
7
79
|
end
|
8
80
|
|
9
81
|
module Try
|
10
82
|
def try
|
11
|
-
context = V8::Context.current
|
12
83
|
V8::C::TryCatch() do |trycatch|
|
13
84
|
result = yield
|
14
85
|
if trycatch.HasCaught()
|
15
|
-
V8::Error(trycatch
|
86
|
+
raise V8::Error(trycatch)
|
16
87
|
else
|
17
88
|
result
|
18
89
|
end
|
@@ -23,37 +94,41 @@ module V8
|
|
23
94
|
module Protect
|
24
95
|
def protect
|
25
96
|
yield
|
26
|
-
rescue Football => e
|
27
|
-
e.kickoff!
|
28
97
|
rescue Exception => e
|
29
|
-
e.
|
30
|
-
e
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
module Football
|
35
|
-
def kickoff!
|
36
|
-
error = V8::C::Exception::Error(message)
|
37
|
-
error.SetHiddenValue("rr::Football", V8::C::External::New(self))
|
98
|
+
error = V8::C::Exception::Error(e.message)
|
99
|
+
error.SetHiddenValue("rr::Cause", V8::C::External::New(e))
|
38
100
|
V8::C::ThrowException(error)
|
39
101
|
end
|
40
102
|
end
|
41
103
|
|
42
104
|
end
|
43
105
|
|
44
|
-
def self.Error(
|
106
|
+
def self.Error(trycatch)
|
107
|
+
exception = trycatch.Exception()
|
45
108
|
value = exception.to_ruby
|
46
|
-
|
47
|
-
|
109
|
+
cause = nil
|
110
|
+
javascript_backtrace = V8::StackTrace.new(trycatch.Message().GetStackTrace())
|
111
|
+
message = if !exception.kind_of?(V8::C::Value)
|
112
|
+
exception.to_s
|
48
113
|
elsif exception.IsNativeError()
|
49
|
-
if
|
50
|
-
|
114
|
+
if cause = exception.GetHiddenValue("rr::Cause")
|
115
|
+
cause = cause.Value()
|
116
|
+
end
|
117
|
+
# SyntaxErrors do not have a JavaScript stack (even if they occur during js execution).
|
118
|
+
# To caputre where the error occured, we need to put it in the message
|
119
|
+
if value['constructor'] == V8::Context.current['SyntaxError']
|
120
|
+
info = trycatch.Message()
|
121
|
+
resource_name = info.GetScriptResourceName().to_ruby
|
122
|
+
"#{value['message']} at #{resource_name}:#{info.GetLineNumber()}:#{info.GetStartColumn() + 1}"
|
51
123
|
else
|
52
|
-
|
124
|
+
exception.Get("message").to_ruby
|
53
125
|
end
|
126
|
+
elsif exception.IsObject()
|
127
|
+
value['message'] || value.to_s
|
54
128
|
else
|
55
|
-
|
129
|
+
value.to_s
|
56
130
|
end
|
131
|
+
V8::Error.new(message, value, javascript_backtrace, cause)
|
57
132
|
end
|
58
133
|
const_set :JSError, Error
|
59
134
|
end
|
data/lib/v8/stack.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
|
2
|
+
module V8
|
3
|
+
|
4
|
+
class StackTrace
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(native)
|
8
|
+
@context = V8::Context.current
|
9
|
+
@native = native
|
10
|
+
end
|
11
|
+
|
12
|
+
def length
|
13
|
+
@context.enter do
|
14
|
+
@native ? @native.GetFrameCount() : 0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def each
|
19
|
+
return unless @native
|
20
|
+
@context.enter do
|
21
|
+
for i in 0..length - 1
|
22
|
+
yield V8::StackFrame.new(@native.GetFrame(i), @context)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
@native ? map(&:to_s).join("\n") : ""
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class StackFrame
|
33
|
+
|
34
|
+
def initialize(native, context)
|
35
|
+
@context = context
|
36
|
+
@native = native
|
37
|
+
end
|
38
|
+
|
39
|
+
def script_name
|
40
|
+
@context.enter do
|
41
|
+
@context.to_ruby(@native.GetScriptName())
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def function_name
|
46
|
+
@context.enter do
|
47
|
+
@context.to_ruby(@native.GetFunctionName())
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def line_number
|
52
|
+
@context.enter do
|
53
|
+
@native.GetLineNumber()
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def column
|
58
|
+
@context.enter do
|
59
|
+
@native.GetColumn()
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def eval?
|
64
|
+
@context.enter do
|
65
|
+
@native.IsEval()
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def constructor?
|
70
|
+
@context.enter do
|
71
|
+
@native.IsConstructor()
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s
|
76
|
+
@context.enter do
|
77
|
+
"at " + if !function_name.empty?
|
78
|
+
"#{function_name} (#{script_name}:#{line_number}:#{column})"
|
79
|
+
else
|
80
|
+
"#{script_name}:#{line_number}:#{column}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/v8/version.rb
CHANGED
data/lib/v8/weak.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module V8
|
2
|
+
module Weak
|
3
|
+
# weak refs are broken on MRI 1.9 and merely slow on 1.8
|
4
|
+
# so we mitigate this by using the 'ref' gem. However, this
|
5
|
+
# only mitigates the problem. Under heavy load, you will still
|
6
|
+
# get different or terminated objects being returned. bad stuff
|
7
|
+
#
|
8
|
+
# for other platforms we just use the stdlib 'weakref'
|
9
|
+
# implementation
|
10
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
|
11
|
+
require 'ref'
|
12
|
+
Ref = ::Ref::WeakReference
|
13
|
+
WeakValueMap = ::Ref::WeakValueMap
|
14
|
+
else
|
15
|
+
require 'weakref'
|
16
|
+
class Ref
|
17
|
+
def initialize(object)
|
18
|
+
@ref = ::WeakRef.new(object)
|
19
|
+
end
|
20
|
+
def object
|
21
|
+
@ref.__getobj__
|
22
|
+
rescue ::WeakRef::RefError
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class WeakValueMap
|
28
|
+
def initialize
|
29
|
+
@values = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def [](key)
|
33
|
+
if ref = @values[key]
|
34
|
+
ref.object
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def []=(key, value)
|
39
|
+
@values[key] = V8::Weak::Ref.new(value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module Cell
|
45
|
+
def weakcell(name, &block)
|
46
|
+
unless storage = instance_variable_get("@#{name}")
|
47
|
+
storage = instance_variable_set("@#{name}", Storage.new)
|
48
|
+
end
|
49
|
+
storage.access(&block)
|
50
|
+
end
|
51
|
+
class Storage
|
52
|
+
def access(&block)
|
53
|
+
if @ref
|
54
|
+
@ref.object || populate(block)
|
55
|
+
else
|
56
|
+
populate(block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def populate(block)
|
63
|
+
occupant = block.call()
|
64
|
+
@ref = V8::Weak::Ref.new(occupant)
|
65
|
+
return occupant
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/spec/v8/error_spec.rb
CHANGED
@@ -1,21 +1,165 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
4
|
+
|
3
5
|
describe V8::Error do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
|
7
|
+
before do
|
8
|
+
@cxt = V8::Context.new
|
9
|
+
@cxt['one'] = lambda do
|
10
|
+
@cxt.eval('two()', 'one.js')
|
11
|
+
end
|
12
|
+
@cxt['two'] = lambda do
|
13
|
+
@cxt.eval('three()', 'two.js')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "captures a message without over nesting when the error is an error" do
|
18
|
+
throw! do |e|
|
19
|
+
e.message.should == "BOOM!"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "captures the js message without over nesting when the error is a normal object" do
|
24
|
+
throw!('{foo: "bar"}') do |e|
|
25
|
+
e.message.should == "[object Object]"
|
26
|
+
end
|
27
|
+
throw!('{message: "bar"}') do |e|
|
28
|
+
e.message.should == "bar"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "captures a thrown value as the message" do
|
33
|
+
throw!('"BOOM!"') do |e|
|
34
|
+
e.message.should == "BOOM!"
|
35
|
+
end
|
36
|
+
throw!('6') do |e|
|
37
|
+
e.message.should == '6'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "has a reference to the root javascript cause" do
|
42
|
+
throw!('"I am a String"') do |e|
|
43
|
+
e.should_not be_in_ruby
|
44
|
+
e.should be_in_javascript
|
45
|
+
e.value['message'].should == "I am a String"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "has a reference to the root ruby cause if one exists" do
|
50
|
+
StandardError.new("BOOM!").tap do |bomb|
|
51
|
+
@cxt['boom'] = lambda do
|
52
|
+
raise bomb
|
9
53
|
end
|
10
|
-
|
11
|
-
cxt.eval('
|
54
|
+
lambda {
|
55
|
+
@cxt.eval('boom()', 'boom.js')
|
56
|
+
}.should(raise_error do |raised|
|
57
|
+
raised.should be_in_ruby
|
58
|
+
raised.should_not be_in_javascript
|
59
|
+
raised.root_cause.should be(bomb)
|
60
|
+
end)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "backtrace" do
|
65
|
+
it "is mixed with ruby and javascript" do
|
66
|
+
throw! do |e|
|
67
|
+
e.backtrace.first.should == "at three.js:1:7"
|
68
|
+
e.backtrace[1].should =~ /error_spec.rb/
|
69
|
+
e.backtrace[2].should == "at two.js:1:1"
|
70
|
+
e.backtrace[3].should =~ /error_spec.rb/
|
71
|
+
e.backtrace[4].should == "at one.js:1:1"
|
12
72
|
end
|
13
|
-
|
14
|
-
|
73
|
+
end
|
74
|
+
|
75
|
+
it "can be set to show only ruby frames" do
|
76
|
+
throw! do |e|
|
77
|
+
e.backtrace(:ruby).each do |frame|
|
78
|
+
frame.should =~ /(\.rb|):\d+/
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it "can be set to show only javascript frames" do
|
84
|
+
throw! do |e|
|
85
|
+
e.backtrace(:javascript).each do |frame|
|
86
|
+
frame.should =~ /\.js:\d:\d/
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "includes a mystery marker when the original frame is unavailable because what got thrown wasn't an error" do
|
92
|
+
throw!("6") do |e|
|
93
|
+
e.backtrace.first.should == 'at three.js:1:1'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it "has a source name and line number when there is a javascript SyntaxError" do
|
98
|
+
lambda do
|
99
|
+
@cxt.eval(<<-INVALID, 'source.js')
|
100
|
+
"this line is okay";
|
101
|
+
"this line has a syntax error because it ends with a colon":
|
102
|
+
"this line is also okay";
|
103
|
+
"how do I find out that line 2 has the syntax error?";
|
104
|
+
INVALID
|
105
|
+
end.should raise_error(V8::JSError) {|error|
|
106
|
+
error.message.should eql 'Unexpected token : at source.js:2:61'
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
it "can start with ruby at the bottom" do
|
111
|
+
@cxt['boom'] = lambda do
|
112
|
+
raise StandardError, "Bif!"
|
15
113
|
end
|
16
114
|
lambda {
|
17
|
-
cxt.eval('
|
18
|
-
}.should
|
115
|
+
@cxt.eval('boom()', "boom.js")
|
116
|
+
}.should(raise_error {|e|
|
117
|
+
e.backtrace.first.should =~ /error_spec\.rb/
|
118
|
+
e.backtrace[1].should =~ /boom.js/
|
119
|
+
})
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
def throw!(js = "new Error('BOOM!')", &block)
|
125
|
+
@cxt['three'] = lambda do
|
126
|
+
@cxt.eval("throw #{js}", 'three.js')
|
19
127
|
end
|
128
|
+
lambda do
|
129
|
+
@cxt['one'].call()
|
130
|
+
end.should(raise_error(V8::JSError, &block))
|
20
131
|
end
|
21
132
|
end
|
133
|
+
|
134
|
+
|
135
|
+
# describe V8::Error do
|
136
|
+
# describe "A ruby exception thrown inside JavaScript" do
|
137
|
+
# before do
|
138
|
+
# @error = StandardError.new('potato')
|
139
|
+
# begin
|
140
|
+
# V8::Context.new do |cxt|
|
141
|
+
# cxt['one'] = lambda do
|
142
|
+
# cxt.eval('two()', 'one.js')
|
143
|
+
# end
|
144
|
+
# cxt['two'] = lambda do
|
145
|
+
# cxt.eval('three()', 'two.js')
|
146
|
+
# end
|
147
|
+
# cxt['three'] = lambda do
|
148
|
+
# raise @error
|
149
|
+
# end
|
150
|
+
# cxt.eval('one()')
|
151
|
+
# end
|
152
|
+
# rescue StandardError => e
|
153
|
+
# @thrown = e
|
154
|
+
# end
|
155
|
+
# end
|
156
|
+
# it "is raised up through the call stack" do
|
157
|
+
# @thrown.should be(@error)
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
# it "shows both the javascript and the ruby callframes" do
|
161
|
+
# puts @error.backtrace.join('<br/>')
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# end
|
165
|
+
# end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: therubyracer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.0beta8
|
5
5
|
segments:
|
6
6
|
hash:
|
7
7
|
platform: x86-linux
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-08-
|
13
|
+
date: 2012-08-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: ref
|
@@ -104,8 +104,9 @@ files:
|
|
104
104
|
- lib/v8/error.rb
|
105
105
|
- lib/v8/function.rb
|
106
106
|
- lib/v8/object.rb
|
107
|
-
- lib/v8/
|
107
|
+
- lib/v8/stack.rb
|
108
108
|
- lib/v8/version.rb
|
109
|
+
- lib/v8/weak.rb
|
109
110
|
- spec/c/array_spec.rb
|
110
111
|
- spec/c/constants_spec.rb
|
111
112
|
- spec/c/exception_spec.rb
|
data/lib/v8/util/weakcell.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
module V8
|
2
|
-
module Util
|
3
|
-
module Weakcell
|
4
|
-
def weakcell(name, &block)
|
5
|
-
unless storage = instance_variable_get("@#{name}")
|
6
|
-
storage = instance_variable_set("@#{name}", Storage.new)
|
7
|
-
end
|
8
|
-
storage.access(&block)
|
9
|
-
end
|
10
|
-
class Storage
|
11
|
-
def access(&block)
|
12
|
-
if @ref
|
13
|
-
@ref.object || populate(block)
|
14
|
-
else
|
15
|
-
populate(block)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def populate(block)
|
22
|
-
occupant = block.call()
|
23
|
-
@ref = Ref::WeakReference.new(occupant)
|
24
|
-
return occupant
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|