block style of highligh.js.
+ * We have to override the padding and the background-color, since we style this
+ * part ourselves. Specifically, we style the surrounding the , while
+ * highlight.js applies the .hljs style directly to the tag.
+ */
+.hljs {
+ background-color: transparent;
+ padding: 0;
+}
+
+@media only screen and (max-width: 768px) {
+ nav.toc {
+ position: fixed;
+ width: 16em;
+ left: -16em;
+ -webkit-overflow-scrolling: touch;
+ -webkit-transition-property: left; /* Safari */
+ -webkit-transition-duration: 0.3s; /* Safari */
+ transition-property: left;
+ transition-duration: 0.3s;
+ -webkit-transition-timing-function: ease-out; /* Safari */
+ transition-timing-function: ease-out;
+ z-index: 2;
+ box-shadow: 5px 0px 5px 0px rgb(210,210,210);
+ }
+
+ nav.toc.show {
+ left: 0;
+ }
+
+ article {
+ margin-left: 0;
+ padding: 3em 0.9em 0 0.9em; /* top right bottom left */
+ overflow-wrap: break-word;
+ }
+
+ article > header {
+ position: fixed;
+ left: 0;
+ z-index: 1;
+ }
+
+ article > header nav, hr {
+ display: none;
+ }
+
+ article > header div#topbar {
+ display: block; /* is mobile */
+ position: fixed;
+ width: 100%;
+ height: 1.5em;
+ padding-top: 1em;
+ padding-bottom: 1em;
+ background-color: #fcfcfc;
+ box-shadow: 0 1px 3px rgba(0,0,0,.26);
+ top: 0;
+ -webkit-transition-property: top; /* Safari */
+ -webkit-transition-duration: 0.3s; /* Safari */
+ transition-property: top;
+ transition-duration: 0.3s;
+ }
+
+ article > header div#topbar.headroom--unpinned.headroom--not-top.headroom--not-bottom {
+ top: -4em;
+ -webkit-transition-property: top; /* Safari */
+ -webkit-transition-duration: 0.7s; /* Safari */
+ transition-property: top;
+ transition-duration: 0.7s;
+ }
+
+ article > header div#topbar span {
+ width: 80%;
+ height: 1.5em;
+ margin-top: -0.1em;
+ margin-left: 0.9em;
+ font-size: 1.2em;
+ overflow: hidden;
+ }
+
+ article > header div#topbar a.fa-bars {
+ float: right;
+ padding: 0.6em;
+ margin-top: -0.6em;
+ margin-right: 0.3em;
+ font-size: 1.5em;
+ }
+
+ article > header div#topbar a.fa-bars:visited {
+ color: #3091d1;
+ }
+
+ article table {
+ overflow-x: auto;
+ display: block;
+ }
+
+ article div.MathJax_Display {
+ overflow: scroll;
+ }
+
+ article span.MathJax {
+ overflow: hidden;
+ }
+}
+
+@media only screen and (max-width: 320px) {
+ body {
+ font-size: 15px;
+ }
+}
diff --git a/julia/docs/make.jl b/julia/docs/make.jl
new file mode 100644
index 00000000000..2384c6a66d3
--- /dev/null
+++ b/julia/docs/make.jl
@@ -0,0 +1,17 @@
+import Pkg
+Pkg.instantiate()
+using Documenter, FlatBuffers
+
+makedocs(
+ modules = [FlatBuffers],
+ format = :html,
+ sitename = "FlatBuffers.jl",
+ pages = ["Home" => "index.md"]
+)
+
+deploydocs(
+ repo = "github.com/JuliaData/FlatBuffers.jl.git",
+ target = "build",
+ deps = nothing,
+ make = nothing
+)
diff --git a/julia/docs/mkdocs.yml b/julia/docs/mkdocs.yml
new file mode 100644
index 00000000000..da901f9e2d0
--- /dev/null
+++ b/julia/docs/mkdocs.yml
@@ -0,0 +1,29 @@
+site_name: FlatBuffers.jl
+repo_url: https://github.com/dmbates/FlatBuffers.jl
+site_description: Julia implementation of google flatbuffers
+site_author: Jacob Quinn
+
+theme: material
+
+extra:
+ palette:
+ primary: 'indigo'
+ accent: 'blue'
+
+extra_css:
+ - assets/Documenter.css
+
+extra_javascript:
+ - https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML
+ - assets/mathjaxhelper.js
+
+markdown_extensions:
+ - extra
+ - tables
+ - fenced_code
+ - mdx_math
+
+docs_dir: 'build'
+
+pages:
+ - Home: index.md
diff --git a/julia/docs/src/index.md b/julia/docs/src/index.md
new file mode 100644
index 00000000000..6e06b214606
--- /dev/null
+++ b/julia/docs/src/index.md
@@ -0,0 +1,97 @@
+# FlatBuffers.jl Documentation
+
+#### Overview
+FlatBuffers.jl provides native Julia support for reading and writing binary structures following the google flatbuffer schema (see [here](https://google.github.io/flatbuffers/flatbuffers_internals.html) for a more in-depth review of the binary format).
+
+The typical language support for flatbuffers involves utilizing the `flatc` compiler to translate a flatbuffer schema file (.fbs) into a langugage-specific set of types/classes and methods. See [here](https://google.github.io/flatbuffers/flatbuffers_guide_writing_schema.html) for the official guide on writing schemas.
+
+This Julia package provides the serialization primitives used by code that has been generated by `flatc`. Since it was originally built without `flatc` support, it can also be used as a minimal set of macros to provide flatbuffer-compatible serialization of existing Julia types. This has led to the Julia code generated by `flatc` appearing somewhat more readable than for other languages.
+
+For example, for this schema:
+```
+namespace example;
+
+table SimpleType {
+ x: int = 1;
+}
+
+root_type SimpleType;
+```
+the code generated by `flatc` looks like this:
+```julia
+module Example
+
+using FlatBuffers
+@with_kw mutable struct SimpleType
+ x::Int32 = 1
+end
+
+# ... other generated stuff
+end
+```
+If you don't want to write a schema, you can pepper your existing Julia types
+with these macros and then call the functions below to produce flatbuffer-compatible
+binaries.
+
+#### Usage
+`FlatBuffers` provides the following functions for reading and writing flatbuffers:
+```
+FlatBuffers.serialize(stream::IO, value::T)
+FlatBuffers.deserialize(stream::IO, ::Type{T})
+```
+These methods are not exported to avoid naming clashes with the `Serialization` module.
+For convenience, there are also two additional constructors defined for each generated type:
+* `T(buf::AbstractVector{UInt8}, pos::Integer=0)`
+* `T(io::IO)`
+
+Here is an example showing how to use them to serialize the example type above.
+```julia
+import FlatBuffers, Example
+
+# create an instance of our type
+val = Example.SimpleType(2)
+
+# serialize it to example.bin
+open("example.bin", "w") do f FlatBuffers.serialize(f, val) end
+
+# read the value back again from file
+val2 = open("example.bin", "r") do f Example.SimpleType(f) end
+```
+In addition, this package provides the following types and methods, which are useful
+when inspecting and constructing flatbuffers:
+* `FlatBuffers.Table{T}` - type for deserializing a Julia type `T` from a flatbuffer
+* `FlatBuffers.Builder{T}` - type for serializing a Julia type `T` to a flatbuffer
+* `FlatBuffers.read` - performs the actual deserializing on a `FlatBuffer.Table`
+* `FlatBuffers.build!` - performs the actual serializing on a `FlatBuffer.Builder`
+
+#### Methods for Generated Types
+For a generated type `T`, in addition to the constructors mentioned above:
+* if `T` has default values, constructors will be defined as per the `@with_kw` macro in [Parameters.jl](https://github.com/mauro3/Parameters.jl)
+* `FlatBuffers.file_extension(T)` - returns the `file_extension` specified in the schema (if any)
+* `FlatBuffers.file_identifier(T)` - returns the `file_identifier` specified in the schema (if any)
+* `FlatBuffers.has_identifier(T, bytes)` - returns whether the given bytes contain the identifier for `T` at the offset designated by the flatbuffers specification
+* `FlatBuffers.slot_offsets(T)` - an array containing the positions of the slots in the vtable for type `T`, accounting for gaps caused by deprecated fields
+* `FlatBuffers.root_type(T)` - returns whether the type is designated as the root type by the schema. Also note however that no `root_type` definition is necessary in Julia; any of the generated `mutable struct`s can be a valid root table type.
+
+#### Circular References
+It's a bit unfortunate that the flatbuffers example uses mutually referential types, something which Julia doesn't have support for yet.
+However, there is a [workaround](https://github.com/JuliaLang/julia/issues/269#issuecomment-68421745) - by modifying the
+code generated by `flatc` slightly to add a type parameter, we can refer to a type that hasn't yet been defined.
+```julia
+FlatBuffers.@with_kw mutable struct Monster{T}
+ # ...
+ test::T = nothing
+ # ...
+end
+```
+In general though, try to avoid schemas which introduce these kinds of circular references.
+For the full `Monster` example see the test suite [here](https://github.com/JuliaData/FlatBuffers.jl/blob/master/test/MyGame/Example/Monster.jl).
+
+#### Internal Utilities
+These functions are used by the code generated by `flatc`. Documentation is also included for many
+internal methods and may be queried using `?` at the REPL.
+* `@ALIGN T size_in_bytes` - convenience macro for forcing a flatbuffer alignment on the Julia type `T` to `size_in_bytes`
+* `@with_kw mutable struct T fields...` - convenience macro for defining default field values for Julia type `T`
+* `@UNION T Union{T1,T2,...}` - convenience macro for defining a flatbuffer union type `T`
+* `@STRUCT struct T fields... end` - convenience macro for defining flatbuffer struct types, ensuring any necessary padding gets added to the type definition
+
diff --git a/julia/src/FlatBuffers.jl b/julia/src/FlatBuffers.jl
new file mode 100644
index 00000000000..0471e20eca3
--- /dev/null
+++ b/julia/src/FlatBuffers.jl
@@ -0,0 +1,615 @@
+module FlatBuffers
+
+# utils
+"""
+serialize(stream::IO, value::T) where {T}
+Serialize `value` to `stream` using the `FlatBuffer` format.
+"""
+function serialize(stream::IO, value::T) where {T}
+ write(stream, bytes(build!(value)))
+end
+
+"""
+deserialize(stream::IO, ::Type{T}) where {T}
+Read a `T` from the flatbuffer-formatted `stream`.
+"""
+function deserialize(stream::IO, ::Type{T}) where {T}
+ read(T, read(stream))
+end
+
+struct UndefinedType end
+const Undefined = UndefinedType()
+getfieldvalue(obj::T, i) where {T} = isdefined(obj, i) ? getfield(obj, i) : Undefined
+getprevfieldvalue(obj::T, i) where {T} = i == 1 ? missing : getfieldvalue(obj, i - 1)
+
+"""
+Scalar
+A Union of the Julia types `T <: Number` that are allowed in FlatBuffers schema
+"""
+const Scalar = Union{Bool,
+Int8, Int16, Int32, Int64,
+UInt8, UInt16, UInt32, UInt64,
+Float32, Float64}
+
+isstruct(T) = isconcretetype(T) && !T.mutable
+isbitstype(T) = fieldcount(T) == 0
+isunionwithnothing(T) = T isa Union && T.a == Nothing && !(isa(T.b, Union))
+
+file_identifier(T) = ""
+file_extension(T) = ""
+slot_offsets(T) = [4 + ((i - 1) * 2) for i = 1:length(T.types)]
+
+default(T, TT, sym) = default(TT)
+default(::Type{T}) where {T <: Scalar} = zero(T)
+default(::Type{T}) where {T <: AbstractString} = ""
+default(::Type{T}) where {T <: Enum} = enumtype(T)(T(0))
+default(::Type{Vector{T}}) where {T} = T[]
+
+# attempt to call default constructors for the type,
+# use above methods as fallback
+function default(::Type{T}, i::Integer) where {T}
+ TT = T.types[i]
+ try
+ return FlatBuffers.default(T, TT, fieldnames(T)[i])
+ # catch because Parameters throws an error if there is no
+ # default value defined...
+ catch
+ end
+ return default(TT)
+end
+
+# fallback that recursively builds a default; for structs/tables
+function default(::Type{T}) where {T}
+ if isa(T, Union) || isa(T, UnionAll)
+ return nothing
+ else
+ return T([default(T, i) for i = 1:length(T.types)]...)
+ end
+end
+
+function typeorder end
+
+enumtype(::Type{<:Enum}) = UInt8
+
+# Types
+"""
+Table
+
+The object containing the flatbuffer and positional information specific to the table.
+The `vtable` containing the offsets for specific members precedes `pos`.
+The actual values in the table follow `pos` offset and size of the vtable.
+
+- `bytes::AbstractVector{UInt8}`: the flatbuffer itself
+- `pos::Integer`: the base position in `bytes` of the table
+"""
+mutable struct Table{T}
+ bytes::AbstractVector{UInt8}
+ pos::Integer
+end
+
+"""
+Builder is a state machine for creating FlatBuffer objects.
+Use a Builder to construct object(s) starting from leaf nodes.
+
+A Builder constructs byte buffers in a last-first manner for simplicity and
+performance.
+"""
+mutable struct Builder{T}
+ bytes::AbstractVector{UInt8}
+ minalign::Int
+ vtable::Vector{Int}
+ objectend::Int
+ vtables::Vector{Int}
+ head::Int
+ nested::Bool
+ finished::Bool
+end
+
+function hexloc(x)
+ "0x" * lpad("$(string(x-1, base=16)) ", 6, '0')
+end
+
+function hexbyte(io, z)
+ printstyled(io, lpad("$(string(z, base=16)) ", 3, '0'), color=Int(z))
+end
+
+function hexoffset(x)
+ "0x$(lpad(string(x, base=16), 4, '0'))"
+end
+
+function stringify(io, buf, offset, x, y, msg="", msgcolor=:blue)
+ y = min(y, length(buf))
+ printstyled(io, hexloc(x + offset), color=:blue)
+ for i = x:y
+ hexbyte(io, buf[i])
+ end
+ if length(msg) > 0
+ printstyled(io, " " * msg, color=msgcolor)
+ end
+ println(io)
+end
+
+function showvtable(io::IO, T, buffer, vtabstart, vtabsize)
+ syms = T.name.names
+ printstyled(io, "vtable start pos: $(hexoffset(vtabstart))\n", color=:green)
+ printstyled(io, "vtable size: $vtabsize\n", color=:green)
+ i = vtabstart + 4
+ soff = slot_offsets(T)
+ numslots = div(soff[end] - 4, 2) + 1
+ field = 1
+ slot = 1
+ numfields = length(T.types)
+ while slot <= numslots
+ # leave holes for deprecated fields
+ j = 2
+ start = field == 1 ? soff[1] : soff[field - 1]
+ while (start + j) < soff[field]
+ # empty slot
+ stringify(io, buffer, 1, i, i+1, "[deprecated field]", :red)
+ slot += 1
+ j += 2
+ i += 2
+ if (i - vtabstart) > vtabsize
+ break
+ end
+ end
+ if (i - vtabstart) > vtabsize
+ break
+ end
+ stringify(io, buffer, 1, i, i+1, "[$(fieldnames(T)[field])]")
+ slot += 1
+ field += 1
+ i += 2
+ if (i - vtabstart) > vtabsize
+ break
+ end
+ end
+ # now we're pointing at data
+ printstyled(io, "payload:\n", color=:green)
+ while i < length(buffer)
+ stringify(io, buffer, 1, i, i+7, "")
+ i += 8
+ end
+end
+
+function Base.show(io::IO, x::Union{Builder{T}, Table{T}}) where {T}
+ printstyled(io, "FlatBuffers.$(typeof(x)):\n", color=:green)
+ buffer = x isa Builder ? x.bytes[x.head+1:end] : x.bytes
+ if isempty(buffer)
+ printstyled(io, " (empty flatbuffer)", color=:red)
+ else
+ pos = Int(typeof(x) <: Table ? x.pos : readbuffer(buffer, 0, Int32))
+ printstyled(io, "root offset: $(hexoffset(pos))\n", color=:green)
+ vtaboff = readbuffer(buffer, pos, Int32)
+ vtabstart = pos - vtaboff
+ vtabsize = readbuffer(buffer, vtabstart, Int16)
+ showvtable(io, T, buffer, vtabstart, vtabsize)
+ end
+end
+
+include("internals.jl")
+
+function Table(::Type{T}, buffer::AbstractVector{UInt8}, pos::Integer) where {T}
+ return Table{T}(buffer, pos)
+end
+
+Table(b::Builder{T}) where {T} = Table(T, b.bytes[b.head+1:end], get(b, b.head, Int32))
+
+getvalue(t, o, ::Type{Nothing}) = nothing
+getvalue(t, o, ::Type{T}) where {T <: Scalar} = get(t, t.pos + o, T)
+getvalue(t, o, ::Type{T}) where {T <: Enum} = T(get(t, t.pos + o, enumtype(T)))
+function getvalue(t, o, ::Type{T}) where {T <: AbstractString}
+ o += get(t, t.pos + o, Int32)
+ strlen = get(t, t.pos + o, Int32)
+ o += t.pos + sizeof(Int32)
+ return String(t.bytes[o + 1:o + strlen])
+end
+function getvalue(t, o, ::Type{Vector{UInt8}})
+ o += get(t, t.pos + o, Int32)
+ len = get(t, t.pos + o, Int32)
+ o += t.pos + sizeof(Int32)
+ return t.bytes[o + 1:o + len] #TODO: maybe not make copy here?
+end
+
+getarray(t, vp, len, ::Type{T}) where {T <: Scalar} = (ptr = convert(Ptr{T}, pointer(t.bytes, vp + 1)); return [unsafe_load(ptr, i) for i = 1:len])
+getarray(t, vp, len, ::Type{T}) where {T <: Enum} = (ptr = convert(Ptr{enumtype(T)}, pointer(t.bytes, vp + 1)); return [unsafe_load(ptr, i) for i = 1:len])
+function getarray(t, vp, len, ::Type{T}) where {T <: Union{AbstractString, Vector{UInt8}}}
+ A = Vector{T}(undef, len)
+ for i = 1:len
+ A[i] = getvalue(t, vp - t.pos, T)
+ vp += sizeof(Int32)
+ end
+ return A
+end
+function getarray(t, vp, len, ::Type{T}) where {T}
+ if isstruct(T)
+ ptr = convert(Ptr{T}, pointer(t.bytes, vp + 1))
+ return [unsafe_load(ptr, i) for i = 1:len]
+ else
+ A = Vector{T}(undef, len)
+ for i = 1:len
+ A[i] = getvalue(t, vp - t.pos, T)
+ vp += sizeof(Int32)
+ end
+ return A
+ end
+end
+
+function getvalue(t, o, ::Type{Vector{T}}) where {T}
+ vl = vectorlen(t, o)
+ vp = vector(t, o)
+ return getarray(t, vp, vl, T)
+end
+
+Base.convert(::Type{T}, e::Integer) where {T <: Enum} = T(e)
+
+# fallback which recursively calls read
+function getvalue(t, o, ::Type{T}) where {T}
+ if isstruct(T)
+ if any(x-> x <: Enum, T.types)
+ args = []
+ o = t.pos + o + 1
+ for typ in T.types
+ val = unsafe_load(convert(Ptr{typ <: Enum ? enumtype(typ) : typ}, pointer(view(t.bytes, o:length(t.bytes)))))
+ push!(args, val)
+ o += sizeof(typ <: Enum ? enumtype(typ) : typ)
+ end
+ return T(args...)
+ else
+ return unsafe_load(convert(Ptr{T}, pointer(view(t.bytes, (t.pos + o + 1):length(t.bytes)))))
+ end
+ else
+ o += t.pos
+ newt = Table{T}(t.bytes, indirect(t, o))
+ return FlatBuffers.read(newt, T)
+ end
+end
+
+function typetoread(prevfield, ::Type{T}, ::Type{TT}) where {T, TT}
+ R = TT
+ nullable = false
+ if isunionwithnothing(R)
+ nullable = true
+ R = TT.b
+ end
+
+ # if it's a self-referential UnionAll, use the more specific type
+ if isa(R, UnionAll) && T <: R
+ R = T
+ end
+
+ # if it's a Union type, use the previous arg to figure out the true type that was serialized
+ if !isunionwithnothing(R) && R isa Union
+ R = typeorder(R, prevfield)
+ end
+
+ if R <: AbstractVector
+ # hacks! if it's a union all, assume it's because we're working around circular dependencies
+ if isa(eltype(R), UnionAll) && T <: eltype(R)
+ return Vector{T}, false, nullable
+ # if it's a vector of Unions, use the previous field to figure out the types of all the elements
+ elseif isa(eltype(R), Union)
+ types = typeorder.(eltype(R), prevfield)
+ R = definestruct(types)
+ return R, true, nullable
+ end
+ end
+
+ return R, false, nullable
+end
+
+"""
+`FlatBuffers.read` parses a `T` at `t.pos` in Table `t`.
+Will recurse as necessary for nested types (Arrays, Tables, etc.)
+"""
+function FlatBuffers.read(t::Table{T1}, ::Type{T}=T1) where {T1, T}
+ args = []
+ numfields = length(T.types)
+ soff = slot_offsets(T)
+ for i = 1:numfields
+ TT = T.types[i]
+ o = offset(t, soff[i])
+ R, isunionvector, nullable = typetoread(i == 1 ? nothing : args[end], T, TT)
+ if o == 0
+ push!(args, nullable ? nothing : default(T, TT, T.name.names[i]))
+ else
+ if isunionvector
+ eval(:(newr = getvalue($t, $o, $R)))
+ eval(:(n = length($R.types)))
+ push!(args, [getfieldvalue(newr, j) for j = 1:n])
+ else
+ push!(args, getvalue(t, o, R))
+ end
+ end
+ end
+
+ return T(args...)
+end
+
+FlatBuffers.read(::Type{T}, buffer::AbstractVector{UInt8}, pos::Integer) where {T} = FlatBuffers.read(Table(T, buffer, pos))
+FlatBuffers.read(b::Builder{T}) where {T} = FlatBuffers.read(Table(T, b.bytes[b.head+1:end], get(b, b.head, Int32)))
+# assume `bytes` is a pure flatbuffer buffer where we can read the root position at the beginning
+FlatBuffers.read(::Type{T}, bytes) where {T} = FlatBuffers.read(T, bytes, read(IOBuffer(bytes), Int32))
+
+has_identifier(::Type{T}, bytes) where {T} = length(bytes) >= 8 && String(bytes[5:8]) == FlatBuffers.file_identifier(T)
+root_type(::Type{T}) where {T} = false
+
+"""
+flat_bytes = bytes(b)
+
+`flat_bytes` are the serialized bytes for the FlatBuffer. This discards the Julia specific `head`.
+"""
+bytes(b::Builder) = unsafe_wrap(Array{UInt8,1}, pointer(b.bytes, b.head+1), (length(b.bytes)-b.head))
+
+function Builder(::Type{T}=Any, size=0) where {T}
+ objectend = 0
+ vtables = zeros(Int, 0)
+ head = size
+ nested = false
+ bytes = zeros(UInt8, size)
+ minalign = 1
+ vtable = zeros(Int, 0)
+ finished = false
+ b = Builder{T}(bytes, minalign, vtable, objectend,
+ vtables, head, nested, finished)
+ return b
+end
+
+# build!
+"`alignment` looks for the largest scalar member of `T` that represents a flatbuffer Struct"
+function alignment(::Type{T}) where {T}
+ largest = 0
+ for typ in T.types
+ largest = isbitstype(typ) ? max(largest,sizeof(typ)) : alignment(typ)
+ end
+ return largest
+end
+
+"""
+`buildvector!` is for building vectors with all kinds of element types,
+even building its elements recursively if needed (Array of Arrays, Array of tables, etc.).
+"""
+function buildvector! end
+
+# empty vector
+function buildvector!(b, A::Vector{Nothing}, len, prev)
+ startvector(b, 1, 0, 1)
+ return endvector(b, 0)
+end
+# scalar type vector
+function buildvector!(b, A::Vector{T}, len, prev) where {T <: Scalar}
+ startvector(b, sizeof(T), len, sizeof(T))
+ foreach(x->prepend!(b, A[x]), len:-1:1)
+ return endvector(b, len)
+end
+function buildvector!(b, A::Vector{T}, len, prev) where {T <: Enum}
+ startvector(b, sizeof(enumtype(T)), len, sizeof(enumtype(T)))
+ foreach(x->prepend!(b, enumtype(T)(A[x])), len:-1:1)
+ return endvector(b, len)
+end
+
+function putoffsetvector!(b, offsets, len)
+ startvector(b, 4, len, 4) #TODO: elsize/alignment correct here?
+ foreach(x->prependoffset!(b, offsets[x]), len:-1:1)
+ return endvector(b, len)
+end
+# byte vector vector
+function buildvector!(b, A::Vector{Vector{UInt8}}, len, prev)
+ offsets = map(x->createbytevector(b, A[x]), 1:len)
+ return putoffsetvector!(b, offsets, len)
+end
+# string vector
+function buildvector!(b, A::Vector{T}, len, prev) where {T <: AbstractString}
+ offsets = map(x->createstring(b, A[x]), 1:len)
+ return putoffsetvector!(b, offsets, len)
+end
+# array vector
+function buildvector!(b, A::Vector{Vector{T}}, len, prev) where {T}
+ offsets = map(x->buildbuffer!(b, A[x]), 1:len)
+ return putoffsetvector!(b, offsets, len)
+end
+
+# make a new struct which has fields of the given type
+function definestruct(types::Vector{DataType})
+ fields = [:($(gensym())::$(TT)) for TT in types]
+ T1 = gensym()
+ eval(:(mutable struct $T1
+ $(fields...)
+ end))
+ return T1
+end
+
+# make a new struct which has fields of the given type
+# and populate them with values from the vector
+function createstruct(types::Vector{DataType}, A::Vector{T}) where {T}
+ T1 = definestruct(types)
+ eval(:(newt = $T1($(A...))))
+ return newt
+end
+
+# struct or table/object vector
+function buildvector!(b, A::Vector{T}, len, prev) where {T}
+ if isstruct(T)
+ # struct
+ startvector(b, sizeof(T), len, alignment(T)) #TODO: forced elsize/alignment correct here?
+ foreach(x->buildbuffer!(b, A[x]), len:-1:1)
+ return endvector(b, len)
+ elseif isa(T, Union)
+ types = typeorder.(T, prev)
+
+ # define a new type, construct one, and pack it into the buffer
+ newt = createstruct(types, A)
+ buildbuffer!(b, newt)
+ else
+ # table/object
+ offsets = map(x->buildbuffer!(b, A[x]), 1:len)
+ return putoffsetvector!(b, offsets, len)
+ end
+end
+
+"""
+`getoffset` checks if a given field argument needs to be built
+offset (Arrays, Strings, other tables) or can be inlined (Scalar or Struct types).
+`getoffset` has a recursive nature in that it will build offset types
+down to their last leaf scalar types before returning the highest-level offset.
+"""
+function getoffset end
+
+getoffset(b, arg::Nothing, prev=nothing) = 0
+getoffset(b, arg::T, prev=nothing) where {T <: Scalar} = 0
+getoffset(b, arg::T, prev=nothing) where {T <: Enum} = 0
+getoffset(b, arg::AbstractString, prev=nothing) = createstring(b, arg)
+getoffset(b, arg::Vector{UInt8}, prev) = createbytevector(b, arg)
+getoffset(b, arg::Vector{T}, prev) where {T} = buildbuffer!(b, arg, prev)
+
+# structs or table/object
+getoffset(b, arg::T, prev=nothing) where {T} = isstruct(T) ? 0 : buildbuffer!(b, arg, prev)
+
+"""
+`putslot!` is one of the final steps in building a flatbuffer.
+It puts the final value in the "data" section of the flatbuffer,
+whether that be an actual value (for `Scalar`, Struct types) or an offset
+to the actual data (Arrays, Strings, other tables)
+"""
+function putslot! end
+
+putslot!(b, i, arg::T, off, default, prev) where {T <: Scalar} = prependslot!(b, i, arg, default)
+putslot!(b, i, arg::T, off, default, prev) where {T <: Enum} = prependslot!(b, i, enumtype(T)(arg), default)
+putslot!(b, i, arg::AbstractString, off, default, prev) = prependoffsetslot!(b, i, off, 0)
+putslot!(b, i, arg::Vector{T}, off, default, prev) where {T} = prependoffsetslot!(b, i, off, 0)
+# structs or table/object
+function putslot!(b, i, arg::T, off, default, prev) where {T}
+ if isstruct(T)
+ prependstructslot!(b, i, buildbuffer!(b, arg, prev), 0)
+ else
+ prependoffsetslot!(b, i, off, 0)
+ end
+end
+
+function needreconstruct(T)
+ for TT in T.types
+ if TT <: Vector && eltype(TT) isa Union && !(eltype(TT) isa UnionAll)
+ return true
+ elseif TT isa Union && !isunionwithnothing(TT)
+ return true
+ end
+ end
+ return false
+end
+
+function reconstructkwargs(arg::T) where {T}
+ kwargs = Dict{Symbol, Any}()
+ numfields = length(T.types)
+ fnames = fieldnames(T)
+ for i = 2:numfields
+ field = getfield(arg, i)
+ prevname = fnames[i - 1]
+ # hack to make the example work
+ TT = field isa Vector ? eltype(field) : typeof(field)
+ if :parameters in propertynames(TT) && length(TT.parameters) > 0
+ TT = TT.name.wrapper
+ end
+ if field isa Vector && eltype(field) isa Union && !(eltype(field) isa UnionAll)
+ kwargs[prevname] = [FlatBuffers.typeorder(TT, typeof(x)) for x in field]
+ elseif (T.types[i] isa Union && !isunionwithnothing(T.types[i]))
+ kwargs[prevname] = FlatBuffers.typeorder(T.types[i], TT)
+ end
+ end
+ return kwargs
+end
+
+function buildbuffer!(b::Builder{T1}, arg::T, prev=nothing) where {T1<:Any, T<:Array}
+ # array of things
+ buildvector!(b, arg, length(arg), prev)
+end
+
+function buildbuffer!(b::Builder{T1}, arg::T, prev=nothing) where {T1<:Any, T<:Any}
+ # populate the _type field before unions/vectors of unions
+ if needreconstruct(T)
+ # reconstruct it so the types before the fields
+ # are populated correctly
+ kwargs = reconstructkwargs(arg)
+ arg = Parameters.reconstruct(arg; kwargs...)
+ end
+ if isstruct(T)
+ # build a struct type with provided `arg`
+ all(isstruct, T.types) || throw(ArgumentError("can't seralize flatbuffer, $T is not a pure struct"))
+ align = alignment(T)
+ prep!(b, align, 2align)
+ for i = length(T.types):-1:1
+ typ = T.types[i]
+ if typ <: Enum
+ prepend!(b, enumtype(typ)(getfield(arg,i)))
+ elseif isbitstype(typ)
+ prepend!(b, getfield(arg,i))
+ else
+ buildbuffer!(b, getfield(arg, i), getprevfieldvalue(arg, i))
+ end
+ end
+ n = offset(b)
+ else
+ # build a table type
+ # check for string/array/table types
+ numfields = length(T.types)
+ # early exit for empty objects
+ if numfields == 0
+ startobject(b, 0)
+ return endobject(b)
+ end
+ os = Int[]
+ isdefault = falses(numfields)
+ for i = 1:numfields
+ push!(os, getoffset(b, getfieldvalue(arg, i), getprevfieldvalue(arg, i)))
+ end
+ # all nested have been written, with offsets in `os[]`
+ # don't use slots for the last N members if they are all default
+ # also leave slots for deprecated fields
+ i = numfields
+ isdefault = getfieldvalue(arg, i) == default(T, i)
+ while isdefault && i > 0
+ i -= 1
+ isdefault = getfieldvalue(arg, i) == default(T, i)
+ end
+ soff = slot_offsets(T)
+ numslots = div(soff[i] - 4, 2) + 1
+ startobject(b, numslots)
+ i = 1
+ field = 1
+ while i <= numslots
+ # leave holes for deprecated fields
+ j = 2
+ start = field == 1 ? soff[1] : soff[field - 1]
+ while (start + j) < soff[field]
+ # empty slot
+ i += 1
+ j += 2
+ end
+ val = getfieldvalue(arg, field)
+ d = default(T, field)
+ if !(isunionwithnothing(T.types[field]) && val == nothing)
+ putslot!(b, i,
+ val,
+ os[field],
+ d,
+ getprevfieldvalue(arg, field)
+ )
+ end
+ field += 1
+ i += 1
+ end
+ n = endobject(b)
+ end
+ return n
+end
+
+function build!(b, arg)
+ n = buildbuffer!(b, arg)
+ finish!(b, n)
+ return b
+end
+
+build!(arg::T) where {T} = build!(Builder(T), arg)
+
+include("macros.jl")
+
+end # module
diff --git a/julia/src/internals.jl b/julia/src/internals.jl
new file mode 100644
index 00000000000..054b573d29e
--- /dev/null
+++ b/julia/src/internals.jl
@@ -0,0 +1,453 @@
+# table.go
+const VtableMetadataFields = 2
+
+const TableOrBuilder = Union{Table,Builder}
+
+const Bytes2Type = Dict{Int, DataType}(1=>UInt8, 2=>UInt16, 4=>UInt32, 8=>UInt64)
+
+Base.get(t::TableOrBuilder, pos, ::Type{T}) where {T} = read(IOBuffer(view(t.bytes, (pos+1):length(t.bytes))), T)
+readbuffer(t::AbstractVector{UInt8}, pos::Int, ::Type{T}) where {T} = read(IOBuffer(view(t, (pos+1):length(t))), T)
+Base.get(t::TableOrBuilder, pos, ::Type{T}) where {T <: Enum} = T(read(IOBuffer(view(t.bytes, (pos+1):length(t.bytes))), Bytes2Type[sizeof(T)]))
+
+"""
+`offset` provides access into the Table's vtable.
+
+Deprecated fields are ignored by checking against the vtable's length.
+"""
+function offset(t::Table, vtableoffset)
+ vtable = t.pos - get(t, t.pos, Int32)
+ return vtableoffset < get(t, vtable, Int16) ? get(t, vtable + vtableoffset, Int16) : 0
+end
+
+"`indirect` retrieves the relative offset stored at `offset`."
+indirect(t::Table, off) = off + get(t, off, Int32)
+
+"""
+`vectorlen` retrieves the length of the vector whose offset is stored at
+`off` in this object.
+"""
+function vectorlen(t::Table, off)
+ off += t.pos
+ off += get(t, off, Int32)
+ return get(t, off, Int32)
+end
+
+"""
+`vector` retrieves the start of data of the vector whose offset is stored
+at `off` in this object.
+"""
+function vector(t::Table, off)
+ off += t.pos
+ off += get(t, off, Int32)
+ # data starts after metadata containing the vector length
+ return off + sizeof(Int32)
+end
+
+"""
+GetVOffsetTSlot retrieves the VOffsetT that the given vtable location
+points to. If the vtable value is zero, the default value `d`
+will be returned.
+"""
+function getoffsetslot(t::Table, slot, d)
+ off = offset(t, slot)
+ if off == 0
+ return d
+ end
+ return off
+end
+
+"""
+`getslot` retrieves the `T` that the given vtable location
+points to. If the vtable value is zero, the default value `d`
+will be returned.
+"""
+function getslot(t::Table, slot, d::T) where {T}
+ off = offset(t, slot)
+ if off == 0
+ return d
+ end
+
+ return get(t, t.pos + off, T)
+end
+
+# builder.go
+value(x::T) where {T <: Enum} = length(T.types) == 0 ? Int(x) : getfield(x,1)
+
+Base.write(sink::Builder, o, x::Union{Bool,UInt8}) = sink.bytes[o+1] = UInt8(x)
+function Base.write(sink::Builder, off, x::T) where {T}
+ off += 1
+ for (i,ind) = enumerate(off:(off + sizeof(T) - 1))
+ sink.bytes[ind] = (x >> ((i-1) * 8)) % UInt8
+ end
+end
+Base.write(b::Builder, o, x::Float32) = write(b, o, reinterpret(UInt32, x))
+Base.write(b::Builder, o, x::Float64) = write(b, o, reinterpret(UInt64, x))
+Base.write(b::Builder, o, x::Enum) = write(b, o, reinterpret(Bytes2Type[sizeof(x)], value(x)))
+
+"Offset relative to the end of the buffer."
+offset(b::Builder) = length(b.bytes) - b.head
+
+pad!(b::Builder, n) = foreach(x->place!(b, 0x00), 1:n)
+
+"""
+`finishedbytes` returns a pointer to the written data in the byte buffer.
+Panics if the builder is not in a finished state (which is caused by calling
+`finish!()`).
+"""
+function finishedbytes(b::Builder)
+ assertfinished(b)
+ return b.bytes[b.head+1:end]
+end
+
+function startobject(b::Builder, numslots)
+ assertnotnested(b)
+ b.nested = true
+ b.vtable = zeros(Int, numslots)
+ b.objectend = offset(b)
+ b.minalign = 1
+ return b
+end
+
+"""
+`endobject` writes data necessary to finish object construction.
+"""
+function endobject(b::Builder{T}) where {T}
+ assertnested(b)
+ n = writevtable!(b)
+ b.nested = false
+ return n
+end
+
+"""
+`prep!` prepares to write an element of `size` after `additionalbytes`
+have been written, e.g. if you write a string, you need to align such
+the int length field is aligned to sizeof(Int32), and the string data follows it
+directly.
+If all you need to do is align, `additionalbytes` will be 0.
+"""
+function prep!(b::Builder, size, additionalbytes)
+ # Track the biggest thing we've ever aligned to.
+ if size > b.minalign
+ b.minalign = size
+ end
+ # Find the amount of alignment needed such that `size` is properly
+ # aligned after `additionalBytes`:
+ alignsize = xor(Int(-1), (length(b.bytes) - b.head) + additionalbytes) + 1
+ alignsize &= (size - 1)
+
+ # Reallocate the buffer if needed:
+ totalsize = alignsize + size + additionalbytes
+ if b.head <= totalsize
+ len = length(b.bytes)
+ prepend!(b.bytes, zeros(UInt8, totalsize))
+ b.head += length(b.bytes) - len
+ end
+ pad!(b, alignsize)
+ return
+end
+
+"""
+`prepend!` prepends a `T` to the Builder buffer.
+Aligns and checks for space.
+"""
+function Base.prepend!(b::Builder, x::T) where {T}
+ prep!(b, sizeof(T), 0)
+ place!(b, x)
+ return
+end
+
+"""
+`place!` prepends a `T` to the Builder, without checking for space.
+"""
+function place!(b::Builder, x::T) where {T}
+ b.head -= sizeof(T)
+ write(b, b.head, x)
+ return
+end
+
+"""
+`startvector` initializes bookkeeping for writing a new vector.
+
+A vector has the following format:
+
++, where T is the type of elements of this vector.
+"""
+function startvector(b::Builder, elemSize, numElems, alignment)
+ assertnotnested(b)
+ b.nested = true
+ prep!(b, sizeof(UInt32), elemSize * numElems)
+ prep!(b, alignment, elemSize * numElems)
+ return offset(b)
+end
+
+"""
+`endvector` writes data necessary to finish vector construction.
+"""
+function endvector(b::Builder, vectorNumElems)
+ assertnested(b)
+ place!(b, UInt32(vectorNumElems))
+ b.nested = false
+ return offset(b)
+end
+
+"""
+`createstring` writes a null-terminated string as a vector.
+"""
+function createstring(b::Builder, s::AbstractString)
+ assertnotnested(b)
+ b.nested = true
+ s = codeunits(s)
+ prep!(b, sizeof(UInt32), length(s) + 1)
+ place!(b, UInt8(0))
+
+ l = length(s)
+
+ b.head -= l
+ copyto!(b.bytes, b.head+1, s, 1, l)
+ return endvector(b, length(s))
+end
+
+"""
+`createbytevector` writes a byte vector
+"""
+function createbytevector(b::Builder, v::AbstractVector{UInt8})
+ assertnotnested(b)
+ b.nested = true
+
+ prep!(b, sizeof(UInt32), length(v))
+
+ l = length(v)
+
+ b.head -= l
+ copyto!(b.bytes, b.head+1, v, 1, l)
+
+ return endvector(b, length(v))
+end
+
+"""
+`prependoffset!` prepends an Int32, relative to where it will be written.
+"""
+function prependoffset!(b::Builder, off)
+ prep!(b, sizeof(Int32), 0) # Ensure alignment is already done.
+ if !(off <= offset(b))
+ throw(ArgumentError("unreachable: $off <= $(offset(b))"))
+ end
+ off2 = offset(b) - off + sizeof(Int32)
+ place!(b, Int32(off2))
+ return
+end
+
+function prependoffsetslot!(b::Builder, o::Int, x::T, d) where {T}
+ if x != T(d)
+ prependoffset!(b, x)
+ slot!(b, o)
+ end
+ return
+end
+
+"""
+`prependslot!` prepends a `T` onto the object at vtable slot `o`.
+If value `x` equals default `d`, then the slot will be set to zero and no
+other data will be written.
+"""
+function prependslot!(b::Builder, o::Int, x::T, d) where {T}
+ if x != T(d)
+ prepend!(b, x)
+ slot!(b, o)
+ end
+ return
+end
+
+"""
+`prependstructslot!` prepends a struct onto the object at vtable slot `o`.
+Structs are stored inline, so nothing additional is being added.
+In generated code, `d` is always 0.
+"""
+function prependstructslot!(b::Builder, voffset, x, d)
+ if x != d
+ assertnested(b)
+ if x != offset(b)
+ throw(ArgumentError("inline data write outside of object"))
+ end
+ slot!(b, voffset)
+ end
+ return
+end
+
+"""
+`slot!` sets the vtable key `voffset` to the current location in the buffer.
+"""
+function slot!(b::Builder, slotnum)
+ b.vtable[slotnum] = offset(b)
+end
+
+"""
+`finish!` finalizes a buffer, pointing to the given `rootTable`.
+"""
+function finish!(b::Builder{T}, rootTable) where {T}
+ assertnotnested(b)
+ identifier = file_identifier(T)
+ n = length(identifier)
+ prep!(b, b.minalign, sizeof(UInt32))
+ for i = 0:(n-1)
+ prepend!(b, UInt8(identifier[n - i]))
+ end
+ prependoffset!(b, Int32(rootTable))
+ b.finished = true
+ return
+end
+
+function assertnested(b::Builder)
+ # If you get this assert, you're in an object while trying to write
+ # data that belongs outside of an object.
+ # To fix this, write non-inline data (like vectors) before creating
+ # objects.
+ if !b.nested
+ throw(ArgumentError("Incorrect creation order: must be inside object."))
+ end
+ return
+end
+
+function assertnotnested(b::Builder)
+ # If you hit this, you're trying to construct a Table/Vector/String
+ # during the construction of its parent table (between the MyTableBuilder
+ # and builder.Finish()).
+ # Move the creation of these view-objects to above the MyTableBuilder to
+ # not get this assert.
+ # Ignoring this assert may appear to work in simple cases, but the reason
+ # it is here is that storing objects in-line may cause vtable offsets
+ # to not fit anymore. It also leads to vtable duplication.
+ if b.nested
+ throw(ArgumentError("Incorrect creation order: object must not be nested."))
+ end
+ return
+end
+
+function assertfinished(b::Builder)
+ # If you get this assert, you're attempting to get access a buffer
+ # which hasn't been finished yet. Be sure to call builder.Finish()
+ # with your root table.
+ # If you really need to access an unfinished buffer, use the bytes
+ # buffer directly.
+ if !b.finished
+ throw(ArgumentError("Incorrect use of FinishedBytes(): must call 'Finish' first."))
+ end
+end
+
+"""
+WriteVtable serializes the vtable for the current object, if applicable.
+
+Before writing out the vtable, this checks pre-existing vtables for equality
+to this one. If an equal vtable is found, point the object to the existing
+vtable and return.
+
+Because vtable values are sensitive to alignment of object data, not all
+logically-equal vtables will be deduplicated.
+
+A vtable has the following format:
+
+
+ * N, where N is the number of fields in
+the schema for this type. Includes deprecated fields.
+Thus, a vtable is made of 2 + N elements, each SizeVOffsetT bytes wide.
+
+An object has the following format:
+
++
+"""
+function writevtable!(b::Builder{T}) where {T}
+ # Prepend a zero scalar to the object. Later in this function we'll
+ # write an offset here that points to the object's vtable:
+ prepend!(b, Int32(0))
+
+ objectOffset = offset(b)
+ existingVtable = 0
+
+ # Search backwards through existing vtables, because similar vtables
+ # are likely to have been recently appended. See
+ # BenchmarkVtableDeduplication for a case in which this heuristic
+ # saves about 30% of the time used in writing objects with duplicate
+ # tables.
+ for i = length(b.vtables):-1:1
+ # Find the other vtable, which is associated with `i`:
+ vt2Offset = b.vtables[i]
+ vt2Start = length(b.bytes) - vt2Offset
+ vt2Len = readbuffer(b.bytes, vt2Start, Int16)
+
+ metadata = VtableMetadataFields * sizeof(Int16)
+ vt2End = vt2Start + vt2Len
+ vt2 = view(b.bytes, (vt2Start + metadata + 1):vt2End) #TODO: might need a +1 on the start of range here
+
+ # Compare the other vtable to the one under consideration.
+ # If they are equal, store the offset and break:
+ if vtableEqual(b.vtable, objectOffset, vt2)
+ existingVtable = vt2Offset
+ break
+ end
+ end
+
+ if existingVtable == 0
+ # Did not find a vtable, so write this one to the buffer.
+
+ # Write out the current vtable in reverse , because
+ # serialization occurs in last-first order:
+ for i = length(b.vtable):-1:1
+ off::Int16 = 0
+ if b.vtable[i] != 0
+ # Forward reference to field;
+ # use 32bit number to assert no overflow:
+ off = objectOffset - b.vtable[i]
+ end
+ prepend!(b, Int16(off))
+ end
+
+ # The two metadata fields are written last.
+
+ # First, store the object bytesize:
+ objectSize::Int16 = objectOffset - b.objectend
+ prepend!(b, objectSize)
+
+ # Second, store the vtable bytesize:
+ vbytes::Int16 = (length(b.vtable) + VtableMetadataFields) * sizeof(Int16)
+ prepend!(b, vbytes)
+
+ # Next, write the offset to the new vtable in the
+ # already-allocated SOffsetT at the beginning of this object:
+ objectStart::Int32 = length(b.bytes) - objectOffset
+ write(b, objectStart, Int32(offset(b) - objectOffset))
+
+ # Finally, store this vtable in memory for future
+ # deduplication:
+ push!(b.vtables, offset(b))
+ else
+ # Found a duplicate vtable.
+
+ objectStart = length(b.bytes) - objectOffset
+ b.head = objectStart
+
+ # Write the offset to the found vtable in the
+ # already-allocated SOffsetT at the beginning of this object:
+ write(b, b.head, Int32(existingVtable - objectOffset))
+ end
+
+ empty!(b.vtable)
+ return objectOffset
+end
+
+"vtableEqual compares an unwritten vtable to a written vtable."
+function vtableEqual(a::Vector{Int}, objectStart, b::AbstractVector{UInt8})
+ if length(a) * sizeof(Int16) != length(b)
+ return false
+ end
+
+ for i = 0:(length(a)-1)
+ x = read(IOBuffer(view(b, (i * sizeof(Int16) + 1):length(b))), Int16)
+
+ # Skip vtable entries that indicate a default value.
+ x == 0 && a[i+1] == 0 && continue
+
+ y = objectStart - a[i+1]
+ x != y && return false
+ end
+ return true
+end
diff --git a/julia/src/macros.jl b/julia/src/macros.jl
new file mode 100644
index 00000000000..2ae6e3a2690
--- /dev/null
+++ b/julia/src/macros.jl
@@ -0,0 +1,232 @@
+export @UNION, @DEFAULT, @ALIGN, @STRUCT, @with_kw
+
+const __module__ = 0
+
+function indexof(needle, haystack)
+ for (i, v) in enumerate(haystack)
+ v == needle && return i-1
+ end
+ return -1
+end
+
+macro UNION(T, TT)
+ typeof(T) == Symbol || throw(ArgumentError("1st argument must be a symbol to represent a Union type"))
+ TT.head == :tuple || throw(ArgumentError("2nd argument must be a tuple of types like `(T1,T2,...)`"))
+ return esc(quote
+ const $T = $(Expr(:curly, :Union, TT.args...))
+ FlatBuffers.typeorder(::Type{$T}, ::Type{TT}) where {TT} = FlatBuffers.indexof(TT, $TT)
+ FlatBuffers.typeorder(::Type{$T}, i::Integer) = ($TT)[i+1]
+ FlatBuffers.isunionwithnothing(::Type{$T}) = false
+ end)
+end
+
+macro ALIGN(T, sz)
+ return esc(quote
+ FlatBuffers.alignment(::Type{$T}) = $sz
+ end)
+end
+
+macro enumtype(T, typ)
+ return esc(quote
+ FlatBuffers.enumtype(::Type{$T}) = $typ
+ end)
+end
+
+# recursively finds largest field of a STRUCT
+fbsizeof(::Type{T}) where {T<:Enum} = sizeof(enumtype(T))
+fbsizeof(::Type{T}) where {T} = sizeof(T)
+
+maxsizeof(::Type{T}) where {T<:Enum} = sizeof(enumtype(T))
+maxsizeof(::Type{T}) where {T} = isbitstype(T) ? sizeof(T) : maximum(map(x->maxsizeof(x), T.types))
+
+nextsizeof(::Type{T}) where {T} = isbitstype(T) ? sizeof(T) : nextsizeof(T.types[1])
+
+function fieldlayout(mod, typ, exprs...)
+ fields = Expr[]
+ values = []
+ largest_field = maximum(map(x->maxsizeof(Core.eval(mod, x.args[2])), exprs))
+ sz = cur_sz = 0
+ x = 0
+ for (i,expr) in enumerate(exprs)
+ T = Core.eval(mod, expr.args[2])
+ if !isbitstype(T)
+ exprs2 = [Expr(:(::), nm, typ) for (nm,typ) in zip(fieldnames(T),T.types)]
+ fields2, values2 = fieldlayout(mod, T, exprs2...)
+ append!(fields, map(x->Expr(:(::), Symbol(string(expr.args[1],'_',x.args[1])), x.args[2]), fields2))
+ append!(values, map(x->x == 0 ? 0 : Expr(:call, :getfield, expr.args[1], QuoteNode(x)), values2))
+ else
+ push!(fields, expr)
+ push!(values, expr.args[1])
+ end
+ sz += cur_sz = fbsizeof(T)
+ if sz % largest_field == 0
+ sz = cur_sz = 0
+ continue
+ end
+ nextsz = i == length(exprs) ? 0 : nextsizeof(Core.eval(mod, exprs[i+1].args[2]))
+ if i == length(exprs) || cur_sz < nextsz || (sz + nextsz) > largest_field
+ # this is the last field and we're not `sz % largest_field`
+ # potential diffs = 7, 6, 5, 4, 3, 2, 1
+ sym = expr.args[1]
+ diff = cur_sz < nextsz ? nextsz - cur_sz : largest_field - sz
+ if diff == 7
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt8)); x += 1
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt16)); x += 1
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt32)); x += 1
+ push!(values, 0); push!(values, 0); push!(values, 0)
+ elseif diff == 6
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt16)); x += 1
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt32)); x += 1
+ push!(values, 0); push!(values, 0)
+ elseif diff == 5
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt8)); x += 1
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt32)); x += 1
+ push!(values, 0); push!(values, 0)
+ elseif diff == 4
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt32)); x += 1
+ push!(values, 0)
+ elseif diff == 3
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt8)); x += 1
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt16)); x += 1
+ push!(values, 0); push!(values, 0)
+ elseif diff == 2
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt16)); x += 1
+ push!(values, 0)
+ elseif diff == 1
+ push!(fields, Expr(:(::), Symbol("_pad_$(sym)_$(typ)_$x"), :UInt8)); x += 1
+ push!(values, 0)
+ end
+ sz = (sz + diff) % largest_field == 0 ? 0 : (cur_sz < nextsz ? sz + diff : 0)
+ cur_sz = 0
+ end
+ end
+ return fields, values
+end
+
+linefilter = x->typeof(x) != LineNumberNode
+
+macro STRUCT(expr)
+ !expr.args[1] || throw(ArgumentError("@STRUCT is only applicable for immutable types"))
+ exprs = filter(linefilter, expr.args[3].args)
+ fields, values = FlatBuffers.fieldlayout(__module__, expr.args[2], exprs...)
+ expr.args[3].args = fields
+ # generate convenience outer constructors if necessary
+ # if there are nested structs or padding:
+ # build an outer constructor that takes all direct, original fields
+ # recursively flatten/splat all nested structs into one big args tuple
+ # adding zeros for padded arguments
+ # pass big, flat, args tuple to inner constructor
+ T = expr.args[2]
+ if any(x->!FlatBuffers.isbitstype(Core.eval(__module__, x.args[2])), exprs) ||
+ length(fields) > length(exprs)
+ exprs2 = map(x->FlatBuffers.isbitstype(Core.eval(__module__, x.args[2])) ? x.args[1] : x, exprs)
+ sig = Expr(:call, T, exprs2...)
+ body = Expr(:call, T, values...)
+ outer = Expr(:function, sig, body)
+ else
+ outer = :(nothing)
+ end
+ return esc(quote
+ $expr
+ $outer
+ end)
+end
+
+macro DEFAULT(T, kwargs...)
+ ifblock = quote end
+ if length(kwargs) > 0 && isa(kwargs[1], Expr) && length(kwargs[1].args) > 1
+ for kw in kwargs
+ push!(ifblock.args, :(if sym == $(QuoteNode(kw.args[1]))
+ return $(kw.args[2])
+ end))
+ end
+ end
+ esc(quote
+ if $T <: Enum
+ FlatBuffers.default(::Type{$T}) = FlatBuffers.enumtype($T)($(kwargs[1]))
+ else
+ function FlatBuffers.default(::Type{$T}, TT, sym)
+ $ifblock
+ return FlatBuffers.default(TT)
+ end
+ end
+ end)
+end
+
+import Parameters
+
+function getdef(typedef::Expr)
+ isstructexpr(x) = x isa Expr && x.head == :struct
+ i = findfirst(x -> x isa Expr && any(isstructexpr.(x.args)), typedef.args)
+ if i == nothing
+ throw(ArgumentError("malformed @with_kw expression"))
+ end
+ wrapper = typedef.args[i]
+ i = findfirst(isstructexpr, wrapper.args)
+ def = wrapper.args[i]
+ return def
+end
+
+function getfielddefs(typedef::Expr)
+ [a for a in getdef(typedef).args[end].args if a isa Expr && a.head == :(::)]
+end
+
+function getconstructor(typedef::Expr)
+ cons = getdef(typedef).args[end].args[end-1].args[1]
+ typevars = []
+ if :head in propertynames(cons) && cons.head == :where
+ typevars = cons.args[2:end]
+ cons = cons.args[1]
+ end
+ cons, typevars
+end
+
+function getkwtype(defs, name)
+ for d in defs
+ if :args in propertynames(d) && d.args[1] == name && d.args[end] isa Symbol
+ return d.args[end]
+ end
+ end
+ return nothing
+end
+
+function createdefaultfns(typedef::Expr)
+ cons, typevars = getconstructor(typedef)
+ T = cons.args[1]
+ params = cons.args[2]
+
+ @assert params.head == :parameters
+
+ kwargs = []
+ defs = getfielddefs(typedef)
+ kwdict = Dict{Any, Any}()
+ for p in params.args
+ name = p.args[1]
+ t = getkwtype(defs, name)
+ if t == nothing
+ continue
+ end
+ value = p.args[end]
+ ifblock = get(kwdict, t, quote end)
+ push!(ifblock.args, :(if sym == $(QuoteNode(name))
+ return convert($t, $value)
+ end))
+ kwdict[t] = ifblock
+ end
+
+ [:(function FlatBuffers.default(::Type{$T}, ::Type{$TT}, sym) where {$(typevars...)}
+ $(kwdict[TT])
+ return FlatBuffers.default($TT)
+ end) for TT in keys(kwdict)]
+end
+
+macro with_kw(typedef)
+ body = Parameters.with_kw(typedef, __module__, true)
+ defaults = createdefaultfns(body)
+ defaultsblock = Expr(:block, body, defaults...)
+ esc(defaultsblock)
+end
+
+#TODO:
+# handle id?
+# nested_flatbuffer
diff --git a/julia/test/defaults.jl b/julia/test/defaults.jl
new file mode 100644
index 00000000000..ee978f8da3d
--- /dev/null
+++ b/julia/test/defaults.jl
@@ -0,0 +1,40 @@
+using FlatBuffers
+using Test
+import Parameters
+
+# test default fields
+@with_kw mutable struct UltimateAnswer
+ answer::Int32 = 42
+ question::String
+ highwaysbuilt::Int32 = 7
+end
+
+x = UltimateAnswer(;question="How many roads must a man walk down?")
+@test x.answer == 42
+@test FlatBuffers.default(UltimateAnswer, Int32, :answer) == 42
+@test FlatBuffers.default(UltimateAnswer, Int32, :highwaysbuilt) == 7
+b = FlatBuffers.Builder(UltimateAnswer)
+FlatBuffers.build!(b, x)
+xbytes = FlatBuffers.bytes(b)
+y = FlatBuffers.read(UltimateAnswer, xbytes)
+
+@test y.answer == x.answer
+@test y.question == x.question
+@test y.highwaysbuilt == x.highwaysbuilt
+@test x.highwaysbuilt == 7
+
+y = Parameters.reconstruct(x, highwaysbuilt = 0)
+b = FlatBuffers.Builder(UltimateAnswer)
+FlatBuffers.build!(b, y)
+ybytes = FlatBuffers.bytes(b)
+
+# check that we save bytes with default integer values
+@test length(ybytes) > length(xbytes)
+
+@test y.answer == x.answer
+@test y.question == x.question
+@test y.highwaysbuilt == 0
+
+y = FlatBuffers.read(UltimateAnswer, ybytes)
+@test y.highwaysbuilt == 0
+
diff --git a/julia/test/flatc.jl b/julia/test/flatc.jl
new file mode 100644
index 00000000000..68bf44316dc
--- /dev/null
+++ b/julia/test/flatc.jl
@@ -0,0 +1,85 @@
+using Test
+import FlatBuffers
+
+# generated code
+include(joinpath(@__DIR__, "..", "..", "tests", "monster_test_generated.jl"))
+import .MyGame
+import .MyGame.Example
+import .MyGame.Example2
+import .MyGame.Example.Any_
+import .MyGame.Example.Monster
+import .MyGame.Example.TestSimpleTableWithEnum
+
+loadmonsterfile(filename) = open(joinpath(@__DIR__, filename), "r") do f Monster(f) end
+
+function checkmonster(monster)
+ @test monster.hp == 80
+ @test monster.mana == 150
+ @test monster.name == "MyMonster"
+
+ vec = monster.pos
+
+ @test vec.x == 1.0
+ @test vec.y == 2.0
+ @test vec.z == 3.0
+ @test vec.test1 == 3.0
+ @test vec.test2 == MyGame.Example.ColorGreen
+ @test vec.test3_a == 5
+ @test vec.test3_b == 6
+
+ monster2 = monster.test
+ @test monster2.name == "Fred"
+
+ @test length(monster.inventory) == 5
+ @test sum(monster.inventory) == 10
+
+ @test monster.vector_of_longs == [10 ^ (2*i) for i = 0:4]
+ @test monster.vector_of_doubles == [-1.7976931348623157e+308, 0, 1.7976931348623157e+308]
+
+ @test length(monster.test4) == 2
+
+ (test0, test1) = monster.test4
+ @test sum([test0.a, test0.b, test1.a, test1.b]) == 100
+
+ @test monster.testarrayofstring == ["test1", "test2"]
+ @test monster.testarrayoftables == []
+ @test monster.testf == 3.14159f0
+end
+
+function checkpassthrough(monster)
+ b = FlatBuffers.Builder(Monster)
+ FlatBuffers.build!(b, monster)
+ bytes = FlatBuffers.bytes(b)
+ @test FlatBuffers.has_identifier(Monster, bytes)
+ newmonster = FlatBuffers.read(Monster, bytes)
+ checkmonster(newmonster)
+end
+
+function checkserialize(monster)
+ io = IOBuffer()
+ FlatBuffers.serialize(io, monster)
+ bytes = take!(io)
+ newmonster = FlatBuffers.deserialize(IOBuffer(bytes), Monster)
+ checkmonster(newmonster)
+end
+
+@test FlatBuffers.root_type(Monster) == true
+@test FlatBuffers.file_identifier(Monster) == "MONS"
+@test FlatBuffers.file_extension(Monster) == "mon"
+
+for testcase in ["test", "python_wire"]
+ mon = loadmonsterfile("monsterdata_$testcase.mon")
+ checkmonster(mon)
+ checkpassthrough(mon)
+ checkserialize(mon)
+end
+
+# test printing
+mon = loadmonsterfile("monsterdata_test.mon")
+b = FlatBuffers.Builder(Monster)
+FlatBuffers.build!(b, mon)
+io = IOBuffer()
+show(io, b)
+output = String(take!(io))
+@test occursin("deprecated field", split(output, "\n")[9])
+
diff --git a/julia/test/internals.jl b/julia/test/internals.jl
new file mode 100644
index 00000000000..392f7ca7e93
--- /dev/null
+++ b/julia/test/internals.jl
@@ -0,0 +1,811 @@
+# Store specific byte patterns in these variables for the fuzzer. These
+# values are taken verbatim from the C++ function FuzzTest1.
+const overflowingInt32Val = read(IOBuffer(UInt8[0x83, 0x33, 0x33, 0x33]), Int32)
+const overflowingInt64Val = read(IOBuffer(UInt8[0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44]), Int64)
+
+# CheckByteLayout verifies the bytes of a Builder in various scenarios.
+function CheckByteLayout()
+ check = want-> begin
+ got = b.bytes[b.head+1:end]
+ @test want == got
+ return
+end
+
+# test 1: numbers
+
+b = FlatBuffers.Builder()
+check(UInt8[])
+FlatBuffers.prepend!(b, true)
+check(UInt8[1])
+FlatBuffers.prepend!(b, Int8(-127))
+check(UInt8[129, 1])
+FlatBuffers.prepend!(b, UInt8(255))
+check(UInt8[255, 129, 1])
+FlatBuffers.prepend!(b, Int16(-32222))
+check(UInt8[0x22, 0x82, 0, 255, 129, 1]) # first pad
+FlatBuffers.prepend!(b, UInt16(0xFEEE))
+check(UInt8[0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1]) # no pad this time
+FlatBuffers.prepend!(b, Int32(-53687092))
+check(UInt8[204, 204, 204, 252, 0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1])
+FlatBuffers.prepend!(b, UInt32(0x98765432))
+check(UInt8[0x32, 0x54, 0x76, 0x98, 204, 204, 204, 252, 0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1])
+
+# test 1b: numbers 2
+
+b = FlatBuffers.Builder()
+prepend!(b, 0x1122334455667788)
+check(UInt8[0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11])
+
+# test 2: 1xbyte vector
+
+b = FlatBuffers.Builder()
+check(UInt8[])
+FlatBuffers.startvector(b, sizeof(Bool), 1, 1)
+check(UInt8[0, 0, 0]) # align to 4bytes
+FlatBuffers.prepend!(b, UInt8(1))
+check(UInt8[1, 0, 0, 0])
+FlatBuffers.endvector(b, 1)
+check(UInt8[1, 0, 0, 0, 1, 0, 0, 0]) # padding
+
+# test 3: 2xbyte vector
+
+b = FlatBuffers.Builder()
+FlatBuffers.startvector(b, sizeof(UInt8), 2, 1)
+check(UInt8[0, 0]) # align to 4bytes
+FlatBuffers.prepend!(b, UInt8(1))
+check(UInt8[1, 0, 0])
+FlatBuffers.prepend!(b, UInt8(2))
+check(UInt8[2, 1, 0, 0])
+FlatBuffers.endvector(b, 2)
+check(UInt8[2, 0, 0, 0, 2, 1, 0, 0]) # padding
+
+# test 3b: 11xbyte vector matches builder size
+
+b = FlatBuffers.Builder(Any, 12)
+FlatBuffers.startvector(b, sizeof(UInt8), 8, 1)
+start = UInt8[]
+check(start)
+for i = 1:11
+ FlatBuffers.prepend!(b, UInt8(i))
+ start = append!(UInt8[i], start)
+ check(start)
+end
+FlatBuffers.endvector(b, 8)
+check(append!(UInt8[8, 0, 0, 0], start))
+
+# test 4: 1xuint16 vector
+
+b = FlatBuffers.Builder()
+FlatBuffers.startvector(b, sizeof(UInt16), 1, 1)
+check(UInt8[0, 0]) # align to 4bytes
+FlatBuffers.prepend!(b, UInt16(1))
+check(UInt8[1, 0, 0, 0])
+FlatBuffers.endvector(b, 1)
+check(UInt8[1, 0, 0, 0, 1, 0, 0, 0]) # padding
+
+# test 5: 2xuint16 vector
+
+b = FlatBuffers.Builder()
+FlatBuffers.startvector(b, sizeof(UInt16), 2, 1)
+check(UInt8[]) # align to 4bytes
+FlatBuffers.prepend!(b, UInt16(0xABCD))
+check(UInt8[0xCD, 0xAB])
+FlatBuffers.prepend!(b, UInt16(0xDCBA))
+check(UInt8[0xBA, 0xDC, 0xCD, 0xAB])
+FlatBuffers.endvector(b, 2)
+check(UInt8[2, 0, 0, 0, 0xBA, 0xDC, 0xCD, 0xAB])
+
+# test 6: CreateString
+
+b = FlatBuffers.Builder()
+FlatBuffers.createstring(b, "foo")
+check(UInt8[3, 0, 0, 0, 'f', 'o', 'o', 0]) # 0-terminated, no pad
+FlatBuffers.createstring(b, "moop")
+check(UInt8[4, 0, 0, 0, 'm', 'o', 'o', 'p', 0, 0, 0, 0, # 0-terminated, 3-byte pad
+ 3, 0, 0, 0, 'f', 'o', 'o', 0])
+
+# test 6b: CreateString unicode
+
+b = FlatBuffers.Builder()
+# These characters are chinese from blog.golang.org/strings
+# We use escape codes here so that editors without unicode support
+# aren't bothered:
+uni_str = "\u65e5\u672c\u8a9e"
+FlatBuffers.createstring(b, uni_str)
+check(UInt8[9, 0, 0, 0, 230, 151, 165, 230, 156, 172, 232, 170, 158, 0, # null-terminated, 2-byte pad
+ 0, 0])
+
+# test 6c: CreateUInt8String
+
+b = FlatBuffers.Builder()
+FlatBuffers.createstring(b, "foo")
+check(UInt8[3, 0, 0, 0, 'f', 'o', 'o', 0]) # 0-terminated, no pad
+FlatBuffers.createstring(b, "moop")
+check(UInt8[4, 0, 0, 0, 'm', 'o', 'o', 'p', 0, 0, 0, 0, # 0-terminated, 3-byte pad
+ 3, 0, 0, 0, 'f', 'o', 'o', 0])
+
+# test 7: empty vtable
+b = FlatBuffers.Builder()
+FlatBuffers.startobject(b, 0)
+check(UInt8[])
+FlatBuffers.endobject(b)
+check(UInt8[4, 0, 4, 0, 4, 0, 0, 0])
+
+# test 8: vtable with one true bool
+b = FlatBuffers.Builder()
+check(UInt8[])
+FlatBuffers.startobject(b, 1)
+check(UInt8[])
+FlatBuffers.prependslot!(b, 1, true, false)
+FlatBuffers.endobject(b)
+check(UInt8[
+ 6, 0, # vtable bytes
+ 8, 0, # length of object including vtable offset
+ 7, 0, # start of bool value
+ 6, 0, 0, 0, # offset for start of vtable (int32)
+ 0, 0, 0, # padded to 4 bytes
+ 1, # bool value
+ ])
+
+# test 9: vtable with one default bool
+b = FlatBuffers.Builder()
+check(UInt8[])
+FlatBuffers.startobject(b, 1)
+check(UInt8[])
+FlatBuffers.prependslot!(b, 1, false, false)
+FlatBuffers.endobject(b)
+check(UInt8[
+ 6, 0, # vtable bytes
+ 4, 0, # end of object from here
+ 0, 0, # entry 1 is zero
+ 6, 0, 0, 0, # offset for start of vtable (int32)
+ ])
+
+# test 10: vtable with one int16
+b = FlatBuffers.Builder()
+FlatBuffers.startobject(b, 1)
+FlatBuffers.prependslot!(b, 1, Int16(0x789A), Int16(0))
+FlatBuffers.endobject(b)
+check(UInt8[
+ 6, 0, # vtable bytes
+ 8, 0, # end of object from here
+ 6, 0, # offset to value
+ 6, 0, 0, 0, # offset for start of vtable (int32)
+ 0, 0, # padding to 4 bytes
+ 0x9A, 0x78,
+ ])
+
+# test 11: vtable with two int16
+b = FlatBuffers.Builder()
+FlatBuffers.startobject(b, 2)
+FlatBuffers.prependslot!(b, 1, Int16(0x3456), Int16(0))
+FlatBuffers.prependslot!(b, 2, Int16(0x789A), Int16(0))
+FlatBuffers.endobject(b)
+check(UInt8[
+ 8, 0, # vtable bytes
+ 8, 0, # end of object from here
+ 6, 0, # offset to value 0
+ 4, 0, # offset to value 1
+ 8, 0, 0, 0, # offset for start of vtable (int32)
+ 0x9A, 0x78, # value 1
+ 0x56, 0x34, # value 0
+ ])
+
+# test 12: vtable with int16 and bool
+b = FlatBuffers.Builder()
+FlatBuffers.startobject(b, 2)
+FlatBuffers.prependslot!(b, 1, Int16(0x3456), Int16(0))
+FlatBuffers.prependslot!(b, 2, true, false)
+FlatBuffers.endobject(b)
+check(UInt8[
+ 8, 0, # vtable bytes
+ 8, 0, # end of object from here
+ 6, 0, # offset to value 0
+ 5, 0, # offset to value 1
+ 8, 0, 0, 0, # offset for start of vtable (int32)
+ 0, # padding
+ 1, # value 1
+ 0x56, 0x34, # value 0
+ ])
+
+# test 12: vtable with empty vector
+b = FlatBuffers.Builder()
+FlatBuffers.startvector(b, sizeof(UInt8), 0, 1)
+vecend = FlatBuffers.endvector(b, 0)
+FlatBuffers.startobject(b, 1)
+FlatBuffers.prependoffsetslot!(b, 1, UInt32(vecend), UInt32(0))
+FlatBuffers.endobject(b)
+check(UInt8[
+ 6, 0, # vtable bytes
+ 8, 0,
+ 4, 0, # offset to vector offset
+ 6, 0, 0, 0, # offset for start of vtable (int32)
+ 4, 0, 0, 0,
+ 0, 0, 0, 0, # length of vector (not in struct)
+ ])
+
+# test 12b: vtable with empty vector of byte and some scalars
+b = FlatBuffers.Builder()
+FlatBuffers.startvector(b, sizeof(UInt8), 0, 1)
+vecend = FlatBuffers.endvector(b, 0)
+FlatBuffers.startobject(b, 2)
+FlatBuffers.prependslot!(b, 1, Int16(55), Int16(0))
+FlatBuffers.prependoffsetslot!(b, 2, UInt32(vecend), UInt32(0))
+FlatBuffers.endobject(b)
+check(UInt8[
+ 8, 0, # vtable bytes
+ 12, 0,
+ 10, 0, # offset to value 0
+ 4, 0, # offset to vector offset
+ 8, 0, 0, 0, # vtable loc
+ 8, 0, 0, 0, # value 1
+ 0, 0, 55, 0, # value 0
+
+ 0, 0, 0, 0, # length of vector (not in struct)
+ ])
+
+# test 13: vtable with 1 int16 and 2-vector of int16
+b = FlatBuffers.Builder()
+FlatBuffers.startvector(b, sizeof(Int16), 2, 1)
+FlatBuffers.prepend!(b, Int16(0x1234))
+FlatBuffers.prepend!(b, Int16(0x5678))
+vecend = FlatBuffers.endvector(b, 2)
+FlatBuffers.startobject(b, 2)
+FlatBuffers.prependoffsetslot!(b, 2, UInt32(vecend), UInt32(0))
+FlatBuffers.prependslot!(b, 1, Int16(55), Int16(0))
+FlatBuffers.endobject(b)
+check(UInt8[
+ 8, 0, # vtable bytes
+ 12, 0, # length of object
+ 6, 0, # start of value 0 from end of vtable
+ 8, 0, # start of value 1 from end of buffer
+ 8, 0, 0, 0, # offset for start of vtable (int32)
+ 0, 0, # padding
+ 55, 0, # value 0
+ 4, 0, 0, 0, # vector position from here
+ 2, 0, 0, 0, # length of vector (uint32)
+ 0x78, 0x56, # vector value 1
+ 0x34, 0x12, # vector value 0
+ ])
+
+# test 14: vtable with 1 struct of 1 int8, 1 int16, 1 int32
+b = FlatBuffers.Builder()
+FlatBuffers.startobject(b, 1)
+FlatBuffers.prep!(b, 4+4+4, 0)
+FlatBuffers.prepend!(b, Int8(55))
+FlatBuffers.pad!(b, 3)
+FlatBuffers.prepend!(b, Int16(0x1234))
+FlatBuffers.pad!(b, 2)
+FlatBuffers.prepend!(b, Int32(0x12345678))
+structStart = FlatBuffers.offset(b)
+FlatBuffers.prependstructslot!(b, 1, structStart, 0)
+FlatBuffers.endobject(b)
+check(UInt8[
+ 6, 0, # vtable bytes
+ 16, 0, # end of object from here
+ 4, 0, # start of struct from here
+ 6, 0, 0, 0, # offset for start of vtable (int32)
+ 0x78, 0x56, 0x34, 0x12, # value 2
+ 0, 0, # padding
+ 0x34, 0x12, # value 1
+ 0, 0, 0, # padding
+ 55, # value 0
+ ])
+
+# test 15: vtable with 1 vector of 2 struct of 2 int8
+b = FlatBuffers.Builder()
+FlatBuffers.startvector(b, sizeof(Int8)*2, 2, 1)
+FlatBuffers.prepend!(b, Int8(33))
+FlatBuffers.prepend!(b, Int8(44))
+FlatBuffers.prepend!(b, Int8(55))
+FlatBuffers.prepend!(b, Int8(66))
+vecend = FlatBuffers.endvector(b, 2)
+FlatBuffers.startobject(b, 1)
+FlatBuffers.prependoffsetslot!(b, 1, UInt32(vecend), UInt32(0))
+FlatBuffers.endobject(b)
+check(UInt8[
+ 6, 0, # vtable bytes
+ 8, 0,
+ 4, 0, # offset of vector offset
+ 6, 0, 0, 0, # offset for start of vtable (int32)
+ 4, 0, 0, 0, # vector start offset
+
+ 2, 0, 0, 0, # vector length
+ 66, # vector value 1,1
+ 55, # vector value 1,0
+ 44, # vector value 0,1
+ 33, # vector value 0,0
+ ])
+
+# test 16: table with some elements
+b = FlatBuffers.Builder()
+FlatBuffers.startobject(b, 2)
+FlatBuffers.prependslot!(b, 1, Int8(33), Int8(0))
+FlatBuffers.prependslot!(b, 2, Int16(66), Int16(0))
+off = FlatBuffers.endobject(b)
+FlatBuffers.finish!(b, off) #TODO
+
+check(UInt8[
+ 12, 0, 0, 0, # root of table: points to vtable offset
+
+ 8, 0, # vtable bytes
+ 8, 0, # end of object from here
+ 7, 0, # start of value 0
+ 4, 0, # start of value 1
+
+ 8, 0, 0, 0, # offset for start of vtable (int32)
+
+ 66, 0, # value 1
+ 0, # padding
+ 33, # value 0
+ ])
+
+# test 17: one unfinished table and one finished table
+b = FlatBuffers.Builder()
+FlatBuffers.startobject(b, 2)
+FlatBuffers.prependslot!(b, 1, Int8(33), Int8(0))
+FlatBuffers.prependslot!(b, 2, Int8(44), Int8(0))
+off = FlatBuffers.endobject(b)
+FlatBuffers.finish!(b, off)
+
+FlatBuffers.startobject(b, 3)
+FlatBuffers.prependslot!(b, 1, Int8(55), Int8(0))
+FlatBuffers.prependslot!(b, 2, Int8(66), Int8(0))
+FlatBuffers.prependslot!(b, 3, Int8(77), Int8(0))
+off = FlatBuffers.endobject(b)
+FlatBuffers.finish!(b, off)
+
+check(UInt8[
+ 16, 0, 0, 0, # root of table: points to object
+ 0, 0, # padding
+
+ 10, 0, # vtable bytes
+ 8, 0, # size of object
+ 7, 0, # start of value 0
+ 6, 0, # start of value 1
+ 5, 0, # start of value 2
+ 10, 0, 0, 0, # offset for start of vtable (int32)
+ 0, # padding
+ 77, # value 2
+ 66, # value 1
+ 55, # value 0
+
+ 12, 0, 0, 0, # root of table: points to object
+
+ 8, 0, # vtable bytes
+ 8, 0, # size of object
+ 7, 0, # start of value 0
+ 6, 0, # start of value 1
+ 8, 0, 0, 0, # offset for start of vtable (int32)
+ 0, 0, # padding
+ 44, # value 1
+ 33, # value 0
+ ])
+
+# test 18: a bunch of bools
+b = FlatBuffers.Builder()
+FlatBuffers.startobject(b, 8)
+FlatBuffers.prependslot!(b, 1, true, false)
+FlatBuffers.prependslot!(b, 2, true, false)
+FlatBuffers.prependslot!(b, 3, true, false)
+FlatBuffers.prependslot!(b, 4, true, false)
+FlatBuffers.prependslot!(b, 5, true, false)
+FlatBuffers.prependslot!(b, 6, true, false)
+FlatBuffers.prependslot!(b, 7, true, false)
+FlatBuffers.prependslot!(b, 8, true, false)
+off = FlatBuffers.endobject(b)
+FlatBuffers.finish!(b, off)
+
+check(UInt8[
+ 24, 0, 0, 0, # root of table: points to vtable offset
+
+ 20, 0, # vtable bytes
+ 12, 0, # size of object
+ 11, 0, # start of value 0
+ 10, 0, # start of value 1
+ 9, 0, # start of value 2
+ 8, 0, # start of value 3
+ 7, 0, # start of value 4
+ 6, 0, # start of value 5
+ 5, 0, # start of value 6
+ 4, 0, # start of value 7
+ 20, 0, 0, 0, # vtable offset
+
+ 1, # value 7
+ 1, # value 6
+ 1, # value 5
+ 1, # value 4
+ 1, # value 3
+ 1, # value 2
+ 1, # value 1
+ 1, # value 0
+ ])
+
+# test 19: three bools
+b = FlatBuffers.Builder()
+FlatBuffers.startobject(b, 3)
+FlatBuffers.prependslot!(b, 1, true, false)
+FlatBuffers.prependslot!(b, 2, true, false)
+FlatBuffers.prependslot!(b, 3, true, false)
+off = FlatBuffers.endobject(b)
+FlatBuffers.finish!(b, off)
+
+check(UInt8[
+ 16, 0, 0, 0, # root of table: points to vtable offset
+
+ 0, 0, # padding
+
+ 10, 0, # vtable bytes
+ 8, 0, # size of object
+ 7, 0, # start of value 0
+ 6, 0, # start of value 1
+ 5, 0, # start of value 2
+ 10, 0, 0, 0, # vtable offset from here
+
+ 0, # padding
+ 1, # value 2
+ 1, # value 1
+ 1, # value 0
+ ])
+
+# test 20: some floats
+b = FlatBuffers.Builder()
+FlatBuffers.startobject(b, 1)
+FlatBuffers.prependslot!(b, 1, Float32(1.0), Float32(0.0))
+off = FlatBuffers.endobject(b)
+
+check(UInt8[
+ 6, 0, # vtable bytes
+ 8, 0, # size of object
+ 4, 0, # start of value 0
+ 6, 0, 0, 0, # vtable offset
+
+ 0, 0, 128, 63, # value 0
+ ])
+end
+
+# CheckManualBuild builds a Monster manually.
+function CheckManualBuild()
+ b = FlatBuffers.Builder()
+ str = FlatBuffers.createstring(b, "MyMonster")
+
+ FlatBuffers.startvector(b, 1, 5, 1)
+ FlatBuffers.prepend!(b, UInt8(4))
+ FlatBuffers.prepend!(b, UInt8(3))
+ FlatBuffers.prepend!(b, UInt8(2))
+ FlatBuffers.prepend!(b, UInt8(1))
+ FlatBuffers.prepend!(b, UInt8(0))
+ inv = FlatBuffers.endvector(b, 5)
+
+ FlatBuffers.startobject(b, 13)
+ FlatBuffers.prependslot!(b, 2, Int16(20), Int16(100))
+ mon2 = FlatBuffers.endobject(b)
+
+ # Test4Vector
+ FlatBuffers.startvector(b, 4, 2, 1)
+
+ # Test 0
+ FlatBuffers.prep!(b, 2, 4)
+ FlatBuffers.pad!(b, 1)
+ FlatBuffers.place!(b, Int8(20))
+ FlatBuffers.place!(b, Int16(10))
+
+ # Test 1
+ FlatBuffers.prep!(b, 2, 4)
+ FlatBuffers.pad!(b, 1)
+ FlatBuffers.place!(b, Int8(40))
+ FlatBuffers.place!(b, Int16(30))
+
+ # end testvector
+ test4 = FlatBuffers.endvector(b, 2)
+
+ FlatBuffers.startobject(b, 13)
+
+ # a vec3
+ FlatBuffers.prep!(b, 16, 32)
+ FlatBuffers.pad!(b, 2)
+ FlatBuffers.prep!(b, 2, 4)
+ FlatBuffers.pad!(b, 1)
+ FlatBuffers.place!(b, UInt8(6))
+ FlatBuffers.place!(b, Int16(5))
+ FlatBuffers.pad!(b, 1)
+ FlatBuffers.place!(b, UInt8(4))
+ FlatBuffers.place!(b, Float64(3.0))
+ FlatBuffers.pad!(b, 4)
+ FlatBuffers.place!(b, Float32(3.0))
+ FlatBuffers.place!(b, Float32(2.0))
+ FlatBuffers.place!(b, Float32(1.0))
+ vec3Loc = FlatBuffers.offset(b)
+ # end vec3
+
+ FlatBuffers.prependstructslot!(b, 1, vec3Loc, 0) # vec3. noop
+ FlatBuffers.prependslot!(b, 3, Int16(80), Int16(100)) # hp
+ FlatBuffers.prependoffsetslot!(b, 4, Int32(str), Int32(0))
+ FlatBuffers.prependoffsetslot!(b, 6, Int32(inv), Int32(0)) # inventory
+ FlatBuffers.prependslot!(b, 8, UInt8(1), UInt8(0))
+ FlatBuffers.prependoffsetslot!(b, 9, Int32(mon2), Int32(0))
+ FlatBuffers.prependoffsetslot!(b, 10, Int32(test4), Int32(0))
+ mon = FlatBuffers.endobject(b)
+
+ FlatBuffers.finish!(b, mon)
+
+ return b.bytes, b.head
+end
+
+# CheckVtableDeduplication verifies that vtables are deduplicated.
+function CheckVtableDeduplication()
+ b = FlatBuffers.Builder()
+
+ FlatBuffers.startobject(b, 4)
+ FlatBuffers.prependslot!(b, 1, UInt8(0), UInt8(0))
+ FlatBuffers.prependslot!(b, 2, UInt8(11), UInt8(0))
+ FlatBuffers.prependslot!(b, 3, UInt8(22), UInt8(0))
+ FlatBuffers.prependslot!(b, 4, Int16(33), Int16(0))
+ obj0 = FlatBuffers.endobject(b)
+
+ FlatBuffers.startobject(b, 4)
+ FlatBuffers.prependslot!(b, 1, UInt8(0), UInt8(0))
+ FlatBuffers.prependslot!(b, 2, UInt8(44), UInt8(0))
+ FlatBuffers.prependslot!(b, 3, UInt8(55), UInt8(0))
+ FlatBuffers.prependslot!(b, 4, Int16(66), Int16(0))
+ obj1 = FlatBuffers.endobject(b)
+
+ FlatBuffers.startobject(b, 4)
+ FlatBuffers.prependslot!(b, 1, UInt8(0), UInt8(0))
+ FlatBuffers.prependslot!(b, 2, UInt8(77), UInt8(0))
+ FlatBuffers.prependslot!(b, 3, UInt8(88), UInt8(0))
+ FlatBuffers.prependslot!(b, 4, Int16(99), Int16(0))
+ obj2 = FlatBuffers.endobject(b)
+
+ got = b.bytes[b.head+1:end]
+
+ want = UInt8[
+ 240, 255, 255, 255, # == -12. offset to dedupped vtable.
+ 99, 0,
+ 88,
+ 77,
+ 248, 255, 255, 255, # == -8. offset to dedupped vtable.
+ 66, 0,
+ 55,
+ 44,
+ 12, 0, # start of vtable
+ 8, 0,
+ 0, 0,
+ 7, 0,
+ 6, 0,
+ 4, 0,
+ 12, 0, 0, 0, # table0
+ 33, 0,
+ 22,
+ 11
+ ]
+
+ @test got == want
+
+ table0 = FlatBuffers.Table{Any}(b.bytes, length(b.bytes) - obj0)
+ table1 = FlatBuffers.Table{Any}(b.bytes, length(b.bytes) - obj1)
+ table2 = FlatBuffers.Table{Any}(b.bytes, length(b.bytes) - obj2)
+
+ function testTable(tab, a, b, c, d)
+ # vtable size
+ got = FlatBuffers.getoffsetslot(tab, 0, Int16(0))
+ @test 12 == got
+ # object size
+ got = FlatBuffers.getoffsetslot(tab, 2, Int16(0))
+ @test 8 == got
+ # default value
+ got = FlatBuffers.getoffsetslot(tab, 4, Int16(0))
+ @test a == got
+ got = FlatBuffers.getslot(tab, 6, UInt8(0))
+ @test b == got
+ val = FlatBuffers.getslot(tab, 8, UInt8(0))
+ c != val && throw(ArgumentError("failed 8, 0: $got"))
+ got = FlatBuffers.getslot(tab, 10, UInt8(0))
+ @test d == got
+ return
+ end
+
+ testTable(table0, UInt16(0), UInt8(11), UInt8(22), UInt8(33))
+ testTable(table1, UInt16(0), UInt8(44), UInt8(55), UInt8(66))
+ testTable(table2, UInt16(0), UInt8(77), UInt8(88), UInt8(99))
+end
+
+# CheckNotInObjectError verifies that `endobject` fails if not inside an
+# object.
+function CheckNotInObjectError()
+ b = FlatBuffers.Builder()
+
+ @test_throws ArgumentError FlatBuffers.endobject(b)
+end
+
+# CheckStringIsNestedError verifies that a string can not be created inside
+# another object.
+function CheckStringIsNestedError()
+ b = FlatBuffers.Builder()
+ FlatBuffers.startobject(b, 0)
+ @test_throws ArgumentError FlatBuffers.createstring(b, "foo")
+end
+
+# CheckByteStringIsNestedError verifies that a bytestring can not be created
+# inside another object.
+function CheckByteStringIsNestedError()
+ b = FlatBuffers.Builder()
+ FlatBuffers.startobject(b, 0)
+ @test_throws ArgumentError FlatBuffers.createstring(b, "foo")
+end
+
+# CheckStructIsNotInlineError verifies that writing a struct in a location
+# away from where it is used will cause a panic.
+function CheckStructIsNotInlineError()
+ b = FlatBuffers.Builder()
+ FlatBuffers.startobject(b, 0)
+ @test_throws ArgumentError FlatBuffers.prependstructslot!(b, 0, 1, 0)
+end
+
+# CheckFinishedBytesError verifies that `FinishedBytes` panics if the table
+# is not finished.
+function CheckFinishedBytesError()
+ b = FlatBuffers.Builder()
+
+ @test_throws ArgumentError FlatBuffers.finishedbytes(b)
+end
+
+function CheckCreateByteVector()
+ raw = UInt8(0):UInt8(29)
+
+ for size = 1:30
+ b1 = FlatBuffers.Builder()
+ b2 = FlatBuffers.Builder()
+ FlatBuffers.startvector(b1, 1, size, 1)
+ for i = size:-1:1
+ FlatBuffers.prepend!(b1, raw[i])
+ end
+ FlatBuffers.endvector(b1, size)
+ FlatBuffers.createbytevector(b2, raw[1:size])
+ @test b1.bytes == b2.bytes
+ end
+end
+
+const InitialLCGSeed = 48271
+mutable struct LCG
+ val::UInt32
+ LCG() = new(UInt32(InitialLCGSeed))
+end
+reset!(lcg::LCG) = lcg.val = UInt32(InitialLCGSeed)
+function next(lcg::LCG)
+ n = UInt32((UInt64(lcg.val) * UInt64(279470273)) % UInt64(4294967291))
+ lcg.val = n
+ return n
+end
+
+# Low level stress/fuzz test: serialize/deserialize a variety of
+# different kinds of data in different combinations
+function checkFuzz(fuzzFields, fuzzObjects, verbose=true)
+
+ # Values we're testing against: chosen to ensure no bits get chopped
+ # off anywhere, and also be different from eachother.
+ boolVal = true
+ int8Val = Int8(-127) # 0x81
+ uint8Val = UInt8(0xFF)
+ int16Val = Int16(-32222) # 0x8222
+ uint16Val = UInt16(0xFEEE)
+ int32Val = Int32(overflowingInt32Val)
+ uint32Val = UInt32(0xFDDDDDDD)
+ int64Val = Int64(overflowingInt64Val)
+ uint64Val = UInt64(0xFCCCCCCCCCCCCCCC)
+ float32Val = Float32(3.14159)
+ float64Val = Float64(3.14159265359)
+
+ testValuesMax = 11 # hardcoded to the number of scalar types
+
+ b = FlatBuffers.Builder()
+ l = LCG()
+
+ objects = fill(0, fuzzObjects)
+
+ # Generate fuzzObjects random objects each consisting of
+ # fuzzFields fields, each of a random type.
+ for i = 1:fuzzObjects
+ FlatBuffers.startobject(b, fuzzFields)
+
+ for f = 1:fuzzFields
+ choice = next(l) % UInt32(testValuesMax)
+ if choice == 0
+ FlatBuffers.prependslot!(b, f, boolVal, false)
+ elseif choice == 1
+ FlatBuffers.prependslot!(b, f, int8Val, Int8(0))
+ elseif choice == 2
+ FlatBuffers.prependslot!(b, f, uint8Val, UInt8(0))
+ elseif choice == 3
+ FlatBuffers.prependslot!(b, f, int16Val, Int16(0))
+ elseif choice == 4
+ FlatBuffers.prependslot!(b, f, uint16Val, UInt16(0))
+ elseif choice == 5
+ FlatBuffers.prependslot!(b, f, int32Val, Int32(0))
+ elseif choice == 6
+ FlatBuffers.prependslot!(b, f, uint32Val, UInt32(0))
+ elseif choice == 7
+ FlatBuffers.prependslot!(b, f, int64Val, Int64(0))
+ elseif choice == 8
+ FlatBuffers.prependslot!(b, f, uint64Val, UInt64(0))
+ elseif choice == 9
+ FlatBuffers.prependslot!(b, f, float32Val, Float32(0))
+ elseif choice == 10
+ FlatBuffers.prependslot!(b, f, float64Val, Float64(0))
+ end
+ end
+
+ off = FlatBuffers.endobject(b)
+
+ # store the offset from the end of the builder buffer,
+ # since it will keep growing:
+ objects[i] = off
+ end
+
+ # Do some bookkeeping to generate stats on fuzzes:
+ stats = Dict{String,Int}()
+ function check(desc, want, got)
+ v = get!(stats, desc, 0)
+ stats[desc] = v + 1
+ @test want == got
+ end
+
+ l = LCG() # Reset.
+
+ # Test that all objects we generated are readable and return the
+ # expected values. We generate random objects in the same order
+ # so this is deterministic.
+ for i = 1:fuzzObjects
+
+ table = FlatBuffers.Table{Any}(b.bytes, length(b.bytes) - objects[i])
+
+ for j = 0:(fuzzFields - 1)
+ f = (FlatBuffers.VtableMetadataFields + j) * sizeof(Int16)
+ choice = next(l) % UInt32(testValuesMax)
+
+ if choice == 0
+ check("bool", boolVal, FlatBuffers.getslot(table, f, false))
+ elseif choice == 1
+ check("int8", int8Val, FlatBuffers.getslot(table, f, Int8(0)))
+ elseif choice == 2
+ check("uint8", uint8Val, FlatBuffers.getslot(table, f, UInt8(0)))
+ elseif choice == 3
+ check("int16", int16Val, FlatBuffers.getslot(table, f, Int16(0)))
+ elseif choice == 4
+ check("uint16", uint16Val, FlatBuffers.getslot(table, f, UInt16(0)))
+ elseif choice == 5
+ check("int32", int32Val, FlatBuffers.getslot(table, f, Int32(0)))
+ elseif choice == 6
+ check("uint32", uint32Val, FlatBuffers.getslot(table, f, UInt32(0)))
+ elseif choice == 7
+ check("int64", int64Val, FlatBuffers.getslot(table, f, Int64(0)))
+ elseif choice == 8
+ check("uint64", uint64Val, FlatBuffers.getslot(table, f, UInt64(0)))
+ elseif choice == 9
+ check("float32", float32Val, FlatBuffers.getslot(table, f, Float32(0)))
+ elseif choice == 10
+ check("float64", float64Val, FlatBuffers.getslot(table, f, Float64(0)))
+ end
+ end
+ end
+
+ # If enough checks were made, verify that all scalar types were used:
+ if fuzzFields*fuzzObjects >= testValuesMax
+ if length(stats) != testValuesMax
+ throw(ArgumentError("fuzzing failed to test all scalar types"))
+ end
+ end
+
+ # Print some counts, if needed:
+ if verbose
+ if fuzzFields == 0 || fuzzObjects == 0
+ println("fuzz\tfields: $fuzzFields \tobjects: $fuzzObjects \t[none]\t 0")
+ else
+ ks = sort!(collect(keys(stats)))
+ for k in ks
+ println("fuzz\tfields: $fuzzFields \tobjects: $fuzzObjects \t$(k): $(stats[k])")
+ end
+ end
+ end
+ return
+end
diff --git a/julia/test/monster.jl b/julia/test/monster.jl
new file mode 100644
index 00000000000..b37a5685f9f
--- /dev/null
+++ b/julia/test/monster.jl
@@ -0,0 +1,77 @@
+module SmallExample
+
+using FlatBuffers
+
+@enum(Color, Red = 1, Green = 2, Blue = 8)
+@DEFAULT Color Red
+
+@STRUCT struct Test
+ a::Int16
+ b::UInt8
+ # _1::UInt8 # padding
+end
+
+mutable struct TestSimpleTableWithEnum
+ color::Color
+end
+
+@DEFAULT TestSimpleTableWithEnum color=Green
+
+@STRUCT struct Vec3
+ x::Float32
+ y::Float32
+ z::Float32
+ # _::UInt32 # padding
+ test1::Float64
+ test2::Color
+ # __::UInt8 # padding
+ test3::Test
+ # ___::UInt16 # padding
+end
+
+@ALIGN Vec3 16
+
+mutable struct Stat
+ id::Union{Nothing, String}
+ val::Int64
+ count::UInt16
+end
+
+# Julia doesn't support forward referencing of types
+# @union Any_ Union{Monster, TestSimpleTableWithEnum}
+
+mutable struct Monster
+ pos::Vec3
+ mana::Int16
+ hp::Int16
+ name::Union{Nothing, String}
+ friendly::Bool # deprecated
+ inventory::Union{Nothing, Vector{UInt8}}
+ color::Color
+ # test_type::Any_
+ # test::Union{Nothing, Vector{UInt8}}
+ test4::Union{Nothing, Vector{Test}}
+ testarrayofstring::Union{Nothing, Vector{String}}
+ testarrayoftables::Union{Nothing, Vector{Monster}}
+ # don't support nested circulr reference objects yet
+ # enemy::Monster
+ testnestedflatbuffer::Union{Nothing, Vector{UInt8}}
+ testempty::Union{Nothing, Stat}
+ testbool::Bool
+ testhashs32_fnv1::Int32
+ testhashu32_fnv1::UInt32
+ testhashs64_fnv1::Int64
+ testhashu64_fnv1::UInt64
+ testhashs32_fnv1a::Int32
+ testhashu32_fnv1a::UInt32
+ testhashs64_fnv1a::Int64
+ testhashu64_fnv1a::UInt64
+ testarrayofbools::Union{Nothing, Vector{Bool}}
+ testf::Float32
+ testf2::Float32
+ testf3::Float32
+end
+
+@DEFAULT Monster hp=Int16(100) mana=Int16(150) color=convert(UInt8, Blue) friendly=false testf=Float32(3.14159) testf2=Float32(3)
+
+end # module
diff --git a/julia/test/monsterdata_python_wire.mon b/julia/test/monsterdata_python_wire.mon
new file mode 100644
index 00000000000..55e37bf0396
Binary files /dev/null and b/julia/test/monsterdata_python_wire.mon differ
diff --git a/julia/test/monsterdata_test.mon b/julia/test/monsterdata_test.mon
new file mode 100644
index 00000000000..3f8397236a1
Binary files /dev/null and b/julia/test/monsterdata_test.mon differ
diff --git a/julia/test/runtests.jl b/julia/test/runtests.jl
new file mode 100644
index 00000000000..7977bbe14f6
--- /dev/null
+++ b/julia/test/runtests.jl
@@ -0,0 +1,313 @@
+using FlatBuffers
+using Test
+
+include("defaults.jl")
+include("internals.jl")
+CheckByteLayout()
+CheckManualBuild()
+CheckVtableDeduplication()
+CheckNotInObjectError()
+CheckStringIsNestedError()
+CheckByteStringIsNestedError()
+CheckStructIsNotInlineError()
+CheckFinishedBytesError()
+CheckCreateByteVector()
+checkFuzz(100, 100, true)
+
+include("flatc.jl")
+
+include("monster.jl")
+
+vec3 = SmallExample.Vec3(1.0, 2.0, 3.0, 3.0, SmallExample.Color(1), SmallExample.Test(5, 6))
+test4 = SmallExample.Test[SmallExample.Test(10, 20), SmallExample.Test(30, 40)]
+testArrayOfString = ["test1","test2"]
+
+mon = SmallExample.Monster(vec3, 150, 80, "MyMonster", false, collect(0x00:0x04),
+ SmallExample.Blue, test4, testArrayOfString, SmallExample.Monster[],
+ UInt8[], SmallExample.Stat("",0,0), false, 0, 0, 0, 0, 0, 0, 0, 0,
+ Bool[], 0, 0, 0)
+b = FlatBuffers.build!(mon)
+monst = FlatBuffers.read(b)
+monst2 = FlatBuffers.read(SmallExample.Monster, FlatBuffers.bytes(b))
+
+@test mon.pos == monst.pos
+@test mon.pos == monst2.pos
+
+# create test types
+# types (Scalar, Enum, struct, T, String, Vector{UInt8})
+mutable struct TestInt8T
+ x::Int8
+end
+
+inst1 = TestInt8T(1)
+
+b = FlatBuffers.Builder(TestInt8T)
+FlatBuffers.build!(b, inst1)
+t = FlatBuffers.Table(b)
+inst1_2 = FlatBuffers.read(t)
+
+@test inst1.x === inst1_2.x
+
+struct TestInt8I
+ x::Int8
+end
+
+inst2 = TestInt8I(2)
+
+mutable struct TestInt8A
+ x::Union{Nothing, Vector{Int8}}
+end
+
+inst3 = TestInt8A([1,2,3])
+
+b = FlatBuffers.Builder(TestInt8A)
+FlatBuffers.build!(b, inst3)
+t = FlatBuffers.Table(b)
+inst3_2 = FlatBuffers.read(t)
+
+@test inst3.x == inst3_2.x
+
+mutable struct TestMixT
+ x::Int8
+ y::String
+ z::Union{Nothing, Vector{Int8}}
+end
+
+inst4 = TestMixT(10,"singin tooralli ooralli addity",[1,2,3])
+
+b = FlatBuffers.Builder(TestMixT)
+FlatBuffers.build!(b, inst4)
+t = FlatBuffers.Table(b)
+inst4_2 = FlatBuffers.read(t)
+
+@test inst4.x == inst4_2.x && inst4.y == inst4_2.y && inst4.z == inst4_2.z
+
+# simple sub-table/type (Stat)
+mutable struct TestSubT
+ x::TestInt8T
+ y::TestInt8I
+ z::TestInt8A
+end
+
+inst5 = TestSubT(inst1, inst2, inst3)
+
+b = FlatBuffers.Builder(TestSubT)
+FlatBuffers.build!(b, inst5)
+t = FlatBuffers.Table(b)
+inst5_2 = FlatBuffers.read(t)
+
+@test inst5.x.x == inst5_2.x.x && inst5.y.x == inst5_2.y.x && inst5.z.x == inst5_2.z.x
+
+# vtable duplicates
+mutable struct TestDupT
+ x::TestInt8T
+ y::TestInt8I
+ z::TestInt8T
+end
+
+inst6 = TestDupT(inst1, inst2, TestInt8T(2))
+
+b = FlatBuffers.Builder(TestDupT)
+FlatBuffers.build!(b, inst6)
+t = FlatBuffers.Table(b)
+inst6_2 = FlatBuffers.read(t)
+
+@test inst6.x.x == inst6_2.x.x && inst6.y.x == inst6_2.y.x && inst6.z.x == inst6_2.z.x
+
+mutable struct TestDup2T
+ x::Vector{TestInt8T}
+end
+
+inst7 = TestDup2T([inst1, TestInt8T(2), TestInt8T(3), TestInt8T(4)])
+
+b = FlatBuffers.Builder(TestDup2T)
+FlatBuffers.build!(b, inst7)
+t = FlatBuffers.Table(b)
+inst7_2 = FlatBuffers.read(t)
+
+@test all(map(x->x.x, inst7.x) .== map(x->x.x, inst7_2.x))
+
+# self-referential type test (type has subtype of itself)
+# type TestCircT
+# x::Int8
+# y::TestCircT
+# end
+#
+# struct TestCircI
+# x::Int8
+# y::TestCircI
+# end
+
+# simple Union (Any_)
+
+# fbs
+# table TestUnionT {
+# x::TestUnionI
+# }
+
+@UNION TestUnionU (Nothing,TestInt8T,TestInt8A)
+
+mutable struct TestUnionT
+ x_type::Int8
+ x::TestUnionU
+end
+
+TestUnionT(x::TestUnionU) = TestUnionT(FlatBuffers.typeorder(TestUnionU, typeof(x)), x)
+
+inst8 = TestUnionT(inst1)
+
+b = FlatBuffers.Builder(TestUnionT)
+FlatBuffers.build!(b, inst8)
+t = FlatBuffers.Table(b)
+inst8_2 = FlatBuffers.read(t)
+
+@test inst8.x_type == inst8_2.x_type && inst8.x.x == inst8_2.x.x
+
+inst9 = TestUnionT(inst3)
+
+b = FlatBuffers.Builder(TestUnionT)
+FlatBuffers.build!(b, inst9)
+t = FlatBuffers.Table(b)
+inst9_2 = FlatBuffers.read(t)
+
+@test inst9.x_type == inst9_2.x_type && inst9.x.x == inst9_2.x.x
+
+const Nachos = String
+const Burgers = Int
+FlatBuffers.@UNION(Delicious,
+ (Burgers, Nachos)
+)
+
+FlatBuffers.@with_kw mutable struct TestVecUnionT
+ xs_type::Vector{UInt8}
+ xs::Vector{TestUnionU}
+ ys_type::Vector{UInt8}
+ ys::Vector{Delicious}
+end
+
+function TestVecUnionT(xs::Vector{A}, ys::Vector{B}) where {A<:TestUnionU, B<:Delicious}
+ xs_type = [FlatBuffers.typeorder(TestUnionU, typeof(x)) for x in xs]
+ ys_type = [FlatBuffers.typeorder(Delicious, typeof(y)) for y in ys]
+ TestVecUnionT(xs_type, xs, ys_type, ys)
+end
+
+dinner = "beef"
+inst10 = TestVecUnionT(TestUnionU[inst1, inst1, inst1], [dinner])
+
+b = FlatBuffers.Builder(TestVecUnionT)
+FlatBuffers.build!(b, inst10)
+
+t = FlatBuffers.Table(b)
+inst10_2 = FlatBuffers.read(t)
+
+@test inst10.xs_type == inst10_2.xs_type
+@test [x.x for x in inst10.xs] == [x.x for x in inst10_2.xs]
+
+inst11 = TestVecUnionT(TestUnionU[inst3, inst3], [dinner for _ = 1:9])
+
+b = FlatBuffers.Builder(TestVecUnionT)
+FlatBuffers.build!(b, inst11)
+t = FlatBuffers.Table(b)
+inst11_2 = FlatBuffers.read(t)
+
+@test inst11.xs_type == inst11_2.xs_type
+@test [x.x for x in inst11.xs] == [x.x for x in inst11_2.xs]
+@test inst11.ys_type == inst11_2.ys_type
+@test [y for y in inst11.ys] == [y for y in inst11_2.ys]
+
+# test @STRUCT macro
+@STRUCT struct A
+ a::Int32
+end
+@test sizeof(A) == 4
+@test all(fieldnames(A) .== [:a])
+@test A(1) == A(1)
+
+@STRUCT struct B
+ a::Int8
+ b::Int32
+end
+@test sizeof(B) == 8
+@test all(fieldnames(B) .== [:a, :_pad_a_B_0, :_pad_a_B_1, :b])
+@test B(1,2) == B(1,2)
+
+@STRUCT struct C
+ a::Int16
+ b::Int32
+ c::Int16
+end
+@test sizeof(C) == 12
+@test all(fieldnames(C) .== [:a, :_pad_a_C_0, :b, :c, :_pad_c_C_1])
+@test C(1,2,3) == C(1,2,3)
+
+@STRUCT struct D
+ a::Int8
+ b::Int64
+end
+@test sizeof(D) == 16
+@test all(fieldnames(D) .== [:a, :_pad_a_D_0, :_pad_a_D_1, :_pad_a_D_2, :b])
+@test D(1,2) == D(1,2)
+
+@STRUCT struct E
+ a::Int64
+ b::Int32
+end
+@test sizeof(E) == 16
+@test all(fieldnames(E) .== [:a, :b, :_pad_b_E_0])
+@test E(1,2) == E(1,2)
+
+@STRUCT struct F
+ a::Int32
+ b::Int16
+ c::Int32
+ d::Int32
+ e::Int64
+end
+@test sizeof(F) == 24
+@test all(fieldnames(F) .== [:a, :b, :_pad_b_F_0, :c, :d, :e])
+@test F(1,2,3,4,5) == F(1,2,3,4,5)
+
+@STRUCT struct G
+ a::Float64
+ b::Int8
+ c::Int16
+ d::Int32
+end
+@test sizeof(G) == 24
+@test all(fieldnames(G) .== [:a, :b, :_pad_b_G_0, :c, :_pad_c_G_1, :d])
+@test G(1,2,3,4) == G(1,2,3,4)
+
+@STRUCT struct H
+ a::Float32
+ b::Int8
+ c::Int16
+end
+@test sizeof(H) == 8
+@test all(fieldnames(H) .== [:a, :b, :_pad_b_H_0, :c])
+@test H(1,2,3) == H(1,2,3)
+
+@STRUCT struct I
+ a::Float64
+ b::Int8
+ c::Int32
+end
+@test sizeof(I) == 16
+@test all(fieldnames(I) .== [:a, :b, :_pad_b_I_0, :_pad_b_I_1, :c])
+@test I(1,2,3) == I(1,2,3)
+
+@STRUCT struct J
+ a::Int8
+ b::A
+end
+@test sizeof(J) == 8
+@test all(fieldnames(J) .== [:a, :_pad_a_J_0, :_pad_a_J_1, :b_a])
+@test J(1,A(2)) == J(1,A(2))
+
+@STRUCT struct K
+ a::J
+ b::I
+ c::J
+end
+@test sizeof(K) == 48
+@test all(fieldnames(K) .== [:a_a, :a__pad_a_J_0, :a__pad__pad_a_J_0_J_0, :a__pad_a_J_1, :a__pad__pad_a_J_1_J_1, :a_b_a, :b_a, :b_b, :b__pad_b_I_0, :b__pad__pad_b_I_0_I_0, :b__pad_b_I_1, :b__pad__pad_b_I_1_I_1, :b_c, :c_a, :c__pad_a_J_0, :c__pad__pad_a_J_0_J_0, :c__pad_a_J_1, :c__pad__pad_a_J_1_J_1, :c_b_a])
+@test K(J(1,A(2)), I(3.0, 4, 5), J(6, A(7))) == K(J(1,A(2)), I(3.0, 4, 5), J(6, A(7)))
diff --git a/samples/julia_sample.sh b/samples/julia_sample.sh
new file mode 100755
index 00000000000..7ff98b6ac80
--- /dev/null
+++ b/samples/julia_sample.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# Copyright 2015 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Note: This script runs on Mac and Linux. It requires `julia` to be installed
+# and `flatc` to be built (using `cmake` in the root directory).
+
+sampledir=$(cd $(dirname $BASH_SOURCE) && pwd)
+rootdir=$(cd $sampledir/.. && pwd)
+currentdir=$(pwd)
+
+if [[ "$sampledir" != "$currentdir" ]]; then
+ echo Error: This script must be run from inside the $sampledir directory.
+ echo You executed it from the $currentdir directory.
+ exit 1
+fi
+
+# Run `flatc`. Note: This requires you to compile using `cmake` from the
+# root `/flatbuffers` directory.
+if [ -e ../flatc ]; then
+ ../flatc --julia monster.fbs
+elif [ -e ../Debug/flatc ]; then
+ ../Debug/flatc --julia monster.fbs
+else
+ echo 'flatc' could not be found. Make sure to build FlatBuffers from the \
+ $rootdir directory.
+ exit 1
+fi
+
+echo Running the Julia sample.
+
+# Execute the sample.
+julia sample_binary.jl
+
+# Clean up the temporary files.
+rm -rf MyGame
diff --git a/samples/monster_generated.jl b/samples/monster_generated.jl
new file mode 100644
index 00000000000..be19e5d8378
--- /dev/null
+++ b/samples/monster_generated.jl
@@ -0,0 +1,9 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+if !isdefined(@__MODULE__(), :MyGame) @__MODULE__().eval(:(module MyGame import FlatBuffers end)) end
+if !isdefined(MyGame, :Sample) MyGame.eval(:(module Sample import FlatBuffers end)) end
+include("MyGame/Sample/Color.jl")
+include("MyGame/Sample/Weapon.jl")
+include("MyGame/Sample/Equipment.jl")
+include("MyGame/Sample/Vec3.jl")
+include("MyGame/Sample/Monster.jl")
diff --git a/samples/sample_binary.jl b/samples/sample_binary.jl
new file mode 100644
index 00000000000..7cea9c4af44
--- /dev/null
+++ b/samples/sample_binary.jl
@@ -0,0 +1,49 @@
+import FlatBuffers
+
+include("monster_generated.jl")
+
+# require the files generated from the schema
+import .MyGame.Sample.Weapon
+import .MyGame.Sample.Monster
+import .MyGame.Sample.Vec3
+import .MyGame.Sample.Color
+import .MyGame.Sample.Equipment
+
+sword = Weapon(;name="Sword", damage=3)
+axe = Weapon(;name="Axe", damage=5)
+orc = Monster(;
+ name="Orc",
+ pos=Vec3(1.0, 2.0, 3.0),
+ hp=300,
+ inventory=collect(1:10),
+ color=MyGame.Sample.ColorRed,
+ weapons=[sword, axe],
+ equipped=axe
+)
+
+# Get the flatbuffer as a string containing the binary data
+io = IOBuffer()
+FlatBuffers.serialize(io, orc)
+bytes = take!(io)
+
+# Build the Monster from the raw bytes
+mon = Monster(bytes)
+
+@assert mon.mana == 150
+@assert mon.hp == 300
+@assert mon.name == "Orc"
+@assert mon.color == MyGame.Sample.ColorRed
+@assert mon.pos.x == 1.0
+@assert mon.pos.y == 2.0
+@assert mon.pos.z == 3.0
+@assert mon.inventory == collect(1:10)
+
+@assert mon.equipped_type == FlatBuffers.typeorder(Equipment, Weapon)
+@assert mon.equipped.name == "Axe"
+@assert mon.equipped.damage == 5
+
+@assert mon.weapons[1].name == "Sword"
+@assert mon.weapons[1].damage == 3
+
+@assert mon.weapons[2].name == "Axe"
+@assert mon.weapons[2].damage == 5
diff --git a/src/flatc_main.cpp b/src/flatc_main.cpp
index f54b94e8e94..152b4504412 100644
--- a/src/flatc_main.cpp
+++ b/src/flatc_main.cpp
@@ -63,6 +63,10 @@ int main(int argc, const char *argv[]) {
flatbuffers::IDLOptions::kJs,
"Generate JavaScript code for tables/structs",
flatbuffers::JSTSMakeRule },
+ { flatbuffers::GenerateJulia, nullptr, "--julia", "Julia", true, nullptr,
+ flatbuffers::IDLOptions::kJulia,
+ "Generate Julia modules for tables/structs",
+ nullptr },
{ flatbuffers::GenerateDart, "-d", "--dart", "Dart", true, nullptr,
flatbuffers::IDLOptions::kDart,
"Generate Dart classes for tables/structs", flatbuffers::DartMakeRule },
diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp
index 706732b0f0c..898b7d04579 100644
--- a/src/idl_gen_cpp.cpp
+++ b/src/idl_gen_cpp.cpp
@@ -546,7 +546,7 @@ class CppGenerator : public BaseGenerator {
// clang-format off
static const char *const ctypename[] = {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
- RTYPE, KTYPE) \
+ RTYPE, KTYPE, JLTYPE) \
#CTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_csharp.cpp b/src/idl_gen_csharp.cpp
index 5045bd95d92..095876a9ae4 100644
--- a/src/idl_gen_csharp.cpp
+++ b/src/idl_gen_csharp.cpp
@@ -120,7 +120,7 @@ class CSharpGenerator : public BaseGenerator {
// clang-format off
static const char * const csharp_typename[] = {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
#NTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp
new file mode 100644
index 00000000000..b2eb017dec9
--- /dev/null
+++ b/src/idl_gen_general.cpp
@@ -0,0 +1,1568 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+#if defined(FLATBUFFERS_CPP98_STL)
+# include
+#endif // defined(FLATBUFFERS_CPP98_STL)
+
+namespace flatbuffers {
+
+// These arrays need to correspond to the IDLOptions::k enum.
+
+struct LanguageParameters {
+ IDLOptions::Language language;
+ // Whether function names in the language typically start with uppercase.
+ bool first_camel_upper;
+ std::string file_extension;
+ std::string string_type;
+ std::string bool_type;
+ std::string open_curly;
+ std::string accessor_type;
+ std::string const_decl;
+ std::string unsubclassable_decl;
+ std::string enum_decl;
+ std::string enum_separator;
+ std::string getter_prefix;
+ std::string getter_suffix;
+ std::string inheritance_marker;
+ std::string namespace_ident;
+ std::string namespace_begin;
+ std::string namespace_end;
+ std::string set_bb_byteorder;
+ std::string get_bb_position;
+ std::string get_fbb_offset;
+ std::string accessor_prefix;
+ std::string accessor_prefix_static;
+ std::string optional_suffix;
+ std::string includes;
+ std::string class_annotation;
+ std::string generated_type_annotation;
+ CommentConfig comment_config;
+};
+
+const LanguageParameters &GetLangParams(IDLOptions::Language lang) {
+ static const LanguageParameters language_parameters[] = {
+ {
+ IDLOptions::kJava,
+ false,
+ ".java",
+ "String",
+ "boolean ",
+ " {\n",
+ "class ",
+ " final ",
+ "final ",
+ "final class ",
+ ";\n",
+ "()",
+ "",
+ " extends ",
+ "package ",
+ ";",
+ "",
+ "_bb.order(ByteOrder.LITTLE_ENDIAN); ",
+ "position()",
+ "offset()",
+ "",
+ "",
+ "",
+ "import java.nio.*;\nimport java.lang.*;\nimport "
+ "java.util.*;\nimport com.google.flatbuffers.*;\n",
+ "\n@SuppressWarnings(\"unused\")",
+ "\n@javax.annotation.Generated(value=\"flatc\")\n",
+ {
+ "/**",
+ " *",
+ " */",
+ },
+ },
+ {
+ IDLOptions::kCSharp,
+ true,
+ ".cs",
+ "string",
+ "bool ",
+ "\n{\n",
+ "struct ",
+ " readonly ",
+ "",
+ "enum ",
+ ",\n",
+ " { get",
+ "} ",
+ " : ",
+ "namespace ",
+ "\n{",
+ "\n}\n",
+ "",
+ "Position",
+ "Offset",
+ "__p.",
+ "Table.",
+ "?",
+ "using global::System;\nusing global::FlatBuffers;\n\n",
+ "",
+ "",
+ {
+ nullptr,
+ "///",
+ nullptr,
+ },
+ },
+ };
+
+ if (lang == IDLOptions::kJava) {
+ return language_parameters[0];
+ } else {
+ FLATBUFFERS_ASSERT(lang == IDLOptions::kCSharp);
+ return language_parameters[1];
+ }
+}
+
+namespace general {
+class GeneralGenerator : public BaseGenerator {
+ public:
+ GeneralGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "."),
+ lang_(GetLangParams(parser_.opts.lang)),
+ cur_name_space_(nullptr) {}
+
+ GeneralGenerator &operator=(const GeneralGenerator &);
+ bool generate() {
+ std::string one_file_code;
+ cur_name_space_ = parser_.current_namespace_;
+
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ std::string enumcode;
+ auto &enum_def = **it;
+ if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
+ GenEnum(enum_def, &enumcode);
+ if (parser_.opts.one_file) {
+ one_file_code += enumcode;
+ } else {
+ if (!SaveType(enum_def.name, *enum_def.defined_namespace, enumcode,
+ false))
+ return false;
+ }
+ }
+
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ std::string declcode;
+ auto &struct_def = **it;
+ if (!parser_.opts.one_file)
+ cur_name_space_ = struct_def.defined_namespace;
+ GenStruct(struct_def, &declcode);
+ if (parser_.opts.one_file) {
+ one_file_code += declcode;
+ } else {
+ if (!SaveType(struct_def.name, *struct_def.defined_namespace, declcode,
+ true))
+ return false;
+ }
+ }
+
+ if (parser_.opts.one_file) {
+ return SaveType(file_name_, *parser_.current_namespace_, one_file_code,
+ true);
+ }
+ return true;
+ }
+
+ // Save out the generated code for a single class while adding
+ // declaration boilerplate.
+ bool SaveType(const std::string &defname, const Namespace &ns,
+ const std::string &classcode, bool needs_includes) const {
+ if (!classcode.length()) return true;
+
+ std::string code;
+ if (lang_.language == IDLOptions::kCSharp) {
+ code =
+ "// \n"
+ "// " +
+ std::string(FlatBuffersGeneratedWarning()) +
+ "\n"
+ "// \n\n";
+ } else {
+ code = "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
+ }
+
+ std::string namespace_name = FullNamespace(".", ns);
+ if (!namespace_name.empty()) {
+ code += lang_.namespace_ident + namespace_name + lang_.namespace_begin;
+ code += "\n\n";
+ }
+ if (needs_includes) {
+ code += lang_.includes;
+ if (parser_.opts.gen_nullable) {
+ code += "\nimport javax.annotation.Nullable;\n";
+ }
+ code += lang_.class_annotation;
+ }
+ if (parser_.opts.gen_generated) {
+ code += lang_.generated_type_annotation;
+ }
+ code += classcode;
+ if (!namespace_name.empty()) code += lang_.namespace_end;
+ auto filename = NamespaceDir(ns) + defname + lang_.file_extension;
+ return SaveFile(filename.c_str(), code, false);
+ }
+
+ const Namespace *CurrentNameSpace() const { return cur_name_space_; }
+
+ std::string FunctionStart(char upper) const {
+ return std::string() + (lang_.language == IDLOptions::kJava
+ ? static_cast(tolower(upper))
+ : upper);
+ }
+
+ std::string GenNullableAnnotation(const Type &t) const {
+ return lang_.language == IDLOptions::kJava && parser_.opts.gen_nullable &&
+ !IsScalar(DestinationType(t, true).base_type)
+ ? " @Nullable "
+ : "";
+ }
+
+ static bool IsEnum(const Type &type) {
+ return type.enum_def != nullptr && IsInteger(type.base_type);
+ }
+
+ std::string GenTypeBasic(const Type &type, bool enableLangOverrides) const {
+ // clang-format off
+ static const char * const java_typename[] = {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, JLTYPE) \
+ #JTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ };
+
+ static const char * const csharp_typename[] = {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, JLTYPE) \
+ #NTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ };
+ // clang-format on
+
+ if (enableLangOverrides) {
+ if (lang_.language == IDLOptions::kCSharp) {
+ if (IsEnum(type)) return WrapInNameSpace(*type.enum_def);
+ if (type.base_type == BASE_TYPE_STRUCT) {
+ return "Offset<" + WrapInNameSpace(*type.struct_def) + ">";
+ }
+ }
+ }
+
+ if (lang_.language == IDLOptions::kJava) {
+ return java_typename[type.base_type];
+ } else {
+ FLATBUFFERS_ASSERT(lang_.language == IDLOptions::kCSharp);
+ return csharp_typename[type.base_type];
+ }
+ }
+
+ std::string GenTypeBasic(const Type &type) const {
+ return GenTypeBasic(type, true);
+ }
+
+ std::string GenTypePointer(const Type &type) const {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return lang_.string_type;
+ case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
+ case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def);
+ case BASE_TYPE_UNION:
+ // Unions in C# use a generic Table-derived type for better type safety
+ if (lang_.language == IDLOptions::kCSharp) return "TTable";
+ // fall through
+ default: return "Table";
+ }
+ }
+
+ std::string GenTypeGet(const Type &type) const {
+ return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type);
+ }
+
+ // Find the destination type the user wants to receive the value in (e.g.
+ // one size higher signed types for unsigned serialized values in Java).
+ Type DestinationType(const Type &type, bool vectorelem) const {
+ if (lang_.language != IDLOptions::kJava) return type;
+ switch (type.base_type) {
+ // We use int for both uchar/ushort, since that generally means less
+ // casting than using short for uchar.
+ case BASE_TYPE_UCHAR: return Type(BASE_TYPE_INT);
+ case BASE_TYPE_USHORT: return Type(BASE_TYPE_INT);
+ case BASE_TYPE_UINT: return Type(BASE_TYPE_LONG);
+ case BASE_TYPE_VECTOR:
+ if (vectorelem) return DestinationType(type.VectorType(), vectorelem);
+ // else fall thru
+ default: return type;
+ }
+ }
+
+ std::string GenOffsetType(const StructDef &struct_def) const {
+ if (lang_.language == IDLOptions::kCSharp) {
+ return "Offset<" + WrapInNameSpace(struct_def) + ">";
+ } else {
+ return "int";
+ }
+ }
+
+ std::string GenOffsetConstruct(const StructDef &struct_def,
+ const std::string &variable_name) const {
+ if (lang_.language == IDLOptions::kCSharp) {
+ return "new Offset<" + WrapInNameSpace(struct_def) + ">(" +
+ variable_name + ")";
+ }
+ return variable_name;
+ }
+
+ std::string GenVectorOffsetType() const {
+ if (lang_.language == IDLOptions::kCSharp) {
+ return "VectorOffset";
+ } else {
+ return "int";
+ }
+ }
+
+ // Generate destination type name
+ std::string GenTypeNameDest(const Type &type) const {
+ return GenTypeGet(DestinationType(type, true));
+ }
+
+ // Mask to turn serialized value into destination type value.
+ std::string DestinationMask(const Type &type, bool vectorelem) const {
+ if (lang_.language != IDLOptions::kJava) return "";
+ switch (type.base_type) {
+ case BASE_TYPE_UCHAR: return " & 0xFF";
+ case BASE_TYPE_USHORT: return " & 0xFFFF";
+ case BASE_TYPE_UINT: return " & 0xFFFFFFFFL";
+ case BASE_TYPE_VECTOR:
+ if (vectorelem) return DestinationMask(type.VectorType(), vectorelem);
+ // else fall thru
+ default: return "";
+ }
+ }
+
+ // Casts necessary to correctly read serialized data
+ std::string DestinationCast(const Type &type) const {
+ if (type.base_type == BASE_TYPE_VECTOR) {
+ return DestinationCast(type.VectorType());
+ } else {
+ switch (lang_.language) {
+ case IDLOptions::kJava:
+ // Cast necessary to correctly read serialized unsigned values.
+ if (type.base_type == BASE_TYPE_UINT) return "(long)";
+ break;
+
+ case IDLOptions::kCSharp:
+ // Cast from raw integral types to enum.
+ if (IsEnum(type)) return "(" + WrapInNameSpace(*type.enum_def) + ")";
+ break;
+
+ default: break;
+ }
+ }
+ return "";
+ }
+
+ // Cast statements for mutator method parameters.
+ // In Java, parameters representing unsigned numbers need to be cast down to
+ // their respective type. For example, a long holding an unsigned int value
+ // would be cast down to int before being put onto the buffer. In C#, one cast
+ // directly cast an Enum to its underlying type, which is essential before
+ // putting it onto the buffer.
+ std::string SourceCast(const Type &type, bool castFromDest) const {
+ if (type.base_type == BASE_TYPE_VECTOR) {
+ return SourceCast(type.VectorType(), castFromDest);
+ } else {
+ switch (lang_.language) {
+ case IDLOptions::kJava:
+ if (castFromDest) {
+ if (type.base_type == BASE_TYPE_UINT)
+ return "(int)";
+ else if (type.base_type == BASE_TYPE_USHORT)
+ return "(short)";
+ else if (type.base_type == BASE_TYPE_UCHAR)
+ return "(byte)";
+ }
+ break;
+ case IDLOptions::kCSharp:
+ if (IsEnum(type)) return "(" + GenTypeBasic(type, false) + ")";
+ break;
+ default: break;
+ }
+ }
+ return "";
+ }
+
+ std::string SourceCast(const Type &type) const { return SourceCast(type, true); }
+
+ std::string SourceCastBasic(const Type &type, bool castFromDest) const {
+ return IsScalar(type.base_type) ? SourceCast(type, castFromDest) : "";
+ }
+
+ std::string SourceCastBasic(const Type &type) const {
+ return SourceCastBasic(type, true);
+ }
+
+ std::string GenEnumDefaultValue(const Value &value) const {
+ auto enum_def = value.type.enum_def;
+ auto vec = enum_def->vals.vec;
+ auto default_value = StringToInt(value.constant.c_str());
+
+ auto result = value.constant;
+ for (auto it = vec.begin(); it != vec.end(); ++it) {
+ auto enum_val = **it;
+ if (enum_val.value == default_value) {
+ result = WrapInNameSpace(*enum_def) + "." + enum_val.name;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ std::string GenDefaultValue(const Value &value, bool enableLangOverrides) const {
+ if (enableLangOverrides) {
+ // handles both enum case and vector of enum case
+ if (lang_.language == IDLOptions::kCSharp &&
+ value.type.enum_def != nullptr &&
+ value.type.base_type != BASE_TYPE_UNION) {
+ return GenEnumDefaultValue(value);
+ }
+ }
+
+ auto longSuffix = lang_.language == IDLOptions::kJava ? "L" : "";
+ switch (value.type.base_type) {
+ case BASE_TYPE_FLOAT: return value.constant + "f";
+ case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
+ case BASE_TYPE_ULONG: {
+ if (lang_.language != IDLOptions::kJava) return value.constant;
+ // Converts the ulong into its bits signed equivalent
+ uint64_t defaultValue = StringToUInt(value.constant.c_str());
+ return NumToString(static_cast(defaultValue)) + longSuffix;
+ }
+ case BASE_TYPE_UINT:
+ case BASE_TYPE_LONG: return value.constant + longSuffix;
+ default: return value.constant;
+ }
+ }
+
+ std::string GenDefaultValue(const Value &value) const {
+ return GenDefaultValue(value, true);
+ }
+
+ std::string GenDefaultValueBasic(const Value &value,
+ bool enableLangOverrides) const {
+ if (!IsScalar(value.type.base_type)) {
+ if (enableLangOverrides) {
+ if (lang_.language == IDLOptions::kCSharp) {
+ switch (value.type.base_type) {
+ case BASE_TYPE_STRING: return "default(StringOffset)";
+ case BASE_TYPE_STRUCT:
+ return "default(Offset<" +
+ WrapInNameSpace(*value.type.struct_def) + ">)";
+ case BASE_TYPE_VECTOR: return "default(VectorOffset)";
+ default: break;
+ }
+ }
+ }
+ return "0";
+ }
+ return GenDefaultValue(value, enableLangOverrides);
+ }
+
+ std::string GenDefaultValueBasic(const Value &value) const {
+ return GenDefaultValueBasic(value, true);
+ }
+
+ void GenEnum(EnumDef &enum_def, std::string *code_ptr) const {
+ std::string &code = *code_ptr;
+ if (enum_def.generated) return;
+
+ // Generate enum definitions of the form:
+ // public static (final) int name = value;
+ // In Java, we use ints rather than the Enum feature, because we want them
+ // to map directly to how they're used in C/C++ and file formats.
+ // That, and Java Enums are expensive, and not universally liked.
+ GenComment(enum_def.doc_comment, code_ptr, &lang_.comment_config);
+ if (enum_def.attributes.Lookup("private")) {
+ // For Java, we leave the enum unmarked to indicate package-private
+ // For C# we mark the enum as internal
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += "internal ";
+ }
+ } else {
+ code += "public ";
+ }
+ code += lang_.enum_decl + enum_def.name;
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += lang_.inheritance_marker +
+ GenTypeBasic(enum_def.underlying_type, false);
+ }
+ code += lang_.open_curly;
+ if (lang_.language == IDLOptions::kJava) {
+ code += " private " + enum_def.name + "() { }\n";
+ }
+ for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+ ++it) {
+ auto &ev = **it;
+ GenComment(ev.doc_comment, code_ptr, &lang_.comment_config, " ");
+ if (lang_.language != IDLOptions::kCSharp) {
+ code += " public static";
+ code += lang_.const_decl;
+ code += GenTypeBasic(enum_def.underlying_type, false);
+ }
+ code += " " + ev.name + " = ";
+ code += NumToString(ev.value);
+ code += lang_.enum_separator;
+ }
+
+ // Generate a generate string table for enum values.
+ // We do not do that for C# where this functionality is native.
+ if (lang_.language != IDLOptions::kCSharp) {
+ // Problem is, if values are very sparse that could generate really big
+ // tables. Ideally in that case we generate a map lookup instead, but for
+ // the moment we simply don't output a table at all.
+ auto range = enum_def.vals.vec.back()->value -
+ enum_def.vals.vec.front()->value + 1;
+ // Average distance between values above which we consider a table
+ // "too sparse". Change at will.
+ static const int kMaxSparseness = 5;
+ if (range / static_cast(enum_def.vals.vec.size()) <
+ kMaxSparseness) {
+ code += "\n public static";
+ code += lang_.const_decl;
+ code += lang_.string_type;
+ code += "[] names = { ";
+ auto val = enum_def.vals.vec.front()->value;
+ for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+ ++it) {
+ while (val++ != (*it)->value) code += "\"\", ";
+ code += "\"" + (*it)->name + "\", ";
+ }
+ code += "};\n\n";
+ code += " public static ";
+ code += lang_.string_type;
+ code += " " + MakeCamel("name", lang_.first_camel_upper);
+ code += "(int e) { return names[e";
+ if (enum_def.vals.vec.front()->value)
+ code += " - " + enum_def.vals.vec.front()->name;
+ code += "]; }\n";
+ }
+ }
+
+ // Close the class
+ code += "}";
+ // Java does not need the closing semi-colon on class definitions.
+ code += (lang_.language != IDLOptions::kJava) ? ";" : "";
+ code += "\n\n";
+ }
+
+ // Returns the function name that is able to read a value of the given type.
+ std::string GenGetter(const Type &type) const {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return lang_.accessor_prefix + "__string";
+ case BASE_TYPE_STRUCT: return lang_.accessor_prefix + "__struct";
+ case BASE_TYPE_UNION: return lang_.accessor_prefix + "__union";
+ case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
+ default: {
+ std::string getter =
+ lang_.accessor_prefix + "bb." + FunctionStart('G') + "et";
+ if (type.base_type == BASE_TYPE_BOOL) {
+ getter = "0!=" + getter;
+ } else if (GenTypeBasic(type, false) != "byte") {
+ getter += MakeCamel(GenTypeBasic(type, false));
+ }
+ return getter;
+ }
+ }
+ }
+
+ // Returns the function name that is able to read a value of the given type.
+ std::string GenGetterForLookupByKey(flatbuffers::FieldDef *key_field,
+ const std::string &data_buffer,
+ const char *num = nullptr) const {
+ auto type = key_field->value.type;
+ auto dest_mask = DestinationMask(type, true);
+ auto dest_cast = DestinationCast(type);
+ auto getter = data_buffer + "." + FunctionStart('G') + "et";
+ if (GenTypeBasic(type, false) != "byte") {
+ getter += MakeCamel(GenTypeBasic(type, false));
+ }
+ getter = dest_cast + getter + "(" + GenOffsetGetter(key_field, num) + ")" +
+ dest_mask;
+ return getter;
+ }
+
+ // Direct mutation is only allowed for scalar fields.
+ // Hence a setter method will only be generated for such fields.
+ std::string GenSetter(const Type &type) const {
+ if (IsScalar(type.base_type)) {
+ std::string setter =
+ lang_.accessor_prefix + "bb." + FunctionStart('P') + "ut";
+ if (GenTypeBasic(type, false) != "byte" &&
+ type.base_type != BASE_TYPE_BOOL) {
+ setter += MakeCamel(GenTypeBasic(type, false));
+ }
+ return setter;
+ } else {
+ return "";
+ }
+ }
+
+ // Returns the method name for use with add/put calls.
+ std::string GenMethod(const Type &type) const {
+ return IsScalar(type.base_type) ? MakeCamel(GenTypeBasic(type, false))
+ : (IsStruct(type) ? "Struct" : "Offset");
+ }
+
+ // Recursively generate arguments for a constructor, to deal with nested
+ // structs.
+ void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
+ const char *nameprefix) const {
+ std::string &code = *code_ptr;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (IsStruct(field.value.type)) {
+ // Generate arguments for a struct inside a struct. To ensure names
+ // don't clash, and to make it obvious these arguments are constructing
+ // a nested struct, prefix the name with the field name.
+ GenStructArgs(*field.value.type.struct_def, code_ptr,
+ (nameprefix + (field.name + "_")).c_str());
+ } else {
+ code += ", ";
+ code += GenTypeBasic(DestinationType(field.value.type, false));
+ code += " ";
+ code += nameprefix;
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ }
+ }
+ }
+
+ // Recusively generate struct construction statements of the form:
+ // builder.putType(name);
+ // and insert manual padding.
+ void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
+ const char *nameprefix) const {
+ std::string &code = *code_ptr;
+ code += " builder." + FunctionStart('P') + "rep(";
+ code += NumToString(struct_def.minalign) + ", ";
+ code += NumToString(struct_def.bytesize) + ");\n";
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+ if (field.padding) {
+ code += " builder." + FunctionStart('P') + "ad(";
+ code += NumToString(field.padding) + ");\n";
+ }
+ if (IsStruct(field.value.type)) {
+ GenStructBody(*field.value.type.struct_def, code_ptr,
+ (nameprefix + (field.name + "_")).c_str());
+ } else {
+ code += " builder." + FunctionStart('P') + "ut";
+ code += GenMethod(field.value.type) + "(";
+ code += SourceCast(field.value.type);
+ auto argname =
+ nameprefix + MakeCamel(field.name, lang_.first_camel_upper);
+ code += argname;
+ code += ");\n";
+ }
+ }
+ }
+
+ std::string GenByteBufferLength(const char *bb_name) const {
+ std::string bb_len = bb_name;
+ if (lang_.language == IDLOptions::kCSharp)
+ bb_len += ".Length";
+ else
+ bb_len += ".capacity()";
+ return bb_len;
+ }
+
+ std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
+ const char *num = nullptr) const {
+ std::string key_offset = "";
+ key_offset += lang_.accessor_prefix_static + "__offset(" +
+ NumToString(key_field->value.offset) + ", ";
+ if (num) {
+ key_offset += num;
+ key_offset +=
+ (lang_.language == IDLOptions::kCSharp ? ".Value, builder.DataBuffer)"
+ : ", _bb)");
+ } else {
+ key_offset += GenByteBufferLength("bb");
+ key_offset += " - tableOffset, bb)";
+ }
+ return key_offset;
+ }
+
+ std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) const {
+ std::string key_getter = " ";
+ key_getter += "int tableOffset = " + lang_.accessor_prefix_static;
+ key_getter += "__indirect(vectorLocation + 4 * (start + middle)";
+ key_getter += ", bb);\n ";
+ if (key_field->value.type.base_type == BASE_TYPE_STRING) {
+ key_getter += "int comp = " + lang_.accessor_prefix_static;
+ key_getter += FunctionStart('C') + "ompareStrings(";
+ key_getter += GenOffsetGetter(key_field);
+ key_getter += ", byteKey, bb);\n";
+ } else {
+ auto get_val = GenGetterForLookupByKey(key_field, "bb");
+ if (lang_.language == IDLOptions::kCSharp) {
+ key_getter += "int comp = " + get_val + ".CompareTo(key);\n";
+ } else {
+ key_getter += GenTypeNameDest(key_field->value.type) + " val = ";
+ key_getter += get_val + ";\n";
+ key_getter += " int comp = val > key ? 1 : val < key ? -1 : 0;\n";
+ }
+ }
+ return key_getter;
+ }
+
+ std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const {
+ std::string key_getter = "";
+ auto data_buffer =
+ (lang_.language == IDLOptions::kCSharp) ? "builder.DataBuffer" : "_bb";
+ if (key_field->value.type.base_type == BASE_TYPE_STRING) {
+ if (lang_.language == IDLOptions::kJava) key_getter += " return ";
+ key_getter += lang_.accessor_prefix_static;
+ key_getter += FunctionStart('C') + "ompareStrings(";
+ key_getter += GenOffsetGetter(key_field, "o1") + ", ";
+ key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")";
+ if (lang_.language == IDLOptions::kJava) key_getter += ";";
+ } else {
+ auto field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o1");
+ if (lang_.language == IDLOptions::kCSharp) {
+ key_getter += field_getter;
+ field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2");
+ key_getter += ".CompareTo(" + field_getter + ")";
+ } else {
+ key_getter +=
+ "\n " + GenTypeNameDest(key_field->value.type) + " val_1 = ";
+ key_getter +=
+ field_getter + ";\n " + GenTypeNameDest(key_field->value.type);
+ key_getter += " val_2 = ";
+ field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2");
+ key_getter += field_getter + ";\n";
+ key_getter +=
+ " return val_1 > val_2 ? 1 : val_1 < val_2 ? -1 : 0;\n ";
+ }
+ }
+ return key_getter;
+ }
+
+ void GenStruct(StructDef &struct_def, std::string *code_ptr) const {
+ if (struct_def.generated) return;
+ std::string &code = *code_ptr;
+
+ // Generate a struct accessor class, with methods of the form:
+ // public type name() { return bb.getType(i + offset); }
+ // or for tables of the form:
+ // public type name() {
+ // int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default;
+ // }
+ GenComment(struct_def.doc_comment, code_ptr, &lang_.comment_config);
+ if (struct_def.attributes.Lookup("private")) {
+ // For Java, we leave the struct unmarked to indicate package-private
+ // For C# we mark the struct as internal
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += "internal ";
+ }
+ } else {
+ code += "public ";
+ }
+ if (lang_.language == IDLOptions::kCSharp &&
+ struct_def.attributes.Lookup("csharp_partial")) {
+ // generate a partial class for this C# struct/table
+ code += "partial ";
+ } else {
+ code += lang_.unsubclassable_decl;
+ }
+ code += lang_.accessor_type + struct_def.name;
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += " : IFlatbufferObject";
+ code += lang_.open_curly;
+ code += " private ";
+ code += struct_def.fixed ? "Struct" : "Table";
+ code += " __p;\n";
+
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += " public ByteBuffer ByteBuffer { get { return __p.bb; } }\n";
+ }
+
+ } else {
+ code += lang_.inheritance_marker;
+ code += struct_def.fixed ? "Struct" : "Table";
+ code += lang_.open_curly;
+ }
+ if (!struct_def.fixed) {
+ // Generate a special accessor for the table that when used as the root
+ // of a FlatBuffer
+ std::string method_name =
+ FunctionStart('G') + "etRootAs" + struct_def.name;
+ std::string method_signature =
+ " public static " + struct_def.name + " " + method_name;
+
+ // create convenience method that doesn't require an existing object
+ code += method_signature + "(ByteBuffer _bb) ";
+ code += "{ return " + method_name + "(_bb, new " + struct_def.name +
+ "()); }\n";
+
+ // create method that allows object reuse
+ code +=
+ method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { ";
+ code += lang_.set_bb_byteorder;
+ code += "return (obj.__assign(_bb." + FunctionStart('G') + "etInt(_bb.";
+ code += lang_.get_bb_position;
+ code += ") + _bb.";
+ code += lang_.get_bb_position;
+ code += ", _bb)); }\n";
+ if (parser_.root_struct_def_ == &struct_def) {
+ if (parser_.file_identifier_.length()) {
+ // Check if a buffer has the identifier.
+ code += " public static ";
+ code += lang_.bool_type + struct_def.name;
+ code += "BufferHasIdentifier(ByteBuffer _bb) { return ";
+ code += lang_.accessor_prefix_static + "__has_identifier(_bb, \"";
+ code += parser_.file_identifier_;
+ code += "\"); }\n";
+ }
+ }
+ }
+ // Generate the __init method that sets the field in a pre-existing
+ // accessor object. This is to allow object reuse.
+ code += " public void __init(int _i, ByteBuffer _bb) ";
+ code += "{ " + lang_.accessor_prefix + "bb_pos = _i; ";
+ code += lang_.accessor_prefix + "bb = _bb; }\n";
+ code +=
+ " public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) ";
+ code += "{ __init(_i, _bb); return this; }\n\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ GenComment(field.doc_comment, code_ptr, &lang_.comment_config, " ");
+ std::string type_name = GenTypeGet(field.value.type);
+ std::string type_name_dest = GenTypeNameDest(field.value.type);
+ std::string conditional_cast = "";
+ std::string optional = "";
+ if (lang_.language == IDLOptions::kCSharp && !struct_def.fixed &&
+ (field.value.type.base_type == BASE_TYPE_STRUCT ||
+ field.value.type.base_type == BASE_TYPE_UNION ||
+ (field.value.type.base_type == BASE_TYPE_VECTOR &&
+ (field.value.type.element == BASE_TYPE_STRUCT ||
+ field.value.type.element == BASE_TYPE_UNION)))) {
+ optional = lang_.optional_suffix;
+ conditional_cast = "(" + type_name_dest + optional + ")";
+ }
+ std::string dest_mask = DestinationMask(field.value.type, true);
+ std::string dest_cast = DestinationCast(field.value.type);
+ std::string src_cast = SourceCast(field.value.type);
+ std::string method_start = " public " +
+ (field.required ? "" : GenNullableAnnotation(field.value.type)) +
+ type_name_dest + optional + " " +
+ MakeCamel(field.name, lang_.first_camel_upper);
+ std::string obj = lang_.language == IDLOptions::kCSharp
+ ? "(new " + type_name + "())"
+ : "obj";
+
+ // Most field accessors need to retrieve and test the field offset first,
+ // this is the prefix code for that:
+ auto offset_prefix = " { int o = " + lang_.accessor_prefix + "__offset(" +
+ NumToString(field.value.offset) +
+ "); return o != 0 ? ";
+ // Generate the accessors that don't do object reuse.
+ if (field.value.type.base_type == BASE_TYPE_STRUCT) {
+ // Calls the accessor that takes an accessor object with a new object.
+ if (lang_.language != IDLOptions::kCSharp) {
+ code += method_start + "() { return ";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "(new ";
+ code += type_name + "()); }\n";
+ }
+ } else if (field.value.type.base_type == BASE_TYPE_VECTOR &&
+ field.value.type.element == BASE_TYPE_STRUCT) {
+ // Accessors for vectors of structs also take accessor objects, this
+ // generates a variant without that argument.
+ if (lang_.language != IDLOptions::kCSharp) {
+ code += method_start + "(int j) { return ";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "(new " + type_name + "(), j); }\n";
+ }
+ } else if (field.value.type.base_type == BASE_TYPE_UNION ||
+ (field.value.type.base_type == BASE_TYPE_VECTOR &&
+ field.value.type.VectorType().base_type == BASE_TYPE_UNION)) {
+ if (lang_.language == IDLOptions::kCSharp) {
+ // Union types in C# use generic Table-derived type for better type
+ // safety.
+ method_start += "";
+ type_name = type_name_dest;
+ }
+ }
+ std::string getter = dest_cast + GenGetter(field.value.type);
+ code += method_start;
+ std::string default_cast = "";
+ // only create default casts for c# scalars or vectors of scalars
+ if (lang_.language == IDLOptions::kCSharp &&
+ (IsScalar(field.value.type.base_type) ||
+ (field.value.type.base_type == BASE_TYPE_VECTOR &&
+ IsScalar(field.value.type.element)))) {
+ // For scalars, default value will be returned by GetDefaultValue().
+ // If the scalar is an enum, GetDefaultValue() returns an actual c# enum
+ // that doesn't need to be casted. However, default values for enum
+ // elements of vectors are integer literals ("0") and are still casted
+ // for clarity.
+ if (field.value.type.enum_def == nullptr ||
+ field.value.type.base_type == BASE_TYPE_VECTOR) {
+ default_cast = "(" + type_name_dest + ")";
+ }
+ }
+ std::string member_suffix = "; ";
+ if (IsScalar(field.value.type.base_type)) {
+ code += lang_.getter_prefix;
+ member_suffix += lang_.getter_suffix;
+ if (struct_def.fixed) {
+ code += " { return " + getter;
+ code += "(" + lang_.accessor_prefix + "bb_pos + ";
+ code += NumToString(field.value.offset) + ")";
+ code += dest_mask;
+ } else {
+ code += offset_prefix + getter;
+ code += "(o + " + lang_.accessor_prefix + "bb_pos)" + dest_mask;
+ code += " : " + default_cast;
+ code += GenDefaultValue(field.value);
+ }
+ } else {
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_STRUCT:
+ if (lang_.language != IDLOptions::kCSharp) {
+ code += "(" + type_name + " obj" + ")";
+ } else {
+ code += lang_.getter_prefix;
+ member_suffix += lang_.getter_suffix;
+ }
+ if (struct_def.fixed) {
+ code += " { return " + obj + ".__assign(" + lang_.accessor_prefix;
+ code += "bb_pos + " + NumToString(field.value.offset) + ", ";
+ code += lang_.accessor_prefix + "bb)";
+ } else {
+ code += offset_prefix + conditional_cast;
+ code += obj + ".__assign(";
+ code += field.value.type.struct_def->fixed
+ ? "o + " + lang_.accessor_prefix + "bb_pos"
+ : lang_.accessor_prefix + "__indirect(o + " +
+ lang_.accessor_prefix + "bb_pos)";
+ code += ", " + lang_.accessor_prefix + "bb) : null";
+ }
+ break;
+ case BASE_TYPE_STRING:
+ code += lang_.getter_prefix;
+ member_suffix += lang_.getter_suffix;
+ code += offset_prefix + getter + "(o + " + lang_.accessor_prefix;
+ code += "bb_pos) : null";
+ break;
+ case BASE_TYPE_VECTOR: {
+ auto vectortype = field.value.type.VectorType();
+ if (vectortype.base_type == BASE_TYPE_UNION &&
+ lang_.language == IDLOptions::kCSharp) {
+ conditional_cast = "(TTable?)";
+ getter += "";
+ }
+ code += "(";
+ if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ if (lang_.language != IDLOptions::kCSharp)
+ code += type_name + " obj, ";
+ getter = obj + ".__assign";
+ } else if (vectortype.base_type == BASE_TYPE_UNION) {
+ if (lang_.language != IDLOptions::kCSharp)
+ code += type_name + " obj, ";
+ }
+ code += "int j)";
+ const auto body = offset_prefix + conditional_cast + getter + "(";
+ if (vectortype.base_type == BASE_TYPE_UNION) {
+ if (lang_.language != IDLOptions::kCSharp)
+ code += body + "obj, ";
+ else
+ code += " where TTable : struct, IFlatbufferObject" + body;
+ } else {
+ code += body;
+ }
+ auto index = lang_.accessor_prefix + "__vector(o) + j * " +
+ NumToString(InlineSize(vectortype));
+ if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ code += vectortype.struct_def->fixed
+ ? index
+ : lang_.accessor_prefix + "__indirect(" + index + ")";
+ code += ", " + lang_.accessor_prefix + "bb";
+ } else {
+ code += index;
+ }
+ code += ")" + dest_mask + " : ";
+
+ code +=
+ field.value.type.element == BASE_TYPE_BOOL
+ ? "false"
+ : (IsScalar(field.value.type.element) ? default_cast + "0"
+ : "null");
+ break;
+ }
+ case BASE_TYPE_UNION:
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += "() where TTable : struct, IFlatbufferObject";
+ code += offset_prefix + "(TTable?)" + getter;
+ code += "(o) : null";
+ } else {
+ code += "(" + type_name + " obj)" + offset_prefix + getter;
+ code += "(obj, o) : null";
+ }
+ break;
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ }
+ code += member_suffix;
+ code += "}\n";
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ code +=
+ " public int " + MakeCamel(field.name, lang_.first_camel_upper);
+ code += "Length";
+ code += lang_.getter_prefix;
+ code += offset_prefix;
+ code += lang_.accessor_prefix + "__vector_len(o) : 0; ";
+ code += lang_.getter_suffix;
+ code += "}\n";
+ // See if we should generate a by-key accessor.
+ if (field.value.type.element == BASE_TYPE_STRUCT &&
+ !field.value.type.struct_def->fixed) {
+ auto &sd = *field.value.type.struct_def;
+ auto &fields = sd.fields.vec;
+ for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
+ auto &key_field = **kit;
+ if (key_field.key) {
+ auto qualified_name = WrapInNameSpace(sd);
+ code += " public " + qualified_name + lang_.optional_suffix + " ";
+ code += MakeCamel(field.name, lang_.first_camel_upper) + "ByKey(";
+ code += GenTypeNameDest(key_field.value.type) + " key)";
+ code += offset_prefix;
+ code += qualified_name + ".__lookup_by_key(";
+ if (lang_.language == IDLOptions::kJava)
+ code += "null, ";
+ code += lang_.accessor_prefix + "__vector(o), key, ";
+ code += lang_.accessor_prefix + "bb) : null; ";
+ code += "}\n";
+ if (lang_.language == IDLOptions::kJava) {
+ code += " public " + qualified_name + lang_.optional_suffix + " ";
+ code += MakeCamel(field.name, lang_.first_camel_upper) + "ByKey(";
+ code += qualified_name + lang_.optional_suffix + " obj, ";
+ code += GenTypeNameDest(key_field.value.type) + " key)";
+ code += offset_prefix;
+ code += qualified_name + ".__lookup_by_key(obj, ";
+ code += lang_.accessor_prefix + "__vector(o), key, ";
+ code += lang_.accessor_prefix + "bb) : null; ";
+ code += "}\n";
+ }
+ break;
+ }
+ }
+ }
+ }
+ // Generate a ByteBuffer accessor for strings & vectors of scalars.
+ if ((field.value.type.base_type == BASE_TYPE_VECTOR &&
+ IsScalar(field.value.type.VectorType().base_type)) ||
+ field.value.type.base_type == BASE_TYPE_STRING) {
+ switch (lang_.language) {
+ case IDLOptions::kJava:
+ code += " public ByteBuffer ";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "AsByteBuffer() { return ";
+ code += lang_.accessor_prefix + "__vector_as_bytebuffer(";
+ code += NumToString(field.value.offset) + ", ";
+ code +=
+ NumToString(field.value.type.base_type == BASE_TYPE_STRING
+ ? 1
+ : InlineSize(field.value.type.VectorType()));
+ code += "); }\n";
+ code += " public ByteBuffer ";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "InByteBuffer(ByteBuffer _bb) { return ";
+ code += lang_.accessor_prefix + "__vector_in_bytebuffer(_bb, ";
+ code += NumToString(field.value.offset) + ", ";
+ code +=
+ NumToString(field.value.type.base_type == BASE_TYPE_STRING
+ ? 1
+ : InlineSize(field.value.type.VectorType()));
+ code += "); }\n";
+ break;
+ case IDLOptions::kCSharp:
+ code += "#if ENABLE_SPAN_T\n";
+ code += " public Span Get";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "Bytes() { return ";
+ code += lang_.accessor_prefix + "__vector_as_span(";
+ code += NumToString(field.value.offset);
+ code += "); }\n";
+ code += "#else\n";
+ code += " public ArraySegment? Get";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "Bytes() { return ";
+ code += lang_.accessor_prefix + "__vector_as_arraysegment(";
+ code += NumToString(field.value.offset);
+ code += "); }\n";
+ code += "#endif\n";
+
+ // For direct blockcopying the data into a typed array
+ code += " public ";
+ code += GenTypeBasic(field.value.type.VectorType());
+ code += "[] Get";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "Array() { return ";
+ code += lang_.accessor_prefix + "__vector_as_array<";
+ code += GenTypeBasic(field.value.type.VectorType());
+ code += ">(";
+ code += NumToString(field.value.offset);
+ code += "); }\n";
+ break;
+ default: break;
+ }
+ }
+ // generate object accessors if is nested_flatbuffer
+ if (field.nested_flatbuffer) {
+ auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer);
+ auto nested_method_name =
+ MakeCamel(field.name, lang_.first_camel_upper) + "As" +
+ nested_type_name;
+ auto get_nested_method_name = nested_method_name;
+ if (lang_.language == IDLOptions::kCSharp) {
+ get_nested_method_name = "Get" + nested_method_name;
+ conditional_cast =
+ "(" + nested_type_name + lang_.optional_suffix + ")";
+ }
+ if (lang_.language != IDLOptions::kCSharp) {
+ code += " public " + nested_type_name + lang_.optional_suffix + " ";
+ code += nested_method_name + "() { return ";
+ code +=
+ get_nested_method_name + "(new " + nested_type_name + "()); }\n";
+ } else {
+ obj = "(new " + nested_type_name + "())";
+ }
+ code += " public " + nested_type_name + lang_.optional_suffix + " ";
+ code += get_nested_method_name + "(";
+ if (lang_.language != IDLOptions::kCSharp)
+ code += nested_type_name + " obj";
+ code += ") { int o = " + lang_.accessor_prefix + "__offset(";
+ code += NumToString(field.value.offset) + "); ";
+ code += "return o != 0 ? " + conditional_cast + obj + ".__assign(";
+ code += lang_.accessor_prefix;
+ code += "__indirect(" + lang_.accessor_prefix + "__vector(o)), ";
+ code += lang_.accessor_prefix + "bb) : null; }\n";
+ }
+ // Generate mutators for scalar fields or vectors of scalars.
+ if (parser_.opts.mutable_buffer) {
+ auto underlying_type = field.value.type.base_type == BASE_TYPE_VECTOR
+ ? field.value.type.VectorType()
+ : field.value.type;
+ // Boolean parameters have to be explicitly converted to byte
+ // representation.
+ auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL
+ ? "(byte)(" + field.name + " ? 1 : 0)"
+ : field.name;
+ auto mutator_prefix = MakeCamel("mutate", lang_.first_camel_upper);
+ // A vector mutator also needs the index of the vector element it should
+ // mutate.
+ auto mutator_params =
+ (field.value.type.base_type == BASE_TYPE_VECTOR ? "(int j, "
+ : "(") +
+ GenTypeNameDest(underlying_type) + " " + field.name + ") { ";
+ auto setter_index =
+ field.value.type.base_type == BASE_TYPE_VECTOR
+ ? lang_.accessor_prefix + "__vector(o) + j * " +
+ NumToString(InlineSize(underlying_type))
+ : (struct_def.fixed
+ ? lang_.accessor_prefix + "bb_pos + " +
+ NumToString(field.value.offset)
+ : "o + " + lang_.accessor_prefix + "bb_pos");
+ if (IsScalar(field.value.type.base_type) ||
+ (field.value.type.base_type == BASE_TYPE_VECTOR &&
+ IsScalar(field.value.type.VectorType().base_type))) {
+ code += " public ";
+ code += struct_def.fixed ? "void " : lang_.bool_type;
+ code += mutator_prefix + MakeCamel(field.name, true);
+ code += mutator_params;
+ if (struct_def.fixed) {
+ code += GenSetter(underlying_type) + "(" + setter_index + ", ";
+ code += src_cast + setter_parameter + "); }\n";
+ } else {
+ code += "int o = " + lang_.accessor_prefix + "__offset(";
+ code += NumToString(field.value.offset) + ");";
+ code += " if (o != 0) { " + GenSetter(underlying_type);
+ code += "(" + setter_index + ", " + src_cast + setter_parameter +
+ "); return true; } else { return false; } }\n";
+ }
+ }
+ }
+ }
+ code += "\n";
+ flatbuffers::FieldDef *key_field = nullptr;
+ if (struct_def.fixed) {
+ // create a struct constructor function
+ code += " public static " + GenOffsetType(struct_def) + " ";
+ code += FunctionStart('C') + "reate";
+ code += struct_def.name + "(FlatBufferBuilder builder";
+ GenStructArgs(struct_def, code_ptr, "");
+ code += ") {\n";
+ GenStructBody(struct_def, code_ptr, "");
+ code += " return ";
+ code += GenOffsetConstruct(
+ struct_def, "builder." + std::string(lang_.get_fbb_offset));
+ code += ";\n }\n";
+ } else {
+ // Generate a method that creates a table in one go. This is only possible
+ // when the table has no struct fields, since those have to be created
+ // inline, and there's no way to do so in Java.
+ bool has_no_struct_fields = true;
+ int num_fields = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ if (IsStruct(field.value.type)) {
+ has_no_struct_fields = false;
+ } else {
+ num_fields++;
+ }
+ }
+ // JVM specifications restrict default constructor params to be < 255.
+ // Longs and doubles take up 2 units, so we set the limit to be < 127.
+ if (has_no_struct_fields && num_fields && num_fields < 127) {
+ // Generate a table constructor of the form:
+ // public static int createName(FlatBufferBuilder builder, args...)
+ code += " public static " + GenOffsetType(struct_def) + " ";
+ code += FunctionStart('C') + "reate" + struct_def.name;
+ code += "(FlatBufferBuilder builder";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ code += ",\n ";
+ code += GenTypeBasic(DestinationType(field.value.type, false));
+ code += " ";
+ code += field.name;
+ if (!IsScalar(field.value.type.base_type)) code += "Offset";
+
+ // Java doesn't have defaults, which means this method must always
+ // supply all arguments, and thus won't compile when fields are added.
+ if (lang_.language != IDLOptions::kJava) {
+ code += " = ";
+ code += GenDefaultValueBasic(field.value);
+ }
+ }
+ code += ") {\n builder.";
+ code += FunctionStart('S') + "tartObject(";
+ code += NumToString(struct_def.fields.vec.size()) + ");\n";
+ for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
+ size; size /= 2) {
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+ if (!field.deprecated &&
+ (!struct_def.sortbysize ||
+ size == SizeOf(field.value.type.base_type))) {
+ code += " " + struct_def.name + ".";
+ code += FunctionStart('A') + "dd";
+ code += MakeCamel(field.name) + "(builder, " + field.name;
+ if (!IsScalar(field.value.type.base_type)) code += "Offset";
+ code += ");\n";
+ }
+ }
+ }
+ code += " return " + struct_def.name + ".";
+ code += FunctionStart('E') + "nd" + struct_def.name;
+ code += "(builder);\n }\n\n";
+ }
+ // Generate a set of static methods that allow table construction,
+ // of the form:
+ // public static void addName(FlatBufferBuilder builder, short name)
+ // { builder.addShort(id, name, default); }
+ // Unlike the Create function, these always work.
+ code += " public static void " + FunctionStart('S') + "tart";
+ code += struct_def.name;
+ code += "(FlatBufferBuilder builder) { builder.";
+ code += FunctionStart('S') + "tartObject(";
+ code += NumToString(struct_def.fields.vec.size()) + "); }\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ if (field.key) key_field = &field;
+ code += " public static void " + FunctionStart('A') + "dd";
+ code += MakeCamel(field.name);
+ code += "(FlatBufferBuilder builder, ";
+ code += GenTypeBasic(DestinationType(field.value.type, false));
+ auto argname = MakeCamel(field.name, false);
+ if (!IsScalar(field.value.type.base_type)) argname += "Offset";
+ code += " " + argname + ") { builder." + FunctionStart('A') + "dd";
+ code += GenMethod(field.value.type) + "(";
+ code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
+ code += SourceCastBasic(field.value.type);
+ code += argname;
+ if (!IsScalar(field.value.type.base_type) &&
+ field.value.type.base_type != BASE_TYPE_UNION &&
+ lang_.language == IDLOptions::kCSharp) {
+ code += ".Value";
+ }
+ code += ", ";
+ if (lang_.language == IDLOptions::kJava)
+ code += SourceCastBasic(field.value.type);
+ code += GenDefaultValue(field.value, false);
+ code += "); }\n";
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ auto vector_type = field.value.type.VectorType();
+ auto alignment = InlineAlignment(vector_type);
+ auto elem_size = InlineSize(vector_type);
+ if (!IsStruct(vector_type)) {
+ // Generate a method to create a vector from a Java array.
+ code += " public static " + GenVectorOffsetType() + " ";
+ code += FunctionStart('C') + "reate";
+ code += MakeCamel(field.name);
+ code += "Vector(FlatBufferBuilder builder, ";
+ code += GenTypeBasic(vector_type) + "[] data) ";
+ code += "{ builder." + FunctionStart('S') + "tartVector(";
+ code += NumToString(elem_size);
+ code += ", data." + FunctionStart('L') + "ength, ";
+ code += NumToString(alignment);
+ code += "); for (int i = data.";
+ code += FunctionStart('L') + "ength - 1; i >= 0; i--) builder.";
+ code += FunctionStart('A') + "dd";
+ code += GenMethod(vector_type);
+ code += "(";
+ code += SourceCastBasic(vector_type, false);
+ code += "data[i]";
+ if (lang_.language == IDLOptions::kCSharp &&
+ (vector_type.base_type == BASE_TYPE_STRUCT ||
+ vector_type.base_type == BASE_TYPE_STRING))
+ code += ".Value";
+ code += "); return ";
+ code += "builder." + FunctionStart('E') + "ndVector(); }\n";
+ // For C#, include a block copy method signature.
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += " public static " + GenVectorOffsetType() + " ";
+ code += FunctionStart('C') + "reate";
+ code += MakeCamel(field.name);
+ code += "VectorBlock(FlatBufferBuilder builder, ";
+ code += GenTypeBasic(vector_type) + "[] data) ";
+ code += "{ builder." + FunctionStart('S') + "tartVector(";
+ code += NumToString(elem_size);
+ code += ", data." + FunctionStart('L') + "ength, ";
+ code += NumToString(alignment);
+ code += "); builder.Add(data); return builder.EndVector(); }\n";
+ }
+ }
+ // Generate a method to start a vector, data to be added manually
+ // after.
+ code += " public static void " + FunctionStart('S') + "tart";
+ code += MakeCamel(field.name);
+ code += "Vector(FlatBufferBuilder builder, int numElems) ";
+ code += "{ builder." + FunctionStart('S') + "tartVector(";
+ code += NumToString(elem_size);
+ code += ", numElems, " + NumToString(alignment);
+ code += "); }\n";
+ }
+ }
+ code += " public static " + GenOffsetType(struct_def) + " ";
+ code += FunctionStart('E') + "nd" + struct_def.name;
+ code += "(FlatBufferBuilder builder) {\n int o = builder.";
+ code += FunctionStart('E') + "ndObject();\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code += " builder." + FunctionStart('R') + "equired(o, ";
+ code += NumToString(field.value.offset);
+ code += "); // " + field.name + "\n";
+ }
+ }
+ code += " return " + GenOffsetConstruct(struct_def, "o") + ";\n }\n";
+ if (parser_.root_struct_def_ == &struct_def) {
+ std::string size_prefix[] = { "", "SizePrefixed" };
+ for (int i = 0; i < 2; ++i) {
+ code += " public static void ";
+ code += FunctionStart('F') + "inish" + size_prefix[i] +
+ struct_def.name;
+ code += "Buffer(FlatBufferBuilder builder, " +
+ GenOffsetType(struct_def);
+ code += " offset) {";
+ code += " builder." + FunctionStart('F') + "inish" + size_prefix[i] +
+ "(offset";
+ if (lang_.language == IDLOptions::kCSharp) { code += ".Value"; }
+
+ if (parser_.file_identifier_.length())
+ code += ", \"" + parser_.file_identifier_ + "\"";
+ code += "); }\n";
+ }
+ }
+ }
+ // Only generate key compare function for table,
+ // because `key_field` is not set for struct
+ if (struct_def.has_key && !struct_def.fixed) {
+ if (lang_.language == IDLOptions::kJava) {
+ code += "\n @Override\n protected int keysCompare(";
+ code += "Integer o1, Integer o2, ByteBuffer _bb) {";
+ code += GenKeyGetter(key_field);
+ code += " }\n";
+ } else {
+ code += "\n public static VectorOffset ";
+ code += "CreateSortedVectorOf" + struct_def.name;
+ code += "(FlatBufferBuilder builder, ";
+ code += "Offset<" + struct_def.name + ">";
+ code += "[] offsets) {\n";
+ code += " Array.Sort(offsets, (Offset<" + struct_def.name +
+ "> o1, Offset<" + struct_def.name + "> o2) => " +
+ GenKeyGetter(key_field);
+ code += ");\n";
+ code += " return builder.CreateVectorOfTables(offsets);\n }\n";
+ }
+
+ code += "\n public static " + struct_def.name + lang_.optional_suffix;
+ code += " __lookup_by_key(";
+ if (lang_.language == IDLOptions::kJava)
+ code += struct_def.name + " obj, ";
+ code += "int vectorLocation, ";
+ code += GenTypeNameDest(key_field->value.type);
+ code += " key, ByteBuffer bb) {\n";
+ if (key_field->value.type.base_type == BASE_TYPE_STRING) {
+ code += " byte[] byteKey = ";
+ if (lang_.language == IDLOptions::kJava)
+ code += "key.getBytes(Table.UTF8_CHARSET.get());\n";
+ else
+ code += "System.Text.Encoding.UTF8.GetBytes(key);\n";
+ }
+ code += " int span = ";
+ code += "bb." + FunctionStart('G') + "etInt(vectorLocation - 4);\n";
+ code += " int start = 0;\n";
+ code += " while (span != 0) {\n";
+ code += " int middle = span / 2;\n";
+ code += GenLookupKeyGetter(key_field);
+ code += " if (comp > 0) {\n";
+ code += " span = middle;\n";
+ code += " } else if (comp < 0) {\n";
+ code += " middle++;\n";
+ code += " start += middle;\n";
+ code += " span -= middle;\n";
+ code += " } else {\n";
+ code += " return ";
+ if (lang_.language == IDLOptions::kJava)
+ code += "(obj == null ? new " + struct_def.name + "() : obj)";
+ else
+ code += "new " + struct_def.name + "()";
+ code += ".__assign(tableOffset, bb);\n";
+ code += " }\n }\n";
+ code += " return null;\n";
+ code += " }\n";
+ }
+ code += "}";
+ // Java does not need the closing semi-colon on class definitions.
+ code += (lang_.language != IDLOptions::kJava) ? ";" : "";
+ code += "\n\n";
+ }
+ const LanguageParameters &lang_;
+ // This tracks the current namespace used to determine if a type need to be
+ // prefixed by its namespace
+ const Namespace *cur_name_space_;
+};
+} // namespace general
+
+bool GenerateGeneral(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ general::GeneralGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+std::string GeneralMakeRule(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX);
+ const auto &lang = GetLangParams(parser.opts.lang);
+
+ std::string make_rule;
+
+ for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end();
+ ++it) {
+ auto &enum_def = **it;
+ if (make_rule != "") make_rule += " ";
+ std::string directory =
+ BaseGenerator::NamespaceDir(parser, path, *enum_def.defined_namespace);
+ make_rule += directory + enum_def.name + lang.file_extension;
+ }
+
+ for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end();
+ ++it) {
+ auto &struct_def = **it;
+ if (make_rule != "") make_rule += " ";
+ std::string directory = BaseGenerator::NamespaceDir(
+ parser, path, *struct_def.defined_namespace);
+ make_rule += directory + struct_def.name + lang.file_extension;
+ }
+
+ make_rule += ": ";
+ auto included_files = parser.GetIncludedFilesRecursive(file_name);
+ for (auto it = included_files.begin(); it != included_files.end(); ++it) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
+}
+
+std::string BinaryFileName(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ auto ext = parser.file_extension_.length() ? parser.file_extension_ : "bin";
+ return path + file_name + "." + ext;
+}
+
+bool GenerateBinary(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ return !parser.builder_.GetSize() ||
+ flatbuffers::SaveFile(
+ BinaryFileName(parser, path, file_name).c_str(),
+ reinterpret_cast(parser.builder_.GetBufferPointer()),
+ parser.builder_.GetSize(), true);
+}
+
+std::string BinaryMakeRule(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ if (!parser.builder_.GetSize()) return "";
+ std::string filebase =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
+ std::string make_rule =
+ BinaryFileName(parser, path, filebase) + ": " + file_name;
+ auto included_files =
+ parser.GetIncludedFilesRecursive(parser.root_struct_def_->file);
+ for (auto it = included_files.begin(); it != included_files.end(); ++it) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
+}
+
+} // namespace flatbuffers
diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp
index 4ed1a80b295..fa6b0dcc20e 100644
--- a/src/idl_gen_go.cpp
+++ b/src/idl_gen_go.cpp
@@ -1165,7 +1165,7 @@ class GoGenerator : public BaseGenerator {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
#GTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_java.cpp b/src/idl_gen_java.cpp
index b257111f824..3e189733bb0 100644
--- a/src/idl_gen_java.cpp
+++ b/src/idl_gen_java.cpp
@@ -144,7 +144,7 @@ class JavaGenerator : public BaseGenerator {
// clang-format off
static const char * const java_typename[] = {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
#JTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_julia.cpp b/src/idl_gen_julia.cpp
new file mode 100644
index 00000000000..6a4888cd8ca
--- /dev/null
+++ b/src/idl_gen_julia.cpp
@@ -0,0 +1,871 @@
+/*
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// loosely based on idl_gen_python.cpp
+
+#include
+#include
+#include
+#include
+#include
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+namespace julia {
+
+const std::string Indent = " ";
+const std::string JuliaFileExtension = ".jl";
+const std::string JuliaPackageName = "FlatBuffers";
+
+// Documentation comments
+const CommentConfig JuliaCommentConfig = { "#=", "# ", "=#" };
+
+// Class to represent a dependency graph
+// with a topological sort operation
+class DepGraph {
+ public:
+ // adjacency list
+ std::vector *> adj;
+ // node names
+ std::vector nodes;
+ unsigned int GetOrCreateNodeID(std::string name) {
+ auto it = std::find(nodes.begin(), nodes.end(), name);
+ if (it == nodes.end()) {
+ nodes.push_back(name);
+ return nodes.size() - 1;
+ }
+ return std::distance(nodes.begin(), it);
+ }
+ DepGraph() {}
+ ~DepGraph() {
+ for (unsigned int i = 0; i < adj.size(); i++) delete adj[i];
+ }
+ void AddDep(std::string parent, std::string child) {
+ unsigned int p = GetOrCreateNodeID(parent);
+ unsigned int c = GetOrCreateNodeID(child);
+ // n = max(p, c)
+ unsigned int n = p > c ? p : c;
+ // if we just created new node ids,
+ // grow the adjacency list to the new size
+ if (n >= adj.size()) {
+ for (unsigned int i = adj.size(); i <= n; i++)
+ adj.push_back(new std::vector());
+ }
+ adj[p]->push_back(c);
+ }
+ // topological sort
+ bool TopSort(unsigned int v, bool visited[], bool visiting[],
+ std::list &stack) {
+ bool has_cycle = false;
+ visited[v] = true;
+ visiting[v] = true;
+ for (auto it = adj[v]->begin(); it != adj[v]->end(); ++it) {
+ if (!visited[*it]) has_cycle = TopSort(*it, visited, visiting, stack);
+ if (visiting[*it] && nodes[v] != nodes[*it]) {
+ printf(
+ "warning: %s\n",
+ ("Circular reference detected (" + nodes[v] + " -> " + nodes[*it] +
+ "). " + "Julia does not currently suport such definitions.")
+ .c_str());
+ has_cycle = true;
+ }
+ }
+ visiting[v] = false;
+ stack.push_back(v);
+ return has_cycle;
+ }
+ // Topological sort of dependency graph, so we can include
+ // julia files in the right order
+ std::vector TopSort() {
+ std::vector sorted_nodes;
+ std::list stack;
+ unsigned int n = adj.size();
+ bool *visited = new bool[n];
+ bool *visiting = new bool[n];
+ for (unsigned int i = 0; i < n; i++) {
+ visited[i] = false;
+ visiting[i] = false;
+ }
+
+ bool has_cycle = false;
+ for (unsigned int i = 0; i < n; i++)
+ if (!visited[i]) has_cycle |= TopSort(i, visited, visiting, stack);
+
+ while (!stack.empty()) {
+ unsigned int id = stack.back();
+ // don't return nodes on which nothing depends
+ if (!adj[id]->empty()) sorted_nodes.push_back(nodes[id]);
+ stack.pop_back();
+ }
+ return sorted_nodes;
+ }
+
+ size_t size() { return adj.size(); }
+};
+
+class TypeVariableTable {
+public:
+ std::string GetOrCreate(std::string type_name) {
+ if (variables_.find(type_name) != variables_.end())
+ return (*variables_.find(type_name)).first;
+ variables_[current_name_] = type_name;
+ std::string ret = current_name_;
+ int i = current_name_.length() - 1;
+ while (i >= 0) {
+ if (current_name_[i] == 'Z') {
+ current_name_[i] = 'A';
+ i--;
+ continue;
+ }
+ current_name_[i]++;
+ break;
+ }
+ if (i < 0)
+ current_name_.append("A");
+ return ret;
+ }
+
+ std::string TypeSignature(bool concrete = false) {
+ std::string signature = "";
+ if (variables_.size() == 0) {
+ return signature;
+ }
+ signature += "{";
+ bool first = true;
+ for (auto it = variables_.begin(); it != variables_.end(); ++it) {
+ if (!first)
+ signature += ", ";
+ signature += concrete ? (*it).second : (*it).first;
+ first = false;
+ }
+ signature += "}";
+ return signature;
+ }
+
+ TypeVariableTable() : current_name_("A") {}
+ ~TypeVariableTable() {}
+
+private:
+ std::string current_name_;
+ std::map variables_;
+};
+
+class ModuleTable {
+ public:
+ bool IsModule(const std::string &m) {
+ return modules_.find(m) != modules_.end();
+ }
+ void AddFile(const std::string &f) { files_.insert(f); }
+ bool IsFile(const std::string &f) { return files_.find(f) != files_.end(); }
+ void AddDependency(std::string mod, std::string parent, std::string child) {
+ if (!IsModule(mod)) modules_[mod] = new DepGraph();
+ modules_[mod]->AddDep(parent, child);
+ }
+ std::vector SortedModuleNames() {
+ std::set module_names;
+ for (auto it = modules_.begin(); it != modules_.end(); ++it)
+ module_names.insert(it->first);
+ // Make sure parent modules come before child modules
+ std::vector sorted_modules(module_names.begin(),
+ module_names.end());
+ std::sort(sorted_modules.begin(), sorted_modules.end());
+ return sorted_modules;
+ }
+ DepGraph *GetDependencies(std::string module) { return modules_[module]; }
+
+ private:
+ std::map modules_;
+ std::set files_;
+};
+
+class JuliaGenerator : public BaseGenerator {
+ public:
+ JuliaGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "" /* not used */,
+ "" /* not used */),
+ root_module_(MakeCamel(file_name_)) {
+ static const char *const keywords[] = {
+ "begin", "while", "if", "for", "try", "return",
+ "break", "continue", "function", "macro", "quote", "let",
+ "local", "global", "const", "do", "struct", "module",
+ "baremodule", "using", "import", "export", "end", "else",
+ "catch", "finally", "true", "false", "Any"
+ };
+ keywords_.insert(std::begin(keywords), std::end(keywords));
+ }
+
+ ~JuliaGenerator() {}
+ bool generate() {
+ if (!GenEnums()) return false;
+ if (!GenStructs()) return false;
+ if (!GenTopLevel()) return false;
+ return true;
+ }
+
+ private:
+ // the root module is the name of the .fbs file which
+ // we are compiling, in camel case
+ const std::string root_module_;
+ ModuleTable module_table_;
+ std::unordered_set keywords_;
+
+ bool GenEnums(void) {
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ auto &enum_def = **it;
+ std::string enumcode;
+ if (enum_def.is_union)
+ GenUnion(enum_def, &enumcode);
+ else
+ GenEnum(enum_def, &enumcode);
+ if (!SaveType(enum_def, enumcode)) return false;
+ }
+ return true;
+ }
+
+ bool GenStructs(void) {
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ auto &struct_def = **it;
+ std::string declcode;
+ GenObject(struct_def, &declcode);
+ if (!SaveType(struct_def, declcode)) return false;
+ }
+ return true;
+ }
+
+ std::string DefineModule(std::string scope, std::string mod) {
+ return "if !isdefined(" + scope + ", :" + mod + ") " + scope +
+ ".eval(:(module " + mod + " __precompile__(false); import " + JuliaPackageName +
+ " end)) end\n";
+ }
+
+ bool GenTopLevel(void) {
+ std::string code = "# ";
+ std::set included;
+ std::set toplevel;
+ code += FlatBuffersGeneratedWarning();
+ code += "\n\n";
+ // include other included .fbs files first
+ for (auto it = parser_.included_files_.begin();
+ it != parser_.included_files_.end(); ++it) {
+ if (it->second.empty()) continue;
+ auto dir = flatbuffers::StripFileName(it->second);
+ auto basename =
+ flatbuffers::StripPath(flatbuffers::StripExtension(it->second));
+ auto toinclude =
+ ConCatPathFileName(dir, basename + "_generated" + JuliaFileExtension);
+ auto fullpath = ConCatPathFileName(path_, toinclude);
+ if (!FileExists(fullpath.c_str())) continue;
+ code += "include(\"" + toinclude + "\")\n";
+ }
+ for (auto it = parser_.namespaces_.begin(); it != parser_.namespaces_.end();
+ ++it) {
+ std::string parent;
+ std::string child;
+ // Gather all parent namespaces for this namespace
+ for (auto component = (*it)->components.begin();
+ component != (*it)->components.end(); ++component) {
+ std::string scope;
+ if (parent.empty()) {
+ parent = *component;
+ child = *component;
+ module_table_.AddDependency(root_module_, parent, child);
+ // only create toplevel modules once
+ if (toplevel.find(*component) == toplevel.end()) {
+ code += DefineModule("@__MODULE__()", *component);
+ toplevel.insert(*component);
+ }
+ } else {
+ child = parent + kPathSeparator + *component;
+ // Add component to parent's list of children
+ module_table_.AddDependency(parent, child, *component);
+
+ std::string mod = parent;
+ std::replace(mod.begin(), mod.end(), kPathSeparator, '.');
+ code += DefineModule(mod, *component);
+ }
+ parent = child;
+ }
+ }
+
+ auto sorted_modules = module_table_.SortedModuleNames();
+ // iterate through child modules first, then parents
+ for (auto m = sorted_modules.begin(); m != sorted_modules.end(); ++m) {
+ GenIncludes(*m, included, &code);
+ }
+ auto filename =
+ ConCatPathFileName(path_, StripExtension(file_name_) + "_generated") +
+ JuliaFileExtension;
+ if (!SaveFile(filename.c_str(), code, false)) return false;
+ return true;
+ }
+
+ // Begin an object declaration.
+ void BeginObject(TypeVariableTable &vars, const StructDef &struct_def, std::string *code_ptr,
+ bool has_defaults) {
+ auto &code = *code_ptr;
+ if (has_defaults) code += JuliaPackageName + ".@with_kw ";
+ if (!struct_def.fixed)
+ code += "mutable struct ";
+ else
+ code += JuliaPackageName + ".@STRUCT struct ";
+ code += NormalizedName(struct_def);
+ code += vars.TypeSignature() + "\n";
+ }
+
+ void EndObject(const StructDef &struct_def,
+ const std::vector &offsets,
+ std::string *code_ptr) const {
+ auto &code = *code_ptr;
+ auto name = NormalizedName(struct_def);
+ code += "end\n";
+ code += JuliaPackageName + ".@ALIGN(" + name + +", " +
+ NumToString(struct_def.minalign) + ")\n";
+ auto method_signature = "(::Type{T}) where {T<:" + name + "}";
+ if (!offsets.empty() && !struct_def.fixed) {
+ bool first = true;
+ int i = 0;
+ code += JuliaPackageName + ".slot_offsets" + method_signature;
+ code += " = [";
+ for (auto it = offsets.begin(); it != offsets.end(); ++it) {
+ if (!first) code += ", ";
+ if (i == 0) code += "\n" + Indent;
+ code += *it;
+ i++;
+ i %= 4; // print four offsets per line
+ first = false;
+ }
+ code += "\n]\n";
+ }
+
+ // emit file identifier and extension for the root type
+ if (parser_.root_struct_def_ == &struct_def) {
+ code += JuliaPackageName + ".root_type" + method_signature + " = true\n";
+ if (!parser_.file_identifier_.empty()) {
+ code += JuliaPackageName + ".file_identifier" + method_signature;
+ code += " = \"" + parser_.file_identifier_ + "\"\n";
+ }
+ if (!parser_.file_extension_.empty()) {
+ code += JuliaPackageName + ".file_extension" + method_signature;
+ code += " = \"" + parser_.file_extension_ + "\"\n";
+ }
+ }
+ code += "\n";
+ }
+
+ std::string EscapeKeyword(const std::string &name) const {
+ return keywords_.find(name) == keywords_.end() ? name : name + "_";
+ }
+
+ std::string NormalizedName(const Definition &child,
+ const Definition *parent = NULL) const {
+ std::string prefix = "";
+ if (parent != NULL) {
+ std::string relname = GetRelativeName(*parent, &child, false);
+ if (!relname.empty()) prefix = relname + ".";
+ }
+ return prefix + EscapeKeyword(child.name);
+ }
+
+ std::string NormalizedName(const EnumVal &ev) const {
+ return EscapeKeyword(ev.name);
+ }
+
+ static void BeginEnum(const std::string enum_name,
+ const std::string enum_type, std::string *code_ptr) {
+ *code_ptr += "@enum " + enum_name + "::" + enum_type + " begin\n";
+ }
+
+ void EnumMember(const EnumDef &enum_def, const std::string &enum_name, const EnumVal ev,
+ std::string *code_ptr) {
+ *code_ptr += Indent + enum_name + NormalizedName(ev);
+ *code_ptr += " = " + enum_def.ToString(ev) + "\n";
+ }
+
+ static void EndEnum(std::string *code_ptr) { *code_ptr += "end\n\n"; }
+
+ static void BeginUnion(const std::string union_name, std::string *code_ptr) {
+ *code_ptr += JuliaPackageName + ".@UNION(" + union_name + ", (\n";
+ }
+
+ static void UnionMember(const std::string type_name, std::string *code_ptr) {
+ *code_ptr += Indent + type_name + ",\n";
+ }
+
+ static void EndUnion(std::string *code_ptr) { *code_ptr += "))\n\n"; }
+
+ void NewObjectFromBuffer(TypeVariableTable &vars, const StructDef &struct_def, std::string *code_ptr) {
+ auto &code = *code_ptr;
+ code += NormalizedName(struct_def) + "(buf::AbstractVector{UInt8})";
+ code += " = " + JuliaPackageName + ".read(" + NormalizedName(struct_def) + vars.TypeSignature(true) +
+ ", buf)\n";
+ code += NormalizedName(struct_def) + "(io::IO) = " + JuliaPackageName;
+ code += ".deserialize(io, " + NormalizedName(struct_def) + vars.TypeSignature(true) + ")\n";
+ }
+
+ void GenScalarField(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr, bool *has_defaults,
+ std::set *imports_ptr) {
+ auto &code = *code_ptr;
+ auto field_name = NormalizedName(field);
+ code += Indent + field_name;
+ code += "::";
+ code += GenTypeGet(field.value.type, &struct_def);
+ if (!field.value.constant.empty() && !struct_def.fixed) {
+ *has_defaults = true;
+ std::string c = field.value.constant;
+ if (field.value.type.base_type == BASE_TYPE_BOOL) {
+ c = c == "0" ? "false" : "true";
+ }
+ code += " = " + c;
+ }
+ if (IsScalarEnum(field.value.type))
+ AddDependency(struct_def, field.value.type, imports_ptr);
+ code += "\n";
+ }
+
+ // whether two namespaces have the same prefix up to "prefix_size"
+ // used for figuring out relative import paths
+ static bool SameNamespacePrefix(Namespace n1, Namespace n2,
+ size_t prefix_size) {
+ if (n1.components.size() < prefix_size) return false;
+ if (n2.components.size() < prefix_size) return false;
+ size_t i;
+ for (i = 0; i < prefix_size; i++) {
+ if (n1.components[i] != n2.components[i]) return false;
+ }
+ return true;
+ }
+
+ static void GetRelativeNamespaces(const Definition &parent_def,
+ const Definition *child_def,
+ Namespace *parent, Namespace *child) {
+ Namespace p, c;
+ if (parent_def.defined_namespace != NULL) p = *parent_def.defined_namespace;
+ if (child_def != NULL && child_def->defined_namespace != NULL)
+ c = *child_def->defined_namespace;
+ *parent = p;
+ *child = c;
+ }
+
+ static void GetRelativeNamespaces(const Definition &def, const Type &type,
+ Namespace *parent, Namespace *child) {
+ Namespace p, c;
+ Definition *child_def = GetBaseDefinition(type);
+ GetRelativeNamespaces(def, child_def, parent, child);
+ }
+
+ static std::string GetRelativeName(const Namespace &parent,
+ const Namespace &child,
+ bool dotprefix = true) {
+ // here we are accounting for modules which are at
+ // different levels of the tree.
+ std::string relname = "";
+ // go up to common level (don't add dots to path if we are
+ // annotating the type of a field - julia doesn't like that)
+ size_t i = 0;
+ size_t n = parent.components.size();
+ while (i <= n && !SameNamespacePrefix(parent, child, n - i)) {
+ if (dotprefix) relname += ".";
+ i++;
+ }
+ // traverse down to the place we need to be
+ size_t m = parent.components.size() - i;
+ size_t j;
+ for (j = m; j < child.components.size(); j++) {
+ if (dotprefix || !relname.empty()) relname += ".";
+ relname += child.components[j];
+ }
+ return relname;
+ }
+
+ static std::string GetRelativeName(const Definition &parent_def,
+ const Definition *child_def,
+ bool dotprefix = false) {
+ Namespace parent, child;
+ GetRelativeNamespaces(parent_def, child_def, &parent, &child);
+ return GetRelativeName(parent, child, dotprefix);
+ }
+
+ static std::string GetRelativeName(const Definition &def, const Type &type,
+ bool dotprefix = false) {
+ Namespace parent, child;
+ GetRelativeNamespaces(def, type, &parent, &child);
+ return GetRelativeName(parent, child, dotprefix);
+ }
+
+ // This definition depends on this type. Add the necessary relative imports.
+ void AddDependency(const Definition &def, const Type &type,
+ std::set *imports_ptr) {
+ Namespace parent, child;
+ Definition *child_def = GetBaseDefinition(type);
+ if (child_def == NULL) return;
+
+ GetRelativeNamespaces(def, child_def, &parent, &child);
+ auto relname = GetRelativeName(parent, child);
+
+ // add relative import
+ if (!relname.empty()) {
+ // special case: we are importing something from a direct parent
+ if (relname.find_first_not_of('.') == std::string::npos)
+ imports_ptr->insert(relname + "." + NormalizedName(*child_def));
+ else
+ imports_ptr->insert(relname);
+ }
+
+ auto module = GetCanonicalName(child);
+ auto child_name =
+ GetCanonicalName(child) + kPathSeparator + NormalizedName(*child_def);
+ auto parent_name =
+ GetCanonicalName(parent) + kPathSeparator + NormalizedName(def);
+ // self-reference - this is fine, but don't add it to the dependency table
+ if (child_name == parent_name) return;
+ module_table_.AddDependency(module, parent_name, child_name);
+ }
+
+ // generate a field which depends upon generated types
+ void GenDependentField(TypeVariableTable &vars,
+ const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr, bool *has_defaults,
+ std::set *imports_ptr) {
+
+ BaseType bt = field.value.type.base_type;
+ Type vartype = (bt == BASE_TYPE_VECTOR) ? field.value.type.VectorType() : field.value.type;
+ std::string type_name = GenTypeGet(field.value.type, &struct_def);
+ if (!struct_def.fixed && !IsScalar(vartype.base_type) && vartype.base_type != BASE_TYPE_STRING) {
+ std::string type_variable = vars.GetOrCreate(GenTypeGet(vartype, &struct_def));
+ type_name = (bt == BASE_TYPE_VECTOR) ? "Vector{" + type_variable + "}" : type_variable;
+ }
+
+ if (bt == BASE_TYPE_STRUCT && !struct_def.fixed) {
+ type_name = "Union{" + type_name + ", Nothing}";
+ }
+ // initialise nullable fields to nothing by default
+ if ((bt == BASE_TYPE_STRUCT || bt == BASE_TYPE_UNION ||
+ bt == BASE_TYPE_VECTOR || bt == BASE_TYPE_STRING) &&
+ !struct_def.fixed) {
+ if (bt == BASE_TYPE_VECTOR)
+ type_name += " = []";
+ else if (bt == BASE_TYPE_STRING)
+ type_name += " = \"\"";
+ else
+ type_name += " = nothing";
+ *has_defaults = true;
+ }
+ *code_ptr += Indent + NormalizedName(field) + "::" + type_name + "\n";
+ AddDependency(struct_def, field.value.type, imports_ptr);
+ }
+
+ void GenStringField(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr, bool *has_defaults) {
+ *code_ptr += Indent + NormalizedName(field) + "::";
+ // initialise strings to be empty by default
+ *code_ptr += GenTypeGet(field.value.type);
+ if (!struct_def.fixed) {
+ *code_ptr += " = \"\"";
+ *has_defaults = true;
+ }
+ *code_ptr += "\n";
+ }
+
+ // Generate a field, conditioned on its child type(s).
+ void GenField(TypeVariableTable &vars, const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr, std::set *imports_ptr,
+ bool *has_defaults) {
+ GenComment(field.doc_comment, code_ptr, &JuliaCommentConfig);
+ if (IsScalar(field.value.type.base_type)) {
+ GenScalarField(struct_def, field, code_ptr, has_defaults, imports_ptr);
+ } else {
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_STRING:
+ GenStringField(struct_def, field, code_ptr, has_defaults);
+ break;
+ case BASE_TYPE_STRUCT:
+ case BASE_TYPE_VECTOR:
+ case BASE_TYPE_UNION:
+ GenDependentField(vars, struct_def, field, code_ptr, has_defaults,
+ imports_ptr);
+ break;
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ }
+ }
+
+ static std::string GenImports(const std::set &imports) {
+ std::string impstr = "";
+ for (auto it = imports.begin(); it != imports.end(); ++it)
+ impstr += "import " + *it + "\n";
+ return impstr;
+ }
+
+ // Generate structs/tables
+ void GenObject(const StructDef &struct_def, std::string *code_ptr) {
+ if (struct_def.generated) return;
+
+ std::set imports;
+
+ bool has_defaults = false;
+
+ // generate all the fields
+ std::vector offsets;
+
+ TypeVariableTable vars;
+ GenComment(struct_def.doc_comment, code_ptr, &JuliaCommentConfig);
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ GenField(vars, struct_def, field, code_ptr, &imports, &has_defaults);
+ offsets.push_back("0x" + IntToStringHex(field.value.offset, 8));
+ }
+ EndObject(struct_def, offsets, code_ptr);
+
+ // need to call BeginObject after EndObject because we don't know
+ // the defaults until we've looked at all the fields.
+ std::string struct_begin;
+ BeginObject(vars, struct_def, &struct_begin, has_defaults);
+
+ *code_ptr = GenImports(imports) + "\n" + struct_begin + *code_ptr;
+
+ // Generate a functions for constructing the object from a buffer
+ NewObjectFromBuffer(vars, struct_def, code_ptr);
+ }
+
+ void GenUnion(const EnumDef &enum_def, std::string *code_ptr) {
+ if (enum_def.generated) return;
+
+ std::set imports;
+ auto union_name = NormalizedName(enum_def);
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ auto &ev = **it;
+ if (ev.name == "NONE") continue;
+ GenComment(ev.doc_comment, code_ptr, &JuliaCommentConfig);
+ std::string val_type = union_name + NormalizedName(ev);
+ *code_ptr += "struct " + val_type + " end\n";
+ std::string type_name = GenTypeGet(ev.union_type, &enum_def);
+ *code_ptr += val_type + "(args...; kwargs...) = " + type_name + "(args...; kwargs...)\n\n";
+ }
+ BeginUnion(union_name, code_ptr);
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ auto &ev = **it;
+ std::string type_name = ev.name == "NONE" ? GenTypeGet(ev.union_type, &enum_def) : union_name + NormalizedName(ev);
+ UnionMember(type_name, code_ptr);
+ // special case, instead make every Union a union with Nothing
+ if (ev.name == "NONE") continue;
+ AddDependency(enum_def, ev.union_type, &imports);
+ }
+ EndUnion(code_ptr);
+
+ *code_ptr = GenImports(imports) + "\n" + *code_ptr;
+ }
+
+ void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
+ if (enum_def.generated) return;
+ GenComment(enum_def.doc_comment, code_ptr, &JuliaCommentConfig);
+ auto enum_name = NormalizedName(enum_def);
+ BeginEnum(enum_name, GenTypeBasic(enum_def.underlying_type), code_ptr);
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ auto &ev = **it;
+ GenComment(ev.doc_comment, code_ptr, &JuliaCommentConfig);
+ EnumMember(enum_def, enum_name, ev, code_ptr);
+ }
+ EndEnum(code_ptr);
+ }
+
+ static std::string GenTypeBasic(const Type &type) {
+ static const char *ctypename[] = {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
+ #JLTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+ return ctypename[type.base_type];
+ }
+
+ std::string GenTypePointer(const Type &type, const Definition *parent) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return "String";
+ case BASE_TYPE_VECTOR:
+ return "Vector{" + GenTypeGet(type.VectorType(), parent) + "}";
+ case BASE_TYPE_STRUCT: return NormalizedName(*type.struct_def, parent);
+ case BASE_TYPE_UNION: return NormalizedName(*type.enum_def, parent);
+ case BASE_TYPE_NONE: return "Nothing";
+ default: return "Any";
+ }
+ }
+
+ static Definition *GetBaseDefinition(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_VECTOR: return GetBaseDefinition(type.VectorType());
+ case BASE_TYPE_STRUCT: return type.struct_def;
+ case BASE_TYPE_UNION: return type.enum_def;
+ default: return NULL;
+ }
+ }
+
+ static bool IsScalarEnum(const Type &type) {
+ return (type.enum_def != nullptr && !type.enum_def->is_union &&
+ IsInteger(type.enum_def->underlying_type.base_type));
+ }
+
+ std::string GenTypeGet(const Type &type, const Definition *parent = NULL) {
+ if (IsScalar(type.base_type))
+ return IsScalarEnum(type) ? NormalizedName(*type.enum_def, parent)
+ : GenTypeBasic(type);
+ return GenTypePointer(type, parent);
+ }
+
+ void BeginFile(const Namespace &ns, std::string *code_ptr) const {
+ auto &code = *code_ptr;
+ code = code + "# " + FlatBuffersGeneratedWarning() + "\n\n";
+ code += GetCanonicalName(ns, '.') + ".eval(quote\n\n";
+ }
+
+ void EndFile(std::string *code_ptr) const {
+ auto &code = *code_ptr;
+ code += "\nend)\n\n";
+ }
+
+ std::string GetDirname(const Definition &def) const {
+ std::string d = path_;
+ if (def.defined_namespace != NULL)
+ d += GetCanonicalName(*def.defined_namespace);
+ return d;
+ }
+
+ std::string GetFilename(const Definition &def) const {
+ return ConCatPathFileName(GetDirname(def), NormalizedName(def)) +
+ JuliaFileExtension;
+ }
+
+ std::string GetModule(const Definition &def) const {
+ if (def.defined_namespace != NULL)
+ return GetCanonicalName(*def.defined_namespace);
+ return root_module_;
+ }
+
+ static std::string GetSubModule(const Definition &def) {
+ if (def.defined_namespace != NULL)
+ return LastNamespacePart(*def.defined_namespace);
+ return "";
+ }
+
+ bool GenIncludes(std::string &mod, std::set &included,
+ std::string *code_ptr) {
+ auto &code = *code_ptr;
+ DepGraph *children = module_table_.GetDependencies(mod);
+ // Include all the contents of this module in the right order
+ auto sorted_children = children->TopSort();
+ for (auto it = sorted_children.rbegin(); it != sorted_children.rend();
+ ++it) {
+ std::string child = *it;
+
+ if (included.find(child) != included.end()) continue;
+
+ // if this module depends on another module, go and generate that module
+ // first
+ if (module_table_.IsModule(child)) {
+ GenIncludes(child, included, code_ptr);
+ included.insert(child);
+ continue;
+ }
+
+ // this is not a direct child of this module, so don't include here
+ if (child.find(mod) == std::string::npos ||
+ child.length() < (mod.length() + 1) ||
+ child.substr(mod.length() + 1).find(kPathSeparator) !=
+ std::string::npos) {
+ continue;
+ }
+
+ // If the file doesn't exist, don't include it
+ // TODO: this doesn't allow types which reference each other,
+ // but Julia doesn't support this yet anyway
+ std::string toinclude = child + JuliaFileExtension;
+ std::string fullpath = ConCatPathFileName(path_, toinclude);
+ if (!module_table_.IsFile(fullpath.c_str())) continue;
+ code += "include(\"" + toinclude + "\")\n";
+
+ included.insert(child);
+ }
+ return true;
+ }
+
+ // Canonical julia name of a namespace (Foo.Bar.Baz)
+ std::string GetCanonicalName(const Namespace &ns,
+ char separator = kPathSeparator) const {
+ std::string name;
+ for (size_t i = 0; i < ns.components.size(); i++) {
+ if (i) name += separator;
+ name += std::string(ns.components[i]);
+ }
+ if (name.empty()) name = root_module_;
+ return name;
+ }
+
+ // Add a dependency between two definitions
+ void AddDependency(const Definition *parent, const Definition *child) {
+ FLATBUFFERS_ASSERT(parent != NULL && child != NULL);
+ auto parent_name = NormalizedName(*parent);
+ auto module = parent_name;
+ if (parent->defined_namespace != NULL)
+ module = GetCanonicalName(*parent->defined_namespace);
+ module_table_.AddDependency(module, parent_name, NormalizedName(*child));
+ }
+
+ // Add a definition as a dependency to its own module
+ void AddToOwnModule(const Definition &def) {
+ auto m = GetModule(def);
+ module_table_.AddDependency(m, m + kPathSeparator + NormalizedName(def), m);
+ }
+
+ // Save out the generated code for a Julia struct
+ bool SaveType(const Definition &def, const std::string &declcode) {
+ if (!declcode.length()) return true;
+ std::string code = "";
+ BeginFile(*def.defined_namespace, &code);
+ code += declcode;
+ EndFile(&code);
+ auto filename = GetFilename(def);
+ EnsureDirExists(GetDirname(def));
+ if (!SaveFile(filename.c_str(), code, false)) return false;
+ module_table_.AddFile(filename);
+ AddToOwnModule(def);
+ return true;
+ }
+};
+
+} // namespace julia
+
+bool GenerateJulia(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ julia::JuliaGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+} // namespace flatbuffers
diff --git a/src/idl_gen_kotlin.cpp b/src/idl_gen_kotlin.cpp
index b12e554d313..c72bbe2f715 100644
--- a/src/idl_gen_kotlin.cpp
+++ b/src/idl_gen_kotlin.cpp
@@ -141,7 +141,7 @@ class KotlinGenerator : public BaseGenerator {
// clang-format off
static const char * const kotlin_typename[] = {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
#KTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_lobster.cpp b/src/idl_gen_lobster.cpp
index d5c99f7eea5..1fda188a826 100644
--- a/src/idl_gen_lobster.cpp
+++ b/src/idl_gen_lobster.cpp
@@ -86,7 +86,7 @@ class LobsterGenerator : public BaseGenerator {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
#PTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_lua.cpp b/src/idl_gen_lua.cpp
index 6ae7dd4cdfe..b490bc114da 100644
--- a/src/idl_gen_lua.cpp
+++ b/src/idl_gen_lua.cpp
@@ -598,7 +598,7 @@ class LuaGenerator : public BaseGenerator {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
#PTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_php.cpp b/src/idl_gen_php.cpp
index 16e47816cd3..8ec1ad28b68 100644
--- a/src/idl_gen_php.cpp
+++ b/src/idl_gen_php.cpp
@@ -863,7 +863,7 @@ class PhpGenerator : public BaseGenerator {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
#NTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp
index bff0d849290..a1017affc50 100644
--- a/src/idl_gen_python.cpp
+++ b/src/idl_gen_python.cpp
@@ -670,7 +670,7 @@ class PythonGenerator : public BaseGenerator {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
#PTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_rust.cpp b/src/idl_gen_rust.cpp
index 3c23f2f9d51..836e7423765 100644
--- a/src/idl_gen_rust.cpp
+++ b/src/idl_gen_rust.cpp
@@ -457,7 +457,7 @@ class RustGenerator : public BaseGenerator {
// clang-format off
static const char * const ctypename[] = {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
- RTYPE, KTYPE) \
+ RTYPE, KTYPE, JLTYPE) \
#RTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -479,7 +479,7 @@ class RustGenerator : public BaseGenerator {
static const char *ctypename[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
- RTYPE, KTYPE) \
+ RTYPE, KTYPE, JLTYPE) \
#RTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp
index 1d0a3caafed..626aa5b0ee9 100644
--- a/src/idl_gen_text.cpp
+++ b/src/idl_gen_text.cpp
@@ -168,7 +168,7 @@ bool Print(const void *val, Type type, int indent,
// clang-format off
switch (vec_type.base_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
case BASE_TYPE_ ## ENUM: \
if (!PrintVector( \
*reinterpret_cast *>(val), \
@@ -188,7 +188,7 @@ bool Print(const void *val, Type type, int indent,
// clang-format off
switch (vec_type.base_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
case BASE_TYPE_ ## ENUM: \
if (!PrintArray( \
*reinterpret_cast *>(val), \
@@ -285,31 +285,31 @@ static bool GenStruct(const StructDef &struct_def, const Table *table,
text += ":";
text += " ";
switch (fd.value.type.base_type) {
- // clang-format off
- #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
- case BASE_TYPE_ ## ENUM: \
- if (!GenField(fd, table, struct_def.fixed, \
- opts, indent + Indent(opts), _text)) { \
- return false; \
- } \
- break;
- FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
- #undef FLATBUFFERS_TD
- // Generate drop-thru case statements for all pointer types:
+ // clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
- case BASE_TYPE_ ## ENUM:
- FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
- FLATBUFFERS_GEN_TYPE_ARRAY(FLATBUFFERS_TD)
- #undef FLATBUFFERS_TD
- if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts),
- prev_val, opts, _text)) {
- return false;
- }
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
+ case BASE_TYPE_ ## ENUM: \
+ if (!GenField(fd, table, struct_def.fixed, \
+ opts, indent + Indent(opts), _text)) { \
+ return false; \
+ } \
break;
- // clang-format on
- }
+ FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // Generate drop-thru case statements for all pointer types:
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
+ case BASE_TYPE_ ## ENUM:
+ FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
+ FLATBUFFERS_GEN_TYPE_ARRAY(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts),
+ prev_val, opts, _text)) {
+ return false;
+ }
+ break;
+ // clang-format on
+ }
// Track prev val for use with union types.
if (struct_def.fixed) {
prev_val = reinterpret_cast(table) + fd.value.offset;
diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp
index 56b1547ca75..4cc86635e01 100644
--- a/src/idl_parser.cpp
+++ b/src/idl_parser.cpp
@@ -40,7 +40,7 @@ const double kPi = 3.14159265358979323846;
const char *const kTypeNames[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
IDLTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -51,7 +51,7 @@ const char *const kTypeNames[] = {
const char kTypeSizes[] = {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
sizeof(CTYPE),
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -223,7 +223,7 @@ static std::string TokenToString(int t) {
FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
#undef FLATBUFFERS_TOKEN
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
IDLTYPE,
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -1180,7 +1180,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
switch (field_value.type.base_type) {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
case BASE_TYPE_ ## ENUM: \
builder_.Pad(field->padding); \
if (struct_def.fixed) { \
@@ -1197,7 +1197,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
#undef FLATBUFFERS_TD
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
case BASE_TYPE_ ## ENUM: \
builder_.Pad(field->padding); \
if (IsStruct(field->value.type)) { \
@@ -1259,7 +1259,7 @@ CheckedError Parser::ParseVectorDelimiters(uoffset_t &count, F body) {
static bool CompareType(const uint8_t *a, const uint8_t *b, BaseType ftype) {
switch (ftype) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
- RTYPE, KTYPE) \
+ RTYPE, KTYPE, JLTYPE) \
case BASE_TYPE_##ENUM: return ReadScalar(a) < ReadScalar(b);
FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
@@ -1315,7 +1315,7 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue,
switch (val.type.base_type) {
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
case BASE_TYPE_ ## ENUM: \
if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \
else { \
@@ -1430,7 +1430,7 @@ CheckedError Parser::ParseArray(Value &array) {
// clang-format off
switch (val.type.base_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
case BASE_TYPE_ ## ENUM: \
if (IsStruct(val.type)) { \
SerializeStruct(builder, *val.type.struct_def, val); \
@@ -1783,7 +1783,7 @@ CheckedError Parser::ParseSingleValue(const std::string *name, Value &e,
// clang-format off
switch (match_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
- CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, JLTYPE) \
case BASE_TYPE_ ## ENUM: {\
CTYPE val; \
ECHECK(atot(e.constant.c_str(), *this, &val)); \
@@ -2019,7 +2019,7 @@ struct EnumValBuilder {
// clang-format off
switch (enum_def.underlying_type.base_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \
- PTYPE, RTYPE, KTYPE) \
+ PTYPE, RTYPE, KTYPE, JLTYPE) \
case BASE_TYPE_##ENUM: { \
if (!IsInteger(BASE_TYPE_##ENUM)) break; \
return ValidateImpl(ev, next ? 1 : 0); \
@@ -2213,7 +2213,7 @@ bool Parser::SupportsAdvancedUnionFeatures() const {
(opts.lang_to_generate &
~(IDLOptions::kCpp | IDLOptions::kJs | IDLOptions::kTs |
IDLOptions::kPhp | IDLOptions::kJava | IDLOptions::kCSharp |
- IDLOptions::kKotlin | IDLOptions::kBinary)) == 0;
+ IDLOptions::kKotlin | IDLOptions::kJulia | IDLOptions::kBinary)) == 0;
}
bool Parser::SupportsAdvancedArrayFeatures() const {
diff --git a/tests/JuliaTest.sh b/tests/JuliaTest.sh
new file mode 100644
index 00000000000..463aa69cd94
--- /dev/null
+++ b/tests/JuliaTest.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+julia juliatest.jl
\ No newline at end of file
diff --git a/tests/MyGame/Example/Ability.jl b/tests/MyGame/Example/Ability.jl
new file mode 100644
index 00000000000..1c6099b445c
--- /dev/null
+++ b/tests/MyGame/Example/Ability.jl
@@ -0,0 +1,16 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+
+FlatBuffers.@STRUCT struct Ability
+ id::UInt32
+ distance::UInt32
+end
+FlatBuffers.@ALIGN(Ability, 4)
+
+Ability(buf::AbstractVector{UInt8}) = FlatBuffers.read(Ability, buf)
+Ability(io::IO) = FlatBuffers.deserialize(io, Ability)
+
+end)
+
diff --git a/tests/MyGame/Example/AnyAmbiguousAliases.jl b/tests/MyGame/Example/AnyAmbiguousAliases.jl
new file mode 100644
index 00000000000..ee10a22bf12
--- /dev/null
+++ b/tests/MyGame/Example/AnyAmbiguousAliases.jl
@@ -0,0 +1,24 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+
+struct AnyAmbiguousAliasesM1 end
+AnyAmbiguousAliasesM1(args...; kwargs...) = Monster(args...; kwargs...)
+
+struct AnyAmbiguousAliasesM2 end
+AnyAmbiguousAliasesM2(args...; kwargs...) = Monster(args...; kwargs...)
+
+struct AnyAmbiguousAliasesM3 end
+AnyAmbiguousAliasesM3(args...; kwargs...) = Monster(args...; kwargs...)
+
+FlatBuffers.@UNION(AnyAmbiguousAliases, (
+ Nothing,
+ AnyAmbiguousAliasesM1,
+ AnyAmbiguousAliasesM2,
+ AnyAmbiguousAliasesM3,
+))
+
+
+end)
+
diff --git a/tests/MyGame/Example/AnyUniqueAliases.jl b/tests/MyGame/Example/AnyUniqueAliases.jl
new file mode 100644
index 00000000000..730d3f5fc21
--- /dev/null
+++ b/tests/MyGame/Example/AnyUniqueAliases.jl
@@ -0,0 +1,25 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+import ..Example2
+
+struct AnyUniqueAliasesM end
+AnyUniqueAliasesM(args...; kwargs...) = Monster(args...; kwargs...)
+
+struct AnyUniqueAliasesTS end
+AnyUniqueAliasesTS(args...; kwargs...) = TestSimpleTableWithEnum(args...; kwargs...)
+
+struct AnyUniqueAliasesM2 end
+AnyUniqueAliasesM2(args...; kwargs...) = Example2.Monster(args...; kwargs...)
+
+FlatBuffers.@UNION(AnyUniqueAliases, (
+ Nothing,
+ AnyUniqueAliasesM,
+ AnyUniqueAliasesTS,
+ AnyUniqueAliasesM2,
+))
+
+
+end)
+
diff --git a/tests/MyGame/Example/Any_.jl b/tests/MyGame/Example/Any_.jl
new file mode 100644
index 00000000000..29a3e1b7b20
--- /dev/null
+++ b/tests/MyGame/Example/Any_.jl
@@ -0,0 +1,25 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+import ..Example2
+
+struct Any_Monster end
+Any_Monster(args...; kwargs...) = Monster(args...; kwargs...)
+
+struct Any_TestSimpleTableWithEnum end
+Any_TestSimpleTableWithEnum(args...; kwargs...) = TestSimpleTableWithEnum(args...; kwargs...)
+
+struct Any_MyGame_Example2_Monster end
+Any_MyGame_Example2_Monster(args...; kwargs...) = Example2.Monster(args...; kwargs...)
+
+FlatBuffers.@UNION(Any_, (
+ Nothing,
+ Any_Monster,
+ Any_TestSimpleTableWithEnum,
+ Any_MyGame_Example2_Monster,
+))
+
+
+end)
+
diff --git a/tests/MyGame/Example/Color.jl b/tests/MyGame/Example/Color.jl
new file mode 100644
index 00000000000..6287e7d5932
--- /dev/null
+++ b/tests/MyGame/Example/Color.jl
@@ -0,0 +1,23 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+#=
+# Composite components of Monster color.
+=#
+@enum Color::UInt8 begin
+ ColorRed = 1
+#=
+# \brief color Green
+# Green is bit_flag with value (1u << 1)
+=#
+ ColorGreen = 2
+#=
+# \brief color Blue (1u << 3)
+=#
+ ColorBlue = 8
+end
+
+
+end)
+
diff --git a/tests/MyGame/Example/Monster.jl b/tests/MyGame/Example/Monster.jl
new file mode 100644
index 00000000000..8cda252027f
--- /dev/null
+++ b/tests/MyGame/Example/Monster.jl
@@ -0,0 +1,87 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+import ..InParentNamespace
+
+FlatBuffers.@with_kw mutable struct Monster{A, B, C, D, E, F, G, H, I, J, K, L, M}
+#=
+# an example documentation comment: monster object
+=#
+ pos::Union{A, Nothing} = nothing
+ mana::Int16 = 150
+ hp::Int16 = 100
+ name::String = ""
+ inventory::Vector{UInt8} = []
+ color::Color = 8
+ test_type::UInt8 = 0
+ test::B = nothing
+ test4::Vector{C} = []
+ testarrayofstring::Vector{String} = []
+#=
+# an example documentation comment: this will end up in the generated code
+# multiline too
+=#
+ testarrayoftables::Vector{D} = []
+ enemy::Union{E, Nothing} = nothing
+ testnestedflatbuffer::Vector{UInt8} = []
+ testempty::Union{F, Nothing} = nothing
+ testbool::Bool = false
+ testhashs32_fnv1::Int32 = 0
+ testhashu32_fnv1::UInt32 = 0
+ testhashs64_fnv1::Int64 = 0
+ testhashu64_fnv1::UInt64 = 0
+ testhashs32_fnv1a::Int32 = 0
+ testhashu32_fnv1a::UInt32 = 0
+ testhashs64_fnv1a::Int64 = 0
+ testhashu64_fnv1a::UInt64 = 0
+ testarrayofbools::Vector{Bool} = []
+ testf::Float32 = 3.14159
+ testf2::Float32 = 3.0
+ testf3::Float32 = 0.0
+ testarrayofstring2::Vector{String} = []
+ testarrayofsortedstruct::Vector{G} = []
+ flex::Vector{UInt8} = []
+ test5::Vector{H} = []
+ vector_of_longs::Vector{Int64} = []
+ vector_of_doubles::Vector{Float64} = []
+ parent_namespace_test::Union{I, Nothing} = nothing
+ vector_of_referrables::Vector{J} = []
+ single_weak_reference::UInt64 = 0
+ vector_of_weak_references::Vector{UInt64} = []
+ vector_of_strong_referrables::Vector{K} = []
+ co_owning_reference::UInt64 = 0
+ vector_of_co_owning_references::Vector{UInt64} = []
+ non_owning_reference::UInt64 = 0
+ vector_of_non_owning_references::Vector{UInt64} = []
+ any_unique_type::UInt8 = 0
+ any_unique::L = nothing
+ any_ambiguous_type::UInt8 = 0
+ any_ambiguous::M = nothing
+ vector_of_enums::Vector{Color} = []
+ signed_enum::Race = -1
+end
+FlatBuffers.@ALIGN(Monster, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:Monster} = [
+ 0x00000004, 0x00000006, 0x00000008, 0x0000000A,
+ 0x0000000E, 0x00000010, 0x00000012, 0x00000014,
+ 0x00000016, 0x00000018, 0x0000001A, 0x0000001C,
+ 0x0000001E, 0x00000020, 0x00000022, 0x00000024,
+ 0x00000026, 0x00000028, 0x0000002A, 0x0000002C,
+ 0x0000002E, 0x00000030, 0x00000032, 0x00000034,
+ 0x00000036, 0x00000038, 0x0000003A, 0x0000003C,
+ 0x0000003E, 0x00000040, 0x00000042, 0x00000044,
+ 0x00000046, 0x00000048, 0x0000004A, 0x0000004C,
+ 0x0000004E, 0x00000050, 0x00000052, 0x00000054,
+ 0x00000056, 0x00000058, 0x0000005A, 0x0000005C,
+ 0x0000005E, 0x00000060, 0x00000062, 0x00000064
+]
+FlatBuffers.root_type(::Type{T}) where {T<:Monster} = true
+FlatBuffers.file_identifier(::Type{T}) where {T<:Monster} = "MONS"
+FlatBuffers.file_extension(::Type{T}) where {T<:Monster} = "mon"
+
+Monster(buf::AbstractVector{UInt8}) = FlatBuffers.read(Monster{Vec3, Any_, Test, Monster, Monster, Stat, Ability, Test, InParentNamespace, Referrable, Referrable, AnyUniqueAliases, AnyAmbiguousAliases}, buf)
+Monster(io::IO) = FlatBuffers.deserialize(io, Monster{Vec3, Any_, Test, Monster, Monster, Stat, Ability, Test, InParentNamespace, Referrable, Referrable, AnyUniqueAliases, AnyAmbiguousAliases})
+
+end)
+
diff --git a/tests/MyGame/Example/Race.jl b/tests/MyGame/Example/Race.jl
new file mode 100644
index 00000000000..652f8b41a15
--- /dev/null
+++ b/tests/MyGame/Example/Race.jl
@@ -0,0 +1,14 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+@enum Race::Int8 begin
+ RaceNone = -1
+ RaceHuman = 0
+ RaceDwarf = 1
+ RaceElf = 2
+end
+
+
+end)
+
diff --git a/tests/MyGame/Example/Referrable.jl b/tests/MyGame/Example/Referrable.jl
new file mode 100644
index 00000000000..5c51738118f
--- /dev/null
+++ b/tests/MyGame/Example/Referrable.jl
@@ -0,0 +1,18 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+
+FlatBuffers.@with_kw mutable struct Referrable
+ id::UInt64 = 0
+end
+FlatBuffers.@ALIGN(Referrable, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:Referrable} = [
+ 0x00000004
+]
+
+Referrable(buf::AbstractVector{UInt8}) = FlatBuffers.read(Referrable, buf)
+Referrable(io::IO) = FlatBuffers.deserialize(io, Referrable)
+
+end)
+
diff --git a/tests/MyGame/Example/Stat.jl b/tests/MyGame/Example/Stat.jl
new file mode 100644
index 00000000000..23fde7b56cd
--- /dev/null
+++ b/tests/MyGame/Example/Stat.jl
@@ -0,0 +1,20 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+
+FlatBuffers.@with_kw mutable struct Stat
+ id::String = ""
+ val::Int64 = 0
+ count::UInt16 = 0
+end
+FlatBuffers.@ALIGN(Stat, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:Stat} = [
+ 0x00000004, 0x00000006, 0x00000008
+]
+
+Stat(buf::AbstractVector{UInt8}) = FlatBuffers.read(Stat, buf)
+Stat(io::IO) = FlatBuffers.deserialize(io, Stat)
+
+end)
+
diff --git a/tests/MyGame/Example/Test.jl b/tests/MyGame/Example/Test.jl
new file mode 100644
index 00000000000..c366a724e77
--- /dev/null
+++ b/tests/MyGame/Example/Test.jl
@@ -0,0 +1,16 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+
+FlatBuffers.@STRUCT struct Test
+ a::Int16
+ b::Int8
+end
+FlatBuffers.@ALIGN(Test, 2)
+
+Test(buf::AbstractVector{UInt8}) = FlatBuffers.read(Test, buf)
+Test(io::IO) = FlatBuffers.deserialize(io, Test)
+
+end)
+
diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.jl b/tests/MyGame/Example/TestSimpleTableWithEnum.jl
new file mode 100644
index 00000000000..41dc5cf42f9
--- /dev/null
+++ b/tests/MyGame/Example/TestSimpleTableWithEnum.jl
@@ -0,0 +1,18 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+
+FlatBuffers.@with_kw mutable struct TestSimpleTableWithEnum
+ color::Color = 2
+end
+FlatBuffers.@ALIGN(TestSimpleTableWithEnum, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:TestSimpleTableWithEnum} = [
+ 0x00000004
+]
+
+TestSimpleTableWithEnum(buf::AbstractVector{UInt8}) = FlatBuffers.read(TestSimpleTableWithEnum, buf)
+TestSimpleTableWithEnum(io::IO) = FlatBuffers.deserialize(io, TestSimpleTableWithEnum)
+
+end)
+
diff --git a/tests/MyGame/Example/TypeAliases.jl b/tests/MyGame/Example/TypeAliases.jl
new file mode 100644
index 00000000000..e5366108deb
--- /dev/null
+++ b/tests/MyGame/Example/TypeAliases.jl
@@ -0,0 +1,31 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+
+FlatBuffers.@with_kw mutable struct TypeAliases
+ i8::Int8 = 0
+ u8::UInt8 = 0
+ i16::Int16 = 0
+ u16::UInt16 = 0
+ i32::Int32 = 0
+ u32::UInt32 = 0
+ i64::Int64 = 0
+ u64::UInt64 = 0
+ f32::Float32 = 0.0
+ f64::Float64 = 0.0
+ v8::Vector{Int8} = []
+ vf64::Vector{Float64} = []
+end
+FlatBuffers.@ALIGN(TypeAliases, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:TypeAliases} = [
+ 0x00000004, 0x00000006, 0x00000008, 0x0000000A,
+ 0x0000000C, 0x0000000E, 0x00000010, 0x00000012,
+ 0x00000014, 0x00000016, 0x00000018, 0x0000001A
+]
+
+TypeAliases(buf::AbstractVector{UInt8}) = FlatBuffers.read(TypeAliases, buf)
+TypeAliases(io::IO) = FlatBuffers.deserialize(io, TypeAliases)
+
+end)
+
diff --git a/tests/MyGame/Example/Vec3.jl b/tests/MyGame/Example/Vec3.jl
new file mode 100644
index 00000000000..adf81b9f156
--- /dev/null
+++ b/tests/MyGame/Example/Vec3.jl
@@ -0,0 +1,20 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example.eval(quote
+
+
+FlatBuffers.@STRUCT struct Vec3
+ x::Float32
+ y::Float32
+ z::Float32
+ test1::Float64
+ test2::Color
+ test3::Test
+end
+FlatBuffers.@ALIGN(Vec3, 8)
+
+Vec3(buf::AbstractVector{UInt8}) = FlatBuffers.read(Vec3, buf)
+Vec3(io::IO) = FlatBuffers.deserialize(io, Vec3)
+
+end)
+
diff --git a/tests/MyGame/Example2/Monster.jl b/tests/MyGame/Example2/Monster.jl
new file mode 100644
index 00000000000..28474d6b12a
--- /dev/null
+++ b/tests/MyGame/Example2/Monster.jl
@@ -0,0 +1,14 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.Example2.eval(quote
+
+
+mutable struct Monster
+end
+FlatBuffers.@ALIGN(Monster, 1)
+
+Monster(buf::AbstractVector{UInt8}) = FlatBuffers.read(Monster, buf)
+Monster(io::IO) = FlatBuffers.deserialize(io, Monster)
+
+end)
+
diff --git a/tests/MyGame/InParentNamespace.jl b/tests/MyGame/InParentNamespace.jl
new file mode 100644
index 00000000000..7e5b662a184
--- /dev/null
+++ b/tests/MyGame/InParentNamespace.jl
@@ -0,0 +1,14 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+MyGame.eval(quote
+
+
+mutable struct InParentNamespace
+end
+FlatBuffers.@ALIGN(InParentNamespace, 1)
+
+InParentNamespace(buf::AbstractVector{UInt8}) = FlatBuffers.read(InParentNamespace, buf)
+InParentNamespace(io::IO) = FlatBuffers.deserialize(io, InParentNamespace)
+
+end)
+
diff --git a/tests/TestAll.sh b/tests/TestAll.sh
index 0fc0acdbc16..71b9c75519d 100644
--- a/tests/TestAll.sh
+++ b/tests/TestAll.sh
@@ -6,6 +6,10 @@ echo "************************ Kotlin:"
sh KotlinTest.sh
+echo "************************ Julia:"
+
+sh JuliaTest.sh
+
echo "************************ Go:"
sh GoTest.sh
diff --git a/tests/generate_code.sh b/tests/generate_code.sh
index 14b621ccf8e..1b663a2e45c 100755
--- a/tests/generate_code.sh
+++ b/tests/generate_code.sh
@@ -15,9 +15,9 @@
# limitations under the License.
set -e
-../flatc --cpp --java --kotlin --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --grpc --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
-../flatc --cpp --java --kotlin --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
-../flatc --cpp --java --kotlin --csharp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs
+../flatc --cpp --java --kotlin --csharp --dart --go --binary --lobster --lua --python --js --ts --php --julia --rust --grpc --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
+../flatc --cpp --java --kotlin --csharp --dart --go --binary --lobster --lua --python --js --ts --php --julia --rust --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
+../flatc --cpp --java --kotlin --csharp --js --ts --php --julia --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs
../flatc --cpp --scoped-enums -o evolution_test ./evolution_test/evolution_v*.fbs
../flatc -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs
../flatc -b --schema --bfbs-comments --bfbs-builtins -I include_test arrays_test.fbs
diff --git a/tests/juliatest.jl b/tests/juliatest.jl
new file mode 100644
index 00000000000..680ab549ecb
--- /dev/null
+++ b/tests/juliatest.jl
@@ -0,0 +1,3 @@
+import Pkg
+Pkg.activate(joinpath(@__DIR__, "..", "julia"))
+Pkg.test("FlatBuffers")
diff --git a/tests/monster_test_generated.jl b/tests/monster_test_generated.jl
new file mode 100644
index 00000000000..092756ac0e1
--- /dev/null
+++ b/tests/monster_test_generated.jl
@@ -0,0 +1,21 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+if !isdefined(@__MODULE__(), :MyGame) @__MODULE__().eval(:(module MyGame __precompile__(false); import FlatBuffers end)) end
+if !isdefined(MyGame, :OtherNameSpace) MyGame.eval(:(module OtherNameSpace __precompile__(false); import FlatBuffers end)) end
+if !isdefined(MyGame, :Example2) MyGame.eval(:(module Example2 __precompile__(false); import FlatBuffers end)) end
+if !isdefined(MyGame, :Example) MyGame.eval(:(module Example __precompile__(false); import FlatBuffers end)) end
+include("MyGame/InParentNamespace.jl")
+include("MyGame/Example2/Monster.jl")
+include("MyGame/Example/Color.jl")
+include("MyGame/Example/Race.jl")
+include("MyGame/Example/Test.jl")
+include("MyGame/Example/Vec3.jl")
+include("MyGame/Example/Stat.jl")
+include("MyGame/Example/Ability.jl")
+include("MyGame/Example/Referrable.jl")
+include("MyGame/Example/TestSimpleTableWithEnum.jl")
+include("MyGame/Example/AnyUniqueAliases.jl")
+include("MyGame/Example/AnyAmbiguousAliases.jl")
+include("MyGame/Example/Monster.jl")
+include("MyGame/Example/Any_.jl")
+include("MyGame/Example/TypeAliases.jl")
diff --git a/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.jl b/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.jl
new file mode 100644
index 00000000000..cfb847c204a
--- /dev/null
+++ b/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.jl
@@ -0,0 +1,13 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+NamespaceA.NamespaceB.eval(quote
+
+@enum EnumInNestedNS::Int8 begin
+ EnumInNestedNSA = 0
+ EnumInNestedNSB = 1
+ EnumInNestedNSC = 2
+end
+
+
+end)
+
diff --git a/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.jl b/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.jl
new file mode 100644
index 00000000000..bba26bb3756
--- /dev/null
+++ b/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.jl
@@ -0,0 +1,16 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+NamespaceA.NamespaceB.eval(quote
+
+
+FlatBuffers.@STRUCT struct StructInNestedNS
+ a::Int32
+ b::Int32
+end
+FlatBuffers.@ALIGN(StructInNestedNS, 4)
+
+StructInNestedNS(buf::AbstractVector{UInt8}) = FlatBuffers.read(StructInNestedNS, buf)
+StructInNestedNS(io::IO) = FlatBuffers.deserialize(io, StructInNestedNS)
+
+end)
+
diff --git a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.jl b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.jl
new file mode 100644
index 00000000000..2440cf36758
--- /dev/null
+++ b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.jl
@@ -0,0 +1,18 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+NamespaceA.NamespaceB.eval(quote
+
+
+FlatBuffers.@with_kw mutable struct TableInNestedNS
+ foo::Int32 = 0
+end
+FlatBuffers.@ALIGN(TableInNestedNS, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:TableInNestedNS} = [
+ 0x00000004
+]
+
+TableInNestedNS(buf::AbstractVector{UInt8}) = FlatBuffers.read(TableInNestedNS, buf)
+TableInNestedNS(io::IO) = FlatBuffers.deserialize(io, TableInNestedNS)
+
+end)
+
diff --git a/tests/namespace_test/NamespaceA/SecondTableInA.jl b/tests/namespace_test/NamespaceA/SecondTableInA.jl
new file mode 100644
index 00000000000..49897bf3760
--- /dev/null
+++ b/tests/namespace_test/NamespaceA/SecondTableInA.jl
@@ -0,0 +1,19 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+NamespaceA.eval(quote
+
+import ..NamespaceC
+
+FlatBuffers.@with_kw mutable struct SecondTableInA
+ refer_to_c::Union{NamespaceC.TableInC, Nothing} = nothing
+end
+FlatBuffers.@ALIGN(SecondTableInA, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:SecondTableInA} = [
+ 0x00000004
+]
+
+SecondTableInA(buf::AbstractVector{UInt8}) = FlatBuffers.read(SecondTableInA, buf)
+SecondTableInA(io::IO) = FlatBuffers.deserialize(io, SecondTableInA)
+
+end)
+
diff --git a/tests/namespace_test/NamespaceA/TableInFirstNS.jl b/tests/namespace_test/NamespaceA/TableInFirstNS.jl
new file mode 100644
index 00000000000..24edc2cf6f1
--- /dev/null
+++ b/tests/namespace_test/NamespaceA/TableInFirstNS.jl
@@ -0,0 +1,21 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+NamespaceA.eval(quote
+
+import .NamespaceB
+
+FlatBuffers.@with_kw mutable struct TableInFirstNS
+ foo_table::Union{NamespaceB.TableInNestedNS, Nothing} = nothing
+ foo_enum::NamespaceB.EnumInNestedNS = 0
+ foo_struct::Union{NamespaceB.StructInNestedNS, Nothing} = nothing
+end
+FlatBuffers.@ALIGN(TableInFirstNS, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:TableInFirstNS} = [
+ 0x00000004, 0x00000006, 0x00000008
+]
+
+TableInFirstNS(buf::AbstractVector{UInt8}) = FlatBuffers.read(TableInFirstNS, buf)
+TableInFirstNS(io::IO) = FlatBuffers.deserialize(io, TableInFirstNS)
+
+end)
+
diff --git a/tests/namespace_test/NamespaceC/TableInC.jl b/tests/namespace_test/NamespaceC/TableInC.jl
new file mode 100644
index 00000000000..23411cd64b2
--- /dev/null
+++ b/tests/namespace_test/NamespaceC/TableInC.jl
@@ -0,0 +1,20 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+NamespaceC.eval(quote
+
+import ..NamespaceA
+
+FlatBuffers.@with_kw mutable struct TableInC
+ refer_to_a1::Union{NamespaceA.TableInFirstNS, Nothing} = nothing
+ refer_to_a2::Union{NamespaceA.SecondTableInA, Nothing} = nothing
+end
+FlatBuffers.@ALIGN(TableInC, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:TableInC} = [
+ 0x00000004, 0x00000006
+]
+
+TableInC(buf::AbstractVector{UInt8}) = FlatBuffers.read(TableInC, buf)
+TableInC(io::IO) = FlatBuffers.deserialize(io, TableInC)
+
+end)
+
diff --git a/tests/namespace_test/namespace_test1_generated.jl b/tests/namespace_test/namespace_test1_generated.jl
new file mode 100644
index 00000000000..845b467abae
--- /dev/null
+++ b/tests/namespace_test/namespace_test1_generated.jl
@@ -0,0 +1,7 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+if !isdefined(@__MODULE__(), :NamespaceA) @__MODULE__().eval(:(module NamespaceA __precompile__(false); import FlatBuffers end)) end
+if !isdefined(NamespaceA, :NamespaceB) NamespaceA.eval(:(module NamespaceB __precompile__(false); import FlatBuffers end)) end
+include("NamespaceA/NamespaceB/EnumInNestedNS.jl")
+include("NamespaceA/NamespaceB/TableInNestedNS.jl")
+include("NamespaceA/NamespaceB/StructInNestedNS.jl")
diff --git a/tests/namespace_test/namespace_test2_generated.jl b/tests/namespace_test/namespace_test2_generated.jl
new file mode 100644
index 00000000000..15bce2ae6e8
--- /dev/null
+++ b/tests/namespace_test/namespace_test2_generated.jl
@@ -0,0 +1,8 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+include("namespace_test1_generated.jl")
+if !isdefined(@__MODULE__(), :NamespaceA) @__MODULE__().eval(:(module NamespaceA __precompile__(false); import FlatBuffers end)) end
+if !isdefined(NamespaceA, :NamespaceB) NamespaceA.eval(:(module NamespaceB __precompile__(false); import FlatBuffers end)) end
+if !isdefined(@__MODULE__(), :NamespaceC) @__MODULE__().eval(:(module NamespaceC __precompile__(false); import FlatBuffers end)) end
+include("NamespaceA/TableInFirstNS.jl")
+include("NamespaceA/SecondTableInA.jl")
diff --git a/tests/union_vector/UnionVector/Attacker.jl b/tests/union_vector/UnionVector/Attacker.jl
new file mode 100644
index 00000000000..036738eb343
--- /dev/null
+++ b/tests/union_vector/UnionVector/Attacker.jl
@@ -0,0 +1,18 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+UnionVector.eval(quote
+
+
+FlatBuffers.@with_kw mutable struct Attacker
+ sword_attack_damage::Int32 = 0
+end
+FlatBuffers.@ALIGN(Attacker, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:Attacker} = [
+ 0x00000004
+]
+
+Attacker(buf::AbstractVector{UInt8}) = FlatBuffers.read(Attacker, buf)
+Attacker(io::IO) = FlatBuffers.deserialize(io, Attacker)
+
+end)
+
diff --git a/tests/union_vector/UnionVector/BookReader.jl b/tests/union_vector/UnionVector/BookReader.jl
new file mode 100644
index 00000000000..cf6a66b6b0a
--- /dev/null
+++ b/tests/union_vector/UnionVector/BookReader.jl
@@ -0,0 +1,15 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+UnionVector.eval(quote
+
+
+FlatBuffers.@STRUCT struct BookReader
+ books_read::Int32
+end
+FlatBuffers.@ALIGN(BookReader, 4)
+
+BookReader(buf::AbstractVector{UInt8}) = FlatBuffers.read(BookReader, buf)
+BookReader(io::IO) = FlatBuffers.deserialize(io, BookReader)
+
+end)
+
diff --git a/tests/union_vector/UnionVector/Character.jl b/tests/union_vector/UnionVector/Character.jl
new file mode 100644
index 00000000000..086a1473dc1
--- /dev/null
+++ b/tests/union_vector/UnionVector/Character.jl
@@ -0,0 +1,18 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+UnionVector.eval(quote
+
+
+FlatBuffers.@UNION(Character, (
+ Nothing,
+ Attacker,
+ Rapunzel,
+ BookReader,
+ BookReader,
+ String,
+ String,
+))
+
+
+end)
+
diff --git a/tests/union_vector/UnionVector/Movie.jl b/tests/union_vector/UnionVector/Movie.jl
new file mode 100644
index 00000000000..16420f18eae
--- /dev/null
+++ b/tests/union_vector/UnionVector/Movie.jl
@@ -0,0 +1,23 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+UnionVector.eval(quote
+
+
+FlatBuffers.@with_kw mutable struct Movie
+ main_character_type::UInt8 = 0
+ main_character::Character = nothing
+ characters_type::Vector{UInt8} = []
+ characters::Vector{Character} = []
+end
+FlatBuffers.@ALIGN(Movie, 1)
+FlatBuffers.slot_offsets(::Type{T}) where {T<:Movie} = [
+ 0x00000004, 0x00000006, 0x00000008, 0x0000000A
+]
+FlatBuffers.root_type(::Type{T}) where {T<:Movie} = true
+FlatBuffers.file_identifier(::Type{T}) where {T<:Movie} = "MOVI"
+
+Movie(buf::AbstractVector{UInt8}) = FlatBuffers.read(Movie, buf)
+Movie(io::IO) = FlatBuffers.deserialize(io, Movie)
+
+end)
+
diff --git a/tests/union_vector/UnionVector/Rapunzel.jl b/tests/union_vector/UnionVector/Rapunzel.jl
new file mode 100644
index 00000000000..9738d47d9c8
--- /dev/null
+++ b/tests/union_vector/UnionVector/Rapunzel.jl
@@ -0,0 +1,15 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+UnionVector.eval(quote
+
+
+FlatBuffers.@STRUCT struct Rapunzel
+ hair_length::Int32
+end
+FlatBuffers.@ALIGN(Rapunzel, 4)
+
+Rapunzel(buf::AbstractVector{UInt8}) = FlatBuffers.read(Rapunzel, buf)
+Rapunzel(io::IO) = FlatBuffers.deserialize(io, Rapunzel)
+
+end)
+
diff --git a/tests/union_vector/union_vector_generated.jl b/tests/union_vector/union_vector_generated.jl
new file mode 100644
index 00000000000..5cb0d7adf75
--- /dev/null
+++ b/tests/union_vector/union_vector_generated.jl
@@ -0,0 +1,7 @@
+# automatically generated by the FlatBuffers compiler, do not modify
+
+include("UnionVector/Attacker.jl")
+include("UnionVector/Rapunzel.jl")
+include("UnionVector/BookReader.jl")
+include("UnionVector/Character.jl")
+include("UnionVector/Movie.jl")