cgen 0.16.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 (52) hide show
  1. data/.gitignore +5 -0
  2. data/History.txt +199 -0
  3. data/README.txt +34 -0
  4. data/examples/bench.rb +14 -0
  5. data/examples/complex.rb +63 -0
  6. data/examples/complex2.rb +48 -0
  7. data/examples/cshadow-example.rb +55 -0
  8. data/examples/cshadow-point.rb +58 -0
  9. data/examples/ctest.rb +34 -0
  10. data/examples/ctest2.rb +32 -0
  11. data/examples/ctest3.rb +179 -0
  12. data/examples/ctest4.rb +18 -0
  13. data/examples/ctest5.rb +27 -0
  14. data/examples/example-ruby-talk-30April2004.rb +65 -0
  15. data/examples/fixed-array.rb +221 -0
  16. data/examples/inherit-example.rb +26 -0
  17. data/examples/inherit-example.txt +80 -0
  18. data/examples/instance-eval.rb +66 -0
  19. data/examples/ivset.rb +55 -0
  20. data/examples/marshal-test.rb +19 -0
  21. data/examples/matrix.rb +91 -0
  22. data/examples/modular-def.rb +87 -0
  23. data/examples/objattr.rb +46 -0
  24. data/examples/opaque-struct-test.rb +36 -0
  25. data/examples/sample.rb +184 -0
  26. data/examples/struct.rb +103 -0
  27. data/examples/test.rb +24 -0
  28. data/examples/yaml.rb +56 -0
  29. data/install.rb +1015 -0
  30. data/lib/cgen/attribute.rb +414 -0
  31. data/lib/cgen/cgen.rb +2041 -0
  32. data/lib/cgen/cshadow.rb +1037 -0
  33. data/lib/cgen/inherit.rb +46 -0
  34. data/rakefile +42 -0
  35. data/tasks/ann.rake +80 -0
  36. data/tasks/bones.rake +20 -0
  37. data/tasks/gem.rake +201 -0
  38. data/tasks/git.rake +40 -0
  39. data/tasks/notes.rake +27 -0
  40. data/tasks/post_load.rake +34 -0
  41. data/tasks/rdoc.rake +51 -0
  42. data/tasks/rubyforge.rake +55 -0
  43. data/tasks/setup.rb +292 -0
  44. data/tasks/spec.rake +54 -0
  45. data/tasks/svn.rake +47 -0
  46. data/tasks/test.rake +40 -0
  47. data/tasks/zentest.rake +36 -0
  48. data/test/test-attribute.rb +430 -0
  49. data/test/test-cgen.rb +127 -0
  50. data/test/test-cshadow.rb +289 -0
  51. data/test/test.rb +17 -0
  52. metadata +123 -0
@@ -0,0 +1,1037 @@
1
+ require "cgen/cgen"
2
+ require "cgen/attribute"
3
+
4
+ # == Overview
5
+ #
6
+ # CShadow is a way of creating objects which transparently mix C data with Ruby
7
+ # data.
8
+ #
9
+ # The CShadow module is a mix-in which adds a C struct to objects which derive
10
+ # from the class which includes it. The data members of the C struct, called
11
+ # shadow attributes, may vary in subclasses of this base class. Shadow attrs can
12
+ # be added to any class inheriting from the base class, and they will propagate
13
+ # along the ordinary ruby inheritance hierarchy. (For now, shadow attributes
14
+ # cannot be defined in modules.)
15
+ #
16
+ # The CShadow module uses CGenerator's structure templates to handle the code
17
+ # generation. CGenerator is also useful for inline C definitons of methods that
18
+ # operate on shadow attributes.
19
+ #
20
+ # CShadow cooperates with the CShadow::Attribute subclasses defined in
21
+ # attribute.rb to handle the mark and free functions, type checking and
22
+ # conversion, serialization, and so on. New attribute types can be added easily.
23
+ #
24
+ # ==Usage
25
+ #
26
+ # class MyClass
27
+ # include CShadow
28
+ #
29
+ # Include CShadow in the base class(es) that need to have shadow attributes. The
30
+ # base class is assigned a CGenerator::Library, which can be accessed using
31
+ # the base class's #shadow_library method. Each subclass of the base class will
32
+ # be associated with a struct defined in this library's .h files.
33
+ #
34
+ # As usual, the #initialize method of the class will be called when the object
35
+ # is created, and the arguments are whatever was passed to #new, including the
36
+ # block, if any. You can write your own #initialize method to assign initial
37
+ # values to the shadow attrs. (Typically, shadow attrs are initialized by
38
+ # default to nil or 0. See attribute.rb.)
39
+ #
40
+ # The file name of the library is the same as the name of the class which
41
+ # includes CShadow, by default, and the library directory is generated in the
42
+ # current dir when #commit is called. To change the name, or to use a different
43
+ # library:
44
+ #
45
+ # shadow_library <Library, Class, or String>
46
+ #
47
+ # The argument can be another library (instance of CShadow::Library), a class
48
+ # which includes CShadow, in which case the library of the class is used, or a
49
+ # string, in which case a new library is created. Using this feature, several
50
+ # base classes (and their descendants) can be kept in the same library. It is
51
+ # not possible to split a tree (a base class which includes CShadow and its
52
+ # descendants) into more than one library.
53
+ #
54
+ # Shadow classes that are placed in the same library can still be put in
55
+ # separate C source files, using #shadow_library_file:
56
+ #
57
+ # shadow_library_file <CFile or String>
58
+ #
59
+ # This setting is inherited (and can be overridden) by subclasses of the current
60
+ # class. If a class calls both #shadow_library and #shadow_library_file then
61
+ # they must be called in that order. Note that anything defined before the
62
+ # #shadow_library_file statement will not be placed in the specifed file.
63
+ #
64
+ # The C include and source files for the current class can be accessed with
65
+ # #shadow_library_include_file and #shadow_library_source_file. This is not
66
+ # required for normal use.
67
+ #
68
+ # Behavior at commit time can be controlled by scheduling #before_commit and
69
+ # #after_commit procs. These procs are called immediately before and after the
70
+ # actual commit, which allows, for example, removing instances that would
71
+ # otherwise cause an exception, or creating the instance of a singleton class.
72
+ # The #before_commit and #after_commit class methods of CShadow delegate to the
73
+ # shadow_library's methods. See CGenerator for details.
74
+ #
75
+ # ===Struct members
76
+ #
77
+ # All shadow structs have a +self+ member of type +VALUE+ which points to the
78
+ # Ruby object.
79
+ #
80
+ # The subclass struct inherits members of the base class struct.
81
+ #
82
+ # There are two types of shadow attributes, declared with #shadow_attr
83
+ # and friends:
84
+ #
85
+ # (1) C data::
86
+ #
87
+ # :d => "double d", :x => "char *foo"
88
+ #
89
+ # (2) Ruby value:
90
+ #
91
+ # :str => String, :obj => Object
92
+ #
93
+ # In addition to shadow attributes, you can declare other struct members using
94
+ #
95
+ # shadow_struct.declare ...
96
+ #
97
+ # as in cgen.
98
+ #
99
+ # For example, the ruby code:
100
+ #
101
+ ####
102
+ #
103
+ # produces the following struct defs (as well as some functions):
104
+ #
105
+ # ===Type checking and conversion
106
+ #
107
+ # In case (1) (C data attribute), assignments to int and double struct members
108
+ # are done with +NUM2INT+ and +NUM2DBL+, which convert between numeric types,
109
+ # but raise exceptions for unconvertible types. The <tt>char *</tt> case uses
110
+ # StringValue (formerly +rb_str2cstr+), which behaves analogously.
111
+ #
112
+ # In case (2) (Ruby value attribute) the assigned value must always be a
113
+ # descendant of the specified type, except that +nil+ is always accepted. The
114
+ # purpose of specifying a class (should also allow type predicate!) is to allow
115
+ # C code to assume that certain struct members are present in the shadow struct,
116
+ # and so it can be safely cast to the "ancestor" struct.
117
+ #
118
+ # ===Adding methods
119
+ #
120
+ # CGenerator provides a general interface to the Ruby-C api. However, for
121
+ # simplicity, CShadow defines three methods in the client class for defining
122
+ # methods and class methods:
123
+ #
124
+ # CShadow.define_c_method CShadow.define_c_class_method
125
+ # CShadow.define_c_function
126
+ #
127
+ # ===Memory management
128
+ #
129
+ # Each type of attribute is responsible for managing any required marking of
130
+ # referenced Ruby objects or freeing of allocated bocks of memory. See Attribute
131
+ # and its subclasses for details.
132
+ #
133
+ # ===C attribute plug-ins
134
+ #
135
+ # CShadow's #shadow_attr methods check for one (no more, no less) matching
136
+ # attribute class. The details of the matching depend on the attribute class.
137
+ # See Attribute for details. Additional attribute classes can easily be added
138
+ # simply by subclassing CShadow::Attribute.
139
+ #
140
+ # ===Namespace usage
141
+ #
142
+ # TODO: prefix every attribute, method, constant name with "cshadow_"?
143
+ #
144
+ # ===Using CShadow classes with YAML
145
+ #
146
+ # CShadow classes can serialize via YAML. Both shadow attributes and ordinary
147
+ # ruby instance variables are serialized. No additional attribute code is
148
+ # needed, because of the generic load/dump mechanism used in cshadow (attributes
149
+ # are pushed into or shifted out of an array, which is passed to or from the
150
+ # serialization protocol, either Marshal or YAML). The ordering of shadow
151
+ # attributes is preserved when dumping to a YAML string.
152
+ #
153
+ # The only user requirement is that, before attempting to load shadow class
154
+ # instances from YAML string, the shadow class types must be registered with
155
+ # YAML. This is simple:
156
+ #
157
+ # CShadow.allow_yaml
158
+ #
159
+ # This method may be called before or after committing. Calling this method also
160
+ # loads the standard yaml library, if it has not already been loaded.
161
+ #
162
+ # See examples/yaml.rb
163
+ #
164
+ # ===Common problems and their solutions
165
+ #
166
+ # Do you get a NameError because accessor methods are not defined? Make sure you
167
+ # commit your class.
168
+ #
169
+ # You assign to an attribute but it doesn't change? Ruby assumes that, in an
170
+ # assignment like "x=1", the left hand side is a local variable. For all writer
171
+ # methods, whether Ruby or C attributes, you need to do "self.x=1".
172
+ #
173
+ # ==Notes
174
+ #
175
+ # * As with most modules, including CShadow more than once has no effect.
176
+ # However, CShadow cannot currently be included in another module.
177
+ #
178
+ # * In addition to the included examples, the RedShift project uses CShadow
179
+ # extensively. See http://rubyforge.org/projects/redshift.
180
+ #
181
+ # ==Limitations:
182
+ #
183
+ # * Hash args are ordered unpredictably, so if struct member order is
184
+ # significant (for example, because you want to pass the struct to C code that
185
+ # expects it that way), use a separate declare statement for each member. Also,
186
+ # take note of the self pointer at the beginning of the struct.
187
+ #
188
+ # * Creating a ruby+shadow object has a bit more time/space overhead than just a
189
+ # C object, so CShadow may not be the best mechansism for managing heap
190
+ # allocated data (for example, if you want lots of 2-element arrays). Also,
191
+ # CShadow objects are fixed in size (though their members can point to other
192
+ # structures).
193
+ #
194
+ # * CShadow attributes are, of course, not dynamic. They are fixed at the time
195
+ # of #commit. Otherwise, they behave essentially like Ruby attributes, except
196
+ # that they can be accessed only with methods or from C code; they cannot be
197
+ # accessed with the @ notation. Of course, the reader and writer for a shadow
198
+ # attribute can be flagged as protected or private. However, a private writer
199
+ # cannot be used, since by definition private methods can only be called in the
200
+ # receiverless form.
201
+ #
202
+ # * CShadow is designed for efficient in-memory structs, not packed,
203
+ # network-ordered data as for example in network protocols. See the
204
+ # bit-struct project for the latter.
205
+ #
206
+ # ==To do:
207
+ #
208
+ # * It should be easier to get a handle to entities. Below, shadow_attr has been
209
+ # hacked to return a list of pairs of functions. But it should be easier and
210
+ # more general.
211
+ #
212
+ # * Optimization: if class A<B, and their free func have the same content, use
213
+ # B's function in A, and don't generate a new function for A. Similarly for all
214
+ # the other kinds of functions.
215
+ #
216
+ # * Allow
217
+ #
218
+ # shadow_attr "int x", "double y"
219
+ #
220
+ # or even
221
+ #
222
+ # attr_accessor :a, :b, "int x", "double y"
223
+ #
224
+ # and (in cgen)
225
+ #
226
+ # declare "int x", "double y"
227
+ #
228
+ # The ruby name will be extracted from the string using the matching pattern.
229
+ #
230
+ # * Generate documentation for the shadow class.
231
+ #
232
+ # * Change name to CStruct? CStructure?
233
+ #
234
+ # * Find a way to propagate append_features so that CShadow can be included in
235
+ # modules and modules can contribute shadow_attrs.
236
+ #
237
+ # * option to omit the "self" pointer, or put it at the end of the struct
238
+ # automatically omit it in a class if no ShadowObjectAttributes point to it?
239
+ #
240
+ # * shadow_struct_constructor class method to use DATA_WRAP_STRUCT
241
+ #
242
+ # * Use CNativeAttribute as a default attribute? Or use the attr class hierarchy
243
+ # to supply defaults?
244
+ #
245
+ module CShadow
246
+ SHADOW_SUFFIX = "_Shadow"
247
+
248
+ class Library < CGenerator::Library
249
+ def initialize(*args) # :nodoc:
250
+ super
251
+
252
+ before_commit do
253
+ @classes_to_commit = []
254
+ ObjectSpace.each_object(CShadowClassMethods) do |cl|
255
+ if cl.shadow_library == self
256
+ @classes_to_commit << cl
257
+ end
258
+ end
259
+
260
+ # This is done here, rather than in #inherited, to get around
261
+ # the Ruby bug with names of nested classes. It's ugly...
262
+ ## this may be fixed in 1.7
263
+ # Better: register classes with libraries...
264
+
265
+ classes = Library.sort_class_tree(@classes_to_commit)
266
+
267
+ classes.each do |cl|
268
+ # make sure the following have been defined by now
269
+ cl.shadow_struct
270
+ cl.new_method; cl.mark_function; cl.free_function
271
+ cl._dump_data_method; cl._load_data_method; cl._alloc_method
272
+ end
273
+ end
274
+
275
+ after_commit do
276
+ for cl in @classes_to_commit
277
+ cl.protect_shadow_attrs
278
+ end
279
+ end
280
+ end
281
+
282
+ # Sort a list of classes. Sorting has the following properties:
283
+ #
284
+ # Deterministic -- you get the same output no matter what order the input
285
+ # is in, because we're using #sort_by.
286
+ #
287
+ # Compatible with original order on classes: superclass comes before
288
+ # subclass.
289
+ #
290
+ # Unrelated classes are ordered alphabetically by name
291
+ #
292
+ # Note that
293
+ #
294
+ # classes.sort {|c,d| (d <=> c) || (c.name <=> d.name)}
295
+ #
296
+ # is wrong.
297
+ #
298
+ def self.sort_class_tree(classes)
299
+ classes.sort_by {|c| c.ancestors.reverse!.map!{|d|d.to_s}}
300
+ end
301
+ end
302
+
303
+ module CShadowClassMethods
304
+ def new # :nodoc:
305
+ raise Library::CommitError,
306
+ "Cannot create shadow objects before committing library"
307
+ end
308
+
309
+ # Primarily for loading yaml data. The hash is of the form
310
+ #
311
+ # { 'attr' => value, ... }
312
+ #
313
+ # where <tt>attr</tt> is either the name of a shadow attr, or
314
+ # the name (without @) of an attribute.
315
+ #
316
+ # Warning: The hash +h+ is modified.
317
+ def new_from_hash(h)
318
+ obj = allocate
319
+
320
+ psa = shadow_attrs.select {|attr| attr.persists}
321
+ shadow_vars = psa.map{|attr|attr.var.to_s}
322
+ from_array = h.values_at(*shadow_vars)
323
+ obj._load_data(from_array)
324
+ shadow_vars.each {|v| h.delete(v) }
325
+
326
+ h.each do |ivar, value|
327
+ obj.instance_variable_set("@#{ivar}", value)
328
+ end
329
+
330
+ obj
331
+ end
332
+
333
+ # Return the base class, which is the ancestor which first included
334
+ # CShadow.
335
+ def base_class
336
+ @base_class ||= superclass.base_class
337
+ end
338
+
339
+ # If +flag+ is +true+, indicate that the class can persist
340
+ # with Marshal and YAML. This is the default. Otherwise
341
+ # if +flag+ is +false+, the entire object will be dumped as +nil+.
342
+ def persistent flag = true
343
+ unless self == base_class
344
+ raise "Persistence must be selected for the base class, " +
345
+ "#{base_class}, and it applies to the tree of subclasses."
346
+ end
347
+ @persistent = flag
348
+ end
349
+ private :persistent
350
+
351
+ def persistent?
352
+ bc = @base_class
353
+ if self == bc
354
+ @persistent
355
+ else
356
+ bc.persistent?
357
+ end
358
+ end
359
+
360
+ # Generate code and load the dynamically linked library. No further C attrs
361
+ # or methods can be defined after calling #commit.
362
+ def commit
363
+ shadow_library.commit
364
+ end
365
+
366
+ # Returns true if and only if the class haas been committed.
367
+ def committed?
368
+ shadow_library.committed?
369
+ end
370
+
371
+ # Register +block+ to be called before the #commit happens.
372
+ def before_commit(&block)
373
+ shadow_library.before_commit(&block)
374
+ end
375
+
376
+ # Register +block+ to be called after the #commit happens.
377
+ def after_commit(&block)
378
+ shadow_library.after_commit(&block)
379
+ end
380
+
381
+ # Each class which includes the CShadow module has this method to
382
+ # iterate over its shadow attributes.
383
+ #
384
+ # Note that the shadow attributes dynamically include inherited ones.
385
+ # (Dynamically in the sense that subsequent changes to superclasses are
386
+ # automatically reflected.) The order is from root to leaf of the
387
+ # inheritance chain, and within each class in order of definition. (TEST
388
+ # THIS)
389
+ def each_shadow_attr(&bl)
390
+ if superclass.respond_to? :each_shadow_attr
391
+ superclass.each_shadow_attr(&bl)
392
+ end
393
+ @shadow_attrs ||= []
394
+ @shadow_attrs.each(&bl)
395
+ end
396
+
397
+ # Returns a proxy Enumerable object referring to the same attributes as
398
+ # #each_shadow_attr. For example:
399
+ #
400
+ # sub_class.shadow_attrs.collect { |attr| attr.var }
401
+ #
402
+ # returns an array of variable names for all attributes of the class.
403
+ def shadow_attrs
404
+ proxy = Object.new.extend Enumerable
405
+ shadow_class = self
406
+ proxy.instance_eval {@target = shadow_class}
407
+ def proxy.each(&bl)
408
+ @target.each_shadow_attr(&bl)
409
+ end
410
+ proxy
411
+ end
412
+
413
+ # If +lib+ provided and this class doesn't have a library yet,
414
+ # set the library to +lib+..
415
+ #
416
+ # If +lib+ not proivided, and the class has a library, return it.
417
+ #
418
+ # If +lib+ not proivided, and the class doesn't have a library,
419
+ # construct a library with a reasonable name, and return it.
420
+ # The name is based on the full path of this class.
421
+ #
422
+ def shadow_library lib = nil
423
+ bc = base_class
424
+ if self == bc
425
+ if defined?(@shadow_library) and @shadow_library
426
+ if lib
427
+ raise RuntimeError,
428
+ "Class #{name} is already associated" +
429
+ " with library #{@shadow_library.name}."
430
+ end
431
+ else
432
+ case lib
433
+ when Library
434
+ @shadow_library = lib
435
+ when Class
436
+ begin
437
+ @shadow_library = lib.shadow_library
438
+ rescue NameError
439
+ raise ScriptError, "#{lib} does not include CShadow."
440
+ end
441
+ when String
442
+ @shadow_library = Library.new(lib)
443
+ when nil
444
+ n = name.dup
445
+ n.gsub!(/_/, '__')
446
+ n.gsub!(/::/, '_') # almost reversible
447
+ @shadow_library = Library.new(n)
448
+ else
449
+ raise ArgumentError,
450
+ "#{lib} is not a CShadow::Library, String, or Class. " +
451
+ "Its class is #{lib.class}"
452
+ end
453
+ end
454
+ @shadow_library
455
+ else
456
+ bc.shadow_library lib
457
+ end
458
+ end
459
+
460
+ # Set or return the shadow library file.
461
+ def shadow_library_file file = nil
462
+ if defined? @shadow_library_file
463
+ if file
464
+ raise RuntimeError,
465
+ "Class #{self} is already associated" +
466
+ " with file #{@shadow_library_file}."
467
+ end
468
+ @shadow_library_file
469
+ elsif file
470
+ case file
471
+ when CGenerator::CFile
472
+ file_name = file.name
473
+ when String
474
+ file_name = file
475
+ else
476
+ raise ArgumentError, "#{file} is not a String or CFile."
477
+ end
478
+ file_name = file_name.sub(/\.[ch]$/, "")
479
+ @shadow_library_file = shadow_library.add_file file_name
480
+ else
481
+ if superclass.respond_to? :shadow_library_file
482
+ superclass.shadow_library_file
483
+ else
484
+ [shadow_library.include_file, shadow_library.source_file]
485
+ end
486
+ end
487
+ end
488
+
489
+ # Return the main C include file for the library.
490
+ def shadow_library_include_file
491
+ shadow_library_file[0]
492
+ end
493
+
494
+ # Return the main C source file for the library.
495
+ def shadow_library_source_file
496
+ shadow_library_file[1]
497
+ end
498
+
499
+ #------------------------
500
+ # :section: Defining methods
501
+ # CGenerator provides a general interface to the Ruby-C api. However, for
502
+ # simplicity, CShadow defines three methods in the client class for defining
503
+ # methods and class methods:
504
+ #------------------------
505
+
506
+ # The block is evaluated in a context that allows commands for listing
507
+ # arguments, declarations, C body code, etc. See CGenerator for details.
508
+ # See examples in examples/matrix.rb and examples/complex.rb. The +subclass+
509
+ # argument is optional and allows the template to belong to a subclass of
510
+ # the function template it would normally belong to.
511
+ #
512
+ # In the case of #define_c_method, a pointer to the object's shadow struct
513
+ # is available in the C variable +shadow+.
514
+ #
515
+ def define_c_method name, subclass = CGenerator::Method, &block
516
+ sf = shadow_library_source_file
517
+ m = sf.define_c_method self, name, subclass
518
+ m.scope :extern
519
+ m.declare :shadow => "#{shadow_struct.name} *shadow"
520
+ m.setup :shadow =>
521
+ "Data_Get_Struct(self, #{shadow_struct.name}, shadow)"
522
+ m.instance_eval(&block) if block
523
+ m
524
+ end
525
+
526
+ # Define a class method for this class.
527
+ def define_c_class_method name,
528
+ subclass = CGenerator::SingletonMethod, &block
529
+ sf = shadow_library_source_file
530
+ m = sf.define_c_singleton_method self, name, subclass
531
+ m.scope :extern
532
+ m.instance_eval(&block) if block
533
+ m
534
+ end
535
+
536
+ # Define a global function in the library of this class.
537
+ def define_c_function name, subclass = CGenerator::Function, &block
538
+ sf = shadow_library_source_file
539
+ m = sf.define_c_function name, subclass
540
+ m.scope :extern
541
+ m.instance_eval(&block) if block
542
+ m
543
+ end
544
+
545
+ #== Internal methods ==#
546
+
547
+ # Construct the name used for the shadow struct.
548
+ def shadow_struct_name
549
+ name.gsub(/_/, '__').gsub(/::/, '_o_') + CShadow::SHADOW_SUFFIX
550
+ # To preserve differences.
551
+ end
552
+
553
+ # Return the object for managing the shadow struct.
554
+ def shadow_struct
555
+ unless defined?(@shadow_struct) and @shadow_struct
556
+ sf = shadow_library_source_file
557
+ ssn = shadow_struct_name
558
+ @shadow_struct = sf.declare_extern_struct(ssn)
559
+ if self == base_class
560
+ @shadow_struct.declare :self => "VALUE self"
561
+ else
562
+ sss = superclass.shadow_struct
563
+ shadow_struct.inherit\
564
+ sss.inherit!,
565
+ "/* #{superclass.shadow_struct.name} members */",
566
+ sss.declare!, " "
567
+
568
+ unless superclass.shadow_library_source_file ==
569
+ shadow_library_source_file
570
+ shadow_library_include_file.include(
571
+ superclass.shadow_library_include_file)
572
+ end
573
+ end
574
+ end
575
+ @shadow_struct
576
+ end
577
+
578
+ # Return the object for managing the +new+ method of the class.
579
+ def new_method
580
+ unless defined?(@new_method) and @new_method
581
+ sf = shadow_library_source_file
582
+ ssn = shadow_struct.name
583
+ @new_method = sf.define_c_singleton_method self,
584
+ :new, AttrClassMethod
585
+ @new_method.instance_eval {
586
+ scope :extern
587
+ c_array_args
588
+ declare :object => "VALUE object"
589
+ declare :shadow => "#{ssn} *shadow"
590
+ setup :shadow_struct => %{
591
+ object = Data_Make_Struct(self,
592
+ #{ssn},
593
+ mark_#{ssn},
594
+ free_#{ssn},
595
+ shadow);
596
+ shadow->self = object;
597
+ }.tabto(0)
598
+ body attr_code!
599
+ body %{
600
+ rb_obj_call_init(object, argc, argv);
601
+ }
602
+ returns "object"
603
+ }
604
+ if superclass.respond_to? :shadow_struct
605
+ @new_method.attr_code superclass.new_method.attr_code!
606
+ end
607
+ end
608
+ @new_method
609
+ end
610
+
611
+ # Return the object for managing the mark function of the class.
612
+ def mark_function
613
+ unless defined?(@mark_function) and @mark_function
614
+ sf = shadow_library_source_file
615
+ ssn = shadow_struct.name
616
+ @mark_function = sf.define_c_function("mark_#{ssn}", MarkFunction)
617
+ @mark_function.instance_eval {
618
+ scope :static
619
+ arguments "#{ssn} *shadow"
620
+ return_type "void"
621
+ }
622
+ if superclass.respond_to? :shadow_struct
623
+ @mark_function.mark superclass.mark_function.mark!
624
+ end
625
+ end
626
+ @mark_function
627
+ end
628
+
629
+ # Return the object for managing the free function of the class.
630
+ def free_function
631
+ unless defined?(@free_function) and @free_function
632
+ sf = shadow_library_source_file
633
+ ssn = shadow_struct.name
634
+ @free_function = sf.define_c_function("free_#{ssn}", FreeFunction)
635
+ @free_function.instance_eval {
636
+ scope :static
637
+ arguments "#{ssn} *shadow"
638
+ return_type "void"
639
+ }
640
+ if superclass.respond_to? :shadow_struct
641
+ @free_function.free superclass.free_function.free!
642
+ end
643
+ end
644
+ @free_function
645
+ end
646
+
647
+ # Return the object for managing the +_dump_data+ method of the class.
648
+ # See ruby's marshal.c.
649
+ def _dump_data_method
650
+ return nil unless persistent?
651
+ unless defined?(@_dump_data_method) and @_dump_data_method
652
+ @_dump_data_method = define_c_method(:_dump_data, AttrMethod) {
653
+ declare :result => "VALUE result"
654
+ setup :result => "result = rb_ary_new()"
655
+ body pre_code!, attr_code!, post_code!
656
+ returns "result"
657
+ }
658
+ if superclass.respond_to? :shadow_struct
659
+ @_dump_data_method.attr_code superclass._dump_data_method.body!
660
+ end
661
+ end
662
+ @_dump_data_method
663
+ end
664
+
665
+ # Return the object for managing the +_load_data+ method of the class.
666
+ # See ruby's marshal.c.
667
+ def _load_data_method
668
+ return nil unless persistent?
669
+ unless defined?(@_load_data_method) and @_load_data_method
670
+ @_load_data_method = define_c_method(:_load_data, AttrMethod) {
671
+ arguments :from_array
672
+ declare :tmp => "VALUE tmp"
673
+ body pre_code!, attr_code!, post_code!
674
+ returns "from_array" ## needed?
675
+ }
676
+ if superclass.respond_to? :shadow_struct
677
+ @_load_data_method.attr_code superclass._load_data_method.body!
678
+ end
679
+ end
680
+ @_load_data_method
681
+ end
682
+
683
+ # Return the object for managing the +alloc+ method of the class.
684
+ def _alloc_method
685
+ ## same as new_method, but no initialize -- factor this
686
+ ## can we use define_c_class_method?
687
+ return nil unless persistent?
688
+ unless defined?(@_alloc_method) and @_alloc_method
689
+ sf = shadow_library_source_file
690
+ ssn = shadow_struct.name
691
+ @_alloc_method = sf.define_alloc_func(self)
692
+ @_alloc_method.instance_eval {
693
+ scope :extern
694
+ arguments 'VALUE klass'
695
+ return_type 'VALUE'
696
+ klass_c_name = "klass"
697
+ declare :object => "VALUE object"
698
+ declare :shadow => "#{ssn} *shadow"
699
+ body %{
700
+ object = Data_Make_Struct(#{klass_c_name},
701
+ #{ssn},
702
+ mark_#{ssn},
703
+ free_#{ssn},
704
+ shadow);
705
+ shadow->self = object;
706
+ }
707
+ returns "object"
708
+ }
709
+ end
710
+ @_alloc_method
711
+ end
712
+
713
+ private
714
+
715
+ # :stopdoc:
716
+ def check_overwrite_shadow_attrs(*symbols)
717
+ for attr in shadow_attrs
718
+ for sym in symbols
719
+ if sym == attr.var
720
+ raise NameError, "#{sym} is a shadow attr."
721
+ end
722
+ end
723
+ end
724
+ end
725
+
726
+ def attr_accessor(*args)
727
+ check_overwrite_shadow_attrs(*args)
728
+ super
729
+ end
730
+
731
+ def attr_reader(*args)
732
+ check_overwrite_shadow_attrs(*args)
733
+ super
734
+ end
735
+
736
+ def attr_writer(*args)
737
+ check_overwrite_shadow_attrs(*args)
738
+ super
739
+ end
740
+ # :startdoc:
741
+
742
+ # Same as #shadow_attr with the +:reader+ and +:writer+ options.
743
+ def shadow_attr_accessor(*args)
744
+ shadow_attr :reader, :writer, *args
745
+ end
746
+
747
+ # Same as #shadow_attr with the +:reader+ option.
748
+ def shadow_attr_reader(*args)
749
+ shadow_attr :reader, *args
750
+ end
751
+
752
+ # Same as #shadow_attr with the +:writer+ option.
753
+ def shadow_attr_writer(*args)
754
+ shadow_attr :writer, *args
755
+ end
756
+
757
+ # call-seq:
758
+ # shadow_attr [options,] :var => decl, ...
759
+ #
760
+ # Adds the specified declarations to the shadow struct of the current class
761
+ # without defining any accessors. The data can be accessed only in C code.
762
+ #
763
+ # Each <tt>:var => decl</tt> pair generates one C struct member (<tt>:x =>
764
+ # "int x,y"</tt> will generate an exception).
765
+ #
766
+ # The same symbol :var cannot be used in both a +shadow_attr_*+ definition
767
+ # and an ordinary +attr_*+ definition. (You can always get around this by
768
+ # manually defining accessor methods.)
769
+ #
770
+ # The available options are:
771
+ #
772
+ # +:persistent+, +:nonpersistent+::
773
+ # Serialize or do not serialize this attribute when dumping with Marshal,
774
+ # YAML, etc. When loading, it is initialized using the attribute's #init
775
+ # code (which, typically, sets it to zero or +nil+). Default is
776
+ # +:persistent+.
777
+ #
778
+ # +:reader+, +:writer+::
779
+ # Creates a reader or writer method, just like #shadow_attr_reader and
780
+ # #shadow_attr_writer. (This is mostly for internal use.)
781
+ #
782
+ # Returns list of handles to the attr funcs: [ [r0,w0], [r1,w1], ... ]
783
+ #
784
+ # Typically, #shadow_attr_accessor and so on are called instead.
785
+ #
786
+ def shadow_attr(*args)
787
+ attr_persists = true
788
+ for arg in args
789
+ case arg
790
+ when Hash; var_map = arg.sort_by {|var, decl| var.to_s}
791
+ when :reader; reader = true
792
+ when :writer; writer = true
793
+ when :nonpersistent; attr_persists = false
794
+ when :persistent; attr_persists = true
795
+ else
796
+ raise SyntaxError,
797
+ "Unrecognized shadow_attr argument: #{arg.inspect}"
798
+ end
799
+ end
800
+
801
+ source_file = shadow_library_source_file
802
+ ssn = shadow_struct.name
803
+ @shadow_attrs ||= []
804
+
805
+ var_map.map do |var, decl|
806
+ var = var.intern if var.is_a? String
807
+
808
+ if instance_methods(false).include?(var.to_s) or
809
+ instance_methods(false).include?(var.to_s + '=')
810
+ raise NameError, "#{var} already has a Ruby attribute."
811
+ end
812
+
813
+ matches = AttributeTypes.collect { |t|
814
+ [t, t.match(decl)]
815
+ }.select {|t, match| match}
816
+
817
+ if matches.size > 1
818
+ raise StandardError, %{
819
+ No unique AttributeType for '#{decl}':
820
+ each of #{matches.map{|t|t[0]}.join ", "} match.
821
+ }.tabto(0)
822
+ end
823
+
824
+ if matches.size < 1
825
+ raise StandardError, "No Attribute type matches '#{decl}'."
826
+ end
827
+
828
+ attr_type, match = matches[0]
829
+ attr = attr_type.new self, var, match, attr_persists
830
+ each_shadow_attr { |a|
831
+ if a.var == attr.var or a.cvar == attr.cvar
832
+ unless a.var == attr.var and a.cdecl == attr.cdecl
833
+ raise NameError, "Attribute #{a.inspect} already exists."
834
+ end
835
+ end
836
+ }
837
+ @shadow_attrs << attr
838
+
839
+ shadow_struct.declare attr.cvar => attr.cdecl
840
+
841
+ new_method.attr_code attr.init if attr.init
842
+
843
+ mark_function.mark attr.mark
844
+ free_function.free attr.free
845
+
846
+ if persistent?
847
+ if attr_persists
848
+ _dump_data_method.attr_code attr.dump
849
+ _load_data_method.attr_code attr.load
850
+ else
851
+ _load_data_method.attr_code attr.init if attr.init
852
+ end
853
+ end
854
+
855
+ if reader
856
+ unless attr.reader
857
+ raise ScriptError, "Can't have a reader method for #{attr}."
858
+ end
859
+ r_meth = source_file.define_c_method(self, var)
860
+ r_meth.instance_eval {
861
+ scope :extern
862
+ declare :shadow => "#{ssn} *shadow"
863
+ declare :result => "VALUE result"
864
+ body "Data_Get_Struct(self, #{ssn}, shadow)", attr.reader
865
+ returns "result"
866
+ }
867
+ end
868
+
869
+ if writer
870
+ unless attr.writer
871
+ raise ScriptError, "Can't have a writer method for #{attr}."
872
+ end
873
+ w_meth = source_file.define_c_method(self, "#{var}=")
874
+ w_meth.instance_eval {
875
+ scope :extern
876
+ c_array_args {
877
+ required :arg
878
+ typecheck :arg => attr.check
879
+ }
880
+ declare :shadow => "#{ssn} *shadow"
881
+ body "Data_Get_Struct(self, #{ssn}, shadow)", attr.writer
882
+ returns "arg"
883
+ }
884
+ end
885
+
886
+ [r_meth, w_meth]
887
+ end
888
+ end
889
+ end
890
+
891
+ def self.append_features base_class # :nodoc:
892
+ unless base_class.is_a? Class
893
+ raise TypeError, "CShadow can be included only in a Class"
894
+ end
895
+
896
+ unless base_class.ancestors.include? self
897
+
898
+ base_class.class_eval {@base_class = self; @persistent = true}
899
+ base_class.extend CShadowClassMethods
900
+
901
+ class << base_class
902
+ ## why can't these be in CShadowClassMethods?
903
+
904
+ alias really_protected protected
905
+ def protected(*args)
906
+ (@to_be_protected ||= []).concat args
907
+ end
908
+
909
+ alias really_private private
910
+ def private(*args)
911
+ (@to_be_private ||= []).concat args
912
+ end
913
+
914
+ def protect_shadow_attrs
915
+ if defined?(@to_be_protected) and @to_be_protected
916
+ really_protected(*@to_be_protected)
917
+ end
918
+ if defined?(@to_be_private) and @to_be_private
919
+ really_private(*@to_be_private)
920
+ end
921
+ ## should undo the aliasing
922
+ end
923
+ public :protect_shadow_attrs
924
+
925
+ end
926
+
927
+ end
928
+
929
+ super
930
+ end
931
+
932
+ class AttrCodeAccumulator < CGenerator::CFragment::StatementAccumulator
933
+ include CGenerator::SetAccumulator
934
+ end
935
+
936
+ class AttrMethod < CGenerator::Method
937
+ accumulator(:attr_code) {AttrCodeAccumulator}
938
+ accumulator(:pre_code) {CGenerator::CFragment::StatementAccumulator}
939
+ accumulator(:post_code) {CGenerator::CFragment::StatementAccumulator}
940
+ end
941
+
942
+ class AttrClassMethod < CGenerator::SingletonMethod
943
+ accumulator(:attr_code) {AttrCodeAccumulator}
944
+ end
945
+
946
+ class MarkFunction < CGenerator::Function
947
+ accumulator(:mark) {AttrCodeAccumulator}
948
+
949
+ def initialize(*args)
950
+ super
951
+ body "rb_gc_mark(shadow->self)", mark!
952
+ end
953
+ end
954
+
955
+ class FreeFunction < CGenerator::Function
956
+ accumulator(:free) {AttrCodeAccumulator}
957
+
958
+ def initialize(*args)
959
+ super
960
+ body free!, "free(shadow)" # free the struct last!
961
+ end
962
+ end
963
+
964
+ def inspect # :nodoc:
965
+ attrs = []
966
+ seen = {self => true}
967
+ each_attr_value do |attr, value|
968
+ if seen[value]
969
+ attrs << "#{attr}=#{value}"
970
+ else
971
+ attrs << "#{attr}=#{value.inspect}"
972
+ end
973
+ seen[value] = true
974
+ end
975
+ super.sub(/(?=>\z)/, " " + attrs.join(", "))
976
+ end
977
+
978
+ # Iterate over each shadow attr and instance var of +self+, yielding the attr
979
+ # name and value in this instance to the block. Differs in three ways from
980
+ # CShadow.each_shadow_attr: it is an instance method of shadow objects, it
981
+ # iterates over both shadow attrs and instance vars, and it yields both the
982
+ # name and the value. (In the case of instance vars, the name does _not_
983
+ # include the "@".)
984
+ def each_attr_value # :yields: attr_name, attr_value
985
+ values = _dump_data
986
+ self.class.shadow_attrs.each_with_index do |attr, i|
987
+ yield attr.var.to_s, values[i]
988
+ end
989
+ instance_variables.each do |ivar|
990
+ yield ivar[1..-1], instance_variable_get(ivar)
991
+ end
992
+ end
993
+
994
+ # Like #each_attr_value, but limited to attr declared as +persistent+.
995
+ def each_persistent_attr_value # :yields: attr_name, attr_value
996
+ values = _dump_data
997
+ psa = self.class.shadow_attrs.select {|attr| attr.persists}
998
+ psa.each_with_index do |attr, i|
999
+ yield attr.var.to_s, values[i]
1000
+ end
1001
+ instance_variables.each do |ivar|
1002
+ yield ivar[1..-1], instance_variable_get(ivar)
1003
+ end
1004
+ end
1005
+
1006
+ # Define YAML methods for CShadow classes. Can be called before
1007
+ # or after #commit. Loads the yaml library.
1008
+ def self.allow_yaml(your_tag = "path.berkeley.edu,2006")
1009
+ return if defined?(@cshadow_allow_yaml) and @cshadow_allow_yaml
1010
+ @cshadow_allow_yaml = true
1011
+
1012
+ require 'yaml'
1013
+
1014
+ yaml_as "tag:#{your_tag}:cshadow"
1015
+
1016
+ def self.yaml_new( klass, tag, val )
1017
+ subtype, subclass = YAML.read_type_class(tag, Object)
1018
+ subclass.new_from_hash(val)
1019
+ end
1020
+
1021
+ module_eval do
1022
+ def add_yaml_map_contents(map)
1023
+ each_persistent_attr_value do |attr, value|
1024
+ map.add(attr, value)
1025
+ end
1026
+ end
1027
+
1028
+ def to_yaml( opts = {} )
1029
+ YAML.quick_emit(object_id, opts) do |out|
1030
+ out.map( taguri, to_yaml_style ) do |map|
1031
+ add_yaml_map_contents(map)
1032
+ end
1033
+ end
1034
+ end
1035
+ end
1036
+ end
1037
+ end