diff --git a/lib/xdrgen.rb b/lib/xdrgen.rb index 03f928c32..6d7da731b 100644 --- a/lib/xdrgen.rb +++ b/lib/xdrgen.rb @@ -10,6 +10,7 @@ module Xdrgen autoload :CLI autoload :Generators autoload :Parser + autoload :Preprocessor autoload :Util autoload :Output diff --git a/lib/xdrgen/ast/definitions/base.rb b/lib/xdrgen/ast/definitions/base.rb index 2d591848c..43df3aae5 100644 --- a/lib/xdrgen/ast/definitions/base.rb +++ b/lib/xdrgen/ast/definitions/base.rb @@ -1,9 +1,14 @@ module Xdrgen::AST::Definitions class Base < Treetop::Runtime::SyntaxNode + attr_writer :ifdefs + + def ifdefs + @ifdefs || [] + end def sub_type :simple end - + end end \ No newline at end of file diff --git a/lib/xdrgen/compilation.rb b/lib/xdrgen/compilation.rb index 6443b5a80..9bcb398a9 100644 --- a/lib/xdrgen/compilation.rb +++ b/lib/xdrgen/compilation.rb @@ -12,13 +12,23 @@ def initialize(source_paths, output_dir:".", language: :ruby, generator: nil, na @options = options end + memoize def raw_source + @source_paths.map{|p| IO.read(p, encoding: "UTF-8")}.join("\n") + end + + memoize def preprocessor + Preprocessor.new(raw_source) + end + memoize def source - @source_paths.map{|p| IO.read(p)}.join("\n") + preprocessor.cleaned_source end memoize def ast parser = Parser.new - parser.parse(source) + result = parser.parse(source) + preprocessor.annotate_ast(result) + result end def compile diff --git a/lib/xdrgen/generators/go.rb b/lib/xdrgen/generators/go.rb index 4d0ec53b2..dc3eaabf5 100644 --- a/lib/xdrgen/generators/go.rb +++ b/lib/xdrgen/generators/go.rb @@ -5,33 +5,383 @@ class Go < Xdrgen::Generators::Base def generate @already_rendered = [] - path = "#{@namespace}_generated.go" - out = @output.open(path) + @split_nested_def_names = Set.new + + # Collect all feature flags used across all definitions + features = collect_all_features(@top) + + if features.empty? + # No ifdefs at all - generate as before (single file) + path = "#{@namespace}_generated.go" + out = @output.open(path) + render_top_matter out + render_definitions(out, @top) + render_bottom_matter out + return + end - render_top_matter out - render_definitions(out, @top) - render_bottom_matter out + # Multi-file generation with build tags + generate_with_ifdefs(features) end private + def generate_with_ifdefs(features) + # Categorize all definitions + main_defs = [] + conditional_defs = {} # condition_key => [{defn:, member_filter:}] + + all_top_defs = collect_all_defs_flat(@top) + all_top_defs.each do |defn| + placement = compute_placement(defn) + case placement[:type] + when :main + main_defs << defn + when :feature + key = conditions_to_key(placement[:conditions]) + conditional_defs[key] ||= [] + conditional_defs[key] << { defn: defn, member_filter: nil } + when :split + placement[:variants].each do |variant| + key = conditions_to_key(variant[:conditions]) + conditional_defs[key] ||= [] + conditional_defs[key] << { defn: defn, member_filter: variant[:member_filter] } + end + end + end + + # Second pass: find nested definitions with ifdef'd members that need + # independent split rendering in conditional files + @split_nested_def_names = Set.new + all_top_defs.each do |defn| + collect_split_nested_defs_recursive(defn).each do |ndefn| + ndefn_features = collect_direct_member_features(ndefn) + ndefn_variants = compute_variants(ndefn, ndefn.ifdefs, ndefn_features) + @split_nested_def_names << name(ndefn) + ndefn_variants.each do |variant| + key = conditions_to_key(variant[:conditions]) + conditional_defs[key] ||= [] + conditional_defs[key] << { defn: ndefn, member_filter: variant[:member_filter] } + end + end + end + + # Generate main file + main_out = @output.open("#{@namespace}_generated.go") + has_main_types = main_defs.any? { |d| !d.is_a?(AST::Definitions::Const) } + if has_main_types + render_top_matter(main_out) + else + render_top_matter_main_ifdef(main_out) + end + @already_rendered = [] + main_defs.each { |defn| render_definition(main_out, defn) } + render_bottom_matter(main_out) + + # Generate conditional files + conditional_defs.each do |condition_key, entries| + build_tag = condition_key_to_build_tag(condition_key) + file_suffix = condition_key_to_file_suffix(condition_key) + file_path = "#{@namespace}_generated_#{file_suffix}.go" + + has_unions = entries.any? { |e| e[:defn].is_a?(AST::Definitions::Union) } + + out = @output.open(file_path) + render_build_tag(out, build_tag) + render_top_matter_conditional(out, include_errors: has_unions) + + @already_rendered = [] + entries.each do |entry| + if entry[:member_filter] + render_definition_with_filter(out, entry[:defn], entry[:member_filter]) + else + render_definition(out, entry[:defn]) + end + end + # No render_bottom_matter for conditional files (fmtTest only in main) + end + end + + # Collect all feature names used in ifdefs across the AST + def collect_all_features(node) + features = Set.new + collect_features_recursive(node, features) + features.to_a.sort + end + + def collect_features_recursive(node, features) + node.definitions.each do |defn| + defn.ifdefs.each { |c| features << c.name } + collect_member_features(defn, features) + if defn.respond_to?(:nested_definitions) + defn.nested_definitions.each do |ndefn| + ndefn.ifdefs.each { |c| features << c.name } + collect_member_features(ndefn, features) + end + end + end + node.namespaces.each { |ns| collect_features_recursive(ns, features) } + end + + def collect_member_features(defn, features) + case defn + when AST::Definitions::Struct + defn.members.each { |m| m.ifdefs.each { |c| features << c.name } } + when AST::Definitions::Enum + defn.members.each { |m| m.ifdefs.each { |c| features << c.name } } + when AST::Definitions::Union + defn.normal_arms.each { |a| a.ifdefs.each { |c| features << c.name } } + end + end + + # Collect top-level definitions in a flat list (nested definitions are + # rendered by their parent via render_nested_definitions) + def collect_all_defs_flat(node) + result = [] + collect_defs_recursive(node, result) + result + end + + def collect_defs_recursive(node, result) + node.definitions.each do |defn| + result << defn + end + node.namespaces.each { |ns| collect_defs_recursive(ns, result) } + end + + # Determine where a definition should be placed + def compute_placement(defn) + top_conditions = defn.ifdefs + member_features = collect_defn_member_features(defn) + + if top_conditions.empty? && member_features.empty? + { type: :main } + elsif member_features.empty? + { type: :feature, conditions: top_conditions } + else + # Need to split the type across files + variants = compute_variants(defn, top_conditions, member_features) + { type: :split, variants: variants } + end + end + + def collect_defn_member_features(defn) + features = Set.new + case defn + when AST::Definitions::Struct + defn.members.each { |m| m.ifdefs.each { |c| features << c.name } } + when AST::Definitions::Enum + defn.members.each { |m| m.ifdefs.each { |c| features << c.name } } + when AST::Definitions::Union + defn.normal_arms.each { |a| a.ifdefs.each { |c| features << c.name } } + end + # Recurse into nested definitions to bubble up their features + if defn.respond_to?(:nested_definitions) + defn.nested_definitions.each do |ndefn| + collect_defn_member_features(ndefn).each { |f| features << f } + end + end + features.to_a + end + + # Non-recursive version: only checks a definition's own direct members + def collect_direct_member_features(defn) + features = Set.new + case defn + when AST::Definitions::Struct + defn.members.each { |m| m.ifdefs.each { |c| features << c.name } } + when AST::Definitions::Enum + defn.members.each { |m| m.ifdefs.each { |c| features << c.name } } + when AST::Definitions::Union + defn.normal_arms.each { |a| a.ifdefs.each { |c| features << c.name } } + end + features.to_a + end + + # Recursively find nested definitions that have their own ifdef'd members + def collect_split_nested_defs_recursive(defn) + result = [] + return result unless defn.respond_to?(:nested_definitions) + defn.nested_definitions.each do |ndefn| + if collect_direct_member_features(ndefn).any? + result << ndefn + end + result.concat(collect_split_nested_defs_recursive(ndefn)) + end + result + end + + def compute_variants(defn, top_conditions, member_features) + # Generate all 2^N combinations of feature on/off + variants = [] + generate_combinations(member_features, 0, [], top_conditions) do |conditions, feature_states| + member_filter = build_member_filter(feature_states) + variants << { conditions: conditions, member_filter: member_filter } + end + variants + end + + def generate_combinations(features, index, current_states, top_conditions, &block) + if index == features.length + all_conditions = top_conditions + current_states.map { |s| Preprocessor::IfdefCondition.new(s[:name], s[:negated]) } + yield all_conditions, current_states.dup + return + end + + feature = features[index] + + # Feature enabled + generate_combinations(features, index + 1, + current_states + [{ name: feature, negated: false }], + top_conditions, &block) + + # Feature disabled + generate_combinations(features, index + 1, + current_states + [{ name: feature, negated: true }], + top_conditions, &block) + end + + def build_member_filter(feature_states) + lambda do |member| + return true if member.ifdefs.empty? + # All of the member's conditions must be satisfied by the feature states + member.ifdefs.all? do |cond| + feature_states.any? { |s| s[:name] == cond.name && s[:negated] == cond.negated } + end + end + end + + # Convert conditions array to a unique key string + def conditions_to_key(conditions) + conditions.map { |c| c.negated ? "!#{c.name}" : c.name }.join(",") + end + + # Convert condition key to Go build tag + def condition_key_to_build_tag(key) + parts = key.split(",") + parts.map do |p| + if p.start_with?("!") + "!#{p[1..].downcase}" + else + p.downcase + end + end.join(" && ") + end + + # Convert condition key to file name suffix + def condition_key_to_file_suffix(key) + parts = key.split(",") + parts.map do |p| + if p.start_with?("!") + "#{p[1..].downcase}_off" + else + "#{p.downcase}_on" + end + end.join("_") + end + + def render_build_tag(out, build_tag) + out.puts "//go:build #{build_tag}" + out.puts "" + end + + def render_top_matter_conditional(out, include_errors: true) + out.puts "//lint:file-ignore S1005 The issue should be fixed in xdrgen. Unfortunately, there's no way to ignore a single file in staticcheck." + out.puts "" + out.puts "// DO NOT EDIT or your changes may be overwritten" + out.puts "package #{@namespace || "main"}" + out.puts "" + out.puts "import (" + out.puts ' "bytes"' + out.puts ' "encoding"' + out.puts ' "errors"' if include_errors + out.puts ' "fmt"' + out.puts "" + out.puts ' "github.com/stellar/go-xdr/xdr3"' + out.puts ")" + out.break + end + + def render_top_matter_main_ifdef(out) + out.puts <<-EOS.strip_heredoc + //lint:file-ignore S1005 The issue should be fixed in xdrgen. Unfortunately, there's no way to ignore a single file in staticcheck. + //lint:file-ignore U1000 fmtTest is not needed anywhere, should be removed in xdrgen. + + // Package #{@namespace || "main"} is generated from: + // + // #{@output.relative_source_paths.join("\n// ")} + // + // DO NOT EDIT or your changes may be overwritten + package #{@namespace || "main"} + + import ( + "encoding" + "errors" + "io" + "fmt" + + "github.com/stellar/go-xdr/xdr3" + ) + EOS + out.break + out.puts <<-EOS.strip_heredoc + // XdrFilesSHA256 is the SHA256 hashes of source files. + var XdrFilesSHA256 = map[string]string{ + #{@output.relative_source_path_sha256_hashes.map(){ |path, hash| %{"#{path}": "#{hash}",} }.join("\n")} + } + EOS + out.break + out.puts <<-EOS.strip_heredoc + var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + + type xdrType interface { + xdrType() + } + + type decoderFrom interface { + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) + } + + // Unmarshal reads an xdr element from `r` into `v`. + func Unmarshal(r io.Reader, v interface{}) (int, error) { + return UnmarshalWithOptions(r, v, xdr.DefaultDecodeOptions) + } + + // UnmarshalWithOptions works like Unmarshal but uses decoding options. + func UnmarshalWithOptions(r io.Reader, v interface{}, options xdr.DecodeOptions) (int, error) { + if decodable, ok := v.(decoderFrom); ok { + d := xdr.NewDecoderWithOptions(r, options) + return decodable.DecodeFrom(d, options.MaxDepth) + } + // delegate to xdr package's Unmarshal + return xdr.UnmarshalWithOptions(r, v, options) + } + + // Marshal writes an xdr element `v` into `w`. + func Marshal(w io.Writer, v interface{}) (int, error) { + if _, ok := v.(xdrType); ok { + if bm, ok := v.(encoding.BinaryMarshaler); ok { + b, err := bm.MarshalBinary() + if err != nil { + return 0, err + } + return w.Write(b) + } + } + // delegate to xdr package's Marshal + return xdr.Marshal(w, v) + } + EOS + out.break + end + def render_typedef(out, typedef) - # Typedefs that wrap a pointer type are not well supported in Go because - # Go does not allow pointer types to have methods. This prevents us from - # defining the EncodeTo method on these types which is very inconvenient - # for the render functions that generate structs that contain these - # types, because xdrgen doesn't know in that moment they are a type - # without EncodeTo. Since this type cannot have its own methods, we make - # it a type alias so at least it inherits the EncodeTo method from the - # aliased type. This is a bit of a hack, and the hack will only work as - # long as the aliased type is another defined type that has an EncodeTo. if typedef.sub_type == :optional out.puts "type #{name typedef} = #{reference typedef.declaration.type}" else out.puts "type #{name typedef} #{reference typedef.declaration.type}" end - # write sizing restrictions case typedef.declaration when Xdrgen::AST::Declarations::String render_maxsize_method out, typedef, typedef.declaration.resolved_size @@ -88,6 +438,9 @@ def render_enum_typedef(out, typedef, enum) end def render_union_typedef(out, typedef, union) + # Resolve the actual arms to use - if we have a filter active, use it + arms = effective_arms(union) + out.puts <<-EOS.strip_heredoc // SwitchFieldName returns the field name in which this union's // discriminant is stored @@ -123,9 +476,7 @@ def render_union_typedef(out, typedef, union) out.break - # Add accessors for of form val, ok := union.GetArmName() - # and val := union.MustArmName() - union.arms.each do |arm| + arms.each do |arm| next if arm.void? out.puts <<-EOS.strip_heredoc // Must#{name arm} retrieves the #{name arm} value from the union, @@ -156,7 +507,10 @@ def render_definitions(out, node) def render_nested_definitions(out, defn) return unless defn.respond_to? :nested_definitions - defn.nested_definitions.each{|ndefn| render_definition out, ndefn} + defn.nested_definitions.each do |ndefn| + next if @split_nested_def_names.include?(name(ndefn)) + render_definition out, ndefn + end end def render_definition(out, defn) @@ -174,6 +528,55 @@ def render_definition(out, defn) @already_rendered << name(defn) + @active_member_filter = nil + render_definition_inner(out, defn) + end + + def render_definition_with_filter(out, defn, member_filter) + if @already_rendered.include? name(defn) + unless defn.is_a?(AST::Definitions::Namespace) + $stderr.puts "warn: #{name(defn)} is defined twice. skipping" + end + return + end + + # For split types, nested definitions may also need filtering + render_nested_definitions_with_filter(out, defn, member_filter) + render_source_comment(out, defn) + + @already_rendered << name(defn) + + @active_member_filter = member_filter + render_definition_inner(out, defn) + @active_member_filter = nil + end + + def render_nested_definitions_with_filter(out, defn, member_filter) + return unless defn.respond_to? :nested_definitions + defn.nested_definitions.each do |ndefn| + next if @split_nested_def_names.include?(name(ndefn)) + # Check if the member containing this nested definition passes the filter + # by finding which member contains this nested definition + parent_member = find_parent_member(defn, ndefn) + if parent_member && member_filter && !member_filter.call(parent_member) + next + end + render_definition(out, ndefn) + end + end + + def find_parent_member(defn, ndefn) + case defn + when AST::Definitions::Struct + defn.members.find { |m| m.declaration.type == ndefn } + when AST::Definitions::Union + defn.normal_arms.find { |a| !a.void? && a.declaration.type == ndefn } + else + nil + end + end + + def render_definition_inner(out, defn) case defn when AST::Definitions::Struct ; render_struct out, defn @@ -198,9 +601,6 @@ def render_definition(out, defn) render_xdr_type_interface out, name(defn) when AST::Definitions::Typedef ; render_typedef out, defn - # Typedefs that wrap a pointer type are not supported in Go because Go - # does not allow pointer types to have methods. Don't define methods - # for the type because that will be a Go compiler error. if defn.sub_type != :optional render_typedef_encode_to_interface out, defn render_decoder_from_interface out, name(defn) @@ -228,28 +628,61 @@ def render_source_comment(out, defn) EOS end + # Returns the effective members for a struct, applying the active filter + def effective_members(struct) + members = struct.members + members = members.select { |m| @active_member_filter.call(m) } if @active_member_filter + members + end + + # Returns the effective members for an enum, applying the active filter + def effective_enum_members(enum) + members = enum.members + members = members.select { |m| @active_member_filter.call(m) } if @active_member_filter + members + end + + # Returns the effective arms for a union, applying the active filter + def effective_arms(union) + arms = union.normal_arms + arms = arms.select { |a| @active_member_filter.call(a) } if @active_member_filter + # Always include the default arm if present + if union.default_arm.present? + arms + [union.default_arm] + else + arms + end + end + + def effective_normal_arms(union) + arms = union.normal_arms + arms = arms.select { |a| @active_member_filter.call(a) } if @active_member_filter + arms + end + def render_struct(out, struct) + members = effective_members(struct) out.puts "type #{name struct} struct {" out.indent do - - struct.members.each do |m| + members.each do |m| out.puts "#{name m} #{reference(m.declaration.type)} #{field_tag struct, m}" end - end out.puts "}" out.break end def render_enum(out, enum) + members = effective_enum_members(enum) + # render the "enum" out.puts "type #{name enum} int32" out.puts "const (" out.indent do - first_member = enum.members.first + first_member = members.first out.puts "#{name enum}#{name first_member} #{name enum} = #{first_member.value}" - rest_members = enum.members.drop(1) + rest_members = members.drop(1) rest_members.each do |m| out.puts "#{name enum}#{name m} #{name enum} = #{m.value}" end @@ -260,7 +693,7 @@ def render_enum(out, enum) out.puts "var #{private_name enum}Map = map[int32]string{" out.indent do - enum.members.each do |m| + members.each do |m| out.puts "#{m.value}: \"#{name enum}#{name m}\"," end @@ -290,12 +723,14 @@ def render_enum(out, enum) end def render_union(out, union) + arms = effective_arms(union) + normal_arms = effective_normal_arms(union) out.puts "type #{name union} struct{" out.indent do out.puts "#{name union.discriminant} #{reference union.discriminant.type}" - union.arms.each do |arm| + arms.each do |arm| next if arm.void? out.puts "#{name arm} *#{reference arm.type} #{field_tag union, arm}" end @@ -336,7 +771,7 @@ def render_union(out, union) # Add accessors for of form val, ok := union.GetArmName() # and val := union.MustArmName() - union.arms.each do |arm| + arms.each do |arm| next if arm.void? out.puts access_arm(arm) end @@ -345,11 +780,12 @@ def render_union(out, union) end def render_struct_encode_to_interface(out, struct) + members = effective_members(struct) name = name(struct) out.puts "// EncodeTo encodes this value using the Encoder." out.puts "func (s *#{name}) EncodeTo(e *xdr.Encoder) error {" out.puts " var err error" - struct.members.each do |m| + members.each do |m| mn = name(m) render_encode_to_body(out, "s.#{mn}", m.type, self_encode: false) end @@ -410,10 +846,6 @@ def render_typedef_encode_to_interface(out, typedef) out.puts "// EncodeTo encodes this value using the Encoder." if is_fixed_array_type(type) || (type.is_a?(AST::Identifier) && type.sub_type == :simple && type.resolved_type.is_a?(AST::Definitions::Typedef) && is_fixed_array_type(type.resolved_type.declaration.type)) - # Implement EncodeTo by pointer for Go array types - # otherwise (if called by value), Go will make a heap allocation - # for every by-value call since the copy required by the call - # tends to escape the stack due to the large array sizes. out.puts "func (s *#{name}) EncodeTo(e *xdr.Encoder) error {" else out.puts "func (s #{name}) EncodeTo(e *xdr.Encoder) error {" @@ -425,9 +857,6 @@ def render_typedef_encode_to_interface(out, typedef) out.break end - # render_encode_to_body assumes there is an `e` variable containing an - # xdr.Encoder, and a variable defined by `var` that is the value to - # encode. def render_encode_to_body(out, var, type, self_encode:) def check_error(str) <<-EOS @@ -473,9 +902,7 @@ def check_error(str) if self_encode newvar = "#{name type}(#{var})" if type.resolved_type.is_a?(AST::Definitions::Typedef) && is_fixed_array_type(type.resolved_type.declaration.type) - # Go array types implement EncodeTo by pointer if type.is_a?(AST::Identifier) - # we are already calling by pointer, so we just need to cast newvar = "(*#{name type})(#{var})" else newvar = "(*#{name type})(&#{var})" @@ -534,6 +961,7 @@ def check_error(str) end def render_struct_decode_from_interface(out, struct) + members = effective_members(struct) name = name(struct) out.puts "// DecodeFrom decodes this value using the Decoder." out.puts "func (s *#{name}) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) {" @@ -544,7 +972,7 @@ def render_struct_decode_from_interface(out, struct) out.puts " var err error" out.puts " var n, nTmp int" declared_variables = [] - struct.members.each do |m| + members.each do |m| mn = name(m) render_decode_from_body(out, "s.#{mn}", m.type, declared_variables: declared_variables, self_encode: false) end @@ -645,9 +1073,6 @@ def render_variable_declaration(out, indent, var, type, declared_variables:) end end - # render_decode_from_body assumes there is an `d` variable containing an - # xdr.Decoder, and a variable defined by `var` that is the value to - # encode. def render_decode_from_body(out, var, type, declared_variables:, self_encode:) tail = <<-EOS n += nTmp @@ -986,7 +1411,6 @@ def escape_name(name) def render_union_constructor(out, union) constructor_name = "New#{name union}" - discriminant_arg = private_name union.discriminant discriminant_type = reference union.discriminant.type @@ -1054,9 +1478,11 @@ def size(size_s) end def switch_for(out, union, ident) + normal_arms = effective_normal_arms(union) + out.puts "switch #{reference union.discriminant.type}(#{ident}) {" - union.normal_arms.each do |arm| + normal_arms.each do |arm| arm.cases.each do |c| value = if c.value.is_a?(AST::Identifier) diff --git a/lib/xdrgen/grammar/enum.treetop b/lib/xdrgen/grammar/enum.treetop index ff3e668d2..7b6cc3429 100644 --- a/lib/xdrgen/grammar/enum.treetop +++ b/lib/xdrgen/grammar/enum.treetop @@ -10,10 +10,12 @@ grammar XdrEnumGrammar rule enum_body - "{" - space? + "{" + space? first_member_n:enum_member - additional_members_n:(space? "," space? enum_member )* + additional_members_n:(space? ","? space? enum_member )* + space? + ","? space? "}" end diff --git a/lib/xdrgen/preprocessor.rb b/lib/xdrgen/preprocessor.rb new file mode 100644 index 000000000..a232b3ac0 --- /dev/null +++ b/lib/xdrgen/preprocessor.rb @@ -0,0 +1,108 @@ +module Xdrgen + class Preprocessor + IfdefCondition = Struct.new(:name, :negated) + + attr_reader :cleaned_source + + def initialize(source) + @original_source = source + @line_conditions = {} # line_number => [IfdefCondition, ...] + process + end + + # Returns the ifdef conditions active at the given byte offset in the source. + def conditions_at_offset(offset) + line = offset_to_line(offset) + @line_conditions[line] || [] + end + + # Annotates the AST with ifdef conditions. + # Sets `ifdefs` on definitions and their sub-parts (struct members, enum members, union arms). + def annotate_ast(ast) + walk_definitions(ast) + end + + private + + def process + lines = @original_source.split("\n", -1) + cleaned_lines = [] + ifdef_stack = [] + + lines.each_with_index do |line, i| + stripped = line.strip + if stripped =~ /\A%?\s*#ifdef\s+(\w+)/ + ifdef_stack.push(IfdefCondition.new($1, false)) + cleaned_lines << (" " * line.length) + elsif stripped =~ /\A%?\s*#else\b/ + if ifdef_stack.any? + top = ifdef_stack.last + ifdef_stack[-1] = IfdefCondition.new(top.name, !top.negated) + end + cleaned_lines << (" " * line.length) + elsif stripped =~ /\A%?\s*#endif\b/ + ifdef_stack.pop if ifdef_stack.any? + cleaned_lines << (" " * line.length) + else + @line_conditions[i] = ifdef_stack.map { |c| IfdefCondition.new(c.name, c.negated) } if ifdef_stack.any? + cleaned_lines << line + end + end + + @cleaned_source = cleaned_lines.join("\n") + end + + def offset_to_line(offset) + @offset_cache ||= build_offset_cache + # Binary search for the line containing offset + idx = @offset_cache.bsearch_index { |line_start| line_start > offset } + idx ? idx - 1 : @offset_cache.length - 1 + end + + def build_offset_cache + cache = [0] + @original_source.each_char.with_index do |c, i| + cache << (i + 1) if c == "\n" + end + cache + end + + def walk_definitions(node) + node.definitions.each do |defn| + annotate_definition(defn) + end + node.namespaces.each { |ns| walk_definitions(ns) } + end + + def annotate_definition(defn) + defn_conditions = conditions_at_offset(defn.interval.first) + defn.ifdefs = defn_conditions + + # Annotate sub-parts with conditions relative to the definition + case defn + when AST::Definitions::Struct + defn.members.each do |m| + all_conds = conditions_at_offset(m.interval.first) + m.ifdefs = all_conds.drop(defn_conditions.length) + end + when AST::Definitions::Enum + defn.members.each do |m| + all_conds = conditions_at_offset(m.interval.first) + m.ifdefs = all_conds.drop(defn_conditions.length) + end + when AST::Definitions::Union + defn.normal_arms.each do |a| + all_conds = conditions_at_offset(a.interval.first) + a.ifdefs = all_conds.drop(defn_conditions.length) + end + end + + # Annotate nested definitions (inline struct/enum/union within members) + if defn.respond_to?(:nested_definitions) + defn.nested_definitions.each do |ndefn| + annotate_definition(ndefn) + end + end + end + end +end diff --git a/spec/fixtures/generator/ifdef.x b/spec/fixtures/generator/ifdef.x new file mode 100644 index 000000000..a4138db43 --- /dev/null +++ b/spec/fixtures/generator/ifdef.x @@ -0,0 +1,77 @@ +// Test fixture for #ifdef support + +// A simple constant (no ifdef) +const BASE_VALUE = 1; + +// An enum with conditional members +enum MyEnum { + MEMBER_A = 0, + MEMBER_B = 1, +#ifdef FEATURE_A + MEMBER_C = 2, +#endif + MEMBER_D = 3 +}; + +// A struct with conditional fields +struct MyStruct { + int field1; + MyEnum field2; +#ifdef FEATURE_A + unsigned int field3; +#endif +}; + +// An entirely conditional type +#ifdef FEATURE_A +struct ConditionalStruct { + int data; +}; +#endif + +// A type with #else +#ifdef FEATURE_A +struct VariantStruct { + int newField; +}; +#else +struct VariantStruct { + unsigned int oldField; +}; +#endif + +// A union with conditional arms +enum UnionType { + UNION_A = 0, + UNION_B = 1, +#ifdef FEATURE_A + UNION_C = 2, +#endif + UNION_D = 3 +}; + +union MyUnion switch (UnionType type) { + case UNION_A: + int armA; + case UNION_B: + unsigned int armB; +#ifdef FEATURE_A + case UNION_C: + unsigned hyper armC; +#endif + case UNION_D: + void; +}; + +// A struct with a nested union that has an ifdef'd arm +struct ContainerStruct { + int baseField; + union switch (UnionType type) { + case UNION_A: int dataA; + case UNION_B: unsigned int dataB; +#ifdef FEATURE_A + case UNION_C: unsigned hyper dataC; +#endif + case UNION_D: void; + } body; +}; diff --git a/spec/lib/xdrgen/generator_spec.rb b/spec/lib/xdrgen/generator_spec.rb index fa66e3ef2..d13bebdc8 100644 --- a/spec/lib/xdrgen/generator_spec.rb +++ b/spec/lib/xdrgen/generator_spec.rb @@ -9,6 +9,9 @@ languages.each do |lang| next if focus_basename.present? && File.basename(path) != focus_basename next if focus_language.present? && lang != focus_language + # ifdef.x uses #ifdef/#else which produces duplicate type names; + # only the Go generator supports splitting these into build-tagged files + next if File.basename(path) == "ifdef.x" && lang != "go" it "can generate #{File.basename path} in #{lang}" do c = generate lang, path diff --git a/spec/output/generator_spec_elixir/ifdef.x/MyXDR_generated.ex b/spec/output/generator_spec_elixir/ifdef.x/MyXDR_generated.ex new file mode 100644 index 000000000..d9dbfac23 --- /dev/null +++ b/spec/output/generator_spec_elixir/ifdef.x/MyXDR_generated.ex @@ -0,0 +1,154 @@ +defmodule MyXDR do + @moduledoc """ + Automatically generated by xdrgen + DO NOT EDIT or your changes may be overwritten + + Target implementation: exdr at https://hex.pm/packages/exdr + """ + + use XDR.Base + + comment ~S""" + === xdr source ============================================================ + + const BASE_VALUE = 1; + + =========================================================================== + """ + define_type("BASE_VALUE", Const, 1); + + comment ~S""" + === xdr source ============================================================ + + enum MyEnum { + MEMBER_A = 0, + MEMBER_B = 1, + + MEMBER_C = 2, + + MEMBER_D = 3 + }; + + =========================================================================== + """ + define_type("MyEnum", Enum, + member_a: 0, + member_b: 1, + member_c: 2, + member_d: 3 + ) + + comment ~S""" + === xdr source ============================================================ + + struct MyStruct { + int field1; + MyEnum field2; + + unsigned int field3; + + }; + + =========================================================================== + """ + define_type("MyStruct", Struct, + field1: build_type(Int), + field2: "MyEnum", + field3: build_type(UnsignedInt) + ) + + comment ~S""" + === xdr source ============================================================ + + struct ConditionalStruct { + int data; + }; + + =========================================================================== + """ + define_type("ConditionalStruct", Struct, + data: build_type(Int) + ) + + comment ~S""" + === xdr source ============================================================ + + struct VariantStruct { + int newField; + }; + + =========================================================================== + """ + define_type("VariantStruct", Struct, + new_field: build_type(Int) + ) + + comment ~S""" + === xdr source ============================================================ + + struct VariantStruct { + unsigned int oldField; + }; + + =========================================================================== + """ + define_type("VariantStruct", Struct, + old_field: build_type(UnsignedInt) + ) + + comment ~S""" + === xdr source ============================================================ + + enum UnionType { + UNION_A = 0, + UNION_B = 1, + + UNION_C = 2, + + UNION_D = 3 + }; + + =========================================================================== + """ + define_type("UnionType", Enum, + union_a: 0, + union_b: 1, + union_c: 2, + union_d: 3 + ) + + comment ~S""" + === xdr source ============================================================ + + union MyUnion switch (UnionType type) { + case UNION_A: + int armA; + case UNION_B: + unsigned int armB; + + case UNION_C: + unsigned hyper armC; + + case UNION_D: + void; + }; + + =========================================================================== + """ + define_type("MyUnion", Union, + switch_type: "UnionType", + switch_name: :type, + switches: [ + {:union_a, :arm_a}, + {:union_b, :arm_b}, + {:union_c, :arm_c}, + {:union_d, XDR.Type.Void}, + ], + arms: [ + arm_a: build_type(Int), + arm_b: build_type(UnsignedInt), + arm_c: build_type(UnsignedHyperInt), + ] + ) + +end diff --git a/spec/output/generator_spec_go/ifdef.x/MyXDR_generated.go b/spec/output/generator_spec_go/ifdef.x/MyXDR_generated.go new file mode 100644 index 000000000..734029350 --- /dev/null +++ b/spec/output/generator_spec_go/ifdef.x/MyXDR_generated.go @@ -0,0 +1,71 @@ +//lint:file-ignore S1005 The issue should be fixed in xdrgen. Unfortunately, there's no way to ignore a single file in staticcheck. +//lint:file-ignore U1000 fmtTest is not needed anywhere, should be removed in xdrgen. + +// Package MyXDR is generated from: +// +// spec/fixtures/generator/ifdef.x +// +// DO NOT EDIT or your changes may be overwritten +package MyXDR + +import ( + "encoding" + "errors" + "io" + "fmt" + + "github.com/stellar/go-xdr/xdr3" +) + +// XdrFilesSHA256 is the SHA256 hashes of source files. +var XdrFilesSHA256 = map[string]string{ + "spec/fixtures/generator/ifdef.x": "d9a6d6f15bd1bdf6e26e1c1e02a718a490b7cf9b5797b90f867e476abe1c02fd", +} + +var ErrMaxDecodingDepthReached = errors.New("maximum decoding depth reached") + +type xdrType interface { + xdrType() +} + +type decoderFrom interface { + DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) +} + +// Unmarshal reads an xdr element from `r` into `v`. +func Unmarshal(r io.Reader, v interface{}) (int, error) { + return UnmarshalWithOptions(r, v, xdr.DefaultDecodeOptions) +} + +// UnmarshalWithOptions works like Unmarshal but uses decoding options. +func UnmarshalWithOptions(r io.Reader, v interface{}, options xdr.DecodeOptions) (int, error) { + if decodable, ok := v.(decoderFrom); ok { + d := xdr.NewDecoderWithOptions(r, options) + return decodable.DecodeFrom(d, options.MaxDepth) + } + // delegate to xdr package's Unmarshal + return xdr.UnmarshalWithOptions(r, v, options) +} + +// Marshal writes an xdr element `v` into `w`. +func Marshal(w io.Writer, v interface{}) (int, error) { + if _, ok := v.(xdrType); ok { + if bm, ok := v.(encoding.BinaryMarshaler); ok { + b, err := bm.MarshalBinary() + if err != nil { + return 0, err + } + return w.Write(b) + } + } + // delegate to xdr package's Marshal + return xdr.Marshal(w, v) +} + +// BaseValue is an XDR Const defines as: +// +// const BASE_VALUE = 1; +// +const BaseValue = 1 + +var fmtTest = fmt.Sprint("this is a dummy usage of fmt") diff --git a/spec/output/generator_spec_go/ifdef.x/MyXDR_generated_feature_a_off.go b/spec/output/generator_spec_go/ifdef.x/MyXDR_generated_feature_a_off.go new file mode 100644 index 000000000..01444aaa8 --- /dev/null +++ b/spec/output/generator_spec_go/ifdef.x/MyXDR_generated_feature_a_off.go @@ -0,0 +1,815 @@ +//go:build !feature_a + +//lint:file-ignore S1005 The issue should be fixed in xdrgen. Unfortunately, there's no way to ignore a single file in staticcheck. + +// DO NOT EDIT or your changes may be overwritten +package MyXDR + +import ( + "bytes" + "encoding" + "errors" + "fmt" + + "github.com/stellar/go-xdr/xdr3" +) + +// MyEnum is an XDR Enum defines as: +// +// enum MyEnum { +// MEMBER_A = 0, +// MEMBER_B = 1, +// +// MEMBER_C = 2, +// +// MEMBER_D = 3 +// }; +// +type MyEnum int32 +const ( + MyEnumMemberA MyEnum = 0 + MyEnumMemberB MyEnum = 1 + MyEnumMemberD MyEnum = 3 +) +var myEnumMap = map[int32]string{ + 0: "MyEnumMemberA", + 1: "MyEnumMemberB", + 3: "MyEnumMemberD", +} + +// ValidEnum validates a proposed value for this enum. Implements +// the Enum interface for MyEnum +func (e MyEnum) ValidEnum(v int32) bool { + _, ok := myEnumMap[v] + return ok +} +// String returns the name of `e` +func (e MyEnum) String() string { + name, _ := myEnumMap[int32(e)] + return name +} + +// EncodeTo encodes this value using the Encoder. +func (e MyEnum) EncodeTo(enc *xdr.Encoder) error { + if _, ok := myEnumMap[int32(e)]; !ok { + return fmt.Errorf("'%d' is not a valid MyEnum enum value", e) + } + _, err := enc.EncodeInt(int32(e)) + return err +} +var _ decoderFrom = (*MyEnum)(nil) +// DecodeFrom decodes this value using the Decoder. +func (e *MyEnum) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyEnum: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + v, n, err := d.DecodeInt() + if err != nil { + return n, fmt.Errorf("decoding MyEnum: %w", err) + } + if _, ok := myEnumMap[v]; !ok { + return n, fmt.Errorf("'%d' is not a valid MyEnum enum value", v) + } + *e = MyEnum(v) + return n, nil +} +// MarshalBinary implements encoding.BinaryMarshaler. +func (s MyEnum) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *MyEnum) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*MyEnum)(nil) + _ encoding.BinaryUnmarshaler = (*MyEnum)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s MyEnum) xdrType() {} + +var _ xdrType = (*MyEnum)(nil) + +// MyStruct is an XDR Struct defines as: +// +// struct MyStruct { +// int field1; +// MyEnum field2; +// +// unsigned int field3; +// +// }; +// +type MyStruct struct { + Field1 int32 + Field2 MyEnum +} + +// EncodeTo encodes this value using the Encoder. +func (s *MyStruct) EncodeTo(e *xdr.Encoder) error { + var err error + if _, err = e.EncodeInt(int32(s.Field1)); err != nil { + return err + } + if err = s.Field2.EncodeTo(e); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*MyStruct)(nil) +// DecodeFrom decodes this value using the Decoder. +func (s *MyStruct) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyStruct: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + s.Field1, nTmp, err = d.DecodeInt() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Int: %w", err) + } + nTmp, err = s.Field2.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding MyEnum: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s MyStruct) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *MyStruct) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*MyStruct)(nil) + _ encoding.BinaryUnmarshaler = (*MyStruct)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s MyStruct) xdrType() {} + +var _ xdrType = (*MyStruct)(nil) + +// VariantStruct is an XDR Struct defines as: +// +// struct VariantStruct { +// unsigned int oldField; +// }; +// +type VariantStruct struct { + OldField uint32 +} + +// EncodeTo encodes this value using the Encoder. +func (s *VariantStruct) EncodeTo(e *xdr.Encoder) error { + var err error + if _, err = e.EncodeUint(uint32(s.OldField)); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*VariantStruct)(nil) +// DecodeFrom decodes this value using the Decoder. +func (s *VariantStruct) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding VariantStruct: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + s.OldField, nTmp, err = d.DecodeUint() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Unsigned int: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s VariantStruct) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *VariantStruct) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*VariantStruct)(nil) + _ encoding.BinaryUnmarshaler = (*VariantStruct)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s VariantStruct) xdrType() {} + +var _ xdrType = (*VariantStruct)(nil) + +// UnionType is an XDR Enum defines as: +// +// enum UnionType { +// UNION_A = 0, +// UNION_B = 1, +// +// UNION_C = 2, +// +// UNION_D = 3 +// }; +// +type UnionType int32 +const ( + UnionTypeUnionA UnionType = 0 + UnionTypeUnionB UnionType = 1 + UnionTypeUnionD UnionType = 3 +) +var unionTypeMap = map[int32]string{ + 0: "UnionTypeUnionA", + 1: "UnionTypeUnionB", + 3: "UnionTypeUnionD", +} + +// ValidEnum validates a proposed value for this enum. Implements +// the Enum interface for UnionType +func (e UnionType) ValidEnum(v int32) bool { + _, ok := unionTypeMap[v] + return ok +} +// String returns the name of `e` +func (e UnionType) String() string { + name, _ := unionTypeMap[int32(e)] + return name +} + +// EncodeTo encodes this value using the Encoder. +func (e UnionType) EncodeTo(enc *xdr.Encoder) error { + if _, ok := unionTypeMap[int32(e)]; !ok { + return fmt.Errorf("'%d' is not a valid UnionType enum value", e) + } + _, err := enc.EncodeInt(int32(e)) + return err +} +var _ decoderFrom = (*UnionType)(nil) +// DecodeFrom decodes this value using the Decoder. +func (e *UnionType) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding UnionType: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + v, n, err := d.DecodeInt() + if err != nil { + return n, fmt.Errorf("decoding UnionType: %w", err) + } + if _, ok := unionTypeMap[v]; !ok { + return n, fmt.Errorf("'%d' is not a valid UnionType enum value", v) + } + *e = UnionType(v) + return n, nil +} +// MarshalBinary implements encoding.BinaryMarshaler. +func (s UnionType) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *UnionType) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*UnionType)(nil) + _ encoding.BinaryUnmarshaler = (*UnionType)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s UnionType) xdrType() {} + +var _ xdrType = (*UnionType)(nil) + +// MyUnion is an XDR Union defines as: +// +// union MyUnion switch (UnionType type) { +// case UNION_A: +// int armA; +// case UNION_B: +// unsigned int armB; +// +// case UNION_C: +// unsigned hyper armC; +// +// case UNION_D: +// void; +// }; +// +type MyUnion struct{ + Type UnionType + ArmA *int32 + ArmB *uint32 +} + +// SwitchFieldName returns the field name in which this union's +// discriminant is stored +func (u MyUnion) SwitchFieldName() string { + return "Type" +} + +// ArmForSwitch returns which field name should be used for storing +// the value for an instance of MyUnion +func (u MyUnion) ArmForSwitch(sw int32) (string, bool) { +switch UnionType(sw) { + case UnionTypeUnionA: + return "ArmA", true + case UnionTypeUnionB: + return "ArmB", true + case UnionTypeUnionD: + return "", true +} +return "-", false +} + +// NewMyUnion creates a new MyUnion. +func NewMyUnion(aType UnionType, value interface{}) (result MyUnion, err error) { + result.Type = aType +switch UnionType(aType) { + case UnionTypeUnionA: + tv, ok := value.(int32) + if !ok { + err = errors.New("invalid value, must be int32") + return + } + result.ArmA = &tv + case UnionTypeUnionB: + tv, ok := value.(uint32) + if !ok { + err = errors.New("invalid value, must be uint32") + return + } + result.ArmB = &tv + case UnionTypeUnionD: + // void +} + return +} +// MustArmA retrieves the ArmA value from the union, +// panicing if the value is not set. +func (u MyUnion) MustArmA() int32 { + val, ok := u.GetArmA() + + if !ok { + panic("arm ArmA is not set") + } + + return val +} + +// GetArmA retrieves the ArmA value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u MyUnion) GetArmA() (result int32, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "ArmA" { + result = *u.ArmA + ok = true + } + + return +} +// MustArmB retrieves the ArmB value from the union, +// panicing if the value is not set. +func (u MyUnion) MustArmB() uint32 { + val, ok := u.GetArmB() + + if !ok { + panic("arm ArmB is not set") + } + + return val +} + +// GetArmB retrieves the ArmB value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u MyUnion) GetArmB() (result uint32, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "ArmB" { + result = *u.ArmB + ok = true + } + + return +} + +// EncodeTo encodes this value using the Encoder. +func (u MyUnion) EncodeTo(e *xdr.Encoder) error { + var err error + if err = u.Type.EncodeTo(e); err != nil { + return err + } +switch UnionType(u.Type) { + case UnionTypeUnionA: + if _, err = e.EncodeInt(int32((*u.ArmA))); err != nil { + return err + } +return nil + case UnionTypeUnionB: + if _, err = e.EncodeUint(uint32((*u.ArmB))); err != nil { + return err + } +return nil + case UnionTypeUnionD: + // Void +return nil +} + return fmt.Errorf("Type (UnionType) switch value '%d' is not valid for union MyUnion", u.Type) +} + +var _ decoderFrom = (*MyUnion)(nil) +// DecodeFrom decodes this value using the Decoder. +func (u *MyUnion) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyUnion: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + nTmp, err = u.Type.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding UnionType: %w", err) + } +switch UnionType(u.Type) { + case UnionTypeUnionA: + u.ArmA = new(int32) + (*u.ArmA), nTmp, err = d.DecodeInt() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Int: %w", err) + } + return n, nil + case UnionTypeUnionB: + u.ArmB = new(uint32) + (*u.ArmB), nTmp, err = d.DecodeUint() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Unsigned int: %w", err) + } + return n, nil + case UnionTypeUnionD: + // Void + return n, nil +} + return n, fmt.Errorf("union MyUnion has invalid Type (UnionType) switch value '%d'", u.Type) +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s MyUnion) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *MyUnion) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*MyUnion)(nil) + _ encoding.BinaryUnmarshaler = (*MyUnion)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s MyUnion) xdrType() {} + +var _ xdrType = (*MyUnion)(nil) + +// ContainerStruct is an XDR Struct defines as: +// +// struct ContainerStruct { +// int baseField; +// union switch (UnionType type) { +// case UNION_A: int dataA; +// case UNION_B: unsigned int dataB; +// +// case UNION_C: unsigned hyper dataC; +// +// case UNION_D: void; +// } body; +// }; +// +type ContainerStruct struct { + BaseField int32 + Body ContainerStructBody +} + +// EncodeTo encodes this value using the Encoder. +func (s *ContainerStruct) EncodeTo(e *xdr.Encoder) error { + var err error + if _, err = e.EncodeInt(int32(s.BaseField)); err != nil { + return err + } + if err = s.Body.EncodeTo(e); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*ContainerStruct)(nil) +// DecodeFrom decodes this value using the Decoder. +func (s *ContainerStruct) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding ContainerStruct: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + s.BaseField, nTmp, err = d.DecodeInt() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Int: %w", err) + } + nTmp, err = s.Body.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding ContainerStructBody: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s ContainerStruct) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *ContainerStruct) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*ContainerStruct)(nil) + _ encoding.BinaryUnmarshaler = (*ContainerStruct)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s ContainerStruct) xdrType() {} + +var _ xdrType = (*ContainerStruct)(nil) + +// ContainerStructBody is an XDR NestedUnion defines as: +// +// union switch (UnionType type) { +// case UNION_A: int dataA; +// case UNION_B: unsigned int dataB; +// +// case UNION_C: unsigned hyper dataC; +// +// case UNION_D: void; +// } +// +type ContainerStructBody struct{ + Type UnionType + DataA *int32 + DataB *uint32 +} + +// SwitchFieldName returns the field name in which this union's +// discriminant is stored +func (u ContainerStructBody) SwitchFieldName() string { + return "Type" +} + +// ArmForSwitch returns which field name should be used for storing +// the value for an instance of ContainerStructBody +func (u ContainerStructBody) ArmForSwitch(sw int32) (string, bool) { +switch UnionType(sw) { + case UnionTypeUnionA: + return "DataA", true + case UnionTypeUnionB: + return "DataB", true + case UnionTypeUnionD: + return "", true +} +return "-", false +} + +// NewContainerStructBody creates a new ContainerStructBody. +func NewContainerStructBody(aType UnionType, value interface{}) (result ContainerStructBody, err error) { + result.Type = aType +switch UnionType(aType) { + case UnionTypeUnionA: + tv, ok := value.(int32) + if !ok { + err = errors.New("invalid value, must be int32") + return + } + result.DataA = &tv + case UnionTypeUnionB: + tv, ok := value.(uint32) + if !ok { + err = errors.New("invalid value, must be uint32") + return + } + result.DataB = &tv + case UnionTypeUnionD: + // void +} + return +} +// MustDataA retrieves the DataA value from the union, +// panicing if the value is not set. +func (u ContainerStructBody) MustDataA() int32 { + val, ok := u.GetDataA() + + if !ok { + panic("arm DataA is not set") + } + + return val +} + +// GetDataA retrieves the DataA value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u ContainerStructBody) GetDataA() (result int32, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "DataA" { + result = *u.DataA + ok = true + } + + return +} +// MustDataB retrieves the DataB value from the union, +// panicing if the value is not set. +func (u ContainerStructBody) MustDataB() uint32 { + val, ok := u.GetDataB() + + if !ok { + panic("arm DataB is not set") + } + + return val +} + +// GetDataB retrieves the DataB value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u ContainerStructBody) GetDataB() (result uint32, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "DataB" { + result = *u.DataB + ok = true + } + + return +} + +// EncodeTo encodes this value using the Encoder. +func (u ContainerStructBody) EncodeTo(e *xdr.Encoder) error { + var err error + if err = u.Type.EncodeTo(e); err != nil { + return err + } +switch UnionType(u.Type) { + case UnionTypeUnionA: + if _, err = e.EncodeInt(int32((*u.DataA))); err != nil { + return err + } +return nil + case UnionTypeUnionB: + if _, err = e.EncodeUint(uint32((*u.DataB))); err != nil { + return err + } +return nil + case UnionTypeUnionD: + // Void +return nil +} + return fmt.Errorf("Type (UnionType) switch value '%d' is not valid for union ContainerStructBody", u.Type) +} + +var _ decoderFrom = (*ContainerStructBody)(nil) +// DecodeFrom decodes this value using the Decoder. +func (u *ContainerStructBody) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding ContainerStructBody: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + nTmp, err = u.Type.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding UnionType: %w", err) + } +switch UnionType(u.Type) { + case UnionTypeUnionA: + u.DataA = new(int32) + (*u.DataA), nTmp, err = d.DecodeInt() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Int: %w", err) + } + return n, nil + case UnionTypeUnionB: + u.DataB = new(uint32) + (*u.DataB), nTmp, err = d.DecodeUint() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Unsigned int: %w", err) + } + return n, nil + case UnionTypeUnionD: + // Void + return n, nil +} + return n, fmt.Errorf("union ContainerStructBody has invalid Type (UnionType) switch value '%d'", u.Type) +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s ContainerStructBody) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *ContainerStructBody) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*ContainerStructBody)(nil) + _ encoding.BinaryUnmarshaler = (*ContainerStructBody)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s ContainerStructBody) xdrType() {} + +var _ xdrType = (*ContainerStructBody)(nil) diff --git a/spec/output/generator_spec_go/ifdef.x/MyXDR_generated_feature_a_on.go b/spec/output/generator_spec_go/ifdef.x/MyXDR_generated_feature_a_on.go new file mode 100644 index 000000000..398fd2d98 --- /dev/null +++ b/spec/output/generator_spec_go/ifdef.x/MyXDR_generated_feature_a_on.go @@ -0,0 +1,986 @@ +//go:build feature_a + +//lint:file-ignore S1005 The issue should be fixed in xdrgen. Unfortunately, there's no way to ignore a single file in staticcheck. + +// DO NOT EDIT or your changes may be overwritten +package MyXDR + +import ( + "bytes" + "encoding" + "errors" + "fmt" + + "github.com/stellar/go-xdr/xdr3" +) + +// MyEnum is an XDR Enum defines as: +// +// enum MyEnum { +// MEMBER_A = 0, +// MEMBER_B = 1, +// +// MEMBER_C = 2, +// +// MEMBER_D = 3 +// }; +// +type MyEnum int32 +const ( + MyEnumMemberA MyEnum = 0 + MyEnumMemberB MyEnum = 1 + MyEnumMemberC MyEnum = 2 + MyEnumMemberD MyEnum = 3 +) +var myEnumMap = map[int32]string{ + 0: "MyEnumMemberA", + 1: "MyEnumMemberB", + 2: "MyEnumMemberC", + 3: "MyEnumMemberD", +} + +// ValidEnum validates a proposed value for this enum. Implements +// the Enum interface for MyEnum +func (e MyEnum) ValidEnum(v int32) bool { + _, ok := myEnumMap[v] + return ok +} +// String returns the name of `e` +func (e MyEnum) String() string { + name, _ := myEnumMap[int32(e)] + return name +} + +// EncodeTo encodes this value using the Encoder. +func (e MyEnum) EncodeTo(enc *xdr.Encoder) error { + if _, ok := myEnumMap[int32(e)]; !ok { + return fmt.Errorf("'%d' is not a valid MyEnum enum value", e) + } + _, err := enc.EncodeInt(int32(e)) + return err +} +var _ decoderFrom = (*MyEnum)(nil) +// DecodeFrom decodes this value using the Decoder. +func (e *MyEnum) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyEnum: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + v, n, err := d.DecodeInt() + if err != nil { + return n, fmt.Errorf("decoding MyEnum: %w", err) + } + if _, ok := myEnumMap[v]; !ok { + return n, fmt.Errorf("'%d' is not a valid MyEnum enum value", v) + } + *e = MyEnum(v) + return n, nil +} +// MarshalBinary implements encoding.BinaryMarshaler. +func (s MyEnum) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *MyEnum) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*MyEnum)(nil) + _ encoding.BinaryUnmarshaler = (*MyEnum)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s MyEnum) xdrType() {} + +var _ xdrType = (*MyEnum)(nil) + +// MyStruct is an XDR Struct defines as: +// +// struct MyStruct { +// int field1; +// MyEnum field2; +// +// unsigned int field3; +// +// }; +// +type MyStruct struct { + Field1 int32 + Field2 MyEnum + Field3 uint32 +} + +// EncodeTo encodes this value using the Encoder. +func (s *MyStruct) EncodeTo(e *xdr.Encoder) error { + var err error + if _, err = e.EncodeInt(int32(s.Field1)); err != nil { + return err + } + if err = s.Field2.EncodeTo(e); err != nil { + return err + } + if _, err = e.EncodeUint(uint32(s.Field3)); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*MyStruct)(nil) +// DecodeFrom decodes this value using the Decoder. +func (s *MyStruct) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyStruct: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + s.Field1, nTmp, err = d.DecodeInt() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Int: %w", err) + } + nTmp, err = s.Field2.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding MyEnum: %w", err) + } + s.Field3, nTmp, err = d.DecodeUint() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Unsigned int: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s MyStruct) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *MyStruct) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*MyStruct)(nil) + _ encoding.BinaryUnmarshaler = (*MyStruct)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s MyStruct) xdrType() {} + +var _ xdrType = (*MyStruct)(nil) + +// ConditionalStruct is an XDR Struct defines as: +// +// struct ConditionalStruct { +// int data; +// }; +// +type ConditionalStruct struct { + Data int32 +} + +// EncodeTo encodes this value using the Encoder. +func (s *ConditionalStruct) EncodeTo(e *xdr.Encoder) error { + var err error + if _, err = e.EncodeInt(int32(s.Data)); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*ConditionalStruct)(nil) +// DecodeFrom decodes this value using the Decoder. +func (s *ConditionalStruct) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding ConditionalStruct: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + s.Data, nTmp, err = d.DecodeInt() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Int: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s ConditionalStruct) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *ConditionalStruct) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*ConditionalStruct)(nil) + _ encoding.BinaryUnmarshaler = (*ConditionalStruct)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s ConditionalStruct) xdrType() {} + +var _ xdrType = (*ConditionalStruct)(nil) + +// VariantStruct is an XDR Struct defines as: +// +// struct VariantStruct { +// int newField; +// }; +// +type VariantStruct struct { + NewField int32 +} + +// EncodeTo encodes this value using the Encoder. +func (s *VariantStruct) EncodeTo(e *xdr.Encoder) error { + var err error + if _, err = e.EncodeInt(int32(s.NewField)); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*VariantStruct)(nil) +// DecodeFrom decodes this value using the Decoder. +func (s *VariantStruct) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding VariantStruct: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + s.NewField, nTmp, err = d.DecodeInt() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Int: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s VariantStruct) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *VariantStruct) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*VariantStruct)(nil) + _ encoding.BinaryUnmarshaler = (*VariantStruct)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s VariantStruct) xdrType() {} + +var _ xdrType = (*VariantStruct)(nil) + +// UnionType is an XDR Enum defines as: +// +// enum UnionType { +// UNION_A = 0, +// UNION_B = 1, +// +// UNION_C = 2, +// +// UNION_D = 3 +// }; +// +type UnionType int32 +const ( + UnionTypeUnionA UnionType = 0 + UnionTypeUnionB UnionType = 1 + UnionTypeUnionC UnionType = 2 + UnionTypeUnionD UnionType = 3 +) +var unionTypeMap = map[int32]string{ + 0: "UnionTypeUnionA", + 1: "UnionTypeUnionB", + 2: "UnionTypeUnionC", + 3: "UnionTypeUnionD", +} + +// ValidEnum validates a proposed value for this enum. Implements +// the Enum interface for UnionType +func (e UnionType) ValidEnum(v int32) bool { + _, ok := unionTypeMap[v] + return ok +} +// String returns the name of `e` +func (e UnionType) String() string { + name, _ := unionTypeMap[int32(e)] + return name +} + +// EncodeTo encodes this value using the Encoder. +func (e UnionType) EncodeTo(enc *xdr.Encoder) error { + if _, ok := unionTypeMap[int32(e)]; !ok { + return fmt.Errorf("'%d' is not a valid UnionType enum value", e) + } + _, err := enc.EncodeInt(int32(e)) + return err +} +var _ decoderFrom = (*UnionType)(nil) +// DecodeFrom decodes this value using the Decoder. +func (e *UnionType) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding UnionType: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + v, n, err := d.DecodeInt() + if err != nil { + return n, fmt.Errorf("decoding UnionType: %w", err) + } + if _, ok := unionTypeMap[v]; !ok { + return n, fmt.Errorf("'%d' is not a valid UnionType enum value", v) + } + *e = UnionType(v) + return n, nil +} +// MarshalBinary implements encoding.BinaryMarshaler. +func (s UnionType) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *UnionType) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*UnionType)(nil) + _ encoding.BinaryUnmarshaler = (*UnionType)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s UnionType) xdrType() {} + +var _ xdrType = (*UnionType)(nil) + +// MyUnion is an XDR Union defines as: +// +// union MyUnion switch (UnionType type) { +// case UNION_A: +// int armA; +// case UNION_B: +// unsigned int armB; +// +// case UNION_C: +// unsigned hyper armC; +// +// case UNION_D: +// void; +// }; +// +type MyUnion struct{ + Type UnionType + ArmA *int32 + ArmB *uint32 + ArmC *uint64 +} + +// SwitchFieldName returns the field name in which this union's +// discriminant is stored +func (u MyUnion) SwitchFieldName() string { + return "Type" +} + +// ArmForSwitch returns which field name should be used for storing +// the value for an instance of MyUnion +func (u MyUnion) ArmForSwitch(sw int32) (string, bool) { +switch UnionType(sw) { + case UnionTypeUnionA: + return "ArmA", true + case UnionTypeUnionB: + return "ArmB", true + case UnionTypeUnionC: + return "ArmC", true + case UnionTypeUnionD: + return "", true +} +return "-", false +} + +// NewMyUnion creates a new MyUnion. +func NewMyUnion(aType UnionType, value interface{}) (result MyUnion, err error) { + result.Type = aType +switch UnionType(aType) { + case UnionTypeUnionA: + tv, ok := value.(int32) + if !ok { + err = errors.New("invalid value, must be int32") + return + } + result.ArmA = &tv + case UnionTypeUnionB: + tv, ok := value.(uint32) + if !ok { + err = errors.New("invalid value, must be uint32") + return + } + result.ArmB = &tv + case UnionTypeUnionC: + tv, ok := value.(uint64) + if !ok { + err = errors.New("invalid value, must be uint64") + return + } + result.ArmC = &tv + case UnionTypeUnionD: + // void +} + return +} +// MustArmA retrieves the ArmA value from the union, +// panicing if the value is not set. +func (u MyUnion) MustArmA() int32 { + val, ok := u.GetArmA() + + if !ok { + panic("arm ArmA is not set") + } + + return val +} + +// GetArmA retrieves the ArmA value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u MyUnion) GetArmA() (result int32, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "ArmA" { + result = *u.ArmA + ok = true + } + + return +} +// MustArmB retrieves the ArmB value from the union, +// panicing if the value is not set. +func (u MyUnion) MustArmB() uint32 { + val, ok := u.GetArmB() + + if !ok { + panic("arm ArmB is not set") + } + + return val +} + +// GetArmB retrieves the ArmB value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u MyUnion) GetArmB() (result uint32, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "ArmB" { + result = *u.ArmB + ok = true + } + + return +} +// MustArmC retrieves the ArmC value from the union, +// panicing if the value is not set. +func (u MyUnion) MustArmC() uint64 { + val, ok := u.GetArmC() + + if !ok { + panic("arm ArmC is not set") + } + + return val +} + +// GetArmC retrieves the ArmC value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u MyUnion) GetArmC() (result uint64, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "ArmC" { + result = *u.ArmC + ok = true + } + + return +} + +// EncodeTo encodes this value using the Encoder. +func (u MyUnion) EncodeTo(e *xdr.Encoder) error { + var err error + if err = u.Type.EncodeTo(e); err != nil { + return err + } +switch UnionType(u.Type) { + case UnionTypeUnionA: + if _, err = e.EncodeInt(int32((*u.ArmA))); err != nil { + return err + } +return nil + case UnionTypeUnionB: + if _, err = e.EncodeUint(uint32((*u.ArmB))); err != nil { + return err + } +return nil + case UnionTypeUnionC: + if _, err = e.EncodeUhyper(uint64((*u.ArmC))); err != nil { + return err + } +return nil + case UnionTypeUnionD: + // Void +return nil +} + return fmt.Errorf("Type (UnionType) switch value '%d' is not valid for union MyUnion", u.Type) +} + +var _ decoderFrom = (*MyUnion)(nil) +// DecodeFrom decodes this value using the Decoder. +func (u *MyUnion) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding MyUnion: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + nTmp, err = u.Type.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding UnionType: %w", err) + } +switch UnionType(u.Type) { + case UnionTypeUnionA: + u.ArmA = new(int32) + (*u.ArmA), nTmp, err = d.DecodeInt() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Int: %w", err) + } + return n, nil + case UnionTypeUnionB: + u.ArmB = new(uint32) + (*u.ArmB), nTmp, err = d.DecodeUint() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Unsigned int: %w", err) + } + return n, nil + case UnionTypeUnionC: + u.ArmC = new(uint64) + (*u.ArmC), nTmp, err = d.DecodeUhyper() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Unsigned hyper: %w", err) + } + return n, nil + case UnionTypeUnionD: + // Void + return n, nil +} + return n, fmt.Errorf("union MyUnion has invalid Type (UnionType) switch value '%d'", u.Type) +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s MyUnion) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *MyUnion) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*MyUnion)(nil) + _ encoding.BinaryUnmarshaler = (*MyUnion)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s MyUnion) xdrType() {} + +var _ xdrType = (*MyUnion)(nil) + +// ContainerStruct is an XDR Struct defines as: +// +// struct ContainerStruct { +// int baseField; +// union switch (UnionType type) { +// case UNION_A: int dataA; +// case UNION_B: unsigned int dataB; +// +// case UNION_C: unsigned hyper dataC; +// +// case UNION_D: void; +// } body; +// }; +// +type ContainerStruct struct { + BaseField int32 + Body ContainerStructBody +} + +// EncodeTo encodes this value using the Encoder. +func (s *ContainerStruct) EncodeTo(e *xdr.Encoder) error { + var err error + if _, err = e.EncodeInt(int32(s.BaseField)); err != nil { + return err + } + if err = s.Body.EncodeTo(e); err != nil { + return err + } + return nil +} + +var _ decoderFrom = (*ContainerStruct)(nil) +// DecodeFrom decodes this value using the Decoder. +func (s *ContainerStruct) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding ContainerStruct: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + s.BaseField, nTmp, err = d.DecodeInt() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Int: %w", err) + } + nTmp, err = s.Body.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding ContainerStructBody: %w", err) + } + return n, nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s ContainerStruct) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *ContainerStruct) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*ContainerStruct)(nil) + _ encoding.BinaryUnmarshaler = (*ContainerStruct)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s ContainerStruct) xdrType() {} + +var _ xdrType = (*ContainerStruct)(nil) + +// ContainerStructBody is an XDR NestedUnion defines as: +// +// union switch (UnionType type) { +// case UNION_A: int dataA; +// case UNION_B: unsigned int dataB; +// +// case UNION_C: unsigned hyper dataC; +// +// case UNION_D: void; +// } +// +type ContainerStructBody struct{ + Type UnionType + DataA *int32 + DataB *uint32 + DataC *uint64 +} + +// SwitchFieldName returns the field name in which this union's +// discriminant is stored +func (u ContainerStructBody) SwitchFieldName() string { + return "Type" +} + +// ArmForSwitch returns which field name should be used for storing +// the value for an instance of ContainerStructBody +func (u ContainerStructBody) ArmForSwitch(sw int32) (string, bool) { +switch UnionType(sw) { + case UnionTypeUnionA: + return "DataA", true + case UnionTypeUnionB: + return "DataB", true + case UnionTypeUnionC: + return "DataC", true + case UnionTypeUnionD: + return "", true +} +return "-", false +} + +// NewContainerStructBody creates a new ContainerStructBody. +func NewContainerStructBody(aType UnionType, value interface{}) (result ContainerStructBody, err error) { + result.Type = aType +switch UnionType(aType) { + case UnionTypeUnionA: + tv, ok := value.(int32) + if !ok { + err = errors.New("invalid value, must be int32") + return + } + result.DataA = &tv + case UnionTypeUnionB: + tv, ok := value.(uint32) + if !ok { + err = errors.New("invalid value, must be uint32") + return + } + result.DataB = &tv + case UnionTypeUnionC: + tv, ok := value.(uint64) + if !ok { + err = errors.New("invalid value, must be uint64") + return + } + result.DataC = &tv + case UnionTypeUnionD: + // void +} + return +} +// MustDataA retrieves the DataA value from the union, +// panicing if the value is not set. +func (u ContainerStructBody) MustDataA() int32 { + val, ok := u.GetDataA() + + if !ok { + panic("arm DataA is not set") + } + + return val +} + +// GetDataA retrieves the DataA value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u ContainerStructBody) GetDataA() (result int32, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "DataA" { + result = *u.DataA + ok = true + } + + return +} +// MustDataB retrieves the DataB value from the union, +// panicing if the value is not set. +func (u ContainerStructBody) MustDataB() uint32 { + val, ok := u.GetDataB() + + if !ok { + panic("arm DataB is not set") + } + + return val +} + +// GetDataB retrieves the DataB value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u ContainerStructBody) GetDataB() (result uint32, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "DataB" { + result = *u.DataB + ok = true + } + + return +} +// MustDataC retrieves the DataC value from the union, +// panicing if the value is not set. +func (u ContainerStructBody) MustDataC() uint64 { + val, ok := u.GetDataC() + + if !ok { + panic("arm DataC is not set") + } + + return val +} + +// GetDataC retrieves the DataC value from the union, +// returning ok if the union's switch indicated the value is valid. +func (u ContainerStructBody) GetDataC() (result uint64, ok bool) { + armName, _ := u.ArmForSwitch(int32(u.Type)) + + if armName == "DataC" { + result = *u.DataC + ok = true + } + + return +} + +// EncodeTo encodes this value using the Encoder. +func (u ContainerStructBody) EncodeTo(e *xdr.Encoder) error { + var err error + if err = u.Type.EncodeTo(e); err != nil { + return err + } +switch UnionType(u.Type) { + case UnionTypeUnionA: + if _, err = e.EncodeInt(int32((*u.DataA))); err != nil { + return err + } +return nil + case UnionTypeUnionB: + if _, err = e.EncodeUint(uint32((*u.DataB))); err != nil { + return err + } +return nil + case UnionTypeUnionC: + if _, err = e.EncodeUhyper(uint64((*u.DataC))); err != nil { + return err + } +return nil + case UnionTypeUnionD: + // Void +return nil +} + return fmt.Errorf("Type (UnionType) switch value '%d' is not valid for union ContainerStructBody", u.Type) +} + +var _ decoderFrom = (*ContainerStructBody)(nil) +// DecodeFrom decodes this value using the Decoder. +func (u *ContainerStructBody) DecodeFrom(d *xdr.Decoder, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, fmt.Errorf("decoding ContainerStructBody: %w", ErrMaxDecodingDepthReached) + } + maxDepth -= 1 + var err error + var n, nTmp int + nTmp, err = u.Type.DecodeFrom(d, maxDepth) + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding UnionType: %w", err) + } +switch UnionType(u.Type) { + case UnionTypeUnionA: + u.DataA = new(int32) + (*u.DataA), nTmp, err = d.DecodeInt() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Int: %w", err) + } + return n, nil + case UnionTypeUnionB: + u.DataB = new(uint32) + (*u.DataB), nTmp, err = d.DecodeUint() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Unsigned int: %w", err) + } + return n, nil + case UnionTypeUnionC: + u.DataC = new(uint64) + (*u.DataC), nTmp, err = d.DecodeUhyper() + n += nTmp + if err != nil { + return n, fmt.Errorf("decoding Unsigned hyper: %w", err) + } + return n, nil + case UnionTypeUnionD: + // Void + return n, nil +} + return n, fmt.Errorf("union ContainerStructBody has invalid Type (UnionType) switch value '%d'", u.Type) +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (s ContainerStructBody) MarshalBinary() ([]byte, error) { + b := bytes.Buffer{} + e := xdr.NewEncoder(&b) + err := s.EncodeTo(e) + return b.Bytes(), err +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (s *ContainerStructBody) UnmarshalBinary(inp []byte) error { + r := bytes.NewReader(inp) + o := xdr.DefaultDecodeOptions + o.MaxInputLen = len(inp) + d := xdr.NewDecoderWithOptions(r, o) + _, err := s.DecodeFrom(d, o.MaxDepth) + return err +} + +var ( + _ encoding.BinaryMarshaler = (*ContainerStructBody)(nil) + _ encoding.BinaryUnmarshaler = (*ContainerStructBody)(nil) +) + +// xdrType signals that this type represents XDR values defined by this package. +func (s ContainerStructBody) xdrType() {} + +var _ xdrType = (*ContainerStructBody)(nil) diff --git a/spec/output/generator_spec_javascript/ifdef.x/MyXDR_generated.js b/spec/output/generator_spec_javascript/ifdef.x/MyXDR_generated.js new file mode 100644 index 000000000..61177f979 --- /dev/null +++ b/spec/output/generator_spec_javascript/ifdef.x/MyXDR_generated.js @@ -0,0 +1,139 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +/* jshint maxstatements:2147483647 */ +/* jshint esnext:true */ + +import * as XDR from '@stellar/js-xdr'; + + +var types = XDR.config(xdr => { +// === xdr source ============================================================ +// +// const BASE_VALUE = 1; +// +// =========================================================================== +xdr.const("BASE_VALUE", 1); + +// === xdr source ============================================================ +// +// enum MyEnum { +// MEMBER_A = 0, +// MEMBER_B = 1, +// +// MEMBER_C = 2, +// +// MEMBER_D = 3 +// }; +// +// =========================================================================== +xdr.enum("MyEnum", { + memberA: 0, + memberB: 1, + memberC: 2, + memberD: 3, +}); + +// === xdr source ============================================================ +// +// struct MyStruct { +// int field1; +// MyEnum field2; +// +// unsigned int field3; +// +// }; +// +// =========================================================================== +xdr.struct("MyStruct", [ + ["field1", xdr.int()], + ["field2", xdr.lookup("MyEnum")], + ["field3", xdr.uint()], +]); + +// === xdr source ============================================================ +// +// struct ConditionalStruct { +// int data; +// }; +// +// =========================================================================== +xdr.struct("ConditionalStruct", [ + ["data", xdr.int()], +]); + +// === xdr source ============================================================ +// +// struct VariantStruct { +// int newField; +// }; +// +// =========================================================================== +xdr.struct("VariantStruct", [ + ["newField", xdr.int()], +]); + +// === xdr source ============================================================ +// +// struct VariantStruct { +// unsigned int oldField; +// }; +// +// =========================================================================== +xdr.struct("VariantStruct", [ + ["oldField", xdr.uint()], +]); + +// === xdr source ============================================================ +// +// enum UnionType { +// UNION_A = 0, +// UNION_B = 1, +// +// UNION_C = 2, +// +// UNION_D = 3 +// }; +// +// =========================================================================== +xdr.enum("UnionType", { + unionA: 0, + unionB: 1, + unionC: 2, + unionD: 3, +}); + +// === xdr source ============================================================ +// +// union MyUnion switch (UnionType type) { +// case UNION_A: +// int armA; +// case UNION_B: +// unsigned int armB; +// +// case UNION_C: +// unsigned hyper armC; +// +// case UNION_D: +// void; +// }; +// +// =========================================================================== +xdr.union("MyUnion", { + switchOn: xdr.lookup("UnionType"), + switchName: "type", + switches: [ + ["unionA", "armA"], + ["unionB", "armB"], + ["unionC", "armC"], + ["unionD", xdr.void()], + ], + arms: { + armA: xdr.int(), + armB: xdr.uint(), + armC: xdr.uhyper(), + }, +}); + +}); +export default types; diff --git a/spec/output/generator_spec_ruby/ifdef.x/MyXDR.rb b/spec/output/generator_spec_ruby/ifdef.x/MyXDR.rb new file mode 100644 index 000000000..7875ba506 --- /dev/null +++ b/spec/output/generator_spec_ruby/ifdef.x/MyXDR.rb @@ -0,0 +1,13 @@ +# This code was automatically generated using xdrgen +# DO NOT EDIT or your changes may be overwritten + +require 'xdr' + +BASE_VALUE = 1 +autoload :MyEnum +autoload :MyStruct +autoload :ConditionalStruct +autoload :VariantStruct +autoload :VariantStruct +autoload :UnionType +autoload :MyUnion diff --git a/spec/output/generator_spec_ruby/ifdef.x/conditional_struct.rb b/spec/output/generator_spec_ruby/ifdef.x/conditional_struct.rb new file mode 100644 index 000000000..56a7d2e69 --- /dev/null +++ b/spec/output/generator_spec_ruby/ifdef.x/conditional_struct.rb @@ -0,0 +1,15 @@ +# This code was automatically generated using xdrgen +# DO NOT EDIT or your changes may be overwritten + +require 'xdr' + +# === xdr source ============================================================ +# +# struct ConditionalStruct { +# int data; +# }; +# +# =========================================================================== +class ConditionalStruct < XDR::Struct + attribute :data, XDR::Int +end diff --git a/spec/output/generator_spec_ruby/ifdef.x/my_enum.rb b/spec/output/generator_spec_ruby/ifdef.x/my_enum.rb new file mode 100644 index 000000000..506176c24 --- /dev/null +++ b/spec/output/generator_spec_ruby/ifdef.x/my_enum.rb @@ -0,0 +1,25 @@ +# This code was automatically generated using xdrgen +# DO NOT EDIT or your changes may be overwritten + +require 'xdr' + +# === xdr source ============================================================ +# +# enum MyEnum { +# MEMBER_A = 0, +# MEMBER_B = 1, +# +# MEMBER_C = 2, +# +# MEMBER_D = 3 +# }; +# +# =========================================================================== +class MyEnum < XDR::Enum + member :member_a, 0 + member :member_b, 1 + member :member_c, 2 + member :member_d, 3 + + seal +end diff --git a/spec/output/generator_spec_ruby/ifdef.x/my_struct.rb b/spec/output/generator_spec_ruby/ifdef.x/my_struct.rb new file mode 100644 index 000000000..fbe3e481c --- /dev/null +++ b/spec/output/generator_spec_ruby/ifdef.x/my_struct.rb @@ -0,0 +1,21 @@ +# This code was automatically generated using xdrgen +# DO NOT EDIT or your changes may be overwritten + +require 'xdr' + +# === xdr source ============================================================ +# +# struct MyStruct { +# int field1; +# MyEnum field2; +# +# unsigned int field3; +# +# }; +# +# =========================================================================== +class MyStruct < XDR::Struct + attribute :field1, XDR::Int + attribute :field2, MyEnum + attribute :field3, XDR::UnsignedInt +end diff --git a/spec/output/generator_spec_ruby/ifdef.x/variant_struct.rb b/spec/output/generator_spec_ruby/ifdef.x/variant_struct.rb new file mode 100644 index 000000000..1a33f1d73 --- /dev/null +++ b/spec/output/generator_spec_ruby/ifdef.x/variant_struct.rb @@ -0,0 +1,15 @@ +# This code was automatically generated using xdrgen +# DO NOT EDIT or your changes may be overwritten + +require 'xdr' + +# === xdr source ============================================================ +# +# struct VariantStruct { +# int newField; +# }; +# +# =========================================================================== +class VariantStruct < XDR::Struct + attribute :new_field, XDR::Int +end