ruboto 0.8.1 → 0.9.0.rc.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,462 +0,0 @@
1
- ######################################################
2
- #
3
- # generate.rb (by Scott Moyer)
4
- #
5
- # Uses the dexmaker project
6
- # (http://code.google.com/p/dexmaker/)
7
- # to generate Ruboto callbacks.
8
- #
9
- ######################################################
10
-
11
- require 'ruboto/base'
12
- require 'fileutils'
13
-
14
- ######################################################
15
- #
16
- # Expand the functionality of TypeId
17
- #
18
-
19
- java_import 'com.google.dexmaker.TypeId'
20
- class TypeId
21
- @@convert_hash = {
22
- nil => VOID,
23
- "java.lang.String" => STRING,
24
- "java.lang.Object" => OBJECT
25
- }
26
-
27
- @@corresponding_class = {
28
- INT => get("Ljava/lang/Integer;"),
29
- FLOAT => get("Ljava/lang/Float;"),
30
- DOUBLE => get("Ljava/lang/Double;"),
31
- BYTE => get("Ljava/lang/Byte;"),
32
- BOOLEAN => get("Ljava/lang/Boolean;"),
33
- CHAR => get("Ljava/lang/Char;"),
34
- SHORT => get("Ljava/lang/Short;"),
35
- LONG => get("Ljava/lang/Long;")
36
- }
37
-
38
- @@conversion_method = {}
39
-
40
- %w(int float double byte boolean char short long).each do |i|
41
- @@convert_hash[i] = const_get(i.upcase)
42
- @@conversion_method[const_get(i.upcase)] = "#{i}Value"
43
- end
44
-
45
- def self.convert_type(type)
46
- rv = @@convert_hash[type]
47
- unless rv
48
- rv = type.split("[")
49
- unless rv[-1].length == 1
50
- rv[-1] = rv[-1].gsub('.', '/')
51
- unless rv[-1] =~ /^L.*;$/
52
- rv[-1] = "L#{rv[-1]};"
53
- end
54
- end
55
- rv = get(rv.join("["))
56
- end
57
- rv
58
- end
59
-
60
- def corresponding_class
61
- @@corresponding_class[self]
62
- end
63
-
64
- def conversion_method
65
- @@conversion_method[self]
66
- end
67
-
68
- def primitive?
69
- @@corresponding_class.key? self
70
- end
71
- end
72
-
73
- ######################################################
74
- #
75
- # Helper methods for class generation
76
- #
77
-
78
- java_import 'com.google.dexmaker.Code'
79
- class Code
80
- def call_super(class_id, method_name, return_value, *parameters)
81
- method_id = class_id.getMethod(return_value ? return_value.type : TypeId::VOID,
82
- method_name, *(parameters.map{|i| i.type}))
83
- invokeSuper(method_id, return_value, getThis(class_id), *parameters)
84
- end
85
- end
86
-
87
- def create_constructor(dex_maker, super_class, class_id, *parameters)
88
- constructor_id = class_id.getConstructor(*parameters)
89
- dex_maker.declare(constructor_id, java.lang.reflect.Modifier::PUBLIC).instance_eval do
90
- parameter_array = []
91
- parameters.each_with_index{|param, i| parameter_array << getParameter(i, param)}
92
-
93
- invokeDirect(super_class.getConstructor(*parameters), nil, getThis(class_id), *parameter_array)
94
- returnVoid
95
- end
96
- end
97
-
98
- ######################################################
99
- #
100
- # Generate a new classes for an interface or class
101
- # Takes a hash
102
- # :use_cache (default true) or regenerate
103
- # :reload (default false) or use existing constant
104
- # :dex_file - where to put the generated jar
105
- # -- for single classes default to use package as path
106
- # -- for multiple classes default to classes.jar
107
- # The remaining key/value pairs represent source => new_class
108
- # -- source is either a string or the actual class/interface
109
- # -- new_class is a string "package.class"
110
- #
111
-
112
- def ruboto_generate(options)
113
- use_cache = options.key?(:use_cache) ? options.delete(:use_cache) : true
114
- reload = options.key?(:reload) ? options.delete(:reload) : false
115
- dex_file = options.delete(:dex_file)
116
-
117
- #
118
- # Already loaded? Just check the first one.
119
- #
120
-
121
- class_name = options.values[0].split('.')[-1]
122
- return Object.const_get(class_name) if Object.const_defined?(class_name) and not reload
123
-
124
- #
125
- # Set up directory
126
- #
127
-
128
- base_dir = "#{$activity.files_dir.absolute_path}/dx"
129
- if dex_file
130
- components = dex_file.split('/')
131
- components.unshift(base_dir) unless components[0] == ""
132
- dex_dir = components[0..-2].join('/')
133
- dex_file = components[-1]
134
- elsif options.size == 1
135
- dex_dir = "#{base_dir}/#{options.values[0].split('.')[0..-2].join('/')}"
136
- dex_file = "#{options.values[0].split('.')[-1]}.jar"
137
- else
138
- dex_dir = base_dir
139
- dex_file = "classes.jar"
140
- end
141
- FileUtils.mkpath dex_dir unless File.exists?(dex_dir)
142
- jar_file = java.io.File.new("#{dex_dir}/#{dex_file}")
143
- puts "Exists: #{jar_file}" if File.exists?(jar_file.to_s)
144
-
145
- #
146
- # Already generated?
147
- #
148
-
149
- if use_cache
150
- rv = ruboto_load_class(jar_file.path, *options.values)
151
- return rv if rv
152
- end
153
-
154
- if File.exists? jar_file.path
155
- File.delete jar_file.path
156
- File.delete jar_file.path.gsub(/\.jar$/, ".dex")
157
- end
158
-
159
- puts "Generating: #{jar_file.path}"
160
-
161
- dex_maker = com.google.dexmaker.DexMaker.new
162
- options.each{|k, v| ruboto_generate_class(dex_maker, k, v)}
163
-
164
- #
165
- # Generate and save
166
- #
167
-
168
- dex = dex_maker.generate
169
- jar_file.createNewFile
170
- jarOut = java.util.jar.JarOutputStream.new(java.io.FileOutputStream.new(jar_file))
171
- jarOut.putNextEntry(java.util.jar.JarEntry.new("classes.dex"))
172
- jarOut.write(dex)
173
- jarOut.closeEntry
174
- jarOut.close
175
-
176
- return ruboto_load_class(jar_file.path, *options.values)
177
- end
178
-
179
- ######################################################
180
- #
181
- # Open a dex jar and load the class(es)
182
- #
183
-
184
- def ruboto_load_class(file_name, *package_class_names)
185
- return nil unless File.exists? file_name
186
-
187
- loader = Java::dalvik.system.DexClassLoader.new(file_name, file_name.split('/')[0..-2].join('/'), nil,
188
- com.google.dexmaker.DexMaker.java_class.class_loader)
189
-
190
- runtime = org.jruby.Ruby.getGlobalRuntime
191
-
192
- rv = []
193
- package_class_names.each do |i|
194
- tmp = org.jruby.javasupport.Java.getProxyClass(runtime,
195
- org.jruby.javasupport.JavaClass.get(runtime, loader.loadClass(i)))
196
- Object.const_set(i.split('.')[-1], tmp)
197
- ruboto_import tmp
198
- rv << tmp
199
- end
200
-
201
- rv.length == 1 ? rv[0] : rv
202
- end
203
-
204
- ######################################################
205
- #
206
- # Does the hard work of generating one class
207
- #
208
-
209
- def ruboto_generate_class(dex_maker, interface_or_class_name, package_class_name)
210
- #
211
- # Basic set up
212
- #
213
-
214
- if interface_or_class_name.is_a?(String)
215
- interface_or_class = eval("Java::#{interface_or_class_name.gsub('$', '::')}")
216
- else
217
- interface_or_class = interface_or_class_name
218
- interface_or_class_name = interface_or_class.java_class.name
219
- end
220
- interface_or_class_id = TypeId.convert_type(interface_or_class_name)
221
- interface = interface_or_class.java_class.interface?
222
- class_type_id = TypeId.convert_type(package_class_name)
223
- parameters = [class_type_id, "#{package_class_name.split('.')[-1]}.generated", java.lang.reflect.Modifier::PUBLIC,
224
- interface ? TypeId::OBJECT : interface_or_class_id]
225
- parameters << TypeId.convert_type(interface_or_class_name) if interface
226
- dex_maker.declare(*parameters)
227
-
228
- #
229
- # Create callbacks field
230
- #
231
-
232
- callbackProcs_field = class_type_id.getField(TypeId.get("[Ljava/lang/Object;"), "callbackProcs")
233
- dex_maker.declare(callbackProcs_field, java.lang.reflect.Modifier::PRIVATE, nil)
234
-
235
- #
236
- # Build constructor and create callbacks array
237
- #
238
-
239
- if interface
240
- create_constructor dex_maker, TypeId::OBJECT, class_type_id
241
- else
242
- interface_or_class.java_class.constructors.each do |c|
243
- parameter_type_array = c.parameter_types.map{|p| TypeId.convert_type(p.name)}
244
- create_constructor dex_maker, interface_or_class_id, class_type_id, *parameter_type_array
245
- end
246
- end
247
-
248
- #
249
- # Build a list of methods
250
- #
251
-
252
- methods = []
253
- if interface
254
- methods = interface_or_class.java_class.declared_instance_methods
255
- else
256
- method_hash, klass = {}, interface_or_class
257
- while klass != nil
258
- klass.java_class.declared_instance_methods.each do |i|
259
- if (i.name[0..1] == "on" or (i.modifiers & java.lang.reflect.Modifier::ABSTRACT) != 0) and
260
- (i.modifiers & java.lang.reflect.Modifier::FINAL) == 0 and not method_hash[i.name]
261
- method_hash[i.name] = i
262
- methods << i
263
- end
264
- end
265
- klass = klass == java.lang.Object ? nil : klass.superclass
266
- end
267
- end
268
-
269
- #
270
- # Build setCallbackProc method
271
- #
272
-
273
- method_id = class_type_id.getMethod(TypeId::VOID, "setCallbackProc", TypeId::INT, TypeId::OBJECT)
274
- dex_maker.declare(method_id, java.lang.reflect.Modifier::PUBLIC).instance_eval do
275
- index = getParameter(0, TypeId::INT)
276
- block = getParameter(1, TypeId::OBJECT)
277
- array = newLocal(TypeId.get("[Ljava/lang/Object;"))
278
- size = newLocal(TypeId::INT)
279
- null = newLocal(TypeId::OBJECT)
280
-
281
- array_exists = com.google.dexmaker.Label.new
282
-
283
- # Does the calback proc array exist yet?
284
- iget(callbackProcs_field, array, getThis(class_type_id))
285
- loadConstant(null, nil)
286
- compare(com.google.dexmaker.Comparison::NE, array_exists, array, null)
287
-
288
- # Create the array the first time
289
- loadConstant(size, methods.length)
290
- newArray(array, size)
291
- iput(callbackProcs_field, getThis(class_type_id), array)
292
-
293
- mark(array_exists)
294
- aput(array, index, block)
295
-
296
- returnVoid
297
- end
298
-
299
- #
300
- # Loop through and build a constant and method for each method in the interface
301
- #
302
-
303
- methods.each_with_index do |m, count|
304
- # Define the constant
305
- constant_name = "CB_" + m.name.gsub(/[A-Z]/, '_\0').upcase.gsub(/^ON_/, "")
306
- const = class_type_id.getField(TypeId::INT, constant_name)
307
- dex_maker.declare(const,
308
- java.lang.reflect.Modifier::PUBLIC | java.lang.reflect.Modifier::STATIC | java.lang.reflect.Modifier::FINAL,
309
- count.to_java(:int))
310
-
311
- # Build the method
312
- parameter_type_array = m.parameter_types.map{|j| TypeId.convert_type(j.name)}
313
- method_id = class_type_id.getMethod(TypeId.convert_type(m.return_type ? m.return_type.name : nil), m.name, *parameter_type_array)
314
- dex_maker.declare(method_id, java.lang.reflect.Modifier::PUBLIC).instance_eval do
315
- parameter_array = []
316
- parameter_type_array.each_with_index{|j, k| parameter_array << getParameter(k, j)}
317
-
318
- # Callback procs array
319
- index = newLocal(TypeId::INT)
320
- array = newLocal(TypeId.get("[Ljava/lang/Object;"))
321
- block = newLocal(TypeId::OBJECT)
322
-
323
- if parameter_array.length > 1
324
- # Call arguments array
325
- p_arr = newLocal(TypeId.get("[Ljava/lang/Object;"))
326
- p_size = newLocal(TypeId::INT)
327
- p_index = newLocal(TypeId::INT)
328
- end
329
-
330
- # Holds nil for comparison
331
- null = newLocal(TypeId::OBJECT)
332
-
333
- # Holds method name
334
- call_string = newLocal(TypeId::STRING)
335
-
336
- # Locals for possible return
337
- ret = retObject = nil
338
- if m.return_type
339
- ret = newLocal(TypeId.get(m.return_type))
340
- retObject = newLocal(TypeId::OBJECT)
341
- retClass = newLocal(TypeId.convert_type("java.lang.Class"))
342
- end
343
-
344
- # Create a local to help convert primitives
345
- tmp_locals = {}
346
- parameter_type_array.each do |p|
347
- tmp_locals[p] = newLocal(p.corresponding_class) if p.primitive?
348
- end
349
- tmp_locals[ret.type] = newLocal(ret.type.corresponding_class) if ret and ret.type.primitive? and tmp_locals[ret.type] == nil
350
-
351
- no_block = com.google.dexmaker.Label.new
352
- done = com.google.dexmaker.Label.new
353
-
354
- # Does the calback proc array exist yet?
355
- iget(callbackProcs_field, array, getThis(class_type_id))
356
- loadConstant(null, nil)
357
- compare(com.google.dexmaker.Comparison::EQ, no_block, array, null)
358
-
359
- # Do we have a callback proc?
360
- loadConstant(index, count)
361
- aget(block, array, index)
362
- compare(com.google.dexmaker.Comparison::EQ, no_block, block, null)
363
-
364
- call_super(class_type_id, m.name, ret, *parameter_array) unless interface
365
-
366
- # Prepare to call Script to call the method
367
- script_class_type_id = TypeId.convert_type("org.ruboto.JRubyAdapter")
368
- loadConstant(call_string, "call")
369
- parameter_types = [ret ? TypeId::OBJECT : TypeId::VOID, "runRubyMethod", TypeId::OBJECT, TypeId::STRING]
370
- method_parameters = [retObject, block, call_string]
371
-
372
- # Set up for different arity
373
- if parameter_array.length == 1
374
- parameter_types << TypeId::OBJECT
375
- # Cast ?
376
- p = parameter_array[0]
377
- # Need to convert primitives to add to array
378
- if p.type.primitive?
379
- newInstance(tmp_locals[p.type], p.type.corresponding_class.getConstructor(p.type), p)
380
- method_parameters << tmp_locals[p.type]
381
- else
382
- method_parameters << p
383
- end
384
- elsif parameter_array.length > 1
385
- # Create and populate an array for method parameters
386
- loadConstant(p_size, parameter_type_array.length.to_java(:int))
387
- newArray(p_arr, p_size)
388
- parameter_array.each_with_index do |p, i|
389
- loadConstant(p_index, i)
390
-
391
- # Need to convert primitives to add to array
392
- if p.type.primitive?
393
- newInstance(tmp_locals[p.type], p.type.corresponding_class.getConstructor(p.type), p)
394
- aput(p_arr, p_index, tmp_locals[p.type])
395
- else
396
- aput(p_arr, p_index, p)
397
- end
398
- end
399
-
400
- parameter_types << TypeId.get("[Ljava/lang/Object;")
401
- method_parameters << p_arr
402
- end
403
-
404
- # Add return class to the call
405
- if ret
406
- parameter_types << TypeId.convert_type("java.lang.Class")
407
- loadConstant(retClass, java.lang.Boolean.java_class)
408
- method_parameters << retClass
409
- end
410
-
411
- # Make the call
412
- method_parameters = [script_class_type_id.getMethod(*parameter_types)] + method_parameters
413
- invokeStatic(*method_parameters)
414
-
415
- # Cast the return
416
- if ret and ret.type.primitive?
417
- cast(tmp_locals[ret.type], retObject)
418
- invokeVirtual(tmp_locals[ret.type].type.getMethod(ret.type, ret.type.conversion_method), ret, tmp_locals[ret.type])
419
- elsif ret
420
- # May need to just copy if type is OBJECT
421
- cast(ret, retObject)
422
- end
423
-
424
- jump(done)
425
- mark(no_block)
426
-
427
- call_super(class_type_id, m.name, ret, *parameter_array) unless interface
428
-
429
- mark(done)
430
- ret ? returnValue(ret) : returnVoid
431
- end
432
- end
433
- end
434
-
435
- ######################################################
436
- #
437
- # Clear all generated dex jars
438
- #
439
-
440
- def ruboto_clear_dex_cache
441
- FileUtils.remove_dir "#{$activity.files_dir.absolute_path}/dx"
442
- end
443
-
444
- ######################################################
445
- #
446
- # Generate classes and configure any widgets
447
- #
448
-
449
- def ruboto_generate_widget(options)
450
- ruboto_generate_widgets(options)
451
- end
452
-
453
- def ruboto_generate_widgets(options)
454
- rv = ruboto_generate(options)
455
- if rv.is_a?(Array)
456
- rv.each{|i| ruboto_import_widget i if i < android.view.View}
457
- else
458
- ruboto_import_widget rv
459
- end
460
- rv
461
- end
462
-
@@ -1,35 +0,0 @@
1
- require 'ruboto/activity'
2
- require 'ruboto/widget'
3
- require 'ruboto/generate'
4
-
5
- ruboto_import_widgets :LinearLayout, :ListView, :TextView
6
-
7
- ruboto_generate("android.widget.ArrayAdapter" => $package_name + ".MyArrayAdapter")
8
-
9
- class GenerateActivity
10
- def on_create(bundle)
11
- super
12
- setTitle File.basename(__FILE__).chomp('_activity.rb').split('_').map { |s| "#{s[0..0].upcase}#{s[1..-1]}" }.join(' ')
13
-
14
- adapter = MyArrayAdapter.new(self, android.R.layout.simple_list_item_1 , AndroidIds::text1, ['Record one', 'Record two'])
15
- adapter.initialize_ruboto_callbacks do
16
- def get_view(position, convert_view, parent)
17
- puts "IN get_view!!!"
18
- @inflater ||= context.getSystemService(Context::LAYOUT_INFLATER_SERVICE)
19
- row = convert_view ? convert_view : @inflater.inflate(mResource, nil)
20
- row.findViewById(mFieldId).text = get_item(position)
21
- row
22
- rescue Exception
23
- puts "Exception getting list item view: #$!"
24
- puts $!.backtrace.join("\n")
25
- convert_view
26
- end
27
- end
28
-
29
- self.content_view =
30
- linear_layout :orientation => LinearLayout::VERTICAL do
31
- @text_view_margins = text_view :text => 'What hath Matz wrought?', :id => 42
32
- @list_view = list_view :adapter => adapter, :id => 43
33
- end
34
- end
35
- end