divine 0.0.2 → 0.0.3
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.
- 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
|
|