costan-tem_ruby 0.10.2

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 (76) hide show
  1. data/CHANGELOG +45 -0
  2. data/LICENSE +21 -0
  3. data/Manifest +75 -0
  4. data/README +8 -0
  5. data/Rakefile +23 -0
  6. data/bin/tem_bench +9 -0
  7. data/bin/tem_ca +13 -0
  8. data/bin/tem_irb +11 -0
  9. data/bin/tem_proxy +65 -0
  10. data/bin/tem_stat +35 -0
  11. data/dev_ca/ca_cert.cer +0 -0
  12. data/dev_ca/ca_cert.pem +32 -0
  13. data/dev_ca/ca_key.pem +27 -0
  14. data/dev_ca/config.yml +14 -0
  15. data/lib/tem/_cert.rb +158 -0
  16. data/lib/tem/apdus/buffers.rb +89 -0
  17. data/lib/tem/apdus/keys.rb +64 -0
  18. data/lib/tem/apdus/lifecycle.rb +13 -0
  19. data/lib/tem/apdus/tag.rb +38 -0
  20. data/lib/tem/auto_conf.rb +25 -0
  21. data/lib/tem/builders/abi.rb +482 -0
  22. data/lib/tem/builders/assembler.rb +314 -0
  23. data/lib/tem/builders/crypto.rb +124 -0
  24. data/lib/tem/builders/isa.rb +120 -0
  25. data/lib/tem/ca.rb +114 -0
  26. data/lib/tem/definitions/abi.rb +65 -0
  27. data/lib/tem/definitions/assembler.rb +23 -0
  28. data/lib/tem/definitions/isa.rb +188 -0
  29. data/lib/tem/ecert.rb +77 -0
  30. data/lib/tem/hive.rb +18 -0
  31. data/lib/tem/keys/asymmetric.rb +116 -0
  32. data/lib/tem/keys/key.rb +48 -0
  33. data/lib/tem/keys/symmetric.rb +47 -0
  34. data/lib/tem/sec_exec_error.rb +63 -0
  35. data/lib/tem/seclosures.rb +81 -0
  36. data/lib/tem/secpack.rb +107 -0
  37. data/lib/tem/tem.rb +31 -0
  38. data/lib/tem/toolkit.rb +101 -0
  39. data/lib/tem/transport/auto_configurator.rb +87 -0
  40. data/lib/tem/transport/java_card_mixin.rb +99 -0
  41. data/lib/tem/transport/jcop_remote_protocol.rb +59 -0
  42. data/lib/tem/transport/jcop_remote_server.rb +171 -0
  43. data/lib/tem/transport/jcop_remote_transport.rb +65 -0
  44. data/lib/tem/transport/pcsc_transport.rb +87 -0
  45. data/lib/tem/transport/transport.rb +10 -0
  46. data/lib/tem_ruby.rb +47 -0
  47. data/tem_ruby.gemspec +35 -0
  48. data/test/_test_cert.rb +70 -0
  49. data/test/builders/test_abi_builder.rb +298 -0
  50. data/test/tem_test_case.rb +26 -0
  51. data/test/tem_unit/test_tem_alu.rb +33 -0
  52. data/test/tem_unit/test_tem_bound_secpack.rb +51 -0
  53. data/test/tem_unit/test_tem_branching.rb +56 -0
  54. data/test/tem_unit/test_tem_crypto_asymmetric.rb +123 -0
  55. data/test/tem_unit/test_tem_crypto_hash.rb +35 -0
  56. data/test/tem_unit/test_tem_crypto_pstore.rb +53 -0
  57. data/test/tem_unit/test_tem_crypto_random.rb +25 -0
  58. data/test/tem_unit/test_tem_emit.rb +23 -0
  59. data/test/tem_unit/test_tem_memory.rb +48 -0
  60. data/test/tem_unit/test_tem_memory_compare.rb +65 -0
  61. data/test/tem_unit/test_tem_output.rb +32 -0
  62. data/test/tem_unit/test_tem_yaml_secpack.rb +47 -0
  63. data/test/test_driver.rb +108 -0
  64. data/test/test_exceptions.rb +35 -0
  65. data/test/transport/test_auto_configurator.rb +114 -0
  66. data/test/transport/test_java_card_mixin.rb +90 -0
  67. data/test/transport/test_jcop_remote.rb +82 -0
  68. data/timings/blank_bound_secpack.rb +18 -0
  69. data/timings/blank_sec.rb +14 -0
  70. data/timings/devchip_decrypt.rb +9 -0
  71. data/timings/post_buffer.rb +10 -0
  72. data/timings/simple_apdu.rb +5 -0
  73. data/timings/timings.rb +64 -0
  74. data/timings/vm_perf.rb +140 -0
  75. data/timings/vm_perf_bound.rb +141 -0
  76. metadata +201 -0
@@ -0,0 +1,314 @@
1
+ # :nodoc: namespace
2
+ module Tem::Builders
3
+
4
+ # Builder class for the code assembler builder.
5
+ class Assembler
6
+ # Creates a builder targeting a module / class.
7
+ #
8
+ # The given parameter should be a class or module.
9
+ def self.define_assembler(class_or_module) # :yields: abi
10
+ yield new(class_or_module)
11
+ end
12
+
13
+ # Defines the ISA targeted by the assembler.
14
+ #
15
+ # This method should be called early in the assembler definition. It creates
16
+ # the proxy and builder classes for the assembling process.
17
+ def target_isa(isa_module)
18
+ @isa = isa_module
19
+ target.const_set :Isa, @isa
20
+ @abi = @isa.const_get :Abi
21
+ target.const_set :Abi, @abi
22
+
23
+ define_proxy_class
24
+ define_builder_class
25
+ augment_target
26
+ end
27
+
28
+ # Defines the methods for implementing a stack directive.
29
+ #
30
+ # The following options are supported:
31
+ # label:: the label serving as the stack marker (required)
32
+ # slot_type:: the ABI type representing a stack slot
33
+ #
34
+ # The following method is defined in the proxy for a directive named 'name':
35
+ # * name(slots = 0) -> places a stack marker and allocates stack slots
36
+ def stack_directive(name, options)
37
+ unless @proxy_class
38
+ raise "target_isa must be called before other builder methods"
39
+ end
40
+ # Capture these in the closure.
41
+ stack_label = options[:label]
42
+ slot_length = @abi.send :"#{options[:slot_type]}_length"
43
+ proxy_defines = Proc.new do
44
+ define_method name.to_sym do |*args|
45
+ case args.length
46
+ when 0
47
+ slots = 0
48
+ when 1
49
+ slots = args.first
50
+ else
51
+ raise "#{name}: given #{args.length} arguments, wanted at most 1"
52
+ end
53
+ @assembler.emit_label stack_label
54
+ if slots > 0
55
+ @assembler.emit_bytes name, :emit => Array.new(slots * slot_length, 0)
56
+ end
57
+ end
58
+ end
59
+ @proxy_class.class_eval &proxy_defines
60
+ (class << @proxy_class; self; end).module_eval &proxy_defines
61
+ end
62
+
63
+ # Defines the methods for implementing a labeling directive.
64
+ #
65
+ # The following method is defined in the proxy for a directive named 'name':
66
+ # * name(label_name) -> creates a label named label_name at the current byte
67
+ def label_directive(name, options = {})
68
+ unless @proxy_class
69
+ raise "target_isa must be called before other builder methods"
70
+ end
71
+ proxy_defines = Proc.new do
72
+ define_method name.to_sym do |label_name|
73
+ @assembler.emit_label label_name.to_sym
74
+ end
75
+ end
76
+ @proxy_class.class_eval &proxy_defines
77
+ (class << @proxy_class; self; end).module_eval &proxy_defines
78
+ end
79
+
80
+ # Defines the methods for implementing a special label directive.
81
+ #
82
+ # The following method is defined in the proxy for a directive named 'name':
83
+ # * name -> creates a label named label_name at the current byte
84
+ def special_label_directive(name, label_name)
85
+ unless @proxy_class
86
+ raise "target_isa must be called before other builder methods"
87
+ end
88
+ proxy_defines = Proc.new do
89
+ define_method name.to_sym do
90
+ @assembler.emit_label label_name.to_sym
91
+ end
92
+ end
93
+ @proxy_class.class_eval &proxy_defines
94
+ (class << @proxy_class; self; end).module_eval &proxy_defines
95
+ end
96
+
97
+ # Defines the methods for implementing a zero-inserting directive.
98
+ #
99
+ # The following method is defined in the proxy for a directive named 'name':
100
+ # * name(abi_type, count = 1) -> creates count zeros of abi_type
101
+ def zeros_directive(name, options = {})
102
+ unless @proxy_class
103
+ raise "target_isa must be called before other builder methods"
104
+ end
105
+ # Capture this in the closure.
106
+ abi = @abi
107
+ proxy_defines = Proc.new do
108
+ define_method name.to_sym do |*args|
109
+ if args.length == 1 || args.length == 2
110
+ type_name, count = args[0], args[1] || 1
111
+ else
112
+ raise "#{name}: given #{args.length} arguments, wanted 1 or 2"
113
+ end
114
+ bytes = count * abi.send(:"#{type_name}_length")
115
+ @assembler.emit_bytes name, :emit => Array.new(bytes, 0)
116
+ end
117
+ end
118
+ @proxy_class.class_eval &proxy_defines
119
+ (class << @proxy_class; self; end).module_eval &proxy_defines
120
+ end
121
+
122
+ # Defines the methods for implementing a data-emitting directive.
123
+ #
124
+ # The following method is defined in the proxy for a directive named 'name':
125
+ # * name(abi_type, values = 1) -> emits the given values as abi_type
126
+ def data_directive(name, options = {})
127
+ unless @proxy_class
128
+ raise "target_isa must be called before other builder methods"
129
+ end
130
+ # Capture this in the closure.
131
+ abi = @abi
132
+ proxy_defines = Proc.new do
133
+ define_method name.to_sym do |abi_type, values|
134
+ values = [values] unless values.instance_of? Array
135
+ data = []
136
+ values.each { |value| data += abi.send :"to_#{abi_type}", value }
137
+ @assembler.emit_bytes :immed, :emit => data
138
+ end
139
+ end
140
+ @proxy_class.class_eval &proxy_defines
141
+ (class << @proxy_class; self; end).module_eval &proxy_defines
142
+ end
143
+
144
+ # (private) Defines the builder class used during assembly.
145
+ #
146
+ # Builders maintain intermediate results during the assembly process. In a
147
+ # nutshell, a builder collects the bytes, labels and linker directives, and
148
+ # puts them together at the end of the assembly process.
149
+ #
150
+ # Builder classes are synthesized automatically if they don't already exist.
151
+ # To have a builder class inherit from another class, define it before calling
152
+ # target_isa. Builder classes are saved as the Builder constant in the
153
+ # assembler class.
154
+ #
155
+ # Builder classes are injected the code in Assembler::CodeBuilderBase. Look
156
+ # there for override hooks into the assembly process.
157
+ def define_builder_class
158
+ if @target.const_defined? :Builder
159
+ @builder_class = @taget.const_get :Builder
160
+ else
161
+ @builder_class = Class.new
162
+ @target.const_set :Builder, @builder_class
163
+ end
164
+ @builder_class.send :include, Assembler::CodeBuilderBase
165
+ end
166
+ private :define_builder_class
167
+
168
+ # (private) Defines the proxy class used during assembly.
169
+ #
170
+ # The proxy class is yielded to the block given to the assemble call, which
171
+ # provides the code to be assembled. For clarity, the proxy class is
172
+ # synthesized so it only contains the methods that are useful for assembly.
173
+ #
174
+ # The proxy class is always automatically synthesized, and is available under
175
+ # the constant Proxy in the assembler class.
176
+ def define_proxy_class
177
+ @proxy_class = Class.new Assembler::ProxyBase
178
+ target.const_set :Proxy, @proxy_class
179
+
180
+ @proxy_class.const_set :Abi, @abi
181
+ @proxy_class.const_set :Isa, @isa
182
+
183
+ # Capture the ISA and ABI in the closure.
184
+ isa = @isa
185
+ proxy_defines = Proc.new do
186
+ isa.instance_methods.each do |method|
187
+ if method[0, 5] == 'emit_'
188
+ isa_method_msg = method.to_sym
189
+ proxy_method_name = method[5, method.length].to_sym
190
+ define_method proxy_method_name do |*args|
191
+ emit_data = isa.send isa_method_msg, *args
192
+ @assembler.emit_bytes proxy_method_name, emit_data
193
+ end
194
+ end
195
+ end
196
+ end
197
+ @proxy_class.class_eval &proxy_defines
198
+ (class << @proxy_class; self; end).module_eval &proxy_defines
199
+ end
200
+ private :define_proxy_class
201
+
202
+ # (private) Augments the target with the assemble method.
203
+ def augment_target
204
+ # Capture this data in the closure.
205
+ proxy_class = @proxy_class
206
+ builder_class = @builder_class
207
+ defines = Proc.new do
208
+ # Assembles code.
209
+ def assemble(&block)
210
+ _assemble block
211
+ end
212
+
213
+ # Internal method for assembling code.
214
+ #
215
+ # We need to use a block to define this method, so we can capture the
216
+ # outer variables (proxy_class and builder_class) in its closure. However,
217
+ # blocks can't take blocks as parameters (at least not in MRI 1.8). So
218
+ # we use a regular method definition (assemble) which wraps the block
219
+ # into a Proc received by _assemble.
220
+ define_method :_assemble do |block|
221
+ code_builder = builder_class.new
222
+ code_builder.start_assembling
223
+ proxy = proxy_class.new(code_builder)
224
+ block.call proxy
225
+ code_builder.done_assembling proxy
226
+ end
227
+ private :_assemble
228
+ end
229
+ @target.class_eval &defines
230
+ (class << @target; self; end).module_eval &defines
231
+ end
232
+
233
+ # The module / class impacted by the builder.
234
+ attr_reader :target
235
+
236
+ # Creates a builder targeting a module / class.
237
+ def initialize(target)
238
+ @target = target
239
+ @isa, @abi, @proxy = nil, nil, nil
240
+ end
241
+ private_class_method :new
242
+ end # class Assembler
243
+
244
+
245
+ # Base class for the assemblers' proxy objects.
246
+ #
247
+ # The proxy object is the object that is yielded out of an assembler's
248
+ # assemble class method.
249
+ class Assembler::ProxyBase
250
+ def initialize(assembler)
251
+ @assembler = assembler
252
+ end
253
+ end
254
+
255
+ # Module injected into the assembler's code builder class.
256
+ module Assembler::CodeBuilderBase
257
+ # Called by assemble before its associated block receives control.
258
+ #
259
+ # This method is responsible for setting up the state needed by the emit_
260
+ # methods.
261
+ def start_assembling
262
+ @bytes = []
263
+ @link_directives = []
264
+ @line_info = []
265
+ @labels = {}
266
+ end
267
+
268
+ # Emits code or data bytes, with associated link directives.
269
+ def emit_bytes(emit_atom_name, emit_data)
270
+ (emit_data[:link_directives] || []).each do |directive|
271
+ directive[:offset] += @bytes.length
272
+ @link_directives << directive
273
+ end
274
+
275
+ emit_data[:emit] ||= []
276
+ if emit_data[:emit].length > 0
277
+ @line_info << [@bytes.length, emit_atom_name, Kernel.caller(2)]
278
+ end
279
+ @bytes += emit_data[:emit] || []
280
+ end
281
+
282
+ # Emits labels, which are symbolic names for addresses.
283
+ def emit_label(label_name)
284
+ raise "label #{label_name} already defined" if @labels[label_name]
285
+ @labels[label_name] = @bytes.length
286
+ end
287
+
288
+ # Called by assemble after its associated block returns.
289
+ #
290
+ # This method is responsible for the final assembly steps (i.e. linking) and
291
+ # returning a processed result. The method's result is returned by assemble.
292
+ def done_assembling(proxy)
293
+ # Process link directives.
294
+ abi = proxy.class.const_get :Abi
295
+ @link_directives.each do |directive|
296
+ if label = directive[:label]
297
+ raise "Label #{label} undefined" unless address = @labels[label]
298
+ else
299
+ address = directive[:address]
300
+ end
301
+ if directive[:relative]
302
+ address -= directive[:offset] + directive[:relative]
303
+ end
304
+ address_bytes = abi.send :"signed_to_#{directive[:type]}", address
305
+ @bytes[directive[:offset], address_bytes.length] = *address_bytes
306
+ end
307
+
308
+ # Wrap all the built data into a nice package and return it.
309
+ { :bytes => @bytes, :link_directives => @link_direcives, :labels => @labels,
310
+ :line_info => @line_info }
311
+ end
312
+ end
313
+
314
+ end # namespace Tem::Builders
@@ -0,0 +1,124 @@
1
+ require 'openssl'
2
+
3
+
4
+ # :nodoc: namespace
5
+ module Tem::Builders
6
+
7
+ # Builder class and namespace for the cryptography builder.
8
+ class Crypto < Abi
9
+ # Creates a builder targeting a module / class.
10
+ #
11
+ # The given parameter should be a class or module
12
+ def self.define_crypto(class_or_module) # :yields: crypto
13
+ yield new(class_or_module)
14
+ end
15
+
16
+ # Defines the methods for handling an asymmetric (public/private) key.
17
+ #
18
+ # ssl_class should be a class in OpenSSL::PKey. privkey_abi_type and
19
+ # pubkey_abi_type should be ABI types similar to those produced by
20
+ # packed_variable_length_numbers.
21
+ #
22
+ # The following methods are defined for a type named 'name':
23
+ # * read_private_name(array, offset) -> key
24
+ # * to_private_name(key) -> array
25
+ # * private_name_class -> Class
26
+ # * read_public_name(array, offset) -> key
27
+ # * to_public_name(key) -> array
28
+ # * public_name_class -> Class
29
+ def asymmetric_key(name, ssl_class, privkey_abi_type, pubkey_abi_type,
30
+ hooks = {})
31
+ object_wrapper "private_#{name}", Tem::Keys::Asymmetric,
32
+ [privkey_abi_type, nil],
33
+ :read => hooks[:read_private] || hooks[:read] ||
34
+ lambda { |k| Tem::Keys::Asymmetric.new k },
35
+ :to => hooks[:to_private] || hooks[:to] ||
36
+ lambda { |k| k.ssl_key },
37
+ :new => hooks[:new_private] || hooks[:new] ||
38
+ lambda { |k| ssl_class.new }
39
+ object_wrapper "public_#{name}", Tem::Keys::Asymmetric,
40
+ [pubkey_abi_type, nil],
41
+ :read => hooks[:read_public] || hooks[:read] ||
42
+ lambda { |k| Tem::Keys::Asymmetric.new k },
43
+ :to => hooks[:to_public] || hooks[:to] ||
44
+ lambda { |k| k.ssl_key },
45
+ :new => hooks[:new_private] || hooks[:new] ||
46
+ lambda { |k| ssl_class.new }
47
+ end
48
+
49
+
50
+ # Defines the methods for a symmetric key.
51
+ #
52
+ # cipher_class should be a class in OpenSSL::Cipher. key_abi_type should be
53
+ # an ABI type similar to that produced by fixed_string.
54
+ #
55
+ # The following methods are defined for a type named 'name':
56
+ # * read_name(array, offset) -> object
57
+ # * to_name(object) -> array
58
+ # * name_class -> Class
59
+ def symmetric_key(name, cipher_class, cipher_name, key_abi_type, hooks = {})
60
+ object_wrapper name, Tem::Keys::Symmetric, [key_abi_type, :key],
61
+ :read => lambda { |k| Tem::Keys::Symmetric.new k },
62
+ :to => lambda { |k| k.ssl_key },
63
+ :new => lambda { |klass|
64
+ k = cipher_class cipher_name
65
+
66
+ unless k.respond_to? :key
67
+ # Some ciphers don't give back the key that they receive.
68
+ # We need to synthesize that.
69
+ class << k
70
+ def key=(new_key)
71
+ super
72
+ @_key = new_key
73
+ end
74
+ def key
75
+ @_key
76
+ end
77
+ end
78
+ end
79
+ }
80
+ end
81
+
82
+ # Defines the methods for a cryptographic hash function.
83
+ #
84
+ # digest_class should be an object similar to the classes in the Digest
85
+ # name-space. Specifically, it should implement the digest method.
86
+ #
87
+ # The following methods are defined for a type named 'name':
88
+ # * name(array | String) -> array
89
+ # * name_length -> number
90
+ # * name_digest_class -> Class
91
+ def crypto_hash(name, digest_class)
92
+ digest_length = digest_class.digest('').length
93
+
94
+ defines = Proc.new do
95
+ define_method :"#{name}" do |data|
96
+ data = data.pack 'C*' unless data.kind_of? String
97
+ digest_class.digest(data).unpack 'C*'
98
+ end
99
+ define_method(:"#{name}_digest_class") { digest_class }
100
+ define_method(:"#{name}_length") { digest_length }
101
+ end
102
+
103
+ @target.class_eval &defines
104
+ (class << @target; self; end).module_eval &defines
105
+ end
106
+ end # class Crypto
107
+
108
+
109
+ # Implementation code for the Crypto methods.
110
+ module Crypto::Impl
111
+ def self.key_from_array(array, offset, ssl_class, abi_type)
112
+ key = ssl_class.new
113
+ numbers = self.send :"read_#{abi_type}", array, offset
114
+ numbers.each { |k, v| key.send :"#{k}=", v }
115
+ end
116
+
117
+ def self.key_to_array(key, abi_type)
118
+ components = self.send :"#{abi_type}_components"
119
+ numbers = Hash[*(components.map { |c| [c, key.send(c.to_sym) ]}.flatten)]
120
+ self.send :"to_#{abi_type}", numbers
121
+ end
122
+ end
123
+
124
+ end # namespace Tem::Builders
@@ -0,0 +1,120 @@
1
+ require 'openssl'
2
+
3
+
4
+ # :nodoc: namespace
5
+ module Tem::Builders
6
+
7
+ # Builder class for the ISA (Instruction Set Architecture) builder.
8
+ class Isa
9
+ # Creates a builder targeting a module / class.
10
+ #
11
+ # class_or_module will receive the ISA method definitions. abi should be a
12
+ # class or module containing the ABI definitions that the ISA definitions
13
+ # refer to.
14
+ #
15
+ # The following options are supported:
16
+ # opcode_type:: the ABI type encoding the instructions' opcodes (required)
17
+ def self.define_isa(class_or_module, abi, options) # :yields: isa
18
+ yield new(class_or_module, abi, options[:opcode_type])
19
+ end
20
+
21
+ # Defines the methods for handling an instruction in the IA.
22
+ #
23
+ # The instruction's arguments are provided as an array of hashes. Each hash
24
+ # describes one argument, and the ordering in the array reflects the encoding
25
+ # order. The following hash keys are supported:
26
+ # name:: if defined, the argument can be provided as a named argument;
27
+ # named arguments must follow positional arguments
28
+ # type:: the ABI type encoding the argument (required)
29
+ # reladdr:: if defined, the encoded argument value is relative to the
30
+ # address of the instruction containing the argument;
31
+ #
32
+ # The result of encoding an instruction is a Hash with the following keys:
33
+ # emit:: the bytes to be emitted into the code stream
34
+ # link_directives:: an array of directives for the code linker
35
+ #
36
+ # Each linker directive refers to an address cell (location in the code
37
+ # representing an address that the linker must adjust. The following keys can
38
+ # be present:
39
+ # type:: the ABI type for the address cell (required)
40
+ # offset:: the address cell's offset in the emitted bytes (required)
41
+ # address:: the absolute address to point to (mutually exclusive with label)
42
+ # label:: the name of a label that the address must point to
43
+ # relative:: if false, the address cell holds an absolute address;
44
+ # otherwise, the cell's value is computed as follows:
45
+ # target address - cell address + value of relative;
46
+ # (optional, default value is false)
47
+ # The following methods are defined for a type named 'name':
48
+ # * encode_name(*arguments) -> Hash
49
+ def instruction(opcode, name, *iargs)
50
+ encoded_opcode = @abi.send :"to_#{@opcode_type}", opcode
51
+ abi = @abi # Capture the ABI in the method closures.
52
+ named_indexes = {}
53
+ iargs.map { |iarg| iarg[:name] }.
54
+ each_with_index { |argname, i| named_indexes[argname] = i if argname }
55
+ arg_encode_msgs = iargs.map { |iarg| :"to_#{iarg[:type]}" }
56
+ defines = Proc.new do
57
+ define_method :"emit_#{name}" do |*args|
58
+ # Flatten arguments by resolving named parameters to indexes.
59
+ arg_index = 0
60
+ fargs = []
61
+ args.each_with_index do |arg, i|
62
+ fargs[i] = arg and next unless arg.kind_of? Hash
63
+
64
+ if i != args.length - 1
65
+ raise "Named arguments must follow inline arguments! (arg #{i})"
66
+ end
67
+ arg.each do |k, v|
68
+ raise "#{name} has no #{k} argument" unless i = named_indexes[k]
69
+ raise "Argument #{k} was already assigned a value" if fargs[i]
70
+ fargs[i] = v
71
+ end
72
+ end
73
+
74
+ arg_count = fargs.inject(0) { |acc, v| v.nil? ? acc : acc + 1 }
75
+ if arg_count != iargs.length
76
+ raise "#{name} requires #{fargs.length} args, given #{arg_count}"
77
+ end
78
+
79
+ # Encode parameters.
80
+ # @lines[@body.length] = Kernel.caller(0)
81
+
82
+ emit = encoded_opcode
83
+ link_directives = []
84
+ fargs.each_with_index do |arg, i|
85
+ if (arg.kind_of? Numeric) && !arg[:reladdr]
86
+ emit += abi.send arg_encode_msgs[i], arg
87
+ else
88
+ link_directive = { :type => iargs[i][:type], :offset => emit.length,
89
+ :relative => iargs[i][:reladdr] || false }
90
+ if arg.kind_of? Numeric
91
+ link_directive[:address] = arg.to_i
92
+ else
93
+ link_directive[:label] = arg.to_sym
94
+ end
95
+ link_directives << link_directive
96
+ emit += abi.send arg_encode_msgs[i], 0
97
+ end
98
+ end
99
+ { :emit => emit, :link_directives => link_directives }
100
+ end
101
+ end
102
+
103
+ @target.class_eval &defines
104
+ (class << @target; self; end).module_eval &defines
105
+ end
106
+
107
+ # The module / class impacted by the builder.
108
+ attr_reader :target
109
+
110
+ # Creates a builder targeting a module / class.
111
+ def initialize(target, abi, opcode_type)
112
+ @target = target
113
+ @target.const_set :Abi, abi
114
+ @abi = abi
115
+ @opcode_type = opcode_type
116
+ end
117
+ private_class_method :new
118
+ end # class Isa
119
+
120
+ end # namespace Tem::Builders