evil-ruby 0.1.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.
- data/COPYING +56 -0
- data/NEWS +7 -0
- data/README +15 -0
- data/Rakefile +109 -0
- data/lib/evil.rb +691 -0
- data/setup.rb +1360 -0
- data/test/tc_all.rb +8 -0
- data/test/tc_inline.rb +11 -0
- data/test/test-extract.rb +112 -0
- metadata +59 -0
data/COPYING
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
evil-ruby is copyrighted free software by Florian Gross <flgr@ccan.de>.
|
2
|
+
You can redistribute it and/or modify it under either the terms of the GPL
|
3
|
+
(see http://www.gnu.org/licenses/gpl.html), or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
13
|
+
make them Freely Available, such as by posting said
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
15
|
+
the author to include your modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) give non-standard binaries non-standard names, with
|
21
|
+
instructions on where to get the original software distribution.
|
22
|
+
|
23
|
+
d) make other distribution arrangements with the author.
|
24
|
+
|
25
|
+
3. You may distribute the software in object code or binary form,
|
26
|
+
provided that you do at least ONE of the following:
|
27
|
+
|
28
|
+
a) distribute the binaries and library files of the software,
|
29
|
+
together with instructions (in the manual page or equivalent)
|
30
|
+
on where to get the original distribution.
|
31
|
+
|
32
|
+
b) accompany the distribution with the machine-readable source of
|
33
|
+
the software.
|
34
|
+
|
35
|
+
c) give non-standard binaries non-standard names, with
|
36
|
+
instructions on where to get the original software distribution.
|
37
|
+
|
38
|
+
d) make other distribution arrangements with the author.
|
39
|
+
|
40
|
+
4. You may modify and include the part of the software into any other
|
41
|
+
software (possibly commercial). But some files in the distribution
|
42
|
+
are not written by the author, so that they are not under these terms.
|
43
|
+
|
44
|
+
For the list of those files and their copying conditions, see the
|
45
|
+
file LEGAL.
|
46
|
+
|
47
|
+
5. The scripts and library files supplied as input to or produced as
|
48
|
+
output from the software do not automatically fall under the
|
49
|
+
copyright of the software, but belong to whomever generated them,
|
50
|
+
and may be sold commercially, and may be aggregated with this
|
51
|
+
software.
|
52
|
+
|
53
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
54
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
55
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
56
|
+
PURPOSE.
|
data/NEWS
ADDED
data/README
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
= README for evil-ruby
|
2
|
+
|
3
|
+
Extends Ruby's semantics by accessing its internals from pure Ruby code.
|
4
|
+
|
5
|
+
|
6
|
+
== Installation
|
7
|
+
|
8
|
+
De-compress archive and enter its top directory. Then type:
|
9
|
+
|
10
|
+
($ su)
|
11
|
+
# ruby setup.rb
|
12
|
+
|
13
|
+
This simple step installs this program under the default location of Ruby
|
14
|
+
libraries. You can also install files into your favorite directory by supplying
|
15
|
+
setup.rb with some options. Try "ruby setup.rb --help".
|
data/Rakefile
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/packagetask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'find'
|
5
|
+
|
6
|
+
readme = File.read("README").gsub("\r\n", "\n")
|
7
|
+
author_line = readme[/^\*\s*Author:.+$/].split(/\s+/, 2)[1] rescue nil
|
8
|
+
|
9
|
+
# Manual globals
|
10
|
+
|
11
|
+
PKG_AUTOREQUIRE = nil
|
12
|
+
PKG_RUBY_VERSION = '>= 1.8.4'
|
13
|
+
PKG_GEM_DEPENDENCIES = {}
|
14
|
+
PKG_RDOC_FILES = ['README', 'NEWS']
|
15
|
+
PKG_RDOC_OPTIONS = %w(--all --main README --title #{PKG_NAME})
|
16
|
+
PKG_FILES = PKG_RDOC_FILES + ['COPYING', 'setup.rb', 'Rakefile']
|
17
|
+
|
18
|
+
# Automatic globals
|
19
|
+
|
20
|
+
PKG_NAME, PKG_VERSION = *File.read("NEWS")[/^==.+$/].split(/\s+/)[1..2]
|
21
|
+
PKG_DESCRIPTION = readme.split(/\n{3,}/)[0].sub(/^=.+$\s*/, "") rescue nil
|
22
|
+
PKG_SUMMARY = readme[/^=.+$/].split(/--/)[1].strip rescue PKG_DESCRIPTION
|
23
|
+
PKG_HOMEPAGE = readme[/^\*\s*Homepage:.+$/].split(/\s+/, 2)[1] rescue nil
|
24
|
+
PKG_EMAIL = author_line[/<(.+)>/, 1] rescue nil
|
25
|
+
PKG_AUTHOR = author_line.sub(PKG_EMAIL, "").sub("<>", "").strip rescue nil
|
26
|
+
|
27
|
+
Find.find('lib/', 'test/', 'bin/') do |file|
|
28
|
+
if FileTest.directory?(file) and file[/\.svn/i] then
|
29
|
+
Find.prune
|
30
|
+
elsif !file[/\.DS_Store/i] then
|
31
|
+
PKG_FILES << file
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
PKG_FILES.reject! { |file| !File.file?(file) }
|
36
|
+
|
37
|
+
PKG_EXE_FILES, PKG_LIB_FILES = *%w(bin/ lib/).map do |dir|
|
38
|
+
PKG_FILES.grep(/#{dir}/i).reject { |f| File.directory?(f) }
|
39
|
+
end
|
40
|
+
|
41
|
+
PKG_EXE_FILES.map! { |exe| exe.sub(%r(^bin/), "") }
|
42
|
+
|
43
|
+
# Tasks
|
44
|
+
|
45
|
+
task :default => :test
|
46
|
+
|
47
|
+
# Test task
|
48
|
+
if File.exist?("test/") then
|
49
|
+
require 'rake/testtask'
|
50
|
+
|
51
|
+
Rake::TestTask.new do |test|
|
52
|
+
test.test_files = ['test/tc_all.rb']
|
53
|
+
end
|
54
|
+
else
|
55
|
+
task :test do
|
56
|
+
puts "No tests to run"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Doc task
|
61
|
+
Rake::RDocTask.new do |rd|
|
62
|
+
rd.rdoc_files.include(PKG_LIB_FILES, PKG_RDOC_FILES)
|
63
|
+
rd.options += PKG_RDOC_OPTIONS
|
64
|
+
end
|
65
|
+
|
66
|
+
# Tar task
|
67
|
+
Rake::PackageTask.new(PKG_NAME, PKG_VERSION) do |pkg|
|
68
|
+
pkg.need_tar = true
|
69
|
+
pkg.package_files = PKG_FILES
|
70
|
+
end
|
71
|
+
|
72
|
+
# Gem task
|
73
|
+
begin
|
74
|
+
require 'rake/gempackagetask'
|
75
|
+
|
76
|
+
spec = Gem::Specification.new do |spec|
|
77
|
+
spec.name = PKG_NAME
|
78
|
+
spec.version = PKG_VERSION
|
79
|
+
spec.summary = PKG_SUMMARY
|
80
|
+
spec.description = PKG_DESCRIPTION
|
81
|
+
|
82
|
+
spec.homepage = PKG_HOMEPAGE
|
83
|
+
spec.email = PKG_EMAIL
|
84
|
+
spec.author = PKG_AUTHOR
|
85
|
+
|
86
|
+
spec.has_rdoc = true
|
87
|
+
spec.extra_rdoc_files = PKG_RDOC_FILES
|
88
|
+
spec.rdoc_options += PKG_RDOC_OPTIONS
|
89
|
+
|
90
|
+
if File.exist?("test/") then
|
91
|
+
spec.test_files = ['test/tc_all.rb']
|
92
|
+
end
|
93
|
+
|
94
|
+
spec.required_ruby_version = PKG_RUBY_VERSION
|
95
|
+
(PKG_GEM_DEPENDENCIES || {}).each do |name, version|
|
96
|
+
spec.add_dependency(name, version)
|
97
|
+
end
|
98
|
+
|
99
|
+
spec.files = PKG_FILES
|
100
|
+
spec.executables = PKG_EXE_FILES
|
101
|
+
spec.autorequire = PKG_AUTOREQUIRE
|
102
|
+
end
|
103
|
+
|
104
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
105
|
+
pkg.need_zip = true
|
106
|
+
pkg.need_tar = true
|
107
|
+
end
|
108
|
+
rescue LoadError
|
109
|
+
end
|
data/lib/evil.rb
ADDED
@@ -0,0 +1,691 @@
|
|
1
|
+
# vim:sw=2
|
2
|
+
# Written in 2004 by Florian Gross <flgr@ccan.de> and
|
3
|
+
# Mauricio Julio Fern�ndez Pradier <batsman.geo@yahoo.com>
|
4
|
+
#
|
5
|
+
# This is licensed under the same license as Ruby.
|
6
|
+
|
7
|
+
module RubyInternal
|
8
|
+
Is_1_8 = RUBY_VERSION[/\A1.[678]/]
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'dl'
|
12
|
+
unless RubyInternal::Is_1_8
|
13
|
+
require 'dl/value'
|
14
|
+
require 'dl/import'
|
15
|
+
end
|
16
|
+
require 'dl/struct'
|
17
|
+
|
18
|
+
# Provides low-level access to Ruby's internals. Be
|
19
|
+
# careful when using this directly, because you can
|
20
|
+
# break Ruby with it.
|
21
|
+
module RubyInternal
|
22
|
+
DL::CPtr = DL::PtrData if Is_1_8
|
23
|
+
DL::SIZEOF_LONG = DL.sizeof("l") if Is_1_8
|
24
|
+
|
25
|
+
unless Is_1_8
|
26
|
+
class ::DL::CPtr
|
27
|
+
alias :old_store :[]=
|
28
|
+
def []=(idx, *args)
|
29
|
+
if args.size == 1 and args[0].is_a?(String) then
|
30
|
+
args[0] = args[0].ord
|
31
|
+
end
|
32
|
+
|
33
|
+
old_store(idx, *args)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
extend self
|
39
|
+
importer = Is_1_8 ? DL::Importable : DL::Importer
|
40
|
+
extend importer
|
41
|
+
|
42
|
+
dlload()
|
43
|
+
|
44
|
+
if Is_1_8
|
45
|
+
def typealias(new, old)
|
46
|
+
super(new, nil, nil, nil, old)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Qfalse = 0
|
51
|
+
Qtrue = 2
|
52
|
+
Qnil = 4
|
53
|
+
Qundef = 6
|
54
|
+
|
55
|
+
T_NONE = 0x00
|
56
|
+
T_NIL = 0x01
|
57
|
+
T_OBJECT = 0x02
|
58
|
+
T_CLASS = 0x03
|
59
|
+
T_ICLASS = 0x04
|
60
|
+
T_MODULE = 0x05
|
61
|
+
T_FLOAT = 0x06
|
62
|
+
T_STRING = 0x07
|
63
|
+
T_REGEXP = 0x08
|
64
|
+
T_ARRAY = 0x09
|
65
|
+
T_FIXNUM = 0x0a
|
66
|
+
T_HASH = 0x0b
|
67
|
+
T_STRUCT = 0x0c
|
68
|
+
T_BIGNUM = 0x0d
|
69
|
+
T_FILE = 0x0e
|
70
|
+
|
71
|
+
if Is_1_8
|
72
|
+
T_TRUE = 0x20
|
73
|
+
T_FALSE = 0x21
|
74
|
+
T_DATA = 0x22
|
75
|
+
T_MATCH = 0x23
|
76
|
+
T_SYMBOL = 0x24
|
77
|
+
|
78
|
+
T_BLOCK = 0x3b
|
79
|
+
T_UNDEF = 0x3c
|
80
|
+
T_VARMAP = 0x3d
|
81
|
+
T_SCOPE = 0x3e
|
82
|
+
T_NODE = 0x3f
|
83
|
+
|
84
|
+
T_MASK = 0x3f
|
85
|
+
else # 1.9 or higher
|
86
|
+
T_TRUE = 0x10
|
87
|
+
T_FALSE = 0x11
|
88
|
+
T_DATA = 0x12
|
89
|
+
T_MATCH = 0x13
|
90
|
+
T_SYMBOL = 0x14
|
91
|
+
|
92
|
+
T_BLOCK = 0x1b
|
93
|
+
T_UNDEF = 0x1c
|
94
|
+
T_VARMAP = 0x1d
|
95
|
+
T_SCOPE = 0x1e
|
96
|
+
T_NODE = 0x1f
|
97
|
+
|
98
|
+
T_MASK = 0x1f
|
99
|
+
end # constants
|
100
|
+
|
101
|
+
typealias "VALUE", "unsigned long"
|
102
|
+
typealias "ID", "unsigned long"
|
103
|
+
typealias "ulong", "unsigned long"
|
104
|
+
Basic = ["long flags", "VALUE klass"]
|
105
|
+
|
106
|
+
RBasic = struct Basic
|
107
|
+
|
108
|
+
RObject = struct(Basic + [
|
109
|
+
"st_table *iv_tbl"
|
110
|
+
])
|
111
|
+
|
112
|
+
RClass = struct(Basic + [
|
113
|
+
"st_table *iv_tbl",
|
114
|
+
"st_table *m_tbl",
|
115
|
+
"VALUE super"
|
116
|
+
])
|
117
|
+
|
118
|
+
RModule = RClass
|
119
|
+
|
120
|
+
RFloat = struct(Basic + [
|
121
|
+
"double value"
|
122
|
+
])
|
123
|
+
|
124
|
+
RString = struct(Basic + [
|
125
|
+
"long len",
|
126
|
+
"char *ptr",
|
127
|
+
"long capa"
|
128
|
+
])
|
129
|
+
|
130
|
+
RArray = struct(Basic + [
|
131
|
+
"long len",
|
132
|
+
"long capa",
|
133
|
+
"VALUE *ptr"
|
134
|
+
])
|
135
|
+
|
136
|
+
RRegexp = struct(Basic + [
|
137
|
+
"re_pattern_buffer *ptr",
|
138
|
+
"long len",
|
139
|
+
"char *str"
|
140
|
+
])
|
141
|
+
|
142
|
+
RHash = struct(Basic + [
|
143
|
+
"st_table *tbl",
|
144
|
+
"int iter_lev",
|
145
|
+
"VALUE ifnone"
|
146
|
+
])
|
147
|
+
|
148
|
+
RFile = struct(Basic + [
|
149
|
+
"OpenFile *fptr"
|
150
|
+
])
|
151
|
+
|
152
|
+
RData = struct(Basic + [
|
153
|
+
"void *dmark",
|
154
|
+
"void *dfree",
|
155
|
+
"void *data"
|
156
|
+
])
|
157
|
+
|
158
|
+
RStruct = struct(Basic + [
|
159
|
+
"long len",
|
160
|
+
"VALUE *ptr"
|
161
|
+
])
|
162
|
+
|
163
|
+
RBignum = struct(Basic + [
|
164
|
+
"char sign",
|
165
|
+
"long len",
|
166
|
+
"void *digits"
|
167
|
+
])
|
168
|
+
|
169
|
+
DMethod = struct [
|
170
|
+
"VALUE klass",
|
171
|
+
"VALUE rklass",
|
172
|
+
"long id",
|
173
|
+
"long oid",
|
174
|
+
"void *body"
|
175
|
+
]
|
176
|
+
|
177
|
+
FrameBase = [
|
178
|
+
"VALUE frame_self",
|
179
|
+
"int frame_argc",
|
180
|
+
"VALUE *frame_argv",
|
181
|
+
"ID frame_last_func",
|
182
|
+
"ID frame_orig_func",
|
183
|
+
"VALUE frame_last_class",
|
184
|
+
"FRAME *frame_prev",
|
185
|
+
"FRAME *frame_tmp",
|
186
|
+
"RNode *frame_node",
|
187
|
+
"int frame_iter",
|
188
|
+
"int frame_flags",
|
189
|
+
"ulong frame_uniq"
|
190
|
+
]
|
191
|
+
|
192
|
+
Frame = struct FrameBase
|
193
|
+
|
194
|
+
Block = struct([
|
195
|
+
"NODE *var",
|
196
|
+
"NODE *body",
|
197
|
+
"VALUE self",
|
198
|
+
] + FrameBase + [
|
199
|
+
"SCOPE *scope",
|
200
|
+
"VALUE klass",
|
201
|
+
"NODE *cref",
|
202
|
+
"int iter",
|
203
|
+
"int vmode",
|
204
|
+
"int flags",
|
205
|
+
"int uniq",
|
206
|
+
"RVarmap *dyna_vars",
|
207
|
+
"VALUE orig_thread",
|
208
|
+
"VALUE wrapper",
|
209
|
+
"VALUE block_obj",
|
210
|
+
"BLOCK *outer",
|
211
|
+
"BLOCK *prev"
|
212
|
+
])
|
213
|
+
|
214
|
+
STD_HASH_TYPE = struct [
|
215
|
+
"void *compare",
|
216
|
+
"void *hash"
|
217
|
+
]
|
218
|
+
|
219
|
+
typealias "ST_DATA_T", "unsigned long"
|
220
|
+
|
221
|
+
ST_TABLE_ENTRY = struct [
|
222
|
+
"int hash",
|
223
|
+
"ST_DATA_T key",
|
224
|
+
"ST_DATA_T record",
|
225
|
+
"ST_TABLE_ENTRY *next"
|
226
|
+
]
|
227
|
+
|
228
|
+
ST_TABLE = struct [
|
229
|
+
"ST_HASH_TYPE *type",
|
230
|
+
"int num_bins",
|
231
|
+
"int num_entries",
|
232
|
+
"ST_TABLE_ENTRY **bins"
|
233
|
+
]
|
234
|
+
|
235
|
+
FL_USHIFT = 11
|
236
|
+
FL_USER0 = 1 << (FL_USHIFT + 0)
|
237
|
+
FL_USER1 = 1 << (FL_USHIFT + 1)
|
238
|
+
FL_USER2 = 1 << (FL_USHIFT + 2)
|
239
|
+
FL_USER3 = 1 << (FL_USHIFT + 3)
|
240
|
+
FL_USER4 = 1 << (FL_USHIFT + 4)
|
241
|
+
FL_USER5 = 1 << (FL_USHIFT + 5)
|
242
|
+
FL_USER6 = 1 << (FL_USHIFT + 6)
|
243
|
+
FL_USER7 = 1 << (FL_USHIFT + 7)
|
244
|
+
|
245
|
+
FL_SINGLETON = FL_USER0
|
246
|
+
FL_MARK = 1 << 6
|
247
|
+
FL_FINALIZE = 1 << 7
|
248
|
+
FL_TAINT = 1 << 8
|
249
|
+
FL_EXIVAR = 1 << 9
|
250
|
+
FL_FREEZE = 1 << 10
|
251
|
+
|
252
|
+
# Executes a block of code that changes
|
253
|
+
# internal Ruby structures. This will
|
254
|
+
# make sure that neither the GC nor other
|
255
|
+
# Threads are run while the block is
|
256
|
+
# getting executed.
|
257
|
+
def critical
|
258
|
+
begin
|
259
|
+
if Is_1_8 then
|
260
|
+
old_critical = Thread.critical
|
261
|
+
Thread.critical = true
|
262
|
+
else
|
263
|
+
# Is it OK to do nothing on 1.9?
|
264
|
+
end
|
265
|
+
|
266
|
+
disabled_gc = !GC.disable
|
267
|
+
|
268
|
+
yield
|
269
|
+
ensure
|
270
|
+
GC.enable if disabled_gc
|
271
|
+
Thread.critical = old_critical if Is_1_8
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
module EmptyModule; end
|
276
|
+
def empty_iclass_ptr(force_new = false)
|
277
|
+
@empty_iclass_ptr ||= nil # avoid warning
|
278
|
+
return @empty_iclass_ptr if @empty_iclass_ptr and not force_new
|
279
|
+
result = Object.new
|
280
|
+
iptr = result.internal_ptr
|
281
|
+
ires = result.internal
|
282
|
+
newires = RClass.new(result.internal_ptr)
|
283
|
+
critical do
|
284
|
+
ires.flags &= ~T_MASK
|
285
|
+
ires.flags |= T_ICLASS
|
286
|
+
ires.klass = EmptyModule.internal_ptr.to_i
|
287
|
+
newires.m_tbl = EmptyModule.internal.m_tbl
|
288
|
+
end
|
289
|
+
@empty_iclass_ptr = iptr
|
290
|
+
return iptr
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
class Object
|
295
|
+
# Returns the singleton class of an Object.
|
296
|
+
# This is just a simple convenience method.
|
297
|
+
#
|
298
|
+
# obj = Object.new
|
299
|
+
# obj.singleton_class.class_eval do
|
300
|
+
# def x; end
|
301
|
+
# end
|
302
|
+
# obj.respond_to?(:x) # => true
|
303
|
+
def singleton_class
|
304
|
+
class << self; self; end
|
305
|
+
end
|
306
|
+
alias :meta_class :singleton_class
|
307
|
+
|
308
|
+
def internal_class
|
309
|
+
# we use this instead of a "cleaner" method (such as a
|
310
|
+
# hash with class => possible flags associations) because
|
311
|
+
# (1) the number of internal types won't change
|
312
|
+
# (2) it'd be slower
|
313
|
+
case internal_type
|
314
|
+
when RubyInternal::T_OBJECT
|
315
|
+
RubyInternal::RObject
|
316
|
+
when RubyInternal::T_CLASS, RubyInternal::T_ICLASS, RubyInternal::T_MODULE
|
317
|
+
RubyInternal::RModule
|
318
|
+
when RubyInternal::T_FLOAT
|
319
|
+
RubyInternal::RFloat
|
320
|
+
when RubyInternal::T_STRING
|
321
|
+
RubyInternal::RString
|
322
|
+
when RubyInternal::T_REGEXP
|
323
|
+
RubyInternal::RRegexp
|
324
|
+
when RubyInternal::T_ARRAY
|
325
|
+
RubyInternal::RArray
|
326
|
+
when RubyInternal::T_HASH
|
327
|
+
RubyInternal::RHash
|
328
|
+
when RubyInternal::T_STRUCT
|
329
|
+
RubyInternal::RStruct
|
330
|
+
when RubyInternal::T_BIGNUM
|
331
|
+
RubyInternal::RBignum
|
332
|
+
when RubyInternal::T_FILE
|
333
|
+
RubyInternal::RFile
|
334
|
+
when RubyInternal::T_DATA
|
335
|
+
RubyInternal::RData
|
336
|
+
else
|
337
|
+
raise "No internal class for #{self}"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def internal_type
|
342
|
+
case self
|
343
|
+
when Fixnum then RubyInternal::T_FIXNUM
|
344
|
+
when NilClass then RubyInternal::T_NIL
|
345
|
+
when FalseClass then RubyInternal::T_FALSE
|
346
|
+
when TrueClass then RubyInternal::T_TRUE
|
347
|
+
when Symbol then RubyInternal::T_SYMBOL
|
348
|
+
else
|
349
|
+
RubyInternal::RBasic.new(self.internal_ptr).flags & RubyInternal::T_MASK
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def internal_ptr(*args)
|
354
|
+
raise(ArgumentError, "Can't get pointer to direct values.") \
|
355
|
+
if direct_value?
|
356
|
+
pos = self.object_id * 2
|
357
|
+
DL::CPtr.new(pos, *args)
|
358
|
+
end
|
359
|
+
|
360
|
+
def internal
|
361
|
+
raise(ArgumentError, "Can't get internal representation" +
|
362
|
+
" of direct values") \
|
363
|
+
if direct_value?
|
364
|
+
|
365
|
+
propagate_magic = nil # forward "declaration"
|
366
|
+
do_magic = lambda do |obj, id|
|
367
|
+
addr = obj.instance_eval { send(id) }
|
368
|
+
sklass = class << obj; self end
|
369
|
+
sklass.instance_eval do
|
370
|
+
define_method(id) do
|
371
|
+
case addr
|
372
|
+
when 0
|
373
|
+
return nil
|
374
|
+
else
|
375
|
+
begin
|
376
|
+
r = RubyInternal::RClass.new DL::CPtr.new(addr, 5 * DL::SIZEOF_LONG)
|
377
|
+
rescue RangeError
|
378
|
+
r = RubyInternal::RClass.new DL::CPtr.new(addr - 2**32, 5 * DL::SIZEOF_LONG)
|
379
|
+
end
|
380
|
+
propagate_magic.call r, true
|
381
|
+
end
|
382
|
+
class << r; self end.instance_eval { define_method(:to_i) { addr } }
|
383
|
+
r
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
propagate_magic = lambda do |obj, dosuper|
|
389
|
+
do_magic.call(obj, :klass)
|
390
|
+
do_magic.call(obj, :super) if dosuper
|
391
|
+
end
|
392
|
+
|
393
|
+
klass = internal_class
|
394
|
+
r = klass.new(internal_ptr)
|
395
|
+
|
396
|
+
case klass
|
397
|
+
when RubyInternal::RClass, RubyInternal::RModule
|
398
|
+
propagate_magic.call r, true
|
399
|
+
else
|
400
|
+
propagate_magic.call r, false
|
401
|
+
end
|
402
|
+
r
|
403
|
+
end
|
404
|
+
|
405
|
+
# Unfreeze a frozen Object. You will be able to make
|
406
|
+
# changes to the object again.
|
407
|
+
#
|
408
|
+
# obj = "Hello World".freeze
|
409
|
+
# obj.frozen? # => true
|
410
|
+
# obj.unfreeze
|
411
|
+
# obj.frozen? # => false
|
412
|
+
# obj.sub!("World", "You!")
|
413
|
+
# obj # => "Hello You!"
|
414
|
+
def unfreeze
|
415
|
+
if $SAFE > 0
|
416
|
+
raise(SecurityError, "Insecure operation `unfreeze' at level #{$SAFE}")
|
417
|
+
end
|
418
|
+
|
419
|
+
return self if direct_value?
|
420
|
+
|
421
|
+
self.internal.flags &= ~RubyInternal::FL_FREEZE
|
422
|
+
return self
|
423
|
+
end
|
424
|
+
|
425
|
+
# Returns true if the Object is one of the Objects which
|
426
|
+
# Ruby stores directly. Fixnums, Symbols, true, false and
|
427
|
+
# nil all are direct values.
|
428
|
+
#
|
429
|
+
# 5.direct_value? # => true
|
430
|
+
# :foo.direct_value? # => true
|
431
|
+
# "foo".direct_value? # => false
|
432
|
+
# 5.0.direct_value? # => false
|
433
|
+
def direct_value?
|
434
|
+
[Fixnum, Symbol, NilClass, TrueClass, FalseClass].any? do |klass|
|
435
|
+
klass === self
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
alias :immediate_value? :direct_value?
|
440
|
+
|
441
|
+
# Changes the class of an Object to a new one. This will
|
442
|
+
# change the methods available on the Object.
|
443
|
+
#
|
444
|
+
# foo_klass = Class.new {}
|
445
|
+
# obj = Object.new
|
446
|
+
# obj.class = foo_klass
|
447
|
+
# obj.class # => foo_klass
|
448
|
+
def class=(new_class)
|
449
|
+
raise(ArgumentError, "Can't change class of direct value.") \
|
450
|
+
if direct_value?
|
451
|
+
raise(ArgumentError, "Class has to be a Class.") \
|
452
|
+
unless new_class.is_a? Class
|
453
|
+
if self.class.to_internal_type and
|
454
|
+
new_class.to_internal_type and
|
455
|
+
self.class.to_internal_type != new_class.to_internal_type
|
456
|
+
msg = "Internal type of class isn't compatible with " +
|
457
|
+
"internal type of object."
|
458
|
+
raise(ArgumentError, msg)
|
459
|
+
end
|
460
|
+
if self.class.to_internal_type == RubyInternal::T_DATA
|
461
|
+
msg = "Internal type of class isn't compatible with " +
|
462
|
+
"internal type of object. (Both are T_DATA, but " +
|
463
|
+
"that doesn't imply that they're compatible.)"
|
464
|
+
raise(ArgumentError, msg)
|
465
|
+
end
|
466
|
+
self.internal.klass = new_class.internal_ptr.to_i
|
467
|
+
return self
|
468
|
+
end
|
469
|
+
|
470
|
+
# Shares the instance variables of two Objects with each
|
471
|
+
# other. If you make a change to such shared instance
|
472
|
+
# variables they will change at both Objects.
|
473
|
+
def share_instance_variables(from_obj)
|
474
|
+
raise(ArgumentError, "Can't share instance variables of" +
|
475
|
+
"direct values") \
|
476
|
+
if direct_value?
|
477
|
+
#FIXME: memleak (?)
|
478
|
+
self.internal.iv_tbl = from_obj.internal.iv_tbl
|
479
|
+
return instance_variables
|
480
|
+
end
|
481
|
+
|
482
|
+
# The Object will acquire a copy of +obj+'s singleton methods.
|
483
|
+
def grab_singleton_methods(obj)
|
484
|
+
original_sklass = class << obj; self end # make sure the singleton class is there
|
485
|
+
RubyInternal::critical do
|
486
|
+
original_sklass.internal.flags &= ~ RubyInternal::FL_SINGLETON
|
487
|
+
class << self; self end.module_eval{ include original_sklass.as_module }
|
488
|
+
original_sklass.internal.flags |= RubyInternal::FL_SINGLETON
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
class Class
|
494
|
+
# Changes the super class of a Class.
|
495
|
+
def superclass=(new_class)
|
496
|
+
k1 = superclass
|
497
|
+
if new_class.nil?
|
498
|
+
self.internal.super = RubyInternal.empty_iclass_ptr.to_i
|
499
|
+
else
|
500
|
+
raise(ArgumentError, "Value of class has to be a Class.") \
|
501
|
+
unless new_class.is_a?(Class)
|
502
|
+
raise(ArgumentError, "superclass= would create circular " +
|
503
|
+
"inheritance structure.") \
|
504
|
+
if new_class.ancestors.include?(self)
|
505
|
+
raise(ArgumentError, "Superclass type incompatible with own type.") \
|
506
|
+
if new_class.to_internal_type != self.to_internal_type
|
507
|
+
self.internal.super = new_class.internal_ptr.to_i
|
508
|
+
end
|
509
|
+
# invalidate the method cache
|
510
|
+
k1.instance_eval { public :__send__ rescue nil }
|
511
|
+
end
|
512
|
+
|
513
|
+
def to_internal_type
|
514
|
+
begin
|
515
|
+
self.allocate.internal.flags & RubyInternal::T_MASK
|
516
|
+
rescue
|
517
|
+
if self.superclass
|
518
|
+
self.superclass.to_internal_type
|
519
|
+
else
|
520
|
+
nil
|
521
|
+
end
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
# Will return the Class converted to a Module.
|
526
|
+
def as_module
|
527
|
+
result = nil
|
528
|
+
RubyInternal.critical do
|
529
|
+
fl_singleton = self.internal.flags & RubyInternal::FL_SINGLETON
|
530
|
+
begin
|
531
|
+
self.internal.flags &= ~ RubyInternal::FL_SINGLETON
|
532
|
+
result = self.clone
|
533
|
+
ensure
|
534
|
+
self.internal.flags |= fl_singleton
|
535
|
+
end
|
536
|
+
o = RubyInternal::RObject.new(result.internal_ptr)
|
537
|
+
o.flags &= ~ RubyInternal::T_MASK
|
538
|
+
o.flags |= RubyInternal::T_MODULE
|
539
|
+
o.klass = Module.internal_ptr.to_i
|
540
|
+
end
|
541
|
+
return result
|
542
|
+
end
|
543
|
+
|
544
|
+
# This will allow your Classes to inherit from multiple
|
545
|
+
# other Classes. If two Classes define the same method
|
546
|
+
# the last one will be used.
|
547
|
+
#
|
548
|
+
# bar_klass = Class.new { def bar; end }
|
549
|
+
# qux_klass = Class.new { def qux; end }
|
550
|
+
# foo_klass = Class.new do
|
551
|
+
# inherit bar_klass, qux_klass
|
552
|
+
# end
|
553
|
+
# foo = foo_klass.new
|
554
|
+
# foo.respond_to?(:bar) # => true
|
555
|
+
# foo.respond_to?(:qux) # => true
|
556
|
+
def inherit(*sources)
|
557
|
+
sources.each do |klass|
|
558
|
+
raise(ArgumentError, "Cyclic inherit detected.") \
|
559
|
+
if klass.ancestors.include?(self)
|
560
|
+
raise(ArgumentError, "Can only inherit from Classes.") \
|
561
|
+
unless klass.is_a?(Class)
|
562
|
+
# the following is needed cause otherwise we could end up inheriting
|
563
|
+
# e.g. a method from String that would assume the object has some
|
564
|
+
# internal structure (RString) and crash otherwise...
|
565
|
+
unless klass.to_internal_type == self.to_internal_type
|
566
|
+
raise(ArgumentError, "Inherit needs consistent internal types.")
|
567
|
+
end
|
568
|
+
include klass.as_module
|
569
|
+
extend klass.singleton_class.as_module
|
570
|
+
end
|
571
|
+
end
|
572
|
+
private :inherit
|
573
|
+
end
|
574
|
+
|
575
|
+
# Like Object, but this provides no methods at all.
|
576
|
+
# You can derivate your own Classes from this Class
|
577
|
+
# if you want them to have no preset methods.
|
578
|
+
#
|
579
|
+
# klass = Class.new(KernellessObject) { def inspect; end }
|
580
|
+
# klass.new.methods # raises NoMethodError
|
581
|
+
#
|
582
|
+
# Classes that are derived from KernellessObject
|
583
|
+
# won't call #initialize from .new by default.
|
584
|
+
#
|
585
|
+
# It is a good idea to define #inspect for subclasses,
|
586
|
+
# because Ruby will go into an endless loop when trying
|
587
|
+
# to create an exception message if it is not there.
|
588
|
+
class KernellessObject
|
589
|
+
class << self
|
590
|
+
def to_internal_type; ::Object.to_internal_type; end
|
591
|
+
|
592
|
+
def allocate
|
593
|
+
obj = ::Object.allocate
|
594
|
+
obj.class = self
|
595
|
+
return obj
|
596
|
+
end
|
597
|
+
|
598
|
+
alias :new :allocate
|
599
|
+
end
|
600
|
+
|
601
|
+
self.superclass = nil
|
602
|
+
end
|
603
|
+
|
604
|
+
class UnboundMethod
|
605
|
+
# Like UnboundMethod#bind this will bind an UnboundMethod
|
606
|
+
# to an Object. However this variant doesn't enforce class
|
607
|
+
# compatibility when it isn't needed. (It still needs
|
608
|
+
# compatible internal types however.)
|
609
|
+
#
|
610
|
+
# Currently it's also generally impossible to force_bind a
|
611
|
+
# foreign method to immediate objects.
|
612
|
+
#
|
613
|
+
# Here's an example:
|
614
|
+
#
|
615
|
+
# foo_klass = Class.new do
|
616
|
+
# def greet; "#{self.inspect} says 'Hi!'"; end
|
617
|
+
# end
|
618
|
+
# obj = []
|
619
|
+
# greet = foo_klass.instance_method(:greet)
|
620
|
+
# greet.bind(obj).call # raises TypeError
|
621
|
+
# greet.force_bind(obj).call # => "[] says 'Hi!'"
|
622
|
+
def force_bind(obj)
|
623
|
+
data = self.internal.data
|
624
|
+
source_class_addr = RubyInternal::DMethod.new(data).klass
|
625
|
+
source_class = ObjectSpace._id2ref(source_class_addr / 2)
|
626
|
+
|
627
|
+
if [Fixnum, Symbol, NilClass, TrueClass, FalseClass].any? do |klass|
|
628
|
+
klass <= source_class
|
629
|
+
end then
|
630
|
+
if not obj.is_a?(source_class) then
|
631
|
+
msg = "Immediate source class and non-immediate new " +
|
632
|
+
"receiver are incompatible"
|
633
|
+
raise(ArgumentError, msg)
|
634
|
+
else
|
635
|
+
return self.bind(obj)
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
if source_class.to_internal_type and
|
640
|
+
source_class.to_internal_type != RubyInternal::T_OBJECT and
|
641
|
+
source_class.to_internal_type != obj.class.to_internal_type
|
642
|
+
msg = "Internal type of source class and new receiver " +
|
643
|
+
"are incompatible"
|
644
|
+
raise(ArgumentError, msg)
|
645
|
+
end
|
646
|
+
|
647
|
+
result = nil
|
648
|
+
RubyInternal.critical do
|
649
|
+
prev_class = obj.internal.klass.to_i
|
650
|
+
begin
|
651
|
+
internal_obj = obj.internal
|
652
|
+
begin
|
653
|
+
internal_obj.klass = source_class_addr
|
654
|
+
result = self.bind(obj)
|
655
|
+
ensure
|
656
|
+
internal_obj.klass = prev_class
|
657
|
+
end
|
658
|
+
rescue TypeError
|
659
|
+
result = self.bind(obj)
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
return result
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
class Proc
|
668
|
+
def self
|
669
|
+
eval "self", self
|
670
|
+
end
|
671
|
+
|
672
|
+
# FIXME: look into possible signedness issues for large Fixnums (2**30 and higher)
|
673
|
+
def self=(new_self)
|
674
|
+
new_self_ptr = new_self.object_id
|
675
|
+
unless new_self.direct_value?
|
676
|
+
new_self_ptr = new_klass_ptr * 2
|
677
|
+
# new_self_ptr += 2 ** 32 if new_klass_ptr < 0 # FIXME: needed?
|
678
|
+
end
|
679
|
+
new_klass_ptr = class << new_self; self; end.object_id * 2 rescue nil.object_id
|
680
|
+
data = RubyInternal::RData.new(internal_ptr).data
|
681
|
+
block = RubyInternal::Block.new(data)
|
682
|
+
|
683
|
+
RubyInternal.critical do
|
684
|
+
block.self = new_self_ptr
|
685
|
+
block.klass = new_klass_ptr
|
686
|
+
end
|
687
|
+
|
688
|
+
return new_self
|
689
|
+
end
|
690
|
+
alias :context= :self=
|
691
|
+
end
|