divine 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +30 -0
- data/README.md +57 -0
- data/Rakefile +45 -18
- data/lib/divine.rb +3 -3
- data/lib/divine/code_generators/code_generator.rb +112 -14
- data/lib/divine/code_generators/java.rb +144 -80
- data/lib/divine/code_generators/javascript.rb +192 -104
- data/lib/divine/code_generators/ruby.rb +133 -60
- data/lib/divine/dsl.rb +70 -12
- data/lib/divine/version.rb +1 -1
- data/test/basic_complex_test/js_test/js_testBasic.js +1 -1
- data/test/basic_complex_test/js_test/js_testComplex.js +1 -1
- data/test/basic_complex_test/ruby_test/ruby_test.rb +10 -10
- data/test/binaryTree_test/js_test/js_test.js +1 -1
- data/test/binaryTree_test/ruby_test/ruby_test.rb +4 -4
- data/test/complex_test/js_test/js_test.js +3 -2
- data/test/ipv6_test/java_test/JavaTest.java +4 -6
- data/test/ipv6_test/js_test/js_test.js +1 -1
- data/test/signed_float_test/ruby_test/ruby_test.rb +36 -0
- data/test/signed_float_test/signed_float_test.rb +14 -0
- data/test/signed_int_test/java_test/JavaTest.java +83 -0
- data/test/signed_int_test/js_test/js_test.js +55 -0
- data/test/signed_int_test/ruby_test/ruby_test.rb +37 -0
- data/test/signed_int_test/signed_int_test.rb +15 -0
- data/test/unify_test/unify_test.rb +24 -13
- metadata +14 -38
- data/test/basic_complex_test/java_test/test_babel.java +0 -368
- data/test/basic_complex_test/js_test/test_babel.js +0 -523
- data/test/basic_complex_test/ruby_test/test_babel.rb +0 -368
- data/test/binaryTree_test/java_test/test_binaryTree.java +0 -301
- data/test/binaryTree_test/js_test/test_binaryTree.js +0 -447
- data/test/binaryTree_test/ruby_test/test_binaryTree.rb +0 -303
- data/test/complex_test/java_test/test_complex.java +0 -331
- data/test/complex_test/js_test/test_complex.js +0 -478
- data/test/complex_test/ruby_test/test_complex.rb +0 -330
- data/test/ipv6_test/java_test/junit.jar +0 -0
- data/test/ipv6_test/java_test/test_ipv6.java +0 -270
- data/test/ipv6_test/js_test/test_ipv6.js +0 -409
- data/test/ipv6_test/ruby_test/test_ipv6.rb +0 -267
- data/test/unify_test/java_test/test_unify.java +0 -171
- data/test/unify_test/js_test/js_test.js +0 -56
- data/test/unify_test/js_test/test_unify.js +0 -326
- data/test/unify_test/ruby_test/ruby_test.rb +0 -35
- data/test/unify_test/ruby_test/test_unify.rb +0 -187
data/.gitignore
CHANGED
@@ -15,3 +15,33 @@ spec/reports
|
|
15
15
|
test/tmp
|
16
16
|
test/version_tmp
|
17
17
|
tmp
|
18
|
+
*.class
|
19
|
+
test/basic_complex_test/java_test/bin.babel
|
20
|
+
test/basic_complex_test/js_test/bin.babel.js
|
21
|
+
test/basic_complex_test/ruby_test/bin.babel.rb
|
22
|
+
test/binaryTree_test/java_test/bin.babel
|
23
|
+
test/binaryTree_test/js_test/bin.babel.js
|
24
|
+
test/binaryTree_test/ruby_test/bin.babel.rb
|
25
|
+
test/complex_test/java_test/bin.babel
|
26
|
+
test/complex_test/js_test/bin.babel.js
|
27
|
+
test/complex_test/ruby_test/bin.babel.rb
|
28
|
+
test/ipv6_test/java_test/bin.babel
|
29
|
+
test/ipv6_test/js_test/bin.babel.js
|
30
|
+
test/ipv6_test/ruby_test/bin.babel.rb
|
31
|
+
test/basic_complex_test/ruby_test/test_babel.rb
|
32
|
+
test/binaryTree_test/ruby_test/test_binaryTree.rb
|
33
|
+
test/complex_test/ruby_test/test_complex.rb
|
34
|
+
test/ipv6_test/ruby_test/test_ipv6.rb
|
35
|
+
test/signed_int_test/ruby_test/test_signed_int.rb
|
36
|
+
test/signed_int_test/js_test/bin.babel.js
|
37
|
+
test/signed_int_test/ruby_test/bin.babel.rb
|
38
|
+
test/basic_complex_test/java_test/test_babel.java
|
39
|
+
test/binaryTree_test/java_test/test_binaryTree.java
|
40
|
+
test/complex_test/java_test/test_complex.java
|
41
|
+
test/ipv6_test/java_test/test_ipv6.java
|
42
|
+
test/signed_int_test/java_test/test_signed_int.java
|
43
|
+
test/basic_complex_test/js_test/test_babel.js
|
44
|
+
test/binaryTree_test/js_test/test_binaryTree.js
|
45
|
+
test/complex_test/js_test/test_complex.js
|
46
|
+
test/ipv6_test/js_test/test_ipv6.js
|
47
|
+
test/signed_int_test/js_test/test_signed_int.js
|
data/README.md
CHANGED
@@ -94,6 +94,63 @@ c2.deserialize(new BabelDataReader(ca));
|
|
94
94
|
console.log(c2);
|
95
95
|
```
|
96
96
|
|
97
|
+
### Versioning
|
98
|
+
```ruby
|
99
|
+
require 'divine'
|
100
|
+
|
101
|
+
struct 'Foobar' do # Default version is '1'
|
102
|
+
int8 :foo
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
struct 'Foobar', version: 2 do
|
107
|
+
int8 :foo
|
108
|
+
int8 :bar # We added a new field named 'bar' in version 2 of Foobar
|
109
|
+
end
|
110
|
+
|
111
|
+
Divine::CodeGenerator.new.generate(:ruby, file: 'test_babel.rb')
|
112
|
+
Divine::CodeGenerator.new.generate(:java, file: 'test_babel.java')
|
113
|
+
```
|
114
|
+
|
115
|
+
There are some basic rules regarding versioning of structs
|
116
|
+
|
117
|
+
* A versioned struct defines all fields you want to serialize/deserialize
|
118
|
+
* You can delete and add fields as you whish between versions
|
119
|
+
* You are not allowed to change type of a defined variable between versions
|
120
|
+
* You cannot have a bigger version number than 255
|
121
|
+
* The class that represents the struct also defines a 'struct_version' that keeps the current version of the struct
|
122
|
+
|
123
|
+
|
124
|
+
### Freezing
|
125
|
+
When starting to use generated code in production, the defined structs cannot be changed without breaking everything (unless you really know what you're doing).
|
126
|
+
To prevent that you change a struct by accident, you can 'freeze' a struct. The easiest way to get started is to add a empty _freeze_ field to the struct, like below
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
require 'divine'
|
130
|
+
|
131
|
+
struct 'Foobar', freeze: '' do
|
132
|
+
int8 :foo
|
133
|
+
int8 :bar
|
134
|
+
end
|
135
|
+
|
136
|
+
Divine::CodeGenerator.new.generate(:ruby, file: 'test_babel.rb')
|
137
|
+
```
|
138
|
+
|
139
|
+
The compiler will throw a runtime exception saying that the MD5 sum differs. Take the MD5 sum from this exception and put into the _freeze_ field as below
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
require 'divine'
|
143
|
+
|
144
|
+
struct 'Foobar', freeze: '3e59aa582d3137a2d0cdba174e5aa6b18beb649b' do
|
145
|
+
int8 :foo
|
146
|
+
int8 :bar
|
147
|
+
end
|
148
|
+
|
149
|
+
Divine::CodeGenerator.new.generate(:ruby, file: 'test_babel.rb')
|
150
|
+
```
|
151
|
+
|
152
|
+
It is now not possible to alter _Foobar_ by accident. If you make a change to the struct, you will also need to provide a correct MD5 sum.
|
153
|
+
|
97
154
|
|
98
155
|
## Installation
|
99
156
|
|
data/Rakefile
CHANGED
@@ -1,27 +1,48 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
|
3
|
+
|
4
|
+
task :default => ["test:all"]
|
5
|
+
|
6
|
+
|
3
7
|
namespace :test do
|
8
|
+
desc "Run all tests"
|
9
|
+
task :all => [:ruby, :js, :java, :unify] # Make sure that the unify test be the last one in the list.
|
4
10
|
|
5
11
|
desc "Run Ruby code test suite"
|
6
|
-
task :ruby
|
7
|
-
ruby
|
8
|
-
ruby "test/
|
12
|
+
task :ruby do
|
13
|
+
generate_source('ruby')
|
14
|
+
ruby "test/signed_int_test/ruby_test/ruby_test.rb"
|
9
15
|
ruby "test/ipv6_test/ruby_test/ruby_test.rb"
|
10
16
|
ruby "test/complex_test/ruby_test/ruby_test.rb"
|
17
|
+
ruby "test/binaryTree_test/ruby_test/ruby_test.rb"
|
18
|
+
ruby "test/basic_complex_test/ruby_test/ruby_test.rb"
|
19
|
+
|
20
|
+
system("find . -name 'test*.rb' | xargs rm") # Remove generated source code files
|
11
21
|
end
|
12
22
|
|
13
23
|
|
14
24
|
desc "Run JS code test suite"
|
15
|
-
task :js
|
16
|
-
|
17
|
-
system("node test/
|
18
|
-
system("node test/binaryTree_test/js_test/js_test.js")
|
25
|
+
task :js do
|
26
|
+
generate_source('js')
|
27
|
+
system("node test/signed_int_test/js_test/js_test.js")
|
19
28
|
system("node test/ipv6_test/js_test/js_test.js")
|
20
29
|
system("node test/complex_test/js_test/js_test.js")
|
30
|
+
system("node test/binaryTree_test/js_test/js_test.js")
|
31
|
+
system("node test/basic_complex_test/js_test/js_testBasic.js")
|
32
|
+
system("node test/basic_complex_test/js_test/js_testComplex.js")
|
33
|
+
|
34
|
+
system("find . -name 'test*.js' | xargs rm") # Remove generated source code files
|
21
35
|
end
|
22
36
|
|
23
37
|
desc "Run java code test suite"
|
24
|
-
task :java
|
38
|
+
task :java do
|
39
|
+
generate_source('java')
|
40
|
+
#system("javac -cp test/java_lib/junit.jar: test/signed_float_test/java_test/*.java")
|
41
|
+
#system("java -cp test/signed_float_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
|
42
|
+
|
43
|
+
system("javac -cp test/java_lib/junit.jar: test/signed_int_test/java_test/*.java")
|
44
|
+
system("java -cp test/signed_int_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
|
45
|
+
|
25
46
|
system("javac -cp test/java_lib/junit.jar: test/ipv6_test/java_test/*.java")
|
26
47
|
system("java -cp test/ipv6_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
|
27
48
|
|
@@ -33,18 +54,24 @@ namespace :test do
|
|
33
54
|
|
34
55
|
system("javac -cp test/java_lib/junit.jar: test/basic_complex_test/java_test/*.java")
|
35
56
|
system("java -cp test/basic_complex_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
|
36
|
-
end
|
37
|
-
|
38
57
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
lang = Rake.application.top_level_tasks[0].match(/:(.*)/)[1]
|
58
|
+
system("find . -name 'test*.java' | xargs rm") # Remove generated source code files
|
59
|
+
system("find . -name '*.class' | xargs rm") # Remove generated .class files
|
60
|
+
end
|
43
61
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
62
|
+
desc "Unify test to compare the produced binary files. Prerequisite Other tests must be run first to generate bin files to be compared"
|
63
|
+
task :unify do
|
64
|
+
ruby "test/unify_test/unify_test.rb"
|
65
|
+
system("find . -name 'bin.babel*' | xargs rm") # Remove generated bin files
|
48
66
|
end
|
49
67
|
end
|
50
68
|
|
69
|
+
|
70
|
+
def generate_source(lang)
|
71
|
+
puts "Divine Version #{Divine::VERSION}"
|
72
|
+
ruby "test/signed_int_test/signed_int_test.rb #{lang}"
|
73
|
+
ruby "test/ipv6_test/ipv6_test.rb #{lang}"
|
74
|
+
ruby "test/complex_test/complex_test.rb #{lang}"
|
75
|
+
ruby "test/binaryTree_test/binaryTree_test.rb #{lang}"
|
76
|
+
ruby "test/basic_complex_test/basic_complex_test.rb #{lang}"
|
77
|
+
end
|
data/lib/divine.rb
CHANGED
@@ -14,9 +14,9 @@ end
|
|
14
14
|
#
|
15
15
|
# Toplevel definition of struct'
|
16
16
|
#
|
17
|
-
def struct(name,
|
18
|
-
puts "struct #{name}"
|
19
|
-
builder = Divine::StructBuilder.new(name,
|
17
|
+
def struct(name, properties=nil, &block)
|
18
|
+
#puts "struct #{name}"
|
19
|
+
builder = Divine::StructBuilder.new(name, properties) # Defined in divine/dsl.rb
|
20
20
|
::Docile.dsl_eval(builder, &block)
|
21
21
|
builder
|
22
22
|
end
|
@@ -1,6 +1,29 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
|
1
5
|
module Divine
|
2
6
|
$language_generators = {}
|
3
7
|
|
8
|
+
class StructHandler
|
9
|
+
attr_reader :name, :latest_version, :structs
|
10
|
+
|
11
|
+
def initialize(structs)
|
12
|
+
@structs = structs
|
13
|
+
@name = structs.first.name
|
14
|
+
@latest_version = structs.last.version
|
15
|
+
@field_hash = structs.map(&:fields).flatten.group_by(&:name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def field(name)
|
19
|
+
@field_hash[name]
|
20
|
+
end
|
21
|
+
|
22
|
+
def field_names
|
23
|
+
@field_hash.keys.sort
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
4
27
|
class BabelHelperMethods
|
5
28
|
def format_src(first_indent, following_indent, is, spc = " ")
|
6
29
|
indent = "#{spc * first_indent}"
|
@@ -27,40 +50,115 @@ module Divine
|
|
27
50
|
ss = str.map(&:to_s).join("_").split(/_/).flatten
|
28
51
|
"#{ss.first.downcase}#{ss[1..-1].map(&:downcase).map(&:capitalize).join}"
|
29
52
|
end
|
53
|
+
|
54
|
+
def get_header_comment_text
|
55
|
+
return [
|
56
|
+
"",
|
57
|
+
"-- DO NOT EDIT THIS FILE --", "",
|
58
|
+
"This file was generated by Divine #{Divine::VERSION} (#{Time.now.to_s})", "",
|
59
|
+
"-- DO NOT EDIT THIS FILE --", ""
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Sanity check the different revisions of the struct
|
65
|
+
#
|
66
|
+
def sanity_check(ss)
|
67
|
+
vos = ss.sort do |x,y|
|
68
|
+
v1,v2 = [x,y].map(&:version)
|
69
|
+
v1 <=> v2
|
70
|
+
end
|
71
|
+
|
72
|
+
# Check version numbers
|
73
|
+
unless vos.map(&:version).uniq.size == ss.size
|
74
|
+
raise "Inconsistent version numbering for '#{ss.first.name}': #{vos.map(&:version).join(', ')}"
|
75
|
+
end
|
76
|
+
|
77
|
+
# Check that we don't have multiple variables with same name
|
78
|
+
vos.each do |s|
|
79
|
+
names = s.fields.group_by(&:name)
|
80
|
+
names.each_pair do |k, v|
|
81
|
+
raise "Multiple fields with same name '#{k}' is defined in '#{s.name}', version #{s.version}" unless v.size == 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Check types between versions
|
86
|
+
check_field_types(vos)
|
87
|
+
|
88
|
+
# Check for changed definitions
|
89
|
+
check_freezed_structs(vos)
|
90
|
+
|
91
|
+
# Return them ordered by version
|
92
|
+
vos
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
#
|
97
|
+
# Check types through all versions, we're not allowed to change the type of a variable between different versions
|
98
|
+
#
|
99
|
+
def check_field_types(ss)
|
100
|
+
types = {}
|
101
|
+
ss.each do |x|
|
102
|
+
x.fields.each do |f|
|
103
|
+
if types[f.name]
|
104
|
+
unless same_type(types[f.name], x, f.name)
|
105
|
+
raise "Cannot change the field type for struct #{x.name}.#{f.name} between version #{types[f.name].version} and #{x.version}"
|
106
|
+
end
|
107
|
+
else
|
108
|
+
types[f.name] = x
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def same_type(t1, t2, field)
|
115
|
+
t1.get_field(field).referenced_types == t2.get_field(field).referenced_types
|
116
|
+
end
|
117
|
+
|
118
|
+
def check_freezed_structs(ss)
|
119
|
+
ss.each do |s|
|
120
|
+
if s.freezed?
|
121
|
+
sig = calculate_signature(s)
|
122
|
+
unless sig == s.freeze_signature
|
123
|
+
raise "Struct '#{s.name}', version #{s.version} has changed! Got signature '#{sig}', expected '#{s.freeze_signature}'"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def calculate_signature(s)
|
130
|
+
str = s.fields.map do |f|
|
131
|
+
"#{f.name}:#{f.referenced_types.inspect}"
|
132
|
+
end.join(",")
|
133
|
+
Digest::SHA1.hexdigest("#{s.name}:#{s.version},#{str}")
|
134
|
+
end
|
30
135
|
end
|
31
136
|
|
137
|
+
|
138
|
+
|
32
139
|
class CodeGenerator
|
33
140
|
def generate(target, opts)
|
34
141
|
gen = $language_generators[target.to_sym]
|
35
|
-
raise "Unknown
|
142
|
+
raise "Unknown target language: #{target}" unless gen
|
36
143
|
puts "Generating code for #{target}"
|
37
144
|
src = gen.generate_code($all_structs, opts)
|
38
|
-
target_dir =
|
145
|
+
target_dir = (opts[:target_dir] || ".") + "/"
|
39
146
|
|
40
147
|
if opts[:package]
|
41
|
-
path = target_dir + opts[:package]
|
42
|
-
|
148
|
+
path = target_dir + opts[:package].gsub(/\./, "/")
|
149
|
+
FileUtils.mkdir_p(path) unless File.exists?(path)
|
43
150
|
for cls in src
|
44
151
|
file_name = path+"/"+cls[:file]
|
45
152
|
writeFile(file_name, cls[:src])
|
46
153
|
end
|
47
154
|
elsif opts[:file]
|
155
|
+
FileUtils.mkdir_p(target_dir) unless File.exists?(target_dir)
|
48
156
|
path = target_dir + opts[:file]
|
49
157
|
writeFile(path, src[0][:src])
|
50
158
|
else
|
51
159
|
puts src
|
52
160
|
end
|
53
161
|
end
|
54
|
-
|
55
|
-
def getTargetDir(dir)
|
56
|
-
# check if the path is relative or absolute if exist
|
57
|
-
if dir && File.directory?(dir)
|
58
|
-
return dir + "/"
|
59
|
-
elsif dir && File.directory?(Dir.pwd + "/" + dir)
|
60
|
-
return Dir.pwd + dir + "/"
|
61
|
-
end
|
62
|
-
""
|
63
|
-
end
|
64
162
|
|
65
163
|
def writeFile(path, content)
|
66
164
|
File.open(path, 'w+') do |f|
|
@@ -1,6 +1,12 @@
|
|
1
1
|
module Divine
|
2
2
|
|
3
3
|
class JavaHelperMethods < BabelHelperMethods
|
4
|
+
def get_header_comment
|
5
|
+
get_header_comment_text.map do |s|
|
6
|
+
"// #{s}"
|
7
|
+
end.join("\n")
|
8
|
+
end
|
9
|
+
|
4
10
|
def java_base_class_template_str
|
5
11
|
<<EOS
|
6
12
|
abstract class BabelBase <%= toplevel_class %> {
|
@@ -33,6 +39,14 @@ abstract class BabelBase <%= toplevel_class %> {
|
|
33
39
|
return (data.read() << 24) | readInt24(data);
|
34
40
|
}
|
35
41
|
|
42
|
+
protected int readSint32(ByteArrayInputStream data) {
|
43
|
+
return (data.read() << 24) | readInt24(data);
|
44
|
+
}
|
45
|
+
|
46
|
+
protected long readSint64(ByteArrayInputStream data) {
|
47
|
+
return (readInt32(data) << 32) | (readInt32(data) & 0xFFFFFFFFL);
|
48
|
+
}
|
49
|
+
|
36
50
|
protected boolean readBool(ByteArrayInputStream data) {
|
37
51
|
return readInt8(data) == 1;
|
38
52
|
}
|
@@ -135,6 +149,26 @@ abstract class BabelBase <%= toplevel_class %> {
|
|
135
149
|
writeInt24((int) (v & 0xFFFFFF), out);
|
136
150
|
}
|
137
151
|
|
152
|
+
protected void writeSint32(int v, ByteArrayOutputStream out) {
|
153
|
+
if (v > Integer.MAX_VALUE) { // Max 2.147.483.647
|
154
|
+
raiseError("Too large sInt32 number: " + v + ", Max = " + Integer.MAX_VALUE);
|
155
|
+
}else if(v < Integer.MIN_VALUE){ // Min -2.147.483.648
|
156
|
+
raiseError("Too small sInt32 number: " + v + ", Min = " + Integer.MIN_VALUE);
|
157
|
+
}
|
158
|
+
writeInt8((int) ((v >> 24) & 0xFF), out);
|
159
|
+
writeInt24((int) (v & 0xFFFFFF), out);
|
160
|
+
}
|
161
|
+
|
162
|
+
protected void writeSint64(long v, ByteArrayOutputStream out) {
|
163
|
+
if (v > Long.MAX_VALUE) { // Max 9,223,372,036,854,775,807
|
164
|
+
raiseError("Too large sInt64 number: " + v + ", Max = " + Integer.MAX_VALUE);
|
165
|
+
}else if(v < Long.MIN_VALUE){ // Min -9,223,372,036,854,775,807
|
166
|
+
raiseError("Too small sInt64 number: " + v + ", Min = " + Integer.MIN_VALUE);
|
167
|
+
}
|
168
|
+
writeInt32(((v >> 32) & 0xFFFFFFFFL), out);
|
169
|
+
writeInt32( (v & 0xFFFFFFFFL) , out);
|
170
|
+
}
|
171
|
+
|
138
172
|
protected void writeBool(boolean v, ByteArrayOutputStream out) {
|
139
173
|
writeInt8(v ? 1 : 0, out);
|
140
174
|
}
|
@@ -242,43 +276,90 @@ abstract class BabelBase <%= toplevel_class %> {
|
|
242
276
|
EOS
|
243
277
|
end
|
244
278
|
|
245
|
-
def
|
246
|
-
|
247
|
-
class
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
<% end %>
|
259
|
-
<% c.complex_fields.each do |f| %>
|
260
|
-
<%= this.java_serialize_complex f %>
|
261
|
-
<% end %>
|
262
|
-
}
|
279
|
+
def java_class_template(sh)
|
280
|
+
code = [
|
281
|
+
"class #{sh.name} extends BabelBase {",
|
282
|
+
:indent,
|
283
|
+
"",
|
284
|
+
|
285
|
+
# PROPERTIES
|
286
|
+
"public int struct_version = #{sh.latest_version};",
|
287
|
+
sh.field_names.map do |fn|
|
288
|
+
f = sh.field(fn).last
|
289
|
+
"public #{java_get_type_declaration(f)} #{fn} = #{java_get_empty_declaration(f)};"
|
290
|
+
end, "",
|
291
|
+
|
263
292
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
293
|
+
# SERiALIZE INTERNAL
|
294
|
+
"@Override",
|
295
|
+
"void serializeInternal(ByteArrayOutputStream baos) throws IOException {",
|
296
|
+
:indent,
|
297
|
+
"writeInt8(this.struct_version, baos);",
|
298
|
+
sh.structs.map do |s|
|
299
|
+
[
|
300
|
+
"if(this.struct_version == #{s.version}) {",
|
301
|
+
:indent,
|
302
|
+
s.simple_fields.map do |f|
|
303
|
+
"#{camelize("write", f.type)}(this.#{f.name}, baos);"
|
304
|
+
end,
|
305
|
+
s.complex_fields.map do |f|
|
306
|
+
[
|
307
|
+
"", "// Serialize #{f.type} '#{f.name}'",
|
308
|
+
java_serialize_internal("this.#{f.name}", f.referenced_types)
|
309
|
+
]
|
310
|
+
end,
|
311
|
+
"return;",
|
312
|
+
:deindent,
|
313
|
+
"}", ""
|
314
|
+
]
|
315
|
+
end, "",
|
316
|
+
"throw new UnsupportedOperationException(\"Unsupported version \" + this.struct_version + \" for type '#{sh.name}'\");",
|
317
|
+
:deindent,
|
318
|
+
"}", "",
|
319
|
+
|
320
|
+
|
321
|
+
# DESERIALIZE
|
322
|
+
"@Override",
|
323
|
+
"public void deserialize(ByteArrayInputStream bais) throws IOException {",
|
324
|
+
:indent,
|
325
|
+
"this.struct_version = readInt8(bais);",
|
326
|
+
sh.structs.map do |s|
|
327
|
+
[
|
328
|
+
"if(this.struct_version == #{s.version}) {",
|
329
|
+
:indent,
|
330
|
+
s.simple_fields.map do |f|
|
331
|
+
"this.#{f.name} = #{camelize("read", f.type)}(bais);"
|
332
|
+
end,
|
333
|
+
s.complex_fields.map do |f|
|
334
|
+
[
|
335
|
+
"", "// Read #{f.type} '#{f.name}'",
|
336
|
+
java_deserialize_internal("this.#{f.name}", f.referenced_types)
|
337
|
+
]
|
338
|
+
end,
|
339
|
+
"return;",
|
340
|
+
:deindent,
|
341
|
+
"}"
|
342
|
+
]
|
343
|
+
end, "",
|
344
|
+
"throw new UnsupportedOperationException(\"Unsupported version \" + this.struct_version + \" for type '#{sh.name}'\");",
|
345
|
+
:deindent,
|
346
|
+
"}", "",
|
347
|
+
|
348
|
+
|
349
|
+
# END OF CLASS
|
350
|
+
:deindent,
|
351
|
+
"}"
|
352
|
+
]
|
353
|
+
|
354
|
+
format_src(3, 3, code)
|
275
355
|
end
|
356
|
+
|
276
357
|
|
277
358
|
def java_get_empty_declaration(types, is_reference_type = false)
|
278
359
|
if types.respond_to? :referenced_types
|
279
360
|
java_get_empty_declaration(types.referenced_types)
|
280
361
|
|
281
|
-
elsif types.respond_to?
|
362
|
+
elsif types.respond_to?(:first) && types.size > 1
|
282
363
|
case types.first
|
283
364
|
when :list
|
284
365
|
"new #{java_get_type_declaration(types, true)}()"
|
@@ -288,13 +369,16 @@ EOS2
|
|
288
369
|
raise "Missing empty declaration for #{types}"
|
289
370
|
end
|
290
371
|
|
372
|
+
elsif types.respond_to?(:first) && types.size == 1
|
373
|
+
java_get_empty_declaration(types.first, is_reference_type)
|
374
|
+
|
291
375
|
else
|
292
376
|
case types
|
293
377
|
when :binary, :short_binary
|
294
378
|
is_reference_type ? "new Byte[0]" : "new byte[0]"
|
295
|
-
when :int8, :int16
|
379
|
+
when :int8, :int16, :sint32
|
296
380
|
"0"
|
297
|
-
when :int32
|
381
|
+
when :int32, :sint64
|
298
382
|
"0L"
|
299
383
|
when :string, :ip_number
|
300
384
|
"\"\""
|
@@ -312,7 +396,7 @@ EOS2
|
|
312
396
|
if types.respond_to? :referenced_types
|
313
397
|
java_get_type_declaration(types.referenced_types, is_reference_type)
|
314
398
|
|
315
|
-
elsif types.respond_to?
|
399
|
+
elsif types.respond_to?(:first) && types.size > 1
|
316
400
|
case types.first
|
317
401
|
when :list
|
318
402
|
subtypes = java_get_type_declaration(types[1], true)
|
@@ -324,14 +408,17 @@ EOS2
|
|
324
408
|
else
|
325
409
|
raise "Missing serialization for #{types}"
|
326
410
|
end
|
411
|
+
|
412
|
+
elsif types.respond_to?(:first) && types.size == 1
|
413
|
+
java_get_type_declaration(types.first, is_reference_type)
|
327
414
|
|
328
415
|
else
|
329
416
|
case types
|
330
417
|
when :binary, :short_binary
|
331
418
|
is_reference_type ? "Byte[]" : "byte[]"
|
332
|
-
when :int8, :int16
|
419
|
+
when :int8, :int16, :sint32
|
333
420
|
is_reference_type ? "Integer" : "int"
|
334
|
-
when :int32
|
421
|
+
when :int32, :sint64
|
335
422
|
is_reference_type ? "Long" : "long"
|
336
423
|
when :string, :ip_number
|
337
424
|
"String"
|
@@ -345,15 +432,6 @@ EOS2
|
|
345
432
|
end
|
346
433
|
end
|
347
434
|
|
348
|
-
def java_serialize_complex(field)
|
349
|
-
types = field.referenced_types
|
350
|
-
as = [
|
351
|
-
"// Serialize #{field.type} '#{field.name}'",
|
352
|
-
java_serialize_internal(field.name, types)
|
353
|
-
]
|
354
|
-
format_src(2, 1, as, "\t")
|
355
|
-
end
|
356
|
-
|
357
435
|
def java_serialize_internal(var, types)
|
358
436
|
if types.respond_to? :first
|
359
437
|
case types.first
|
@@ -398,15 +476,6 @@ EOS2
|
|
398
476
|
end
|
399
477
|
end
|
400
478
|
|
401
|
-
def java_deserialize_complex(field)
|
402
|
-
types = field.referenced_types
|
403
|
-
as = [
|
404
|
-
"// Deserialize #{field.type} '#{field.name}'",
|
405
|
-
java_deserialize_internal("this.#{field.name}", types)
|
406
|
-
]
|
407
|
-
format_src(2, 1, as, "\t")
|
408
|
-
end
|
409
|
-
|
410
479
|
def java_deserialize_internal(var, types)
|
411
480
|
if types.respond_to? :first
|
412
481
|
case types.first
|
@@ -465,16 +534,14 @@ EOS2
|
|
465
534
|
|
466
535
|
class JavaGenerator < JavaHelperMethods
|
467
536
|
def generate_code(structs, opts)
|
468
|
-
pp opts
|
469
537
|
$debug_java = true if opts[:debug]
|
470
538
|
base_template = Erubis::Eruby.new(java_base_class_template_str)
|
471
|
-
class_template = Erubis::Eruby.new(java_class_template_str)
|
472
539
|
keys = structs.keys.sort
|
473
540
|
src = keys.map do |k|
|
474
541
|
ss = structs[k]
|
475
|
-
#
|
476
|
-
|
477
|
-
|
542
|
+
# Check different aspects the the structs
|
543
|
+
vss = sanity_check(ss)
|
544
|
+
java_class_template(StructHandler.new(vss))
|
478
545
|
end
|
479
546
|
|
480
547
|
# User defined super class?
|
@@ -492,28 +559,25 @@ EOS2
|
|
492
559
|
end
|
493
560
|
|
494
561
|
def java_get_begin_module(opts)
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
"import
|
513
|
-
|
514
|
-
"import java.nio.charset.Charset;\n\n"
|
515
|
-
].join("\n")
|
516
|
-
end
|
562
|
+
str = "#{get_header_comment}\n\n"
|
563
|
+
str << "package #{opts[:package]};" if opts[:package]
|
564
|
+
str << get_java_imports
|
565
|
+
str << "\n\n"
|
566
|
+
return str
|
567
|
+
end
|
568
|
+
|
569
|
+
def get_java_imports
|
570
|
+
[
|
571
|
+
"java.io.ByteArrayInputStream",
|
572
|
+
"java.io.ByteArrayOutputStream",
|
573
|
+
"java.io.IOException",
|
574
|
+
"java.util.ArrayList",
|
575
|
+
"java.util.HashMap",
|
576
|
+
"java.util.regex.Pattern",
|
577
|
+
"java.nio.charset.Charset"
|
578
|
+
].map do |i|
|
579
|
+
"import #{i};"
|
580
|
+
end.join("\n")
|
517
581
|
end
|
518
582
|
end
|
519
583
|
|