costan-tem_ruby 0.10.2

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