cgen 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
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