6 use File::Path qw(make_path);
9 use List::Util qw(first);
14 'studly-caps' => sub { my $s = shift; $s =~ s/(?:^|_)(.)/\U$1/g; return $s; },
15 'camel-case' => sub { my $s = shift; $s =~ s/(?:_)(.)/\U$1/g; return $s; },
16 'upper-leadin' => sub { my $s = shift; $s =~ s/^([^_]+)/\U$1/; return $s; },
17 'identity' => sub { return $_[0]; },
22 $s =~ s/u32:|u64:/p/g;
23 $s =~ s/\$\{([^\}]+)\}/$1/g;
32 'struct:<default>' => {
35 options => [ 'default=none', 'access=rw', 'field:rename=studly-caps' ],
36 regex => qr/^struct:<default>$/,
39 'union:<default>' => {
42 options => [ 'default=all', 'access=rw', 'field:rename=studly-caps' ],
43 regex => qr/^union:<default>$/,
49 options => [ 'call:rename=call', 'access=r' ],
50 regex => qr/^call:<default>$/,
57 regex => qr/^func:<default>$/,
64 regex => qr/^enum:<default>$/,
80 my $conf = new config($vars, $file);
81 $self->{api} = $conf->{objects};
82 foreach my $obj (@{$self->{api}}) {
83 $self->{index}->{"$obj->{type}:$obj->{name}"} = $obj;
85 foreach my $k (keys %defaultTable) {
86 if (!defined($self->{index}->{$k})) {
87 $self->{index}->{$k} = $defaultTable{$k};
88 push @{$self->{api}}, $defaultTable{$k};
94 my $info = loadAPIFile($name);
96 $self->{data} = { %{$self->{data}}, %{$info} };
99 foreach my $p (@{$conf->{pragmas}}) {
100 if ($p->[0] eq '%require') {
108 # add phantom 'api' entries for anything else required
109 foreach my $s (findDependencies($self)) {
110 my $n = "$s->{type}:$s->{name}";
111 my $def = $self->{index}->{"$s->{type}:<default>"};
113 die "no default for implicit dependency $n" if (!$def);
117 match => "$s->{type}:$s->{name}",
121 $obj->{rename} = $obj->{"$obj->{type}:rename"} ? $obj->{"$obj->{type}:rename"}->($obj->{name}) : $obj->{name};
123 print " implicit $n\n";
124 push @{$self->{api}}, $obj;
125 $self->{index}->{$n} = $obj;
131 # TODO: could match the regexes to every possible type in the api and create a direct index
133 foreach my $obj (grep { $_->{type} eq 'type' && $_->{name} =~ m@<.*>@ } @{$self->{api}}) {
134 $copyIndex->{$obj->{name}} = $obj;
137 foreach my $obj (grep { $_->{type} eq 'type' && $_->{name} =~ m@^/.*/$@ } @{$self->{api}}) {
138 push @{$self->{types}}, initType($self, $copyIndex, $obj);
146 # class.name to class/name.java
151 $name = $api->{vars}->{package}.'.'.$name;
153 $name = $api->{vars}->{output}.'/'.$name.'.java';
161 my $path = $api->classToPath($name);
164 rename($path.'~', $path) || die ("rename failed: $!");
170 my $path = $api->classToPath($name);
171 my $dir = dirname($path);
173 make_path($dir) if (!-d $dir);
175 open(my $f, ">", $path.'~') || die ("Cannot open '$path' for writing");
176 print "writing '$path'\n";
192 $type->{options} = [ @{$obj->{options}} ];
194 # FIXME: per-item options, set etc?
195 foreach my $inc (@{$obj->{items}}) {
196 $type->{items}->{$inc->{match}} = $inc->{literal};
199 my $v = optionValue('select', undef, $obj);
200 $type->{select} = $v if defined($v);
202 #print "init $obj->{name}\n";
203 foreach my $c (split /,/,optionValue('copy', undef, $obj)) {
204 my $copy = $index->{$c};
208 my $proto = initType($api, $index, $copy);
210 foreach my $k (keys %{$proto->{items}}) {
211 $type->{items}->{$k} = $proto->{items}->{$k} if !defined($type->{items}->{$k});
214 push @{$type->{options}}, @{$proto->{options}};
216 die ("type copy target $c not found");
220 $type->{regex} = qr/$1/ if $obj->{name} =~ m@/(.*)/@;
225 # returns { type=>$type, match=>$match)
226 # match is named groups from regex (%+)
230 my $deref = $m->{deref};
233 foreach my $type (@{$api->{types}}) {
234 my $select = !defined($type->{select}) || defined($m->{$type->{select}});
236 if ($select && ($deref =~ m/$type->{regex}/)) {
239 return { deref=>$deref, type=>$type, match=>\%match };
242 die ("cannot find matching type '$deref' for member $m->{name}");
246 # find value of first option of the given name
251 #print "optionValue $name\n";
252 foreach my $obj (@_) {
253 foreach my $opt (@{$obj->{options}}) {
254 #print "? $name $opt = ".($opt =~m/^\Q$name\E=(.*)$/)."\n";
255 #print " = $opt\n" if ($opt =~m/^\Q$name\E=(.*)$/);
256 return $1 if ($opt =~m/^\Q$name\E=(.*)$/);
262 # look for all matching options of the given name
263 # multiple objects are searched, the first one with
264 # the given parameter overrides the rest.
265 # name, object, object *
271 foreach my $obj (@_) {
272 foreach my $opt (@{$obj->{options}}) {
273 push @list, $1 if ($opt =~m/^$rx=(.*)$/);
275 last if ($#list >= 0);
280 # same as above but doesn't short-circuit
281 sub optionValuesAll {
286 foreach my $obj (@_) {
287 foreach my $opt (@{$obj->{options}}) {
288 push @list, $1 if ($opt =~m/^$rx=(.*)$/);
294 # find first occurance of a flag
298 foreach my $obj (@_) {
299 foreach my $opt (@{$obj->{options}}) {
300 return 1 if ($opt eq $name);
310 foreach my $i (@{$s->{items}}) {
311 return $i if $i->{match} eq $name;
325 my @all = @{$s->{items}};
328 foreach my $m (@all) {
329 $index{$m->{name}} = $m;
332 foreach my $inc (@{$obj->{items}}) {
333 my $d = $index{$inc->{match}};
336 next if $visited{$d->{type}.':'.$d->{name}}++;
337 push @fields, [ $inc, $d ];
339 foreach my $d (grep { $_->{name} =~ m/$inc->{regex}/ } @all) {
340 next if $visited{$d->{type}.':'.$d->{name}}++;
341 push @fields, [ $inc, $d ];
346 if (optionValue('default', undef, $obj, $api->{index}->{'struct:<default>'}) eq 'all') {
347 #print "* add all items\n";
348 foreach my $d (@all) {
349 next if $visited{$d->{type}.':'.$d->{name}}++;
350 push @fields, [ $obj, $d ];
361 foreach my $i (@{$s->{items}}) {
362 return $i if $i->{name} eq $name;
367 # ######################################################################
369 sub addDependencies {
375 #print "add deps for '$s->{name}'\n";
376 if ($s->{type} =~ m/^(struct|union)$/n) {
377 # include embedded structures always
378 foreach my $d (grep { $_->{type} =~ m/^(struct|union):/ && $_->{deref} =~ m/\[\d+\$\{|^\$\{/ } @{$s->{items}}) {
379 #print " embedded $d->{name} $d->{deref}\n";
383 # include selected fields optionally
385 foreach my $i (findAllItems($api, $obj, $s)) {
386 my ($inc, $d) = @{$i};
387 #print " selected $d->{name} $d->{type} $d->{deref}\n";
388 $add->($d->{type}) if ($d->{type} =~ m/^(struct|union|func|call|enum):/);
389 # HACK: enum types are integers but ctype includes the actual type
390 $add->("enum:$1") if ($d->{ctype} =~ m/^enum (.+)/);
393 } elsif ($s->{type} =~ m/^(call|func)/n) {
394 # for calls/func need all fields
395 foreach my $d (grep { $_->{type} =~ m/^(struct|union|func|call|enum):/ } @{$s->{items}}, $s->{result}) {
396 #print " argument $d->{name} $d->{type} $d->{deref}\n";
402 # use either {match} or {regex} to get all matches for a data type
407 my $data = $api->{data};
409 if ($inc->{match} eq 'func:<matcher>') {
410 # or just last option?
413 if (defined($inc->{literal})) {
414 $code = 'sub { '.$inc->{literal}.' }';
416 my @options = @{$inc->{options}};
417 $code = 'sub { '.$options[$#options].'(@_) }';
420 my $match = eval $code;
422 if (!defined($match)) {
423 die "unable to parse match function $inc->{match} $! $@";
425 grep { $match->($_, $ctx) } grep { $_->{type} eq $inc->{type} } values %$data;
427 my $s = $data->{$inc->{match}};
432 map { $data->{$_} } grep { $_ =~ m/$inc->{regex}/ } keys %$data;
437 # find all extra types used by the api requested
438 sub findDependencies {
440 my %data = %{$api->{data}};
443 my $setdeps = sub { my $d = shift; $deps{$d} = 1; };
445 print "Root types\n";
446 foreach my $obj (@{$api->{api}}) {
447 if ($obj->{type} eq 'library') {
448 foreach my $inc (@{$obj->{items}}) {
449 next if ($inc->{type} eq 'library');
451 #print "? $inc->{regex}\n";
452 foreach my $s (findMatches($api, $inc, $obj)) {
453 my $n = "$s->{type}:$s->{name}";
459 addDependencies($api, $obj, $s, $setdeps);
462 } elsif ($obj->{type} =~ m/^(struct|union|call|func|enum|define)$/) {
463 #foreach my $n (grep { $_ =~ m/$obj->{regex}/ } keys %data) {
464 foreach my $s (findMatches($api, $obj, $obj)) {
465 my $n = "$s->{type}:$s->{name}";
469 addDependencies($api, $obj, $s, $setdeps);
474 # at this point 'seen' contains everything explicitly requested
475 # and deps is anything else they need but not referenced directly
476 # recursively grab anything else
479 my @stack = sort keys %deps;
480 my $pushstack = sub { my $d = shift; push @stack, $d; };
481 while ($#stack >= 0) {
482 my $n = shift @stack;
490 print "Add referent: $n\n";
492 addDependencies($api, $api->{index}->{"$s->{type}:<default>"}, $s, $pushstack);
493 } elsif ($n =~ m/^(.*):(.*)$/) {
494 print "Add anonymous: $n\n";
495 # type not know, add anonymous
503 $api->{data}->{$n} = $s;
506 # maybe it should have some skeleton metadata?
507 # depends on where it's used i suppose
511 print "Added ".($#list+1)." dependencies\n";
515 # ######################################################################
521 unless ($info = do $file) {
522 die "couldn't parse $file: $@" if $@;
523 die "couldn't import $file: $!" unless defined $info;
524 die "couldn't run $file" unless $info;
532 my $rename = $renameTable{'identity'};
534 foreach my $n (split /,/,$how) {
536 my $new = $renameTable{$n};
538 if ($n =~ m@^s/(.*)/(.*)/$@) {
541 $rename = sub { my $s=shift; $s = $old->($s); $s =~ s/$rx/$rp/; return $s;};
543 $rename = sub { my $s=shift; $s = $old->($s); return $new->($s); };
546 $rename = sub { return $x; };
556 # Find any anonymous types and add them in
558 foreach my $s (values %{$api->{data}}) {
559 # FIXME: fix the list names in export.cc and export-defines
560 $s->{items} = $s->{fields} if $s->{fields};
561 $s->{items} = $s->{arguments} if $s->{arguments};
562 $s->{items} = $s->{values} if $s->{values};
564 foreach my $m (grep { $_->{type} =~ m/struct:|union:/} @{$s->{items}}) {
565 $anonymous{$m->{type}} = 1 if !defined($api->{data}->{$m->{type}});
569 $s->{result}->{name} = 'result$' if $s->{type} =~ m/func|call/;
571 # add a canonical deref for all types
572 if ($s->{type} =~ m/func|call|struct|union/) {
573 foreach my $m (grep { !defined($_->{deref}) } @{$s->{items}}, ($s->{type} =~ m/func|call/) ? $s->{result} : ()) {
574 if ($m->{type} =~ m/^(union|struct):(.*)/) {
575 $m->{deref} = "\${$2}";
576 } elsif ($m->{ctype} eq 'bitfield') {
577 $m->{deref} = "bitfield";
579 $m->{deref} = $m->{type};
584 # all 'defines' are output by default
585 $s->{output} = 1 if $s->{type} eq 'define';
588 foreach my $k (sort keys %anonymous) {
590 if ($k =~ m/^(.*):(.*)$/) {
591 $api->{data}->{$k} = {
605 # Note that type:name regexes always start at the beginning
607 foreach my $obj (@{$api->{api}}) {
608 if ($obj->{name} =~ m@^/(.*)/$@) {
609 $obj->{regex} = qr/^$obj->{type}:$1/;
611 $obj->{regex} = qr/^$obj->{type}:$obj->{name}$/;
613 $obj->{match} = "$obj->{type}:$obj->{name}";
615 foreach my $opt (@{$obj->{options}}) {
616 if ($opt =~ m/^(.+:rename)=(.*)$/) {
617 $obj->{$1} = parseRename($2);
618 } elsif ($opt =~ m/^rename=(.*)$/) {
619 $obj->{"$obj->{type}:rename"} = parseRename($1);
623 my $defmode = ($obj->{type} eq 'library' ? 'func' : 'field');
624 foreach my $inc (@{$obj->{items}}) {
625 my $match = $inc->{match};
628 if ($inc->{match} =~ m/^(.*):(.*)$/) {
633 $inc->{type} = $mode;
634 if ($match =~ m@^/(.*)/$@) {
635 $inc->{regex} = $mode ne 'field' ? qr/$mode:$1/ : qr/$1/;
637 $inc->{regex} = $mode ne 'field' ? qr/^$mode:$match$/ : qr/^$match$/;
640 foreach my $opt (@{$inc->{options}}) {
641 if ($opt =~ m/^rename=(.*)$/) {
642 #print "option $opt ".Dumper($inc);
643 $inc->{"$mode:rename"} = parseRename($1);
647 $inc->{"$mode:rename"} = $obj->{"$mode:rename"} if (!(defined($inc->{"$mode:rename"})) && defined($obj->{"$mode:rename"}));
653 # 'lib/struct/func' level 'lib.inc' level 'func/struct.inc' level setting
654 # array:name1 array:name1 array char* is array, void* is a Segment
655 # array-size:name1=name2 array-size:name1=name2 array-size=name2 implied array size from name2
656 # implied:name1=expr implied:name1=expr implied=expr value of name1 is calculated from other arguments
657 # instance:name1 instance:name1 instance parameter/return is instance, implies non-static function
659 # scope:name1=scope[,close] scope:name1=scope[,close] scope=scope[,close] parameter/return is a new instance with given scope
660 # success:name1=values success:name1=values success=values which parameter and values indicate integer success
661 # pointer types are considered in/out and implied
662 # success:name1=!null success:name1=!null success=!null which parameter and values indicate non-null success
663 # return:name1 return:name1 return return this output argument instead of the return code
665 # access:name1=rwi access:name1=rwi access=rwi set access type, read, write, indexed
666 # access=rwi access=rwi access=rwi - can also be set as default
668 # onsuccess=blah onsuccess=blah onsuccess=blah
670 # 'name' is parameter name, or parameter index, or result$ for return value
671 # scope is instance for instance-scope, explicit for explicit scope, global for global scope. default is global.
673 # err, maybe this format makes more sense:
674 # name1:success=values name1:success=values success=values
675 # name1:instance name1:instance instance
694 my @itemAnyOptions = (
698 # process struct->field
699 # api, obj (struct, func), inc (field entry), s (struct), m (struct field)
700 # Note: keep in sync with processFunc
707 my $def = $api->{index}->{"$s->{type}:<default>"};
709 #print "process $s->{type}:$s->{name}.$m->{name}\n";
710 #print " $m->{name}\n";
712 foreach my $flag (@itemFlags) {
713 my $value = optionFlag("$flag", $inc);
714 $value = optionFlag("$flag:$m->{name}", $obj, $def) if !defined($value);
715 $m->{$flag} = $value if (defined($value));
717 foreach my $option (@itemOptions) {
718 my $value = optionValue("$option", undef, $inc);
719 $value = optionValue("$option:$m->{name}", undef, $obj, $def) if !defined($value);
720 $m->{$option} = $value if (defined($value));
722 foreach my $option (@itemAnyOptions) {
723 my $value = optionValue("$option", undef, $inc);
724 $value = optionValue("$option:$m->{name}", undef, $obj, $def) if !defined($value);
725 $value = optionValue("$option", undef, $obj, $def) if !defined($value);
726 $m->{$option} = $value if (defined($value));
730 $m->{rename} = (first { defined $_ } $inc->{'field:rename'}, $obj->{'field:rename'}, $def->{'field:rename'}, $renameTable{identity})->($m->{name});
733 # process func or call main type
734 sub processTypeFunc {
738 my $def = $api->{index}->{"$obj->{type}:<default>"};
743 if ($seen->{"$s->{type}:$s->{name}"}++) { print "warning: seen $s->{type}:$s->{name}\n"; next; }
745 print " $s->{name}\n" if ($api->{vars}->{verbose} > 1);
747 foreach my $m ($s->{result}, @{$s->{items}}) {
748 my $inc = findItem($obj, $m->{name});
750 processField($api, $obj, $inc, $s, $m);
753 $s->{rename} = (first { defined $_ } $obj->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name});
754 $s->{access} = optionValue('access', '', $obj, $def);
755 $v = optionValue('onsuccess', undef, $obj, $def);
756 $s->{onsuccess} = $v if defined $v;
762 # process library->func
763 # process struct->func
764 # api, obj (library, struct), inc (func entry), s ($data->{func:name})
765 # Note: keep in sync with processField
772 my $def = $api->{index}->{"$s->{type}:<default>"};
775 print "process $s->{type}:$s->{name}\n" if ($api->{vars}->{verbose} > 1);
777 foreach my $m (defined($s->{result}) ? $s->{result} : (), @{$s->{items}}) {
778 #print " $m->{name}\n";
780 foreach my $flag (@itemFlags) {
781 my $value = optionFlag("$flag:$m->{name}", $inc, $obj, $def);
782 $value = optionFlag("$flag:$index", $inc, $obj, $def) if !defined($value);
783 $m->{$flag} = $value if (defined($value));
785 foreach my $option (@itemOptions) {
786 my $value = optionValue("$option:$m->{name}", undef, $inc, $obj, $def);
787 $value = optionValue("$option:$index", undef, $inc, $obj, $def) if !defined($value);
788 $m->{$option} = $value if (defined($value));
790 foreach my $option (@itemAnyOptions) {
791 my $value = optionValue("$option:$m->{name}", undef, $inc, $obj, $def);
792 $value = optionValue("$option:$index", undef, $inc, $obj, $def) if !defined($value);
793 $value = optionValue("$option", undef, $inc, $obj, $def) if !defined($value);
794 $m->{$option} = $value if (defined($value));
800 $s->{rename} = (first { defined $_ } $inc->{"$s->{type}:rename"}, $obj->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name});
801 $s->{access} = optionValue('access', '', $inc, $obj, $def);
802 $v = optionValue('onsuccess', undef, $inc, $obj, $def);
803 $s->{onsuccess} = $v if defined ($v);
808 # finally link up array sizes and work out what is output or not
809 # TODO: struct field array-size should be output but not included in constructors?
810 sub postProcessType {
814 foreach my $m (defined($s->{result}) ? $s->{result} : (), @{$s->{items}}) {
815 $static = 0 if ($m->{instance});
817 # FIXME: collect multiple scopes here
818 $s->{scope} = 'explicit' if ($m->{scope} =~ m/^explicit$|^explicit,(.*)$/);
819 $s->{scope} = 'global' if ($m->{scope} eq 'global');
820 $s->{scope} = 'object' if ($m->{scope} eq 'object');
822 if ($m->{'array-size'}) {
823 my $size = findField($s, $m->{'array-size'});
825 print Dumper($s) if (!defined($size));
826 die "can't find array-size=$m->{'array-size'}" if !defined($size);
828 $size->{output} = 0 if ($s->{type} =~ m/func|call/);
829 $size->{'array-size-source'} = $m;
830 $m->{'array-size'} = $size;
833 # no java arg for instance parameter
834 $m->{output} = 0 if ($m->{instance});
835 # don't generate java args for values calculated
836 $m->{output} = 0 if (defined($m->{implied}));
837 # don't generate java args for return statuss unless they're also the constructed value
838 $m->{output} = 0 if (defined($m->{success}) && !$m->{scope});
839 # don't generate java args for output arguments
840 $m->{output} = 0 if ($m->{scope} && $m->{name} ne 'result$');
842 # link success/return fields to struct/func
843 $s->{success} = $m if defined($m->{success});
844 $s->{return} = $m if ($m->{return});
847 $s->{static} = $static;
849 # TODO: default scope? or from struct:<default> etc?
852 # transfer info from {api} to {data}
861 next if $seen->{$m->{name}}++;
863 processField($api, $obj, $inc, $s, $m);
867 # process a struct/union
868 # these have fields as well as potentially other included types
873 my $def = $api->{index}->{"$obj->{type}:<default>"};
878 next if ($seen->{"$s->{type}:$s->{name}"}++);
880 print "process type $s->{type}:$s->{name}\n" if ($api->{vars}->{verbose} > 1);
882 # process the struct/union fields first
883 foreach my $inc (grep { $_->{type} eq 'field' } @{$obj->{items}}) {
884 my @list = grep { $_->{name} =~ m/$inc->{regex}/ } @{$s->{items}};
886 processFields($api, $memberseen, $obj, $inc, $s, @list);
889 if (optionValue('default', undef, $obj, $def) eq 'all') {
890 print " + adding all fields\n" if ($api->{vars}->{verbose} > 1);
891 processFields($api, $memberseen, $obj, undef, $s, @{$s->{items}});
894 $s->{rename} = (first { defined $_ } $obj->{"$s->{type}:rename"}, $def->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name});
899 # handle other types included/mark them no-output
900 $seen->{"$s->{type}:$s->{name}"} = 0;
901 processLibrary($api, $seen, $obj, $s);
911 my $data = $api->{data};
913 return if ($seen->{"$lib->{type}:$lib->{name}"}++);
915 print "process library $lib->{type}:$lib->{name}\n";
916 #print 'lib='.Dumper($lib);
917 #print 'lib.options='.Dumper($lib->{options});
919 # TODO: embedded types
921 foreach my $inc (@{$lib->{items}}) {
922 print " $inc->{match}\n" if ($api->{vars}->{verbose} > 1);
924 if ($inc->{type} =~ m/func|call/on) {
925 my @list = findMatches($api, $inc, $ctx);
927 #print 'inc='.Dumper($inc);
928 #print "match $inc->{regex} .options=".Dumper($inc->{options});
930 foreach my $s (@list) {
931 if ($seen->{"$s->{type}:$s->{name}"}++) { print "warning: seen $s->{type}:$s->{name}\n"; next; }
933 #print " $s->{name}\n";
934 processFunc($api, $lib, $inc, $s);
936 #print 'func='.Dumper($s);
938 } elsif ($inc->{type} eq 'library') {
939 foreach my $l (grep { "$_->{type}:$_->{name}" =~ m/$inc->{regex}/ } @{$api->{api}}) {
940 # included libraries are never output directly
942 processLibrary($api, $seen, $l, $ctx);
944 } elsif ($inc->{type} =~ m/define|enum/on) {
945 # suppress direct output of anything included
946 foreach my $c (findMatches($api, $inc, $ctx)) {
956 my %data = %{$api->{data}};
958 # apply requested options to specific objects (not defaults)
959 foreach my $obj (grep {$_->{type} =~ m/^(func|call|struct|union)$/} @{$api->{api}}) {
960 my @list = findMatches($api, $obj, $obj);
962 if ($obj->{type} =~ m/func|call/) {
963 processTypeFunc($api, $seen, $obj, @list);
965 processType($api, $seen, $obj, @list);
970 foreach my $lib (grep {$_->{type} eq 'library'} @{$api->{api}}) {
971 next if defined($lib->{output});
973 processLibrary($api, $seen, $lib, $lib);
976 # apply options for default object types
977 foreach my $obj (grep {$_->{name} eq '<default>'} @{$api->{api}}) {
980 @list = grep { !defined($seen->{"$_->{type}:$_->{name}"}) && $_->{type} eq $obj->{type} } values %data;
982 print "apply $obj->{type}:$obj->{name} to ".($#list + 1)." objects\n" if ($#list >= 0);
984 if ($obj->{type} =~ m/func|call/) {
985 processTypeFunc($api, $seen, $obj, @list);
987 processType($api, $seen, $obj, @list);