rubycc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +56 -0
- data/LICENSE.txt +21 -0
- data/NOTICE +52 -0
- data/README.md +208 -0
- data/data/README.md +117 -0
- data/data/r10_corpus_scan.json +4082 -0
- data/data/r10_manual_classification.json +5342 -0
- data/data/r10_verification_m4a.json +1531 -0
- data/data/r10_verification_m4b.json +1818 -0
- data/data/r10_verification_m4c.json +1489 -0
- data/data/r10_verification_m4d.json +318 -0
- data/data/r10_verification_rbs.json +192 -0
- data/data/verified_gems.json +397 -0
- data/exe/rmake +16 -0
- data/exe/rubycc +9 -0
- data/exe/rubycc-ar +114 -0
- data/exe/rubycc-doctor +14 -0
- data/exe/rubycc-pkgconf +8 -0
- data/include/float.h +87 -0
- data/include/iso646.h +18 -0
- data/include/libc/alloca.h +18 -0
- data/include/libc/arpa/inet.h +61 -0
- data/include/libc/assert.h +43 -0
- data/include/libc/dirent.h +72 -0
- data/include/libc/dlfcn.h +58 -0
- data/include/libc/features.h +205 -0
- data/include/libc/glibc/aarch64/ctype.h +108 -0
- data/include/libc/glibc/aarch64/endian.h +54 -0
- data/include/libc/glibc/aarch64/errno.h +154 -0
- data/include/libc/glibc/aarch64/fcntl.h +144 -0
- data/include/libc/glibc/aarch64/inttypes.h +182 -0
- data/include/libc/glibc/aarch64/limits.h +63 -0
- data/include/libc/glibc/aarch64/pthread.h +131 -0
- data/include/libc/glibc/aarch64/setjmp.h +84 -0
- data/include/libc/glibc/aarch64/stdint.h +174 -0
- data/include/libc/glibc/aarch64/sys/epoll.h +88 -0
- data/include/libc/glibc/aarch64/sys/fcntl.h +19 -0
- data/include/libc/glibc/aarch64/sys/select.h +75 -0
- data/include/libc/glibc/aarch64/sys/stat.h +136 -0
- data/include/libc/glibc/aarch64/sys/syscall.h +167 -0
- data/include/libc/glibc/aarch64/sys/time.h +74 -0
- data/include/libc/glibc/aarch64/sys/types.h +127 -0
- data/include/libc/glibc/aarch64/time.h +113 -0
- data/include/libc/glibc/x86_64/ctype.h +108 -0
- data/include/libc/glibc/x86_64/endian.h +54 -0
- data/include/libc/glibc/x86_64/errno.h +154 -0
- data/include/libc/glibc/x86_64/fcntl.h +134 -0
- data/include/libc/glibc/x86_64/inttypes.h +182 -0
- data/include/libc/glibc/x86_64/limits.h +61 -0
- data/include/libc/glibc/x86_64/pthread.h +111 -0
- data/include/libc/glibc/x86_64/setjmp.h +82 -0
- data/include/libc/glibc/x86_64/stdint.h +163 -0
- data/include/libc/glibc/x86_64/sys/epoll.h +91 -0
- data/include/libc/glibc/x86_64/sys/fcntl.h +19 -0
- data/include/libc/glibc/x86_64/sys/select.h +75 -0
- data/include/libc/glibc/x86_64/sys/stat.h +130 -0
- data/include/libc/glibc/x86_64/sys/syscall.h +188 -0
- data/include/libc/glibc/x86_64/sys/time.h +74 -0
- data/include/libc/glibc/x86_64/sys/types.h +123 -0
- data/include/libc/glibc/x86_64/time.h +113 -0
- data/include/libc/grp.h +50 -0
- data/include/libc/langinfo.h +126 -0
- data/include/libc/link.h +18 -0
- data/include/libc/locale.h +83 -0
- data/include/libc/math.h +191 -0
- data/include/libc/netinet/in.h +126 -0
- data/include/libc/netinet/tcp.h +44 -0
- data/include/libc/poll.h +46 -0
- data/include/libc/pwd.h +58 -0
- data/include/libc/regex.h +53 -0
- data/include/libc/sched.h +35 -0
- data/include/libc/signal.h +204 -0
- data/include/libc/stdio.h +157 -0
- data/include/libc/stdlib.h +92 -0
- data/include/libc/string.h +74 -0
- data/include/libc/strings.h +25 -0
- data/include/libc/sys/cdefs.h +126 -0
- data/include/libc/sys/inotify.h +111 -0
- data/include/libc/sys/ioctl.h +36 -0
- data/include/libc/sys/mman.h +65 -0
- data/include/libc/sys/param.h +41 -0
- data/include/libc/sys/resource.h +109 -0
- data/include/libc/sys/socket.h +167 -0
- data/include/libc/sys/statfs.h +82 -0
- data/include/libc/sys/timerfd.h +55 -0
- data/include/libc/sys/uio.h +40 -0
- data/include/libc/sys/un.h +25 -0
- data/include/libc/sys/utsname.h +35 -0
- data/include/libc/sys/wait.h +135 -0
- data/include/libc/termios.h +179 -0
- data/include/libc/unistd.h +194 -0
- data/include/stdalign.h +16 -0
- data/include/stdarg.h +31 -0
- data/include/stdatomic.h +158 -0
- data/include/stdbool.h +15 -0
- data/include/stdckdint.h +28 -0
- data/include/stddef.h +60 -0
- data/include/stdnoreturn.h +18 -0
- data/include/x86intrin.h +16 -0
- data/lib/rubycc/backend/aarch64.rb +1724 -0
- data/lib/rubycc/backend/x86_64.rb +1369 -0
- data/lib/rubycc/compile_error.rb +36 -0
- data/lib/rubycc/compiler.rb +305 -0
- data/lib/rubycc/doctor/builder.rb +151 -0
- data/lib/rubycc/doctor/cli.rb +190 -0
- data/lib/rubycc/doctor/fetcher.rb +84 -0
- data/lib/rubycc/doctor/gemfile.rb +115 -0
- data/lib/rubycc/doctor/verified_gems.rb +114 -0
- data/lib/rubycc/doctor.rb +16 -0
- data/lib/rubycc/driver.rb +463 -0
- data/lib/rubycc/front/ast.rb +528 -0
- data/lib/rubycc/front/constant_evaluator.rb +631 -0
- data/lib/rubycc/front/initializer_resolver.rb +592 -0
- data/lib/rubycc/front/lexeme_reader.rb +460 -0
- data/lib/rubycc/front/lexer.rb +232 -0
- data/lib/rubycc/front/parser.rb +4122 -0
- data/lib/rubycc/front/token.rb +49 -0
- data/lib/rubycc/ir/call_convention.rb +486 -0
- data/lib/rubycc/ir/generator.rb +6036 -0
- data/lib/rubycc/ir/ir.rb +417 -0
- data/lib/rubycc/link/compat_runtime.rb +89 -0
- data/lib/rubycc/link/errors.rb +13 -0
- data/lib/rubycc/link/executable_linker.rb +405 -0
- data/lib/rubycc/link/library_resolver.rb +437 -0
- data/lib/rubycc/link/partial_linker.rb +546 -0
- data/lib/rubycc/link/shared_linker.rb +1732 -0
- data/lib/rubycc/mkmf_shim.rb +83 -0
- data/lib/rubycc/objfile/ar_archive.rb +437 -0
- data/lib/rubycc/objfile/elf_reader.rb +646 -0
- data/lib/rubycc/objfile/elf_writer.rb +891 -0
- data/lib/rubycc/objfile/relocatable_writer.rb +376 -0
- data/lib/rubycc/pkgconf/cli.rb +139 -0
- data/lib/rubycc/pkgconf/errors.rb +39 -0
- data/lib/rubycc/pkgconf/model.rb +28 -0
- data/lib/rubycc/pkgconf/parser.rb +109 -0
- data/lib/rubycc/pkgconf/pkgconf.rb +15 -0
- data/lib/rubycc/pkgconf/resolver.rb +70 -0
- data/lib/rubycc/pkgconf/search_path.rb +38 -0
- data/lib/rubycc/pkgconf/system_path_filter.rb +131 -0
- data/lib/rubycc/preprocess/constant_expression.rb +219 -0
- data/lib/rubycc/preprocess/glibc_version.rb +124 -0
- data/lib/rubycc/preprocess/pp_token.rb +62 -0
- data/lib/rubycc/preprocess/preprocessor.rb +2020 -0
- data/lib/rubycc/preprocess/scanner.rb +290 -0
- data/lib/rubycc/preprocess/token_converter.rb +157 -0
- data/lib/rubycc/rmake/cli.rb +143 -0
- data/lib/rubycc/rmake/errors.rb +71 -0
- data/lib/rubycc/rmake/executor.rb +818 -0
- data/lib/rubycc/rmake/expander.rb +251 -0
- data/lib/rubycc/rmake/makefile.rb +352 -0
- data/lib/rubycc/rmake/model.rb +137 -0
- data/lib/rubycc/rmake/parser.rb +226 -0
- data/lib/rubycc/rmake/rmake.rb +15 -0
- data/lib/rubycc/type.rb +1236 -0
- data/lib/rubycc/version.rb +5 -0
- data/lib/rubycc.rb +36 -0
- data/lib/rubygems_plugin.rb +102 -0
- metadata +219 -0
|
@@ -0,0 +1,2020 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
require_relative "scanner"
|
|
5
|
+
require_relative "token_converter"
|
|
6
|
+
require_relative "constant_expression"
|
|
7
|
+
require_relative "glibc_version"
|
|
8
|
+
require_relative "../front/constant_evaluator"
|
|
9
|
+
require_relative "../compile_error"
|
|
10
|
+
|
|
11
|
+
module Rubycc
|
|
12
|
+
module Preprocess
|
|
13
|
+
# Entry point for translation phases 1-4: source text in, a Front::Token
|
|
14
|
+
# stream out. It scans preprocessing tokens (line splicing and comment
|
|
15
|
+
# removal), executes directives, expands macros, and converts what remains
|
|
16
|
+
# into ordinary tokens.
|
|
17
|
+
#
|
|
18
|
+
# The macro table and the #include search path are instance state so an
|
|
19
|
+
# includee shares them with its includer: a translation unit is preprocessed
|
|
20
|
+
# as a single accumulating pass, with #include recursion feeding an includee's
|
|
21
|
+
# tokens through the same line-oriented loop. Conditional groups (#if and its
|
|
22
|
+
# kin) select which lines reach that loop. Both object-like and function-like
|
|
23
|
+
# macros are expanded (6.10.3), the latter gathering their arguments across
|
|
24
|
+
# line boundaries; the "#" (stringize) and "##" (paste) operators, the
|
|
25
|
+
# compiler-supplied macros (__FILE__ and kin), the __has_* queries and
|
|
26
|
+
# "#pragma once" complete the model.
|
|
27
|
+
class Preprocessor
|
|
28
|
+
# The compiler-supplied ("freestanding") headers rubycc ships in the gem's
|
|
29
|
+
# top-level include/ directory: stdarg.h, stddef.h and kin, which glibc
|
|
30
|
+
# does not provide because they are the compiler's responsibility. Resolved
|
|
31
|
+
# relative to this file so it works from a source checkout and an installed
|
|
32
|
+
# gem alike (preprocess -> rubycc -> lib -> gem root, then include/).
|
|
33
|
+
BUNDLED_INCLUDE_DIR = File.expand_path("../../../include", __dir__).freeze
|
|
34
|
+
|
|
35
|
+
# The bundled libc compatibility headers (R8): rubycc's own copies of the C
|
|
36
|
+
# library's headers (stdio.h, stdlib.h, string.h and kin), shipped so a
|
|
37
|
+
# hosted translation unit compiles on a host that lacks the system libc's
|
|
38
|
+
# development headers (the distroless target). They live in two layers under
|
|
39
|
+
# include/libc/: a common declaration layer (BUNDLED_LIBC_INCLUDE_DIR) and a
|
|
40
|
+
# target-specific layer that pins the type widths, struct layouts and macro
|
|
41
|
+
# values to one concrete libc-and-arch ABI. Two such layers ship today,
|
|
42
|
+
# glibc/x86-64 and glibc/aarch64; which one is on the search path is chosen
|
|
43
|
+
# per instance by the `libc_arch` keyword (see #initialize), so a cross
|
|
44
|
+
# compile reads the target's ABI rather than the host's. This constant names
|
|
45
|
+
# the x86-64 default's directory -- kept so the default path and the tests
|
|
46
|
+
# that pin its order can refer to it -- while #default_system_include_paths
|
|
47
|
+
# substitutes the instance's own arch directory (@libc_arch_include_dir). The
|
|
48
|
+
# arch layer is searched before the common layer so a same-named header in it
|
|
49
|
+
# (an ABI-specific override) wins over the shared declaration.
|
|
50
|
+
# The libc the headers describe is *not* a directory axis: both layers carry
|
|
51
|
+
# the glibc and the musl value of everything the two disagree on, selected
|
|
52
|
+
# by #if on LIBC_MUSL_MACRO (see LIBCS), so the directory names below still
|
|
53
|
+
# say "glibc" only because that is where the files have always lived.
|
|
54
|
+
BUNDLED_LIBC_ARCH_INCLUDE_DIR = File.expand_path("../../../include/libc/glibc/x86_64", __dir__).freeze
|
|
55
|
+
BUNDLED_LIBC_INCLUDE_DIR = File.expand_path("../../../include/libc", __dir__).freeze
|
|
56
|
+
|
|
57
|
+
# The bundled libc-and-arch layers that ship under include/libc/glibc/;
|
|
58
|
+
# `libc_arch` (see #initialize) must name one of them.
|
|
59
|
+
LIBC_ARCHS = %w[x86_64 aarch64].freeze
|
|
60
|
+
|
|
61
|
+
# The C libraries the bundled headers can be read under; `libc` (see
|
|
62
|
+
# #initialize) must name one of them. This is an axis of its own, at right
|
|
63
|
+
# angles to `libc_arch`: the two libcs disagree on a handful of ABI values
|
|
64
|
+
# (struct rusage's size, O_ACCMODE, the fast-integer widths, BUFSIZ and
|
|
65
|
+
# kin) on every machine alike, so the difference is expressed as #if
|
|
66
|
+
# branches inside the bundled headers rather than as another directory
|
|
67
|
+
# layer -- fifteen-odd divergences do not justify duplicating a hundred
|
|
68
|
+
# files whose remaining content is identical, and an #if keeps both
|
|
69
|
+
# measured values side by side where they can be audited (R8).
|
|
70
|
+
#
|
|
71
|
+
# The musl side is complete only for x86-64: the aarch64 arch layer's five
|
|
72
|
+
# ABI-switched headers carry glibc's values alone, because the musl
|
|
73
|
+
# figures were measured on x86-64 and an arch layer is exactly where a
|
|
74
|
+
# value may move between machines (each of those files says so in its own
|
|
75
|
+
# provenance note). An aarch64 musl target therefore reads musl's common
|
|
76
|
+
# layer and glibc's arch layer until an aarch64 musl run measures it.
|
|
77
|
+
LIBCS = %w[glibc musl].freeze
|
|
78
|
+
|
|
79
|
+
# The macro the bundled headers select their musl branch on. It is
|
|
80
|
+
# predefined (to 1) only when `libc` is "musl"; on glibc it stays
|
|
81
|
+
# undefined, so `#if defined(__RUBYCC_LIBC_MUSL__)` reads "the musl ABI"
|
|
82
|
+
# and its #else arm is the long-standing glibc one. Unlike the platform
|
|
83
|
+
# macros it is reserved on *both* settings (see #reject_reserved_name): it
|
|
84
|
+
# names which ABI the bundled headers were pinned to when the compiler was
|
|
85
|
+
# configured, so a translation unit that could -D it into existence, or
|
|
86
|
+
# -U it away, would get headers describing one libc and an object laid out
|
|
87
|
+
# for the other.
|
|
88
|
+
LIBC_MUSL_MACRO = "__RUBYCC_LIBC_MUSL__"
|
|
89
|
+
|
|
90
|
+
# The libc system header directories, in the order gcc reports them for
|
|
91
|
+
# angled includes. Only the C library's own directories are listed; the
|
|
92
|
+
# compiler's private include directory is deliberately absent, because
|
|
93
|
+
# BUNDLED_INCLUDE_DIR supplies those headers instead.
|
|
94
|
+
#
|
|
95
|
+
# The first entry is Debian's multiarch directory, which is named after the
|
|
96
|
+
# target: `/usr/include/x86_64-linux-gnu` holds the x86-64 `bits/`, and its
|
|
97
|
+
# AArch64 counterpart holds a different one. It therefore belongs to
|
|
98
|
+
# `libc_arch` exactly like the bundled arch layer does -- see
|
|
99
|
+
# LIBC_SYSTEM_INCLUDE_PATHS_FOR, which #libc_system_include_paths uses per
|
|
100
|
+
# instance. Naming one target here unconditionally was a real defect: an
|
|
101
|
+
# AArch64 host searched a directory that does not exist and never looked in
|
|
102
|
+
# its own (GAPS V).
|
|
103
|
+
LIBC_MULTIARCH_INCLUDE_DIRS = {
|
|
104
|
+
"x86_64" => "/usr/include/x86_64-linux-gnu",
|
|
105
|
+
"aarch64" => "/usr/include/aarch64-linux-gnu"
|
|
106
|
+
}.freeze
|
|
107
|
+
|
|
108
|
+
def self.libc_system_include_paths_for(libc_arch)
|
|
109
|
+
multiarch = LIBC_MULTIARCH_INCLUDE_DIRS.fetch(libc_arch) do
|
|
110
|
+
raise ArgumentError, "unsupported libc arch: #{libc_arch.inspect}"
|
|
111
|
+
end
|
|
112
|
+
[multiarch, "/usr/include"].freeze
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# The x86-64 baseline, kept as a constant for the same reason
|
|
116
|
+
# DEFAULT_SYSTEM_INCLUDE_PATHS is: it is the shape every per-instance list
|
|
117
|
+
# takes, with its own arch in the multiarch slot.
|
|
118
|
+
LIBC_SYSTEM_INCLUDE_PATHS = libc_system_include_paths_for("x86_64")
|
|
119
|
+
|
|
120
|
+
# The default system include search path: the bundled freestanding headers
|
|
121
|
+
# first (so rubycc's stdarg.h/stddef.h win over any same-named file further
|
|
122
|
+
# down), then the bundled libc compatibility headers (arch layer before
|
|
123
|
+
# common layer), then the host libc directories. A bundled libc header thus
|
|
124
|
+
# wins over the host's same-named one, yet can still reach the host copy via
|
|
125
|
+
# #include_next, which resumes the search past whichever directory the
|
|
126
|
+
# bundled header was found in. The host directories stay on the path by
|
|
127
|
+
# default (they are only dropped in the distroless mode, where they are
|
|
128
|
+
# absent anyway); the whole default path is appended after the user's
|
|
129
|
+
# -I/-isystem directories, and suppressed entirely by -nostdinc.
|
|
130
|
+
#
|
|
131
|
+
# This is the x86-64 baseline: #default_system_include_paths builds the same
|
|
132
|
+
# list per instance, substituting the instance's own arch layer for the arch
|
|
133
|
+
# slot, so a cross compile's path is this shape with a different arch layer.
|
|
134
|
+
DEFAULT_SYSTEM_INCLUDE_PATHS = [
|
|
135
|
+
BUNDLED_INCLUDE_DIR,
|
|
136
|
+
BUNDLED_LIBC_ARCH_INCLUDE_DIR,
|
|
137
|
+
BUNDLED_LIBC_INCLUDE_DIR,
|
|
138
|
+
*LIBC_SYSTEM_INCLUDE_PATHS
|
|
139
|
+
].freeze
|
|
140
|
+
|
|
141
|
+
# The hermetic-headers system search path: the bundled freestanding and
|
|
142
|
+
# bundled libc layers only, with the host libc directories dropped. Selected
|
|
143
|
+
# by RUBYCC_HERMETIC_HEADERS (see #default_system_include_paths) so a full
|
|
144
|
+
# `gem install` can be driven with rubycc's own headers exclusively -- the
|
|
145
|
+
# distroless posture -- without every conftest command having to pass
|
|
146
|
+
# -nostdinc explicitly. It is the same set the distroless ruby.h build uses,
|
|
147
|
+
# so if a translation unit reaches for a declaration only the host's real
|
|
148
|
+
# headers carry, it fails here the way it would on a headerless image
|
|
149
|
+
# instead of silently borrowing it from /usr/include. Like
|
|
150
|
+
# DEFAULT_SYSTEM_INCLUDE_PATHS this is the x86-64 baseline;
|
|
151
|
+
# #default_system_include_paths builds the per-instance equivalent with the
|
|
152
|
+
# instance's own arch layer.
|
|
153
|
+
HERMETIC_SYSTEM_INCLUDE_PATHS = [
|
|
154
|
+
BUNDLED_INCLUDE_DIR,
|
|
155
|
+
BUNDLED_LIBC_ARCH_INCLUDE_DIR,
|
|
156
|
+
BUNDLED_LIBC_INCLUDE_DIR
|
|
157
|
+
].freeze
|
|
158
|
+
|
|
159
|
+
# The environment variable that switches the default system search path to
|
|
160
|
+
# the hermetic (bundled-only) set. Any non-empty value other than "0"
|
|
161
|
+
# enables it; the default (unset) keeps the host libc directories on the
|
|
162
|
+
# path, so existing behaviour is unchanged.
|
|
163
|
+
HERMETIC_HEADERS_ENV = "RUBYCC_HERMETIC_HEADERS"
|
|
164
|
+
|
|
165
|
+
# A guard against unbounded #include recursion (a header that includes
|
|
166
|
+
# itself); 200 is comfortably deeper than any sane header nesting.
|
|
167
|
+
INCLUDE_DEPTH_LIMIT = 200
|
|
168
|
+
|
|
169
|
+
# The cumulative ceiling on how many tokens macro expansion may process
|
|
170
|
+
# across a whole translation unit. Blue-painting (see #expand_tokens) stops
|
|
171
|
+
# self-reference and mutual recursion, but nothing otherwise bounds an
|
|
172
|
+
# exponentially expanding macro — the classic "#define B1 B0 B0 ... #define
|
|
173
|
+
# B40 B39 B39" doubles its output each level, so "B40" would materialize
|
|
174
|
+
# 2^40 tokens and exhaust CPU and memory long before finishing. Charging one
|
|
175
|
+
# unit per token pulled from the work queue and tripping this ceiling turns
|
|
176
|
+
# that runaway into a located CompileError. The bound is a whole-run
|
|
177
|
+
# cumulative budget (expand_tokens runs once per gathered line and once per
|
|
178
|
+
# #if condition, and recurses for each argument), so an expansion that
|
|
179
|
+
# explodes across many small calls is still caught. The real #include
|
|
180
|
+
# <ruby.h> header graph — the whole CRuby + libc header set, a worst-case
|
|
181
|
+
# legitimate input — consumes about 137k, so one million leaves a 7x margin
|
|
182
|
+
# while it never fires on real code. It is deliberately not larger: because
|
|
183
|
+
# a doubling macro is rejected only after the full budget is processed, the
|
|
184
|
+
# ceiling also caps the worst-case work a hostile input can force (about
|
|
185
|
+
# three seconds here), so raising it would trade rejection latency for
|
|
186
|
+
# headroom no real translation unit needs.
|
|
187
|
+
EXPANSION_TOKEN_LIMIT = 1_000_000
|
|
188
|
+
|
|
189
|
+
# The ceiling on conditional-directive nesting within a single file. A
|
|
190
|
+
# deeply nested tower of "#if"s is not a stack risk (the frames are held in
|
|
191
|
+
# a heap array, not on the Ruby stack), but capping it keeps a hostile
|
|
192
|
+
# source from building an arbitrarily large conditional stack; 256 is far
|
|
193
|
+
# beyond any real header's conditional nesting.
|
|
194
|
+
CONDITIONAL_NESTING_LIMIT = 256
|
|
195
|
+
|
|
196
|
+
# The ceiling on parenthesis nesting inside a function-like macro's argument
|
|
197
|
+
# list. #collect_arguments balances parentheses with a plain integer depth
|
|
198
|
+
# counter (no recursion, so this is not a stack guard), but bounding it
|
|
199
|
+
# rejects a pathological "M(((((...)))))" up front rather than scanning an
|
|
200
|
+
# unbounded run. It is generous — a macro argument is a full expression, and
|
|
201
|
+
# the parser re-checks nesting downstream — so this only trips on clearly
|
|
202
|
+
# abusive input.
|
|
203
|
+
MACRO_ARGUMENT_NESTING_LIMIT = 2000
|
|
204
|
+
|
|
205
|
+
# The directives that steer a conditional group (6.10.1). They are acted on
|
|
206
|
+
# whether or not the enclosing group is active, so nesting stays balanced
|
|
207
|
+
# inside a skipped region; every other directive is inert while skipping.
|
|
208
|
+
# Kept as a Hash (used only for membership) so the check is O(1).
|
|
209
|
+
CONDITIONAL_DIRECTIVES = %w[if ifdef ifndef elif else endif].to_h { |name| [name, true] }.freeze
|
|
210
|
+
|
|
211
|
+
# One nesting level of a conditional. `active` is whether this frame's
|
|
212
|
+
# current group is being emitted (its condition held and the frame itself
|
|
213
|
+
# is reachable); `taken` records whether any group of this #if has been
|
|
214
|
+
# selected yet, so a later #elif/#else knows to stay inert; `seen_else`
|
|
215
|
+
# guards against an #elif/#else after the #else; `parent_active` is whether
|
|
216
|
+
# the enclosing context was active when the #if opened, which freezes the
|
|
217
|
+
# whole conditional off when it was not. `token` is the opening directive,
|
|
218
|
+
# for an "unterminated conditional" diagnosed at end of file.
|
|
219
|
+
Frame = Struct.new(:active, :taken, :seen_else, :parent_active, :token)
|
|
220
|
+
|
|
221
|
+
# One entry of the macro table. `kind` is :object or :function; `params`
|
|
222
|
+
# is the ordered parameter-name list (empty for an object macro, and for a
|
|
223
|
+
# function macro it excludes any trailing "..."); `variadic` records that a
|
|
224
|
+
# variable part is present so the rest arguments are meaningful; `va_name`
|
|
225
|
+
# is the identifier that stands for the rest arguments in the replacement
|
|
226
|
+
# list -- "__VA_ARGS__" for the ISO bare-"..." form and the written name for
|
|
227
|
+
# the GNU "name..." form, nil when not variadic; `replacement` is the raw
|
|
228
|
+
# replacement-list tokens as written.
|
|
229
|
+
Macro = Struct.new(:kind, :params, :variadic, :replacement, :va_name)
|
|
230
|
+
|
|
231
|
+
# One function-like invocation's arguments. `raw` is each argument's
|
|
232
|
+
# unexpanded tokens (what "#" and "##" operate on), `commas` the top-level
|
|
233
|
+
# comma tokens between them (kept verbatim so #__VA_ARGS__ can reproduce the
|
|
234
|
+
# exact spacing), and `expanded` a per-argument memo of the fully expanded
|
|
235
|
+
# form, filled lazily so an argument used only by "#"/"##" is never expanded.
|
|
236
|
+
Invocation = Struct.new(:raw, :commas, :expanded)
|
|
237
|
+
|
|
238
|
+
# The macros the preprocessor supplies itself (6.10.8): each is expanded
|
|
239
|
+
# from the use site, so its value cannot be a fixed replacement list stored
|
|
240
|
+
# at definition time. __GNUC__ is deliberately absent (DESIGN R7), so a
|
|
241
|
+
# header cannot select a gcc-specific path. None may be redefined or undefined.
|
|
242
|
+
# Kept as a Hash (used only for membership) so the check is O(1).
|
|
243
|
+
BUILTIN_MACROS = %w[__FILE__ __LINE__ __STDC__ __STDC_VERSION__ __RUBYCC__].to_h { |name| [name, true] }.freeze
|
|
244
|
+
|
|
245
|
+
# The identifiers __has_builtin (6.10.1) answers true for: exactly the
|
|
246
|
+
# builtins rubycc's front end actually recognizes — the varargs intrinsics,
|
|
247
|
+
# the branch-prediction hint, the stack allocator, offsetof, the
|
|
248
|
+
# constant/choose folds, the count-leading/trailing-zero scans, the
|
|
249
|
+
# unreachable hint, memcpy, the three overflow-checked arithmetic forms,
|
|
250
|
+
# the nine __atomic_* forms and the ten legacy __sync_* forms. Every other
|
|
251
|
+
# builtin query is false, so a header that guards a fallback behind
|
|
252
|
+
# __has_builtin (e.g. json's bswap path) takes the fallback for one rubycc
|
|
253
|
+
# does not provide. Kept in sync with the parser's builtin keywords. Kept
|
|
254
|
+
# as a Hash (used only for membership) so the check is O(1).
|
|
255
|
+
KNOWN_BUILTINS = %w[__builtin_va_start __builtin_va_arg __builtin_va_end __builtin_va_copy
|
|
256
|
+
__builtin_expect __builtin_alloca __builtin_offsetof
|
|
257
|
+
__builtin_constant_p __builtin_choose_expr
|
|
258
|
+
__builtin_ctz __builtin_ctzll __builtin_clz __builtin_clzll
|
|
259
|
+
__builtin_unreachable __builtin_memcpy
|
|
260
|
+
__builtin_add_overflow __builtin_sub_overflow
|
|
261
|
+
__builtin_mul_overflow
|
|
262
|
+
__atomic_load_n __atomic_store_n __atomic_exchange_n
|
|
263
|
+
__atomic_compare_exchange_n
|
|
264
|
+
__atomic_fetch_add __atomic_fetch_sub
|
|
265
|
+
__atomic_add_fetch __atomic_sub_fetch
|
|
266
|
+
__atomic_or_fetch __atomic_thread_fence
|
|
267
|
+
__sync_fetch_and_add __sync_fetch_and_sub
|
|
268
|
+
__sync_add_and_fetch __sync_sub_and_fetch
|
|
269
|
+
__sync_or_and_fetch __sync_lock_test_and_set
|
|
270
|
+
__sync_lock_release __sync_synchronize
|
|
271
|
+
__sync_bool_compare_and_swap
|
|
272
|
+
__sync_val_compare_and_swap].to_h { |name| [name, true] }.freeze
|
|
273
|
+
|
|
274
|
+
# The platform macros gcc keeps predefined even under strict ISO C
|
|
275
|
+
# (-std=c11): only the reserved forms (a leading underscore followed by
|
|
276
|
+
# another underscore or an uppercase letter, 7.1.3), so headers relying on
|
|
277
|
+
# "linux", "unix" or "i386" (the non-reserved spellings, gcc drops these
|
|
278
|
+
# under -std=c11) still see them undefined here. __GNUC__ is deliberately
|
|
279
|
+
# excluded (DESIGN R7): rubycc targets Linux/ELF LP64 only, so this fixed
|
|
280
|
+
# set is enough for glibc's own dispatch (e.g. gnu/stubs.h) to settle on
|
|
281
|
+
# the right branch, without claiming gcc compatibility beyond that. Unlike
|
|
282
|
+
# BUILTIN_MACROS these are ordinary #define'd entries in @macros, so a
|
|
283
|
+
# translation unit may #undef or redefine them (gcc allows this too).
|
|
284
|
+
#
|
|
285
|
+
# These are the macros every supported target shares; the CPU-identifying
|
|
286
|
+
# ones are per-target and arrive through `arch_macros` below.
|
|
287
|
+
PREDEFINED_PLATFORM_MACROS = %w[__linux__ __gnu_linux__ __unix__ __ELF__
|
|
288
|
+
__LP64__ _LP64 __STDC_HOSTED__].freeze
|
|
289
|
+
|
|
290
|
+
# The CPU-identifying macros for each target, the subset of gcc's that
|
|
291
|
+
# glibc's own headers dispatch on. Getting these from the target rather
|
|
292
|
+
# than fixing them was forced by the aarch64 backend: a unit compiled for
|
|
293
|
+
# aarch64 that asked `#ifdef __x86_64__` used to take the x86-64 branch,
|
|
294
|
+
# and the cross libc headers were being read under the wrong CPU identity.
|
|
295
|
+
X86_64_ARCH_MACROS = %w[__x86_64__ __amd64__].freeze
|
|
296
|
+
AARCH64_ARCH_MACROS = %w[__aarch64__ __AARCH64EL__].freeze
|
|
297
|
+
|
|
298
|
+
# The numeric macros gcc predefines: the limit/size ones describing the
|
|
299
|
+
# target's fundamental types, and the memory-order enumerators the
|
|
300
|
+
# __atomic_* builtins take as an argument (grouped here because they are
|
|
301
|
+
# the same kind of thing — a fixed integer replacement text a translation
|
|
302
|
+
# unit may #undef, not a use-site-computed BUILTIN_MACROS entry).
|
|
303
|
+
# glibc's headers reach for these directly when __GNUC__
|
|
304
|
+
# is absent (e.g. limits.h's __LONG_MAX__ via ruby's special_consts.h), so
|
|
305
|
+
# they must carry gcc's exact spellings — value base (hex vs decimal) and
|
|
306
|
+
# integer suffix — for a gcc-differential #if to agree. The right-hand sides
|
|
307
|
+
# are the verbatim replacement texts of `gcc -dM -E </dev/null` on this
|
|
308
|
+
# x86-64 LP64 target; they become ordinary object macros (a translation unit
|
|
309
|
+
# may #undef or redefine them). __WCHAR_MIN__'s value is a parenthesized
|
|
310
|
+
# expression referring to another of these, which expands recursively at the
|
|
311
|
+
# use site like any macro. Only the reserved "__X__" forms are provided;
|
|
312
|
+
# __GNUC__ and version macros stay absent (DESIGN R7). The replacement text
|
|
313
|
+
# is re-scanned into pp-tokens rather than hand-built, so multi-token values
|
|
314
|
+
# need no special casing.
|
|
315
|
+
PREDEFINED_NUMERIC_MACROS = {
|
|
316
|
+
"__CHAR_BIT__" => "8",
|
|
317
|
+
"__SCHAR_MAX__" => "0x7f",
|
|
318
|
+
"__SHRT_MAX__" => "0x7fff",
|
|
319
|
+
"__INT_MAX__" => "0x7fffffff",
|
|
320
|
+
"__LONG_MAX__" => "0x7fffffffffffffffL",
|
|
321
|
+
"__LONG_LONG_MAX__" => "0x7fffffffffffffffLL",
|
|
322
|
+
"__WCHAR_MAX__" => "0x7fffffff",
|
|
323
|
+
"__WCHAR_MIN__" => "(-__WCHAR_MAX__ - 1)",
|
|
324
|
+
"__WINT_MAX__" => "0xffffffffU",
|
|
325
|
+
"__WINT_MIN__" => "0U",
|
|
326
|
+
"__PTRDIFF_MAX__" => "0x7fffffffffffffffL",
|
|
327
|
+
"__SIZE_MAX__" => "0xffffffffffffffffUL",
|
|
328
|
+
"__INTMAX_MAX__" => "0x7fffffffffffffffL",
|
|
329
|
+
"__UINTMAX_MAX__" => "0xffffffffffffffffUL",
|
|
330
|
+
"__INTPTR_MAX__" => "0x7fffffffffffffffL",
|
|
331
|
+
"__UINTPTR_MAX__" => "0xffffffffffffffffUL",
|
|
332
|
+
"__SIZEOF_INT__" => "4",
|
|
333
|
+
"__SIZEOF_LONG__" => "8",
|
|
334
|
+
"__SIZEOF_LONG_LONG__" => "8",
|
|
335
|
+
"__SIZEOF_SHORT__" => "2",
|
|
336
|
+
"__SIZEOF_POINTER__" => "8",
|
|
337
|
+
"__SIZEOF_SIZE_T__" => "8",
|
|
338
|
+
"__SIZEOF_PTRDIFF_T__" => "8",
|
|
339
|
+
"__SIZEOF_FLOAT__" => "4",
|
|
340
|
+
"__SIZEOF_DOUBLE__" => "8",
|
|
341
|
+
"__SIZEOF_WCHAR_T__" => "4",
|
|
342
|
+
"__SIZEOF_WINT_T__" => "4",
|
|
343
|
+
# The memory-order arguments the __atomic_* builtins take (C11 7.17.3's
|
|
344
|
+
# memory_order enumerators, which gcc predefines under these spellings).
|
|
345
|
+
# The values are the verbatim ones `gcc -dM -E </dev/null` prints on this
|
|
346
|
+
# target, so a header comparing them (or building one out of another)
|
|
347
|
+
# agrees with a gcc build. rubycc implements every atomic operation at
|
|
348
|
+
# the strongest order regardless of which of these is passed — see
|
|
349
|
+
# IR::Generator#gen_builtin_atomic — but the constants must still carry
|
|
350
|
+
# gcc's values, because a caller may compute with them.
|
|
351
|
+
"__ATOMIC_RELAXED" => "0",
|
|
352
|
+
"__ATOMIC_CONSUME" => "1",
|
|
353
|
+
"__ATOMIC_ACQUIRE" => "2",
|
|
354
|
+
"__ATOMIC_RELEASE" => "3",
|
|
355
|
+
"__ATOMIC_ACQ_REL" => "4",
|
|
356
|
+
"__ATOMIC_SEQ_CST" => "5"
|
|
357
|
+
}.freeze
|
|
358
|
+
|
|
359
|
+
# The libc this host's C library is: "musl" or "glibc" (see LIBCS). Read
|
|
360
|
+
# from RbConfig's arch triplet, which is how MRI itself distinguishes a
|
|
361
|
+
# musl build ("x86_64-linux-musl") from a glibc one ("x86_64-linux") --
|
|
362
|
+
# the same source test/abi_harness/harness.rb's #host_libc and
|
|
363
|
+
# tools/verify_gem_tests.rb's environment_string read, so the compiler,
|
|
364
|
+
# the ABI harness and the verification records all agree on what "this
|
|
365
|
+
# environment" is. It is the default for `libc` below, which is what makes
|
|
366
|
+
# an unconfigured compile on a musl host read the musl branches; a cross
|
|
367
|
+
# compile passes the target's own.
|
|
368
|
+
def self.host_libc
|
|
369
|
+
RbConfig::CONFIG["arch"].to_s.include?("musl") ? "musl" : "glibc"
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# The glibc version macros, predefined on a glibc target so the bundled
|
|
373
|
+
# <features.h> does not have to name a version it cannot know. __GLIBC__
|
|
374
|
+
# is a constant (glibc's major has been 2 since 1997); the minor is
|
|
375
|
+
# measured from the C library the compile will link against, because a
|
|
376
|
+
# single shipped header set otherwise reports one host's version on every
|
|
377
|
+
# host, and a version gate then selects a branch the local libc may not be
|
|
378
|
+
# able to back (docs/development/GAPS.md gap U).
|
|
379
|
+
GLIBC_MAJOR_MACRO = "__GLIBC__"
|
|
380
|
+
GLIBC_MINOR_MACRO = "__GLIBC_MINOR__"
|
|
381
|
+
GLIBC_MAJOR = 2
|
|
382
|
+
|
|
383
|
+
# The measured glibc minor version for the `libc_arch` target, or nil when
|
|
384
|
+
# this host offers nothing to measure (see GlibcVersion). nil is not an
|
|
385
|
+
# error and not a substitute value: the two macros are then left
|
|
386
|
+
# undefined, and the bundled <features.h> supplies its own fallback pair
|
|
387
|
+
# -- the reference platform's 2.39 -- exactly as it did before this was
|
|
388
|
+
# measured at all. Keeping the fallback in the header rather than
|
|
389
|
+
# repeating the number here also keeps a header read outside rubycc (or
|
|
390
|
+
# under -nostdinc with the host's own headers) on the same value.
|
|
391
|
+
def self.host_glibc_minor(libc_arch = "x86_64")
|
|
392
|
+
GlibcVersion.minor_for(libc_arch)
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# `char_unsigned` says whether plain `char` is unsigned on the target being
|
|
396
|
+
# compiled for (it is under AAPCS64, and is not under the x86-64 System V
|
|
397
|
+
# psABI, hence the default). When it is, __CHAR_UNSIGNED__ joins the
|
|
398
|
+
# predefined macros with the value gcc gives it, so a header can select the
|
|
399
|
+
# same branch it would there — the bundled <limits.h> uses exactly that to
|
|
400
|
+
# pick CHAR_MIN/CHAR_MAX.
|
|
401
|
+
# `arch_macros` names the target's CPU-identifying macros (see
|
|
402
|
+
# X86_64_ARCH_MACROS); it defaults to x86-64's, the default target.
|
|
403
|
+
# `libc_arch` selects which bundled libc-and-arch header layer sits on the
|
|
404
|
+
# default search path ("x86_64" or "aarch64", see LIBC_ARCHS); it defaults to
|
|
405
|
+
# x86-64 so an unconfigured host compile is byte-for-byte unchanged, and a
|
|
406
|
+
# cross compile passes the target's own so its ABI headers (struct stat's
|
|
407
|
+
# 128-byte aarch64 layout, the narrower nlink_t/blksize_t, the unsigned
|
|
408
|
+
# WCHAR_MIN/MAX and kin) are read instead of the host's.
|
|
409
|
+
# `libc` selects which C library's ABI those bundled headers describe
|
|
410
|
+
# ("glibc" or "musl", see LIBCS); it defaults to the host's own (see
|
|
411
|
+
# .host_libc), and on "musl" it predefines LIBC_MUSL_MACRO so the headers
|
|
412
|
+
# take their musl branches.
|
|
413
|
+
# `glibc_minor` is the glibc minor version the version macros are to
|
|
414
|
+
# report on a glibc target; it defaults to the one measured from the C
|
|
415
|
+
# library that target links against (see .host_glibc_minor), and nil --
|
|
416
|
+
# which is also what an unmeasurable host yields -- leaves both macros
|
|
417
|
+
# undefined for the bundled <features.h> to fall back on. It is a keyword
|
|
418
|
+
# so a caller can pin a version deliberately (a cross compile against a
|
|
419
|
+
# sysroot this host cannot search, and the tests' fallback case). The
|
|
420
|
+
# default measures only on a glibc target: on "musl" the value is unused,
|
|
421
|
+
# and reading a C library to answer a question nobody asks would cost
|
|
422
|
+
# every musl translation unit a megabyte-scale read for nothing.
|
|
423
|
+
def initialize(char_unsigned: false, arch_macros: X86_64_ARCH_MACROS, libc_arch: "x86_64",
|
|
424
|
+
libc: Preprocessor.host_libc,
|
|
425
|
+
glibc_minor: (libc == "glibc" ? Preprocessor.host_glibc_minor(libc_arch) : nil))
|
|
426
|
+
unless LIBC_ARCHS.include?(libc_arch)
|
|
427
|
+
raise ArgumentError, "unsupported libc arch: #{libc_arch.inspect} (expected one of #{LIBC_ARCHS.join(", ")})"
|
|
428
|
+
end
|
|
429
|
+
unless LIBCS.include?(libc)
|
|
430
|
+
raise ArgumentError, "unsupported libc: #{libc.inspect} (expected one of #{LIBCS.join(", ")})"
|
|
431
|
+
end
|
|
432
|
+
unless glibc_minor.nil? || (glibc_minor.is_a?(Integer) && !glibc_minor.negative?)
|
|
433
|
+
raise ArgumentError, "glibc minor version must be a non-negative Integer or nil: #{glibc_minor.inspect}"
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# The bundled libc arch layer this instance searches (see
|
|
437
|
+
# BUNDLED_LIBC_ARCH_INCLUDE_DIR). For the x86-64 default it equals that
|
|
438
|
+
# constant, so the default search path is identical to before.
|
|
439
|
+
@libc_arch_include_dir = File.expand_path("../../../include/libc/glibc/#{libc_arch}", __dir__)
|
|
440
|
+
# The host libc directories this instance searches. The multiarch slot
|
|
441
|
+
# follows the same `libc_arch` as the bundled layer above, so a compile
|
|
442
|
+
# never looks for another target's `bits/` (GAPS V).
|
|
443
|
+
@libc_system_include_paths = self.class.libc_system_include_paths_for(libc_arch)
|
|
444
|
+
# name (String) => Macro.
|
|
445
|
+
@macros = {}
|
|
446
|
+
(arch_macros + PREDEFINED_PLATFORM_MACROS).each { |name| @macros[name] = predefined_target_macro }
|
|
447
|
+
@macros["__CHAR_UNSIGNED__"] = predefined_target_macro if char_unsigned
|
|
448
|
+
@macros[LIBC_MUSL_MACRO] = predefined_target_macro if libc == "musl"
|
|
449
|
+
PREDEFINED_NUMERIC_MACROS.each { |name, text| @macros[name] = predefined_numeric_macro(text) }
|
|
450
|
+
# The glibc version pair, defined only on a glibc target and only when
|
|
451
|
+
# the version could be measured. They are ordinary numeric macros like
|
|
452
|
+
# the ones above (a translation unit may #undef or redefine them), and
|
|
453
|
+
# the bundled <features.h> defines each only when it is absent, so this
|
|
454
|
+
# is what makes a version gate agree with the host's own headers. On
|
|
455
|
+
# musl, and when nothing could be measured, nothing is defined here and
|
|
456
|
+
# <features.h> keeps supplying the reference platform's pair, which is
|
|
457
|
+
# what every compile did before the measurement existed.
|
|
458
|
+
if libc == "glibc" && glibc_minor
|
|
459
|
+
@macros[GLIBC_MAJOR_MACRO] = predefined_numeric_macro(GLIBC_MAJOR.to_s)
|
|
460
|
+
@macros[GLIBC_MINOR_MACRO] = predefined_numeric_macro(glibc_minor.to_s)
|
|
461
|
+
end
|
|
462
|
+
@include_depth = 0
|
|
463
|
+
# Absolute paths of files that asked (via "#pragma once") to be read at
|
|
464
|
+
# most once; a later #include resolving to one of them is skipped.
|
|
465
|
+
@pragma_once = {}
|
|
466
|
+
# Absolute path => index into @include_paths of the -I directory a file
|
|
467
|
+
# was found in. Only files resolved along the search path get an entry
|
|
468
|
+
# (the main source file and a quote-relative resolution beside its
|
|
469
|
+
# includer never do), which is exactly what #include_next needs to tell
|
|
470
|
+
# "resume the search past here" from "there is no here" (GNU extension).
|
|
471
|
+
@include_origin = {}
|
|
472
|
+
# Resolved include path (the exact spelling #include resolves to) =>
|
|
473
|
+
# that file's scanned pp-token array. Scanning (phases 2-3) is a pure
|
|
474
|
+
# function of the file's bytes — no macro state reaches it — and both
|
|
475
|
+
# PPToken and the directive walk are non-mutating (painting copies), so
|
|
476
|
+
# one scan per header serves every re-#include verbatim. This is where
|
|
477
|
+
# a real unit burns most of its time otherwise: ruby.h's include graph
|
|
478
|
+
# re-includes the same headers hundreds of times, and each guard-skipped
|
|
479
|
+
# body still had to be re-scanned to find its #endif (Step 108).
|
|
480
|
+
@scan_cache = {}
|
|
481
|
+
# Resolved include path => the file's include-guard macro name, or nil
|
|
482
|
+
# when its shape rules the optimization out (gcc's multiple-include
|
|
483
|
+
# optimization, Step 109). A header whose entire significant content is
|
|
484
|
+
# wrapped in one "#ifndef G ... #endif" (or "#if !defined(G)") behaves,
|
|
485
|
+
# when G is defined, exactly like an empty file: the walk would activate
|
|
486
|
+
# nothing and change no state. So a re-#include whose recorded guard is
|
|
487
|
+
# currently defined skips the directive walk outright. Detection is a
|
|
488
|
+
# pure function of the scanned tokens (see #detect_include_guard); the
|
|
489
|
+
# skip consults the live macro table, so an #undef of the guard makes
|
|
490
|
+
# the next #include process the file again.
|
|
491
|
+
@guard_cache = {}
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def run(source, filename:, include_paths: [], defines: [], system_includes: true)
|
|
495
|
+
TokenConverter.new.convert(
|
|
496
|
+
preprocess(source, filename: filename, include_paths: include_paths,
|
|
497
|
+
defines: defines, system_includes: system_includes)
|
|
498
|
+
)
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
# Runs translation phases 1-4 and returns the resulting preprocessing-token
|
|
502
|
+
# stream (terminated by the unit's :eof), before it is converted into
|
|
503
|
+
# Front tokens. It is what #run builds on, and what the `-E` driver mode
|
|
504
|
+
# re-spells into preprocessed text. `defines` is the ordered command-line
|
|
505
|
+
# `-D`/`-U` list (see #apply_command_line_definitions).
|
|
506
|
+
# `system_includes` (the default) appends the compiler-supplied and libc
|
|
507
|
+
# directories after the caller's -I/-isystem set, so an angled #include of
|
|
508
|
+
# <stdarg.h> or a libc header resolves with no explicit -I; the driver's
|
|
509
|
+
# -nostdinc passes it false to search only the caller's directories.
|
|
510
|
+
def preprocess(source, filename:, include_paths: [], defines: [], system_includes: true)
|
|
511
|
+
system_paths = system_includes ? default_system_include_paths : []
|
|
512
|
+
@system_include_paths = system_paths.map { |path| File.expand_path(path) }
|
|
513
|
+
@include_paths = include_paths + system_paths
|
|
514
|
+
# #resolve_include's cache keys a resolved path off @include_paths (and,
|
|
515
|
+
# for quote includes, the includer's directory), so it must start empty
|
|
516
|
+
# every run rather than survive across calls with a different search path.
|
|
517
|
+
@resolve_cache = {}
|
|
518
|
+
# The whole-run macro-expansion budget (see EXPANSION_TOKEN_LIMIT), reset
|
|
519
|
+
# here so every translation unit starts with a full allowance.
|
|
520
|
+
@expansion_tokens = 0
|
|
521
|
+
# The presumed-line state a #line directive sets (6.10.4): a delta added
|
|
522
|
+
# to a token's physical line for __LINE__, and a presumed file name for
|
|
523
|
+
# __FILE__ (nil = the token's own file). It is per-file, saved and
|
|
524
|
+
# restored across #include (see #process_include).
|
|
525
|
+
@presumed_line_delta = 0
|
|
526
|
+
@presumed_file = nil
|
|
527
|
+
apply_command_line_definitions(defines)
|
|
528
|
+
pp_tokens = Scanner.new(source, filename: filename).scan
|
|
529
|
+
output = []
|
|
530
|
+
process_lines(pp_tokens, filename, output)
|
|
531
|
+
# process_lines stops at the unit's end-of-file marker without emitting
|
|
532
|
+
# it; carry it through so the converter can terminate its stream.
|
|
533
|
+
output << pp_tokens.last
|
|
534
|
+
output
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
private
|
|
538
|
+
|
|
539
|
+
# The system search path appended after the caller's -I/-isystem set: the
|
|
540
|
+
# full default (bundled headers ahead of the host libc directories), or the
|
|
541
|
+
# hermetic bundled-only set when RUBYCC_HERMETIC_HEADERS selects it. The arch
|
|
542
|
+
# slot is this instance's @libc_arch_include_dir rather than the fixed
|
|
543
|
+
# constant, so a cross compile searches the target's ABI layer; for the
|
|
544
|
+
# x86-64 default the result equals DEFAULT_SYSTEM_INCLUDE_PATHS /
|
|
545
|
+
# HERMETIC_SYSTEM_INCLUDE_PATHS exactly. The environment is read per call
|
|
546
|
+
# rather than cached so a process can flip the mode between translation units
|
|
547
|
+
# (the mkmf conftest sequence does not, but a test driving several compiles
|
|
548
|
+
# in-process may).
|
|
549
|
+
def default_system_include_paths
|
|
550
|
+
bundled = [BUNDLED_INCLUDE_DIR, @libc_arch_include_dir, BUNDLED_LIBC_INCLUDE_DIR]
|
|
551
|
+
return bundled if hermetic_headers?
|
|
552
|
+
|
|
553
|
+
[*bundled, *@libc_system_include_paths]
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def hermetic_headers?
|
|
557
|
+
value = ENV[HERMETIC_HEADERS_ENV]
|
|
558
|
+
!value.nil? && !value.empty? && value != "0"
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
# Applies the driver's command-line `-D`/`-U` requests before the source is
|
|
562
|
+
# read, in the order they were given (so a later `-U` undoes an earlier
|
|
563
|
+
# `-D`, as gcc does). Each is turned into the directive it stands for and
|
|
564
|
+
# run through the ordinary directive machinery — `-DNAME` into
|
|
565
|
+
# `#define NAME 1`, `-DNAME=VALUE` into `#define NAME VALUE` (the split on
|
|
566
|
+
# the first `=` keeps a function-like `-DF(x)=…` intact), and `-UNAME` into
|
|
567
|
+
# `#undef NAME` — so redefinition and validation behave exactly as an
|
|
568
|
+
# in-source directive would. Their (empty) output is discarded; only the
|
|
569
|
+
# effect on the shared macro table matters.
|
|
570
|
+
def apply_command_line_definitions(defines)
|
|
571
|
+
return if defines.empty?
|
|
572
|
+
|
|
573
|
+
preamble = defines.map { |op, arg| command_line_directive(op, arg) }.join("\n") + "\n"
|
|
574
|
+
tokens = Scanner.new(preamble, filename: "<command-line>").scan
|
|
575
|
+
process_lines(tokens, "<command-line>", [])
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
def command_line_directive(op, arg)
|
|
579
|
+
case op
|
|
580
|
+
when :define
|
|
581
|
+
name, value = arg.split("=", 2)
|
|
582
|
+
"#define #{name} #{value || "1"}"
|
|
583
|
+
when :undef
|
|
584
|
+
"#undef #{arg}"
|
|
585
|
+
end
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
# Walks one file's preprocessing tokens a logical line at a time. A line
|
|
589
|
+
# whose first token is "#" is a directive (6.10); every other line has its
|
|
590
|
+
# tokens macro-expanded into `output`, but only while every enclosing
|
|
591
|
+
# conditional is active. `stack` is this file's conditional nesting; a
|
|
592
|
+
# frame it opens must be closed in the same file (a #include appears only on
|
|
593
|
+
# an active line and runs with its own fresh stack), so any frame still open
|
|
594
|
+
# at end of file is an unterminated conditional.
|
|
595
|
+
#
|
|
596
|
+
# A function-like macro invocation may span several physical lines, so the
|
|
597
|
+
# tokens of a run of consecutive active non-directive lines (newlines and
|
|
598
|
+
# all) are gathered and expanded together. A directive line, an inactive
|
|
599
|
+
# region, or end of file ends the run: activeness only ever changes at a
|
|
600
|
+
# directive, so a run is uniformly active, and a call left open where a run
|
|
601
|
+
# ends is diagnosed there (a directive splitting an argument list is 6.10.3
|
|
602
|
+
# undefined behavior). Newlines are dropped by the expander, so the output
|
|
603
|
+
# stream stays free of them.
|
|
604
|
+
def process_lines(tokens, filename, output)
|
|
605
|
+
stack = []
|
|
606
|
+
index = 0
|
|
607
|
+
at_line_start = true
|
|
608
|
+
run = []
|
|
609
|
+
while index < tokens.length
|
|
610
|
+
tok = tokens[index]
|
|
611
|
+
break if tok.eof?
|
|
612
|
+
|
|
613
|
+
if tok.newline?
|
|
614
|
+
run << tok if active?(stack)
|
|
615
|
+
at_line_start = true
|
|
616
|
+
index += 1
|
|
617
|
+
elsif at_line_start && tok.punct?("#")
|
|
618
|
+
expand_run(run, output)
|
|
619
|
+
run = []
|
|
620
|
+
index = process_directive(tokens, index, filename, output, stack)
|
|
621
|
+
at_line_start = true
|
|
622
|
+
else
|
|
623
|
+
at_line_start = false
|
|
624
|
+
run << tok if active?(stack)
|
|
625
|
+
index += 1
|
|
626
|
+
end
|
|
627
|
+
end
|
|
628
|
+
expand_run(run, output)
|
|
629
|
+
raise_at(stack.last.token, "unterminated conditional directive") unless stack.empty?
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def expand_run(run, output)
|
|
633
|
+
expand_tokens(run, output) unless run.empty?
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
# Whether output is currently being emitted: true unless some enclosing
|
|
637
|
+
# conditional selected against it. A frame's `active` already folds in its
|
|
638
|
+
# parents' state, so only the innermost need be consulted.
|
|
639
|
+
def active?(stack)
|
|
640
|
+
stack.empty? || stack.last.active
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
# Dispatches the directive that "#" at `hash_index` introduces and returns
|
|
644
|
+
# the index of the first token past its terminating newline. A conditional
|
|
645
|
+
# directive is always acted on (it may re-activate a skipped region or nest
|
|
646
|
+
# inside it); every other directive, and even a malformed one, is silently
|
|
647
|
+
# dropped while skipping, matching how gcc discards an excluded group whole.
|
|
648
|
+
# An empty directive line (just "#") is the null directive (6.10p2).
|
|
649
|
+
def process_directive(tokens, hash_index, filename, output, stack)
|
|
650
|
+
hash = tokens[hash_index]
|
|
651
|
+
args, next_index = collect_line(tokens, hash_index + 1)
|
|
652
|
+
return next_index if args.empty?
|
|
653
|
+
|
|
654
|
+
name = args[0]
|
|
655
|
+
body = args[1..]
|
|
656
|
+
if name.type == :identifier && CONDITIONAL_DIRECTIVES.key?(name.text)
|
|
657
|
+
dispatch_conditional(name, body, stack)
|
|
658
|
+
return next_index
|
|
659
|
+
end
|
|
660
|
+
return next_index unless active?(stack)
|
|
661
|
+
|
|
662
|
+
unless name.type == :identifier
|
|
663
|
+
raise_at(name, "invalid preprocessing directive '##{name.text}'")
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
case name.text
|
|
667
|
+
when "include" then handle_include(hash, body, output, filename)
|
|
668
|
+
when "include_next" then handle_include_next(hash, body, output, filename)
|
|
669
|
+
when "define" then handle_define(name, body)
|
|
670
|
+
when "undef" then handle_undef(name, body)
|
|
671
|
+
when "line" then handle_line(name, body)
|
|
672
|
+
when "error" then handle_error(hash, body)
|
|
673
|
+
when "pragma" then handle_pragma(body, filename)
|
|
674
|
+
else
|
|
675
|
+
raise_at(name, "invalid preprocessing directive '##{name.text}'")
|
|
676
|
+
end
|
|
677
|
+
next_index
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
# --- conditional inclusion (6.10.1) ----------------------------------------
|
|
681
|
+
|
|
682
|
+
def dispatch_conditional(name, body, stack)
|
|
683
|
+
case name.text
|
|
684
|
+
when "if" then handle_if(name, body, stack, :if)
|
|
685
|
+
when "ifdef" then handle_if(name, body, stack, :ifdef)
|
|
686
|
+
when "ifndef" then handle_if(name, body, stack, :ifndef)
|
|
687
|
+
when "elif" then handle_elif(name, body, stack)
|
|
688
|
+
when "else" then handle_else(name, stack)
|
|
689
|
+
when "endif" then handle_endif(name, stack)
|
|
690
|
+
end
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
# Opens a new conditional frame. Its first group's condition is only
|
|
694
|
+
# evaluated when the enclosing context is active; inside a skipped region
|
|
695
|
+
# the frame is pushed inert (never taken, so no #elif/#else can revive it)
|
|
696
|
+
# and the condition is left unread, so an undefined name or bad expression
|
|
697
|
+
# there is not diagnosed (6.10.1p6, and matching gcc).
|
|
698
|
+
def handle_if(name, body, stack, kind)
|
|
699
|
+
if stack.length >= CONDITIONAL_NESTING_LIMIT
|
|
700
|
+
raise_at(name, "#{name.text} directives nested too deeply")
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
if active?(stack)
|
|
704
|
+
condition = evaluate_group(name, body, kind)
|
|
705
|
+
stack.push(Frame.new(condition, condition, false, true, name))
|
|
706
|
+
else
|
|
707
|
+
stack.push(Frame.new(false, true, false, false, name))
|
|
708
|
+
end
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
def evaluate_group(name, body, kind)
|
|
712
|
+
case kind
|
|
713
|
+
when :if then evaluate_if_expression(name, body)
|
|
714
|
+
when :ifdef then defined_condition(name, body, true)
|
|
715
|
+
when :ifndef then defined_condition(name, body, false)
|
|
716
|
+
end
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
def handle_elif(name, body, stack)
|
|
720
|
+
raise_at(name, "#elif without #if") if stack.empty?
|
|
721
|
+
|
|
722
|
+
frame = stack.last
|
|
723
|
+
raise_at(name, "#elif after #else") if frame.seen_else
|
|
724
|
+
# Only evaluate when the enclosing context is active and no earlier group
|
|
725
|
+
# of this #if was taken; otherwise this branch cannot win, so its
|
|
726
|
+
# expression is left unevaluated (and undiagnosed).
|
|
727
|
+
if frame.parent_active && !frame.taken
|
|
728
|
+
condition = evaluate_if_expression(name, body)
|
|
729
|
+
frame.active = condition
|
|
730
|
+
frame.taken = true if condition
|
|
731
|
+
else
|
|
732
|
+
frame.active = false
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
def handle_else(name, stack)
|
|
737
|
+
raise_at(name, "#else without #if") if stack.empty?
|
|
738
|
+
|
|
739
|
+
frame = stack.last
|
|
740
|
+
raise_at(name, "#else after #else") if frame.seen_else
|
|
741
|
+
frame.seen_else = true
|
|
742
|
+
# The else group is taken exactly when the context is active and nothing
|
|
743
|
+
# earlier was; either way no further group of this #if can be selected.
|
|
744
|
+
frame.active = frame.parent_active && !frame.taken
|
|
745
|
+
frame.taken = true
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
def handle_endif(name, stack)
|
|
749
|
+
raise_at(name, "#endif without #if") if stack.empty?
|
|
750
|
+
|
|
751
|
+
stack.pop
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
# #ifdef/#ifndef NAME: NAME must be a lone identifier, and the frame is
|
|
755
|
+
# active when the macro's presence in the table matches the sense wanted.
|
|
756
|
+
def defined_condition(name, body, want_defined)
|
|
757
|
+
raise_at(name, "no macro name given in ##{name.text} directive") if body.empty?
|
|
758
|
+
|
|
759
|
+
macro = body[0]
|
|
760
|
+
raise_at(macro, "macro names must be identifiers") unless macro.type == :identifier
|
|
761
|
+
raise_at(body[1], "extra tokens at end of ##{name.text} directive") if body.length > 1
|
|
762
|
+
# gcc makes "#ifdef" of the __has_* query operators true, and a header
|
|
763
|
+
# uses that to prefer the operator over a config.h fallback. They are
|
|
764
|
+
# safe to expose because each answers honestly — __has_builtin says 0
|
|
765
|
+
# for a builtin rubycc lacks, and a header __has_include reports present
|
|
766
|
+
# is one rubycc can now compile (the gcc alternate keywords the kernel
|
|
767
|
+
# UAPI headers lean on, "__signed__" and kin, are recognized).
|
|
768
|
+
defined_macro_name?(macro.text) == want_defined
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
# Whether `defined`/#ifdef/#ifndef sees NAME as a macro (6.10.8p1): an
|
|
772
|
+
# ordinary entry in the macro table, one of the self-supplied builtins
|
|
773
|
+
# (__FILE__ etc., 6.10.8.1) that never occupies a table slot because its
|
|
774
|
+
# value is computed at the use site, or a __has_* query operator (gcc
|
|
775
|
+
# extension both directives share, see query_operator_name?).
|
|
776
|
+
def defined_macro_name?(name)
|
|
777
|
+
@macros.key?(name) || BUILTIN_MACROS.key?(name) || query_operator_name?(name)
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
# Evaluates a #if/#elif controlling constant-expression (6.10.1) to a
|
|
781
|
+
# boolean. In order: fold each preprocessor operator (`defined` and the
|
|
782
|
+
# `__has_*` queries) to 1/0 with its operand unexpanded, macro-expand what
|
|
783
|
+
# remains, fold operators a second time (a header may hide a __has_* query
|
|
784
|
+
# behind a macro, e.g. RBIMPL_HAS_BUILTIN(x) -> __has_builtin(x), so the
|
|
785
|
+
# operator only surfaces after expansion; gcc likewise re-honors a
|
|
786
|
+
# macro-produced `defined`), then replace every surviving identifier with 0,
|
|
787
|
+
# convert to ordinary tokens, and evaluate the parsed expression with the
|
|
788
|
+
# shared constant evaluator. A non-zero result is true. One post-expansion
|
|
789
|
+
# pass suffices: fold_operators never expands its own operands, so a query
|
|
790
|
+
# it uncovers cannot itself reveal another.
|
|
791
|
+
def evaluate_if_expression(directive, body)
|
|
792
|
+
raise_at(directive, "##{directive.text} with no expression") if body.empty?
|
|
793
|
+
|
|
794
|
+
expanded = []
|
|
795
|
+
expand_tokens(fold_operators(body), expanded)
|
|
796
|
+
refolded = fold_operators(expanded)
|
|
797
|
+
neutral = refolded.map { |tok| tok.type == :identifier ? number_zero(tok) : tok }
|
|
798
|
+
tokens = to_front_tokens(neutral, directive)
|
|
799
|
+
node = ConstantExpressionParser.new(tokens, directive.text).parse
|
|
800
|
+
evaluate_constant(node)
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
# Converts the neutralized expression tokens to the Front::Token stream the
|
|
804
|
+
# expression parser consumes, terminating it with an :eof. A floating
|
|
805
|
+
# constant is not an integer constant-expression, so it is rejected here
|
|
806
|
+
# (6.10.1p1) before it can reach the evaluator.
|
|
807
|
+
def to_front_tokens(pp_tokens, directive)
|
|
808
|
+
tokens = TokenConverter.new.convert(pp_tokens)
|
|
809
|
+
floating = tokens.find { |tok| tok.type == :float }
|
|
810
|
+
raise_at_front(floating, "floating constant in preprocessor expression") if floating
|
|
811
|
+
tokens << Front::Token.new(type: :eof, value: nil, filename: directive.filename,
|
|
812
|
+
line: directive.line, column: directive.column,
|
|
813
|
+
source_line: directive.source_line)
|
|
814
|
+
tokens
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
def evaluate_constant(node)
|
|
818
|
+
Front::ConstantEvaluator.evaluate(node) != 0
|
|
819
|
+
rescue Front::ConstantEvaluator::DivisionByZero => e
|
|
820
|
+
raise_at_front(e.token, "division by zero in preprocessor expression")
|
|
821
|
+
rescue Front::ConstantEvaluator::NotConstant => e
|
|
822
|
+
raise_at_front(e.token, "token is not valid in preprocessor expressions")
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
# Rewrites each preprocessor operator in a #if expression to the pp-number 1
|
|
826
|
+
# or 0, its operand left unexpanded (6.10.1p1, p4): `defined` against the
|
|
827
|
+
# macro table, `__has_include` against the include search, `__has_attribute`
|
|
828
|
+
# true only for the layout attributes rubycc honors (aligned/packed), and
|
|
829
|
+
# `__has_builtin`
|
|
830
|
+
# true only for the varargs intrinsics. Any other token passes through to be
|
|
831
|
+
# macro-expanded (an unrecognized identifier later neutralizes to 0).
|
|
832
|
+
def fold_operators(body)
|
|
833
|
+
result = []
|
|
834
|
+
index = 0
|
|
835
|
+
while index < body.length
|
|
836
|
+
tok = body[index]
|
|
837
|
+
if tok.type == :identifier && (handler = PP_OPERATORS[tok.text])
|
|
838
|
+
value, index = send(handler, tok, body, index + 1)
|
|
839
|
+
result << number_flag(tok, value)
|
|
840
|
+
else
|
|
841
|
+
result << tok
|
|
842
|
+
index += 1
|
|
843
|
+
end
|
|
844
|
+
end
|
|
845
|
+
result
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
# Each #if operator's fold method, keyed by its spelling.
|
|
849
|
+
PP_OPERATORS = {
|
|
850
|
+
"defined" => :fold_defined, "__has_include" => :fold_has_include,
|
|
851
|
+
"__has_attribute" => :fold_has_attribute, "__has_builtin" => :fold_has_builtin
|
|
852
|
+
}.freeze
|
|
853
|
+
|
|
854
|
+
def fold_defined(operator, body, index)
|
|
855
|
+
name, index = read_defined_operand(operator, body, index)
|
|
856
|
+
[defined_macro_name?(name), index]
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
# gcc treats the __has_* query operators as satisfying `defined`, so
|
|
860
|
+
# `#if defined(__has_builtin)` is 1 there; a header uses that to prefer
|
|
861
|
+
# the operator over a config.h fallback (ruby/internal/has/builtin.h).
|
|
862
|
+
# The `defined` operator itself is not answered here (only the __has_*
|
|
863
|
+
# queries), so this excludes it from PP_OPERATORS.
|
|
864
|
+
def query_operator_name?(name)
|
|
865
|
+
PP_OPERATORS.key?(name) && name != "defined"
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
# __has_include ( "f" | <f> ): true when the header resolves like the same
|
|
869
|
+
# #include would (quote relative to this file, then the search path).
|
|
870
|
+
def fold_has_include(operator, body, index)
|
|
871
|
+
raise_at(operator, "missing '(' after '__has_include'") unless body[index]&.punct?("(")
|
|
872
|
+
|
|
873
|
+
close = closing_paren(operator, body, index)
|
|
874
|
+
kind, name = parse_header_name(operator, body[(index + 1)...close])
|
|
875
|
+
[include_exists?(kind, name, operator.filename), close + 1]
|
|
876
|
+
end
|
|
877
|
+
|
|
878
|
+
# The GNU attributes rubycc gives real semantics: the layout attributes the
|
|
879
|
+
# parser honors on a struct/union (Step 28), and the constructor/destructor
|
|
880
|
+
# attributes that register a function in .init_array / .fini_array (Step
|
|
881
|
+
# 155). Every other attribute is accepted and discarded, so __has_attribute
|
|
882
|
+
# answers true only for these. Kept as a Hash (used only for membership) so
|
|
883
|
+
# the check is O(1).
|
|
884
|
+
KNOWN_ATTRIBUTES = %w[aligned packed constructor destructor].to_h { |name| [name, true] }.freeze
|
|
885
|
+
|
|
886
|
+
# __has_attribute ( X ): true for the attributes rubycc actually acts on
|
|
887
|
+
# (in either the plain or the "__name__" spelling), false for every other
|
|
888
|
+
# name. The name is normalized the same way the parser normalizes an
|
|
889
|
+
# attribute token.
|
|
890
|
+
def fold_has_attribute(operator, body, index)
|
|
891
|
+
name, index = read_paren_identifier(operator, body, index, "__has_attribute")
|
|
892
|
+
[KNOWN_ATTRIBUTES.key?(normalize_attribute_name(name)), index]
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
# Collapses a "__name__" attribute spelling to "name" (a single leading and
|
|
896
|
+
# trailing "__" pair stripped when both are present), matching the parser's
|
|
897
|
+
# #normalize_attribute_name so "__aligned__" and "aligned" agree here too.
|
|
898
|
+
def normalize_attribute_name(name)
|
|
899
|
+
if name.start_with?("__") && name.end_with?("__") && name.length > 4
|
|
900
|
+
name[2..-3]
|
|
901
|
+
else
|
|
902
|
+
name
|
|
903
|
+
end
|
|
904
|
+
end
|
|
905
|
+
|
|
906
|
+
def fold_has_builtin(operator, body, index)
|
|
907
|
+
name, index = read_paren_identifier(operator, body, index, "__has_builtin")
|
|
908
|
+
[KNOWN_BUILTINS.key?(name), index]
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
# The index of the ")" that closes the "(" at `index`; the operand between
|
|
912
|
+
# them is a header name, which has no nested parentheses.
|
|
913
|
+
def closing_paren(operator, body, index)
|
|
914
|
+
close = index + 1
|
|
915
|
+
close += 1 until body[close].nil? || body[close].punct?(")")
|
|
916
|
+
raise_at(operator, "missing ')' after '#{operator.text}'") if body[close].nil?
|
|
917
|
+
close
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
# Reads a "( identifier )" operand for the __has_* attribute/builtin queries,
|
|
921
|
+
# returning [identifier-text, index-past-")"].
|
|
922
|
+
def read_paren_identifier(operator, body, index, what)
|
|
923
|
+
name = body[index + 1]
|
|
924
|
+
unless body[index]&.punct?("(") && name&.type == :identifier && body[index + 2]&.punct?(")")
|
|
925
|
+
raise_at(operator, "operator '#{what}' requires a parenthesized identifier")
|
|
926
|
+
end
|
|
927
|
+
[name.text, index + 3]
|
|
928
|
+
end
|
|
929
|
+
|
|
930
|
+
# Whether a header name resolves without reading it: quote form beside the
|
|
931
|
+
# querying file then along the search path, angled form only the latter.
|
|
932
|
+
def include_exists?(kind, name, includer)
|
|
933
|
+
directories = []
|
|
934
|
+
directories << File.dirname(includer) if kind == :quote
|
|
935
|
+
directories.concat(@include_paths)
|
|
936
|
+
directories.any? { |dir| File.file?(File.join(dir, name)) }
|
|
937
|
+
end
|
|
938
|
+
|
|
939
|
+
# Reads the operand of a `defined` operator starting at `index`, returning
|
|
940
|
+
# [macro-name, index-past-operand]. Accepts NAME or ( NAME ).
|
|
941
|
+
def read_defined_operand(directive, body, index)
|
|
942
|
+
if body[index]&.punct?("(")
|
|
943
|
+
name = body[index + 1]
|
|
944
|
+
unless name && name.type == :identifier && body[index + 2]&.punct?(")")
|
|
945
|
+
raise_at(directive, "operator 'defined' requires an identifier")
|
|
946
|
+
end
|
|
947
|
+
[name.text, index + 3]
|
|
948
|
+
else
|
|
949
|
+
name = body[index]
|
|
950
|
+
raise_at(directive, "operator 'defined' requires an identifier") unless name&.type == :identifier
|
|
951
|
+
|
|
952
|
+
[name.text, index + 1]
|
|
953
|
+
end
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
def number_flag(site, present)
|
|
957
|
+
number_token(site, present ? "1" : "0")
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
def number_zero(site)
|
|
961
|
+
number_token(site, "0")
|
|
962
|
+
end
|
|
963
|
+
|
|
964
|
+
def number_token(site, text)
|
|
965
|
+
PPToken.new(
|
|
966
|
+
type: :pp_number, text: text,
|
|
967
|
+
filename: site.filename, line: site.line, column: site.column,
|
|
968
|
+
source_line: site.source_line, space_before: site.space_before
|
|
969
|
+
)
|
|
970
|
+
end
|
|
971
|
+
|
|
972
|
+
# A PREDEFINED_TARGET_MACROS entry: an ordinary object macro whose sole
|
|
973
|
+
# replacement token is the pp-number "1". Its position ("<built-in>", line
|
|
974
|
+
# 0) is a placeholder that no diagnostic ever surfaces: #substitute always
|
|
975
|
+
# relocates a replacement token to the invocation site via #relocate
|
|
976
|
+
# before it can reach the token stream, so this position only ever exists
|
|
977
|
+
# transiently inside the macro table.
|
|
978
|
+
def predefined_target_macro
|
|
979
|
+
token = PPToken.new(
|
|
980
|
+
type: :pp_number, text: "1",
|
|
981
|
+
filename: "<built-in>", line: 0, column: 0, source_line: ""
|
|
982
|
+
)
|
|
983
|
+
Macro.new(:object, [], false, [token])
|
|
984
|
+
end
|
|
985
|
+
|
|
986
|
+
# A PREDEFINED_NUMERIC_MACROS entry: an ordinary object macro whose
|
|
987
|
+
# replacement is `text` re-scanned into pp-tokens, so a multi-token value
|
|
988
|
+
# (like __WCHAR_MIN__'s parenthesized expression) becomes a proper token
|
|
989
|
+
# list without hand-building each token. The scanner appends an :eof (and
|
|
990
|
+
# never a newline for a single line), dropped here. As with the target
|
|
991
|
+
# macros the tokens carry a placeholder "<built-in>" location that #relocate
|
|
992
|
+
# replaces with the use site before any diagnostic could reference it.
|
|
993
|
+
def predefined_numeric_macro(text)
|
|
994
|
+
tokens = Scanner.new(text, filename: "<built-in>").scan.reject(&:eof?)
|
|
995
|
+
Macro.new(:object, [], false, tokens)
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
# The tokens of the logical line starting at `start` (up to but excluding
|
|
999
|
+
# the newline), paired with the index just past that newline. A line ended
|
|
1000
|
+
# by end-of-file yields the eof index so the caller's loop can stop there.
|
|
1001
|
+
def collect_line(tokens, start)
|
|
1002
|
+
stop = start
|
|
1003
|
+
stop += 1 until tokens[stop].newline? || tokens[stop].eof?
|
|
1004
|
+
rest = tokens[start...stop]
|
|
1005
|
+
next_index = tokens[stop].newline? ? stop + 1 : stop
|
|
1006
|
+
[rest, next_index]
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
# --- #include ----------------------------------------------------------
|
|
1010
|
+
|
|
1011
|
+
def handle_include(hash, body, output, includer)
|
|
1012
|
+
kind, name = parse_include_name(hash, body)
|
|
1013
|
+
path = resolve_include(kind, name, includer, hash)
|
|
1014
|
+
process_include(hash, name, path, output)
|
|
1015
|
+
end
|
|
1016
|
+
|
|
1017
|
+
# #include_next NAME (GNU extension): like #include, but the search for
|
|
1018
|
+
# NAME resumes just past the -I directory `includer` itself was found in,
|
|
1019
|
+
# rather than starting over from the front of the list. It exists so a
|
|
1020
|
+
# header can shadow a same-named one further down the search path while
|
|
1021
|
+
# still #including that later copy (gcc's fixinclude wrappers use it on
|
|
1022
|
+
# limits.h and syslimits.h). Both "NAME" and <NAME> are accepted, but
|
|
1023
|
+
# unlike a plain #include the quote form does not additionally search
|
|
1024
|
+
# `includer`'s own directory (gcc's behavior).
|
|
1025
|
+
def handle_include_next(hash, body, output, includer)
|
|
1026
|
+
kind, name = parse_include_name(hash, body)
|
|
1027
|
+
path = resolve_include_next(kind, name, includer, hash)
|
|
1028
|
+
process_include(hash, name, path, output)
|
|
1029
|
+
end
|
|
1030
|
+
|
|
1031
|
+
# Reads and processes the header resolved to `path`, shared by #include
|
|
1032
|
+
# and #include_next once each has found its file.
|
|
1033
|
+
def process_include(hash, name, path, output)
|
|
1034
|
+
# A header that asked for "#pragma once" is read at most once per unit; a
|
|
1035
|
+
# later #include resolving to the same file is silently skipped (6.10.6).
|
|
1036
|
+
return if @pragma_once.key?(File.expand_path(path))
|
|
1037
|
+
|
|
1038
|
+
# A guarded header whose guard macro is currently defined would emit
|
|
1039
|
+
# nothing and change nothing — skip its walk entirely (see @guard_cache).
|
|
1040
|
+
guard = @guard_cache[path]
|
|
1041
|
+
return if guard && @macros.key?(guard)
|
|
1042
|
+
|
|
1043
|
+
raise_at(hash, "#include nested too deeply") if @include_depth >= INCLUDE_DEPTH_LIMIT
|
|
1044
|
+
|
|
1045
|
+
@include_depth += 1
|
|
1046
|
+
# A #line presumption is per-file (6.10.4): the includee starts with its
|
|
1047
|
+
# own natural line/file numbering, and the includer's presumption resumes
|
|
1048
|
+
# when the include returns.
|
|
1049
|
+
saved_delta = @presumed_line_delta
|
|
1050
|
+
saved_file = @presumed_file
|
|
1051
|
+
@presumed_line_delta = 0
|
|
1052
|
+
@presumed_file = nil
|
|
1053
|
+
begin
|
|
1054
|
+
# The includee's tokens carry its own filename and line numbers (N3), so
|
|
1055
|
+
# a diagnostic raised inside it points at the header, not the includer.
|
|
1056
|
+
# A header's scan is cached under its resolved spelling: the token
|
|
1057
|
+
# array is reused read-only across re-#includes (see @scan_cache),
|
|
1058
|
+
# while the directive walk below still runs each time so conditional
|
|
1059
|
+
# inclusion sees the current macro state.
|
|
1060
|
+
tokens = @scan_cache[path] ||=
|
|
1061
|
+
Scanner.new(read_source(path, name, hash), filename: path).scan
|
|
1062
|
+
process_lines(tokens, path, output)
|
|
1063
|
+
# Analyze the guard shape once per file, after a successful walk.
|
|
1064
|
+
@guard_cache[path] = detect_include_guard(tokens) unless @guard_cache.key?(path)
|
|
1065
|
+
ensure
|
|
1066
|
+
@presumed_line_delta = saved_delta
|
|
1067
|
+
@presumed_file = saved_file
|
|
1068
|
+
@include_depth -= 1
|
|
1069
|
+
end
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
# Decides whether a scanned file has the classic include-guard shape: its
|
|
1073
|
+
# first significant line opens "#ifndef G" (or "#if !defined(G)" /
|
|
1074
|
+
# "#if !defined G") and the matching "#endif" is the last significant
|
|
1075
|
+
# thing in the file. Returns G's name, or nil for any other shape. Two
|
|
1076
|
+
# disqualifiers matter for correctness, not just fit: an #else/#elif on
|
|
1077
|
+
# the guard level would make the file emit *different* content when the
|
|
1078
|
+
# guard is defined (so skipping would be wrong), and tokens after the
|
|
1079
|
+
# closing #endif would be lost by a skip. The C23 #elifdef/#elifndef
|
|
1080
|
+
# spellings are rejected the same way, defensively — this compiler does
|
|
1081
|
+
# not process them, but a guard analysis must not assume that. Purely
|
|
1082
|
+
# syntactic over the token array: like the scan itself, the answer never
|
|
1083
|
+
# depends on macro state, so it is computed once per file.
|
|
1084
|
+
def detect_include_guard(tokens)
|
|
1085
|
+
index = 0
|
|
1086
|
+
index += 1 while tokens[index]&.newline?
|
|
1087
|
+
return nil unless tokens[index]&.punct?("#")
|
|
1088
|
+
|
|
1089
|
+
args, index = collect_line(tokens, index + 1)
|
|
1090
|
+
name = guard_macro_name(args)
|
|
1091
|
+
return nil if name.nil?
|
|
1092
|
+
|
|
1093
|
+
depth = 1
|
|
1094
|
+
at_line_start = true
|
|
1095
|
+
while index < tokens.length
|
|
1096
|
+
tok = tokens[index]
|
|
1097
|
+
break if tok.eof?
|
|
1098
|
+
|
|
1099
|
+
if tok.newline?
|
|
1100
|
+
at_line_start = true
|
|
1101
|
+
index += 1
|
|
1102
|
+
elsif at_line_start && tok.punct?("#")
|
|
1103
|
+
args, index = collect_line(tokens, index + 1)
|
|
1104
|
+
directive = args[0]
|
|
1105
|
+
next unless directive&.type == :identifier
|
|
1106
|
+
|
|
1107
|
+
case directive.text
|
|
1108
|
+
when "if", "ifdef", "ifndef"
|
|
1109
|
+
depth += 1
|
|
1110
|
+
when "elif", "else", "elifdef", "elifndef"
|
|
1111
|
+
return nil if depth == 1
|
|
1112
|
+
when "endif"
|
|
1113
|
+
depth -= 1
|
|
1114
|
+
if depth.zero?
|
|
1115
|
+
index += 1 while tokens[index]&.newline?
|
|
1116
|
+
return tokens[index].nil? || tokens[index].eof? ? name : nil
|
|
1117
|
+
end
|
|
1118
|
+
end
|
|
1119
|
+
else
|
|
1120
|
+
at_line_start = false
|
|
1121
|
+
index += 1
|
|
1122
|
+
end
|
|
1123
|
+
end
|
|
1124
|
+
# Ran out before the guard closed: malformed (or truncated) file; the
|
|
1125
|
+
# directive walk owns the diagnostic, this is simply not a guard.
|
|
1126
|
+
nil
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
# The macro a candidate guard line tests, or nil when the line is not one
|
|
1130
|
+
# of the two recognized spellings.
|
|
1131
|
+
def guard_macro_name(args)
|
|
1132
|
+
directive = args[0]
|
|
1133
|
+
return nil unless directive&.type == :identifier
|
|
1134
|
+
|
|
1135
|
+
if directive.text == "ifndef"
|
|
1136
|
+
return args[1].text if args.length == 2 && args[1].type == :identifier
|
|
1137
|
+
elsif directive.text == "if"
|
|
1138
|
+
rest = args[1..]
|
|
1139
|
+
bang = rest[0]&.punct?("!") && rest[1]&.type == :identifier && rest[1].text == "defined"
|
|
1140
|
+
return nil unless bang
|
|
1141
|
+
return rest[2].text if rest.length == 3 && rest[2].type == :identifier
|
|
1142
|
+
if rest.length == 5 && rest[2].punct?("(") && rest[3].type == :identifier && rest[4].punct?(")")
|
|
1143
|
+
return rest[3].text
|
|
1144
|
+
end
|
|
1145
|
+
end
|
|
1146
|
+
nil
|
|
1147
|
+
end
|
|
1148
|
+
|
|
1149
|
+
# Parses a #include/#include_next operand. A directly written header-name
|
|
1150
|
+
# is already complete and is kept verbatim: in particular, identifiers
|
|
1151
|
+
# inside `<...>` are not accidentally expanded. Otherwise the operand is
|
|
1152
|
+
# macro-expanded and the resulting token sequence must form a header-name
|
|
1153
|
+
# (6.10.2); this is what permits `#define H "extconf.h"` followed by
|
|
1154
|
+
# `#include H`, as emitted by mkmf for pg.
|
|
1155
|
+
def parse_include_name(hash, body)
|
|
1156
|
+
return parse_header_name(hash, body) if direct_header_name?(body)
|
|
1157
|
+
|
|
1158
|
+
expanded = []
|
|
1159
|
+
expand_tokens(body, expanded)
|
|
1160
|
+
parse_header_name(hash, expanded)
|
|
1161
|
+
end
|
|
1162
|
+
|
|
1163
|
+
# Whether `body` is already a complete quoted or angled header-name. The
|
|
1164
|
+
# scanner does not treat header-names specially, so an angled form arrives
|
|
1165
|
+
# as "<" ... ">". A malformed direct form is deliberately left for the
|
|
1166
|
+
# macro-expansion path: a macro can legally supply the missing pieces.
|
|
1167
|
+
def direct_header_name?(body)
|
|
1168
|
+
return true if body.length == 1 && body[0].type == :string
|
|
1169
|
+
|
|
1170
|
+
return false unless body.first&.punct?("<")
|
|
1171
|
+
|
|
1172
|
+
body.index { |token| token.punct?(">") } == body.length - 1
|
|
1173
|
+
end
|
|
1174
|
+
|
|
1175
|
+
# Reconstructs a header name from a complete token sequence. The
|
|
1176
|
+
# characters are taken verbatim (6.10.2), never macro-expanded here; the
|
|
1177
|
+
# caller has already decided whether expansion was needed.
|
|
1178
|
+
def parse_header_name(hash, body)
|
|
1179
|
+
raise_at(hash, "#include expects \"FILENAME\" or <FILENAME>") if body.empty?
|
|
1180
|
+
|
|
1181
|
+
first = body[0]
|
|
1182
|
+
if first.type == :string
|
|
1183
|
+
raise_at(body[1], "extra tokens at end of #include directive") if body.length > 1
|
|
1184
|
+
[:quote, first.text[1..-2]]
|
|
1185
|
+
elsif first.punct?("<")
|
|
1186
|
+
close = body.index { |t| t.punct?(">") }
|
|
1187
|
+
raise_at(first, "missing terminating > character") if close.nil?
|
|
1188
|
+
if close < body.length - 1
|
|
1189
|
+
raise_at(body[close + 1], "extra tokens at end of #include directive")
|
|
1190
|
+
end
|
|
1191
|
+
name = body[1...close].map(&:text).join
|
|
1192
|
+
raise_at(first, "empty filename in #include directive") if name.empty?
|
|
1193
|
+
[:angle, name]
|
|
1194
|
+
else
|
|
1195
|
+
raise_at(first, "#include expects \"FILENAME\" or <FILENAME>")
|
|
1196
|
+
end
|
|
1197
|
+
end
|
|
1198
|
+
|
|
1199
|
+
# Resolves a header name to a filesystem path. A quoted include is looked
|
|
1200
|
+
# for first beside the file that names it, then along the search path; an
|
|
1201
|
+
# angled include only along the search path (6.10.2p2-3). A match found
|
|
1202
|
+
# along the search path records which directory it came from, so a later
|
|
1203
|
+
# #include_next from this same file knows where to resume.
|
|
1204
|
+
#
|
|
1205
|
+
# The same header name is resolved over and over within one translation
|
|
1206
|
+
# unit (each use site re-#includes it, guarded headers included, before
|
|
1207
|
+
# Step 109's scan cache short-circuits their body), so successful lookups
|
|
1208
|
+
# are memoized in @resolve_cache. A quote include's cache key carries the
|
|
1209
|
+
# includer's directory since the "beside" candidate is includer-relative;
|
|
1210
|
+
# an angle include's key is just the name, since it never consults the
|
|
1211
|
+
# includer. A cache hit skips #record_include_origin: the first resolution
|
|
1212
|
+
# already recorded whatever origin this same path implies.
|
|
1213
|
+
def resolve_include(kind, name, includer, hash)
|
|
1214
|
+
key = kind == :quote ? [File.dirname(includer), name] : name
|
|
1215
|
+
cached = @resolve_cache[key]
|
|
1216
|
+
return cached if cached
|
|
1217
|
+
|
|
1218
|
+
if kind == :quote
|
|
1219
|
+
beside = File.join(File.dirname(includer), name)
|
|
1220
|
+
if File.file?(beside)
|
|
1221
|
+
@resolve_cache[key] = beside
|
|
1222
|
+
return beside
|
|
1223
|
+
end
|
|
1224
|
+
end
|
|
1225
|
+
|
|
1226
|
+
index, path = search_include_paths(name, 0)
|
|
1227
|
+
raise_at(hash, "#{name}: No such file or directory") unless path
|
|
1228
|
+
|
|
1229
|
+
record_include_origin(path, index)
|
|
1230
|
+
@resolve_cache[key] = path
|
|
1231
|
+
path
|
|
1232
|
+
end
|
|
1233
|
+
|
|
1234
|
+
# Resolves a header name for #include_next: search resumes one directory
|
|
1235
|
+
# past wherever `includer` itself was found along the search path. A file
|
|
1236
|
+
# with no recorded origin (the main source file, or a quote-relative
|
|
1237
|
+
# resolution beside its includer) has no "here" to resume past, so gcc
|
|
1238
|
+
# falls back to plain #include semantics for it, which this does too.
|
|
1239
|
+
def resolve_include_next(kind, name, includer, hash)
|
|
1240
|
+
origin = @include_origin[File.expand_path(includer)]
|
|
1241
|
+
return resolve_include(kind, name, includer, hash) unless origin
|
|
1242
|
+
|
|
1243
|
+
index, path = search_include_paths(name, origin + 1)
|
|
1244
|
+
raise_at(hash, "#{name}: No such file or directory") unless path
|
|
1245
|
+
|
|
1246
|
+
record_include_origin(path, index)
|
|
1247
|
+
path
|
|
1248
|
+
end
|
|
1249
|
+
|
|
1250
|
+
# The first directory in @include_paths, starting the scan at `start`,
|
|
1251
|
+
# holding `name`; returns [its index, the joined path] or [nil, nil].
|
|
1252
|
+
def search_include_paths(name, start)
|
|
1253
|
+
@include_paths.each_with_index do |dir, index|
|
|
1254
|
+
next if index < start
|
|
1255
|
+
|
|
1256
|
+
candidate = File.join(dir, name)
|
|
1257
|
+
return [index, candidate] if File.file?(candidate)
|
|
1258
|
+
end
|
|
1259
|
+
[nil, nil]
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
def record_include_origin(path, index)
|
|
1263
|
+
@include_origin[File.expand_path(path)] = index
|
|
1264
|
+
end
|
|
1265
|
+
|
|
1266
|
+
def read_source(path, name, hash)
|
|
1267
|
+
File.read(path)
|
|
1268
|
+
rescue SystemCallError
|
|
1269
|
+
raise_at(hash, "#{name}: No such file or directory")
|
|
1270
|
+
end
|
|
1271
|
+
|
|
1272
|
+
# --- #define / #undef ------------------------------------------------------
|
|
1273
|
+
|
|
1274
|
+
def handle_define(directive, body)
|
|
1275
|
+
raise_at(directive, "no macro name given in #define directive") if body.empty?
|
|
1276
|
+
|
|
1277
|
+
name = body[0]
|
|
1278
|
+
raise_at(name, "macro names must be identifiers") unless name.type == :identifier
|
|
1279
|
+
reject_reserved_name(name, "define")
|
|
1280
|
+
|
|
1281
|
+
rest = body[1..]
|
|
1282
|
+
first = rest[0]
|
|
1283
|
+
# A "(" abutting the name with no separating space opens a parameter list
|
|
1284
|
+
# (function-like); a space before it, or its absence, makes an object
|
|
1285
|
+
# macro whose replacement merely begins with that token.
|
|
1286
|
+
macro =
|
|
1287
|
+
if first && first.punct?("(") && !first.space_before
|
|
1288
|
+
parse_function_macro(name, rest)
|
|
1289
|
+
else
|
|
1290
|
+
Macro.new(:object, [], false, rest)
|
|
1291
|
+
end
|
|
1292
|
+
validate_replacement(macro)
|
|
1293
|
+
|
|
1294
|
+
existing = @macros[name.text]
|
|
1295
|
+
if existing.nil?
|
|
1296
|
+
@macros[name.text] = macro
|
|
1297
|
+
elsif !identical_macro?(existing, macro) &&
|
|
1298
|
+
!compatible_restrict_redefinition?(name.text, existing, macro) &&
|
|
1299
|
+
!system_header_redefinition?(name)
|
|
1300
|
+
# A benign redefinition (an identical definition) is allowed; a
|
|
1301
|
+
# differing one is an error (6.10.3p2, simplified to token spellings).
|
|
1302
|
+
raise_at(name, "macro '#{name.text}' redefined")
|
|
1303
|
+
else
|
|
1304
|
+
# Ruby's musl config.h and libpq's pg_config.h use the two reserved
|
|
1305
|
+
# spellings of the same restrict qualifier. Keep the later spelling
|
|
1306
|
+
# so subsequent expansion follows the header that most recently
|
|
1307
|
+
# established the platform contract.
|
|
1308
|
+
@macros[name.text] = macro
|
|
1309
|
+
end
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
# GCC accepts the musl/Ruby and libpq pair of restrict definitions as a
|
|
1313
|
+
# harmless portability difference: `__restrict__` and `__restrict` are
|
|
1314
|
+
# both recognized by the front end as the same ignored qualifier. Do not
|
|
1315
|
+
# weaken ordinary macro redefinition diagnostics for any other spelling.
|
|
1316
|
+
def compatible_restrict_redefinition?(name, existing, replacement)
|
|
1317
|
+
return false unless name == "restrict"
|
|
1318
|
+
return false unless existing.kind == :object && replacement.kind == :object
|
|
1319
|
+
|
|
1320
|
+
spellings = ["__restrict", "__restrict__"]
|
|
1321
|
+
[existing.replacement, replacement.replacement].all? do |tokens|
|
|
1322
|
+
tokens.length == 1 && tokens.first.type == :identifier && spellings.include?(tokens.first.text)
|
|
1323
|
+
end
|
|
1324
|
+
end
|
|
1325
|
+
|
|
1326
|
+
# GCC diagnoses a conflicting redefinition from a system header as a
|
|
1327
|
+
# warning, not a preprocessing error. Rubycc has no warning stream, so
|
|
1328
|
+
# keep the header's later definition and continue; this is required by
|
|
1329
|
+
# libffi, whose target header changes FFI_GO_CLOSURES after fiddle's local
|
|
1330
|
+
# compatibility header intentionally set it to zero. User-source
|
|
1331
|
+
# conflicts retain the strict diagnostic above.
|
|
1332
|
+
def system_header_redefinition?(name)
|
|
1333
|
+
filename = name.filename.to_s
|
|
1334
|
+
return false if filename.empty? || filename.start_with?("<")
|
|
1335
|
+
|
|
1336
|
+
path = File.expand_path(filename)
|
|
1337
|
+
@system_include_paths.any? { |root| path == root || path.start_with?("#{root}/") }
|
|
1338
|
+
end
|
|
1339
|
+
|
|
1340
|
+
# Parses a function-like macro's parameter list, `rest` being the tokens
|
|
1341
|
+
# after the macro name with rest[0] the opening "(". Returns the Macro, its
|
|
1342
|
+
# `replacement` the tokens past the closing ")". The list is a comma-
|
|
1343
|
+
# separated run of identifiers, optionally empty, optionally ending in a
|
|
1344
|
+
# variable part: the ISO bare "..." (spelled "__VA_ARGS__" in the body, and
|
|
1345
|
+
# "(...)" alone is allowed) or the GNU "name..." (a parameter name fused to
|
|
1346
|
+
# "...", spelled by that name in the body).
|
|
1347
|
+
def parse_function_macro(name, rest)
|
|
1348
|
+
params = []
|
|
1349
|
+
variadic = false
|
|
1350
|
+
va_name = nil
|
|
1351
|
+
index = 1
|
|
1352
|
+
unless rest[index]&.punct?(")")
|
|
1353
|
+
loop do
|
|
1354
|
+
token = rest[index]
|
|
1355
|
+
if token&.punct?("...")
|
|
1356
|
+
variadic = true
|
|
1357
|
+
va_name = "__VA_ARGS__"
|
|
1358
|
+
index += 1
|
|
1359
|
+
break
|
|
1360
|
+
elsif token&.type == :identifier
|
|
1361
|
+
raise_at(token, "duplicate macro parameter \"#{token.text}\"") if params.include?(token.text)
|
|
1362
|
+
|
|
1363
|
+
# A "name..." names the variable part; the name owns the rest
|
|
1364
|
+
# arguments and no further parameters may follow it.
|
|
1365
|
+
if rest[index + 1]&.punct?("...")
|
|
1366
|
+
variadic = true
|
|
1367
|
+
va_name = token.text
|
|
1368
|
+
index += 2
|
|
1369
|
+
break
|
|
1370
|
+
end
|
|
1371
|
+
|
|
1372
|
+
params << token.text
|
|
1373
|
+
index += 1
|
|
1374
|
+
else
|
|
1375
|
+
raise_at(token || name, "expected parameter name in macro parameter list")
|
|
1376
|
+
end
|
|
1377
|
+
|
|
1378
|
+
separator = rest[index]
|
|
1379
|
+
if separator&.punct?(",")
|
|
1380
|
+
index += 1
|
|
1381
|
+
elsif separator&.punct?(")")
|
|
1382
|
+
break
|
|
1383
|
+
else
|
|
1384
|
+
raise_at(separator || name, "expected ',' or ')' in macro parameter list")
|
|
1385
|
+
end
|
|
1386
|
+
end
|
|
1387
|
+
raise_at(rest[index] || name, "missing ')' in macro parameter list") unless rest[index]&.punct?(")")
|
|
1388
|
+
end
|
|
1389
|
+
Macro.new(:function, params, variadic, rest[(index + 1)..] || [], va_name)
|
|
1390
|
+
end
|
|
1391
|
+
|
|
1392
|
+
# Checks a replacement list for well-formed "#" and "##" placement at
|
|
1393
|
+
# definition time (6.10.3.2p1, 6.10.3.3p1). "##" may not sit at either end
|
|
1394
|
+
# of any replacement list; in a function-like macro "#" must be followed by
|
|
1395
|
+
# a parameter (or __VA_ARGS__), the only operands it can stringize. In an
|
|
1396
|
+
# object-like macro "#" is an ordinary token, so it is left unexamined.
|
|
1397
|
+
def validate_replacement(macro)
|
|
1398
|
+
rep = macro.replacement
|
|
1399
|
+
edge = rep.first if rep.first&.punct?("##")
|
|
1400
|
+
edge ||= rep.last if rep.last&.punct?("##")
|
|
1401
|
+
raise_at(edge, "'##' cannot appear at either end of a macro expansion") if edge
|
|
1402
|
+
|
|
1403
|
+
return unless macro.kind == :function
|
|
1404
|
+
|
|
1405
|
+
rep.each_with_index do |tok, index|
|
|
1406
|
+
next unless tok.punct?("#")
|
|
1407
|
+
|
|
1408
|
+
operand = rep[index + 1]
|
|
1409
|
+
raise_at(tok, "'#' is not followed by a macro parameter") unless parameter_ref?(macro, operand)
|
|
1410
|
+
end
|
|
1411
|
+
end
|
|
1412
|
+
|
|
1413
|
+
# Whether `tok` names one of `macro`'s parameters, counting the variable
|
|
1414
|
+
# part's name (ISO "__VA_ARGS__" or the GNU "name...") as a parameter; the
|
|
1415
|
+
# two spellings "#" and "##" may take.
|
|
1416
|
+
def parameter_ref?(macro, tok)
|
|
1417
|
+
return false unless tok&.type == :identifier
|
|
1418
|
+
|
|
1419
|
+
macro.params.include?(tok.text) || variadic_ref?(macro, tok)
|
|
1420
|
+
end
|
|
1421
|
+
|
|
1422
|
+
# A macro name may not shadow a builtin (6.10.8.4) nor be the "defined"
|
|
1423
|
+
# operator (6.10.1p4); both diagnose rather than silently redefine. The
|
|
1424
|
+
# libc-selecting macro is refused the same way, on either libc setting: it
|
|
1425
|
+
# records the ABI the bundled headers were pinned to at configuration
|
|
1426
|
+
# time, so a unit that could define or undefine it would read one libc's
|
|
1427
|
+
# headers while the rest of the compile assumed the other's.
|
|
1428
|
+
def reject_reserved_name(name, verb)
|
|
1429
|
+
if name.text == "defined"
|
|
1430
|
+
raise_at(name, "\"defined\" cannot be used as a macro name")
|
|
1431
|
+
elsif BUILTIN_MACROS.key?(name.text) || name.text == LIBC_MUSL_MACRO
|
|
1432
|
+
verb = verb == "define" ? "define" : "undefine"
|
|
1433
|
+
raise_at(name, "cannot #{verb} builtin macro \"#{name.text}\"")
|
|
1434
|
+
end
|
|
1435
|
+
end
|
|
1436
|
+
|
|
1437
|
+
def handle_undef(directive, body)
|
|
1438
|
+
raise_at(directive, "no macro name given in #undef directive") if body.empty?
|
|
1439
|
+
|
|
1440
|
+
name = body[0]
|
|
1441
|
+
raise_at(name, "macro names must be identifiers") unless name.type == :identifier
|
|
1442
|
+
reject_reserved_name(name, "undef")
|
|
1443
|
+
raise_at(body[1], "extra tokens at end of #undef directive") if body.length > 1
|
|
1444
|
+
# Undefining a name that was never a macro is not an error (6.10.3.5p2).
|
|
1445
|
+
@macros.delete(name.text)
|
|
1446
|
+
end
|
|
1447
|
+
|
|
1448
|
+
# Two definitions are the same when their kind, parameter names, variadic
|
|
1449
|
+
# flag, variable-part name and replacement-list spellings all agree
|
|
1450
|
+
# (6.10.3p1-2); the name distinguishes an ISO from a GNU named variadic.
|
|
1451
|
+
def identical_macro?(one, other)
|
|
1452
|
+
one.kind == other.kind && one.variadic == other.variadic &&
|
|
1453
|
+
one.va_name == other.va_name && one.params == other.params &&
|
|
1454
|
+
identical_replacement?(one.replacement, other.replacement)
|
|
1455
|
+
end
|
|
1456
|
+
|
|
1457
|
+
def identical_replacement?(one, other)
|
|
1458
|
+
return false unless one.length == other.length
|
|
1459
|
+
|
|
1460
|
+
one.zip(other).all? { |a, b| a.type == b.type && a.text == b.text }
|
|
1461
|
+
end
|
|
1462
|
+
|
|
1463
|
+
# --- #error ----------------------------------------------------------------
|
|
1464
|
+
|
|
1465
|
+
def handle_error(hash, body)
|
|
1466
|
+
# The message is the directive's tokens spelled out and single-spaced; it
|
|
1467
|
+
# is never macro-expanded (matching gcc).
|
|
1468
|
+
message = body.map(&:text).join(" ")
|
|
1469
|
+
raise_at(hash, message.empty? ? "#error" : message)
|
|
1470
|
+
end
|
|
1471
|
+
|
|
1472
|
+
# --- #line -----------------------------------------------------------------
|
|
1473
|
+
|
|
1474
|
+
# Acts on a "#line" directive (6.10.4). Its arguments are macro-expanded
|
|
1475
|
+
# first (6.10.4p5), then read as a digit sequence and an optional string:
|
|
1476
|
+
# the *next* source line is presumed to be that line number, and the
|
|
1477
|
+
# presumed file name is changed when a string is given (kept otherwise).
|
|
1478
|
+
# Both feed __LINE__ / __FILE__ (see #expand_builtin) and diagnostic
|
|
1479
|
+
# wording; the physical tokens keep their real positions, only the
|
|
1480
|
+
# presumption changes. A file that generates code with #line markers (e.g.
|
|
1481
|
+
# gperf's zonetab.h "#line 1 \"zonetab.list\"") relies on this being
|
|
1482
|
+
# accepted rather than a hard error.
|
|
1483
|
+
def handle_line(directive, body)
|
|
1484
|
+
raise_at(directive, "#line directive requires a positive integer argument") if body.empty?
|
|
1485
|
+
|
|
1486
|
+
expanded = []
|
|
1487
|
+
expand_tokens(body, expanded)
|
|
1488
|
+
|
|
1489
|
+
number = expanded[0]
|
|
1490
|
+
unless number&.type == :pp_number && number.text.match?(/\A[0-9]+\z/)
|
|
1491
|
+
raise_at(number || directive, "#line directive requires a positive integer argument")
|
|
1492
|
+
end
|
|
1493
|
+
line_no = number.text.to_i
|
|
1494
|
+
|
|
1495
|
+
presumed_file = @presumed_file
|
|
1496
|
+
rest = expanded[1..]
|
|
1497
|
+
unless rest.empty?
|
|
1498
|
+
file = rest[0]
|
|
1499
|
+
unless file.type == :string && file.text.start_with?('"')
|
|
1500
|
+
raise_at(file, "invalid filename in #line directive")
|
|
1501
|
+
end
|
|
1502
|
+
raise_at(rest[1], "extra tokens at end of #line directive") if rest.length > 1
|
|
1503
|
+
presumed_file = decode_line_filename(file.text)
|
|
1504
|
+
end
|
|
1505
|
+
|
|
1506
|
+
# The next physical line (the directive's line + 1) is presumed to be
|
|
1507
|
+
# `line_no`, so a token on physical line P presents as
|
|
1508
|
+
# line_no + (P - (directive.line + 1)) = P + (line_no - directive.line - 1).
|
|
1509
|
+
@presumed_line_delta = line_no - directive.line - 1
|
|
1510
|
+
@presumed_file = presumed_file
|
|
1511
|
+
end
|
|
1512
|
+
|
|
1513
|
+
# The raw name a #line filename string literal denotes: its surrounding
|
|
1514
|
+
# quotes removed and its \" and \\ escapes resolved, so __FILE__ (which
|
|
1515
|
+
# re-escapes a raw value) reproduces the original name.
|
|
1516
|
+
def decode_line_filename(spelling)
|
|
1517
|
+
spelling[1..-2].gsub(/\\(["\\])/, '\1')
|
|
1518
|
+
end
|
|
1519
|
+
|
|
1520
|
+
# --- #pragma ---------------------------------------------------------------
|
|
1521
|
+
|
|
1522
|
+
# Acts on a "#pragma" (6.10.6). "#pragma once" records the current file so a
|
|
1523
|
+
# future #include of it is skipped; every other pragma (including a bare
|
|
1524
|
+
# one) is accepted and discarded. The "_Pragma" operator form (6.10.9) is
|
|
1525
|
+
# handled during rescanning; see #consume_pragma_operator.
|
|
1526
|
+
def handle_pragma(body, filename)
|
|
1527
|
+
first = body[0]
|
|
1528
|
+
return unless first&.type == :identifier && first.text == "once"
|
|
1529
|
+
|
|
1530
|
+
@pragma_once[File.expand_path(filename)] = true
|
|
1531
|
+
end
|
|
1532
|
+
|
|
1533
|
+
# --- macro expansion -------------------------------------------------------
|
|
1534
|
+
|
|
1535
|
+
# Expands `tokens` into `output` by rescanning (6.10.3.4). The tokens are
|
|
1536
|
+
# driven through a work queue: the head is examined and, when it names a
|
|
1537
|
+
# macro it is not painted against, replaced in place by its (rescannable)
|
|
1538
|
+
# substitution, which is pushed back on the front so the next turn sees it.
|
|
1539
|
+
# Anything that is not an active macro name is emitted; newlines are the
|
|
1540
|
+
# inter-line glue of a gathered run and never reach the output.
|
|
1541
|
+
#
|
|
1542
|
+
# Painting is per token: an object macro, and every literal token of a
|
|
1543
|
+
# function macro's replacement, is painted with the macro's own name added
|
|
1544
|
+
# to the token it came from, so that name cannot expand again through it
|
|
1545
|
+
# (this alone halts self-reference and mutual recursion). Argument tokens
|
|
1546
|
+
# keep their own painting untouched, which is what lets a macro name that
|
|
1547
|
+
# arrives from an argument still expand in its new surroundings.
|
|
1548
|
+
def expand_tokens(tokens, output)
|
|
1549
|
+
queue = tokens.dup
|
|
1550
|
+
until queue.empty?
|
|
1551
|
+
tok = queue.shift
|
|
1552
|
+
# Charge one unit of the whole-run expansion budget per token examined.
|
|
1553
|
+
# A macro's substitution is pushed back onto the queue and re-examined,
|
|
1554
|
+
# so an exponentially expanding macro is charged for every token it
|
|
1555
|
+
# generates and trips this ceiling instead of running away.
|
|
1556
|
+
@expansion_tokens += 1
|
|
1557
|
+
if @expansion_tokens > EXPANSION_TOKEN_LIMIT
|
|
1558
|
+
raise_at(tok, "macro expansion is too large (possible runaway or exponentially expanding macro)")
|
|
1559
|
+
end
|
|
1560
|
+
if pragma_operator?(tok)
|
|
1561
|
+
consume_pragma_operator(tok, queue)
|
|
1562
|
+
elsif expandable_builtin?(tok)
|
|
1563
|
+
queue.unshift(*expand_builtin(tok))
|
|
1564
|
+
elsif expandable_macro?(tok)
|
|
1565
|
+
macro = @macros[tok.text]
|
|
1566
|
+
if macro.kind == :object
|
|
1567
|
+
queue.unshift(*substitute(tok, macro, nil))
|
|
1568
|
+
else
|
|
1569
|
+
expand_function_macro(tok, macro, queue, output)
|
|
1570
|
+
end
|
|
1571
|
+
elsif !tok.newline?
|
|
1572
|
+
output << tok
|
|
1573
|
+
end
|
|
1574
|
+
end
|
|
1575
|
+
end
|
|
1576
|
+
|
|
1577
|
+
# Whether `tok` should expand now: an identifier naming a macro that its own
|
|
1578
|
+
# painting does not forbid (6.10.3.4).
|
|
1579
|
+
def expandable_macro?(tok)
|
|
1580
|
+
tok.type == :identifier && @macros.key?(tok.text) && !tok.suppress.include?(tok.text)
|
|
1581
|
+
end
|
|
1582
|
+
|
|
1583
|
+
# Whether `tok` names a compiler-supplied macro (6.10.8). These cannot be
|
|
1584
|
+
# redefined (rejected at #define), so the user table never hides one; and
|
|
1585
|
+
# each expands to a non-identifier, so it can neither recurse nor need paint.
|
|
1586
|
+
def expandable_builtin?(tok)
|
|
1587
|
+
tok.type == :identifier && BUILTIN_MACROS.key?(tok.text)
|
|
1588
|
+
end
|
|
1589
|
+
|
|
1590
|
+
# Whether `tok` is the "_Pragma" operator (6.10.9). It is handled during
|
|
1591
|
+
# rescanning, not as a directive, so it works whether written literally or
|
|
1592
|
+
# produced by macro expansion (Ruby's config.h defines its symbol-export
|
|
1593
|
+
# markers as "_Pragma(...)"), which is exactly the case that must be
|
|
1594
|
+
# accepted for <ruby.h> to preprocess.
|
|
1595
|
+
def pragma_operator?(tok)
|
|
1596
|
+
tok.type == :identifier && tok.text == "_Pragma"
|
|
1597
|
+
end
|
|
1598
|
+
|
|
1599
|
+
# Consumes a "_Pragma ( string-literal )" operator and drops all four
|
|
1600
|
+
# tokens. The operand would destringize into a #pragma line, but the only
|
|
1601
|
+
# pragma this compiler acts on is "once", which is meaningless mid-file via
|
|
1602
|
+
# _Pragma, so every operand is accepted and ignored (ROADMAP's "accept
|
|
1603
|
+
# only"). Newlines between the tokens are inter-token whitespace. A "_Pragma"
|
|
1604
|
+
# not followed by a parenthesized string literal is a hard error.
|
|
1605
|
+
def consume_pragma_operator(operator, queue)
|
|
1606
|
+
skip_queued_newlines(queue)
|
|
1607
|
+
malformed_pragma(operator) unless queue.first&.punct?("(")
|
|
1608
|
+
queue.shift # "("
|
|
1609
|
+
skip_queued_newlines(queue)
|
|
1610
|
+
malformed_pragma(operator) unless queue.first&.type == :string
|
|
1611
|
+
queue.shift # the string literal (ignored)
|
|
1612
|
+
skip_queued_newlines(queue)
|
|
1613
|
+
malformed_pragma(operator) unless queue.first&.punct?(")")
|
|
1614
|
+
queue.shift # ")"
|
|
1615
|
+
end
|
|
1616
|
+
|
|
1617
|
+
def skip_queued_newlines(queue)
|
|
1618
|
+
queue.shift while queue.first&.newline?
|
|
1619
|
+
end
|
|
1620
|
+
|
|
1621
|
+
def malformed_pragma(operator)
|
|
1622
|
+
raise_at(operator, "_Pragma takes a parenthesized string literal")
|
|
1623
|
+
end
|
|
1624
|
+
|
|
1625
|
+
# The single token a builtin macro stands for at its use site: the current
|
|
1626
|
+
# file and line (6.10.8.1), or a fixed conformance constant. __FILE__ is the
|
|
1627
|
+
# source name as a string literal with " and \ escaped; __LINE__ the line as
|
|
1628
|
+
# a preprocessing number.
|
|
1629
|
+
def expand_builtin(tok)
|
|
1630
|
+
token =
|
|
1631
|
+
case tok.text
|
|
1632
|
+
# __FILE__/__LINE__ honor any #line presumption in force (6.10.4): the
|
|
1633
|
+
# presumed file overrides the token's own, and the presumed-line delta
|
|
1634
|
+
# shifts its physical line. Both are the identity until a #line runs.
|
|
1635
|
+
when "__FILE__" then string_token(tok, @presumed_file || tok.filename)
|
|
1636
|
+
when "__LINE__" then number_token(tok, (tok.line + @presumed_line_delta).to_s)
|
|
1637
|
+
when "__STDC__" then number_token(tok, "1")
|
|
1638
|
+
when "__STDC_VERSION__" then number_token(tok, "201112L")
|
|
1639
|
+
when "__RUBYCC__" then number_token(tok, "1")
|
|
1640
|
+
end
|
|
1641
|
+
[token]
|
|
1642
|
+
end
|
|
1643
|
+
|
|
1644
|
+
# A string-literal preprocessing token whose spelling encodes `value`, its
|
|
1645
|
+
# embedded " and \ escaped so the converter reads back the original text.
|
|
1646
|
+
def string_token(site, value)
|
|
1647
|
+
PPToken.new(
|
|
1648
|
+
type: :string, text: quote(value),
|
|
1649
|
+
filename: site.filename, line: site.line, column: site.column,
|
|
1650
|
+
source_line: site.source_line, space_before: site.space_before
|
|
1651
|
+
)
|
|
1652
|
+
end
|
|
1653
|
+
|
|
1654
|
+
def quote(value)
|
|
1655
|
+
"\"#{value.gsub(/["\\]/) { |ch| "\\#{ch}" }}\""
|
|
1656
|
+
end
|
|
1657
|
+
|
|
1658
|
+
# Handles a function-like macro name pulled from the queue. A name not
|
|
1659
|
+
# followed (across any newlines) by "(" is a plain identifier, not a call
|
|
1660
|
+
# (6.10.3p10), so it is emitted as is. Otherwise the arguments up to the
|
|
1661
|
+
# matching ")" are consumed and checked, then the painted substitution is
|
|
1662
|
+
# pushed back on the queue for rescanning. Arguments are carried both raw
|
|
1663
|
+
# (for "#"/"##") and, lazily, pre-expanded, so each is expanded at most once.
|
|
1664
|
+
def expand_function_macro(tok, macro, queue, output)
|
|
1665
|
+
unless call_follows?(queue)
|
|
1666
|
+
output << tok
|
|
1667
|
+
return
|
|
1668
|
+
end
|
|
1669
|
+
|
|
1670
|
+
raw, commas = collect_arguments(tok, queue)
|
|
1671
|
+
raw = match_arity(tok, macro, raw)
|
|
1672
|
+
invocation = Invocation.new(raw, commas, Array.new(raw.length))
|
|
1673
|
+
queue.unshift(*substitute(tok, macro, invocation))
|
|
1674
|
+
end
|
|
1675
|
+
|
|
1676
|
+
# Whether the next non-newline token waiting in `queue` opens an argument
|
|
1677
|
+
# list; a call may sit on a later line than its macro name.
|
|
1678
|
+
def call_follows?(queue)
|
|
1679
|
+
index = 0
|
|
1680
|
+
index += 1 while queue[index]&.newline?
|
|
1681
|
+
queue[index] ? queue[index].punct?("(") : false
|
|
1682
|
+
end
|
|
1683
|
+
|
|
1684
|
+
# Consumes the argument list of a call: the leading newlines and "(" already
|
|
1685
|
+
# confirmed by #call_follows?, then tokens up to the matching ")", split on
|
|
1686
|
+
# top-level commas. Nested parentheses are balanced so a comma or ")" inside
|
|
1687
|
+
# them belongs to an argument, and a newline is inter-token space that is
|
|
1688
|
+
# dropped. Returns [arguments, commas]: the argument token lists and the
|
|
1689
|
+
# separating comma tokens themselves (kept so #__VA_ARGS__ can reproduce the
|
|
1690
|
+
# exact spelling). Running out of tokens is an unterminated invocation.
|
|
1691
|
+
def collect_arguments(tok, queue)
|
|
1692
|
+
queue.shift while queue.first&.newline?
|
|
1693
|
+
queue.shift # the "("
|
|
1694
|
+
arguments = []
|
|
1695
|
+
commas = []
|
|
1696
|
+
current = []
|
|
1697
|
+
depth = 0
|
|
1698
|
+
loop do
|
|
1699
|
+
raise_at(tok, "unterminated function-like macro invocation") if queue.empty?
|
|
1700
|
+
|
|
1701
|
+
token = queue.shift
|
|
1702
|
+
if token.newline?
|
|
1703
|
+
next
|
|
1704
|
+
elsif depth.zero? && token.punct?(")")
|
|
1705
|
+
arguments << current
|
|
1706
|
+
return [arguments, commas]
|
|
1707
|
+
elsif depth.zero? && token.punct?(",")
|
|
1708
|
+
arguments << current
|
|
1709
|
+
commas << token
|
|
1710
|
+
current = []
|
|
1711
|
+
else
|
|
1712
|
+
if token.punct?("(")
|
|
1713
|
+
depth += 1
|
|
1714
|
+
if depth > MACRO_ARGUMENT_NESTING_LIMIT
|
|
1715
|
+
raise_at(token, "macro argument parentheses nested too deeply")
|
|
1716
|
+
end
|
|
1717
|
+
end
|
|
1718
|
+
depth -= 1 if token.punct?(")")
|
|
1719
|
+
current << token
|
|
1720
|
+
end
|
|
1721
|
+
end
|
|
1722
|
+
end
|
|
1723
|
+
|
|
1724
|
+
# Checks a call's argument count against the macro and returns the argument
|
|
1725
|
+
# list to substitute. A parameterless macro admits only "F()", which the
|
|
1726
|
+
# collector reports as one empty argument and which is normalized to none. A
|
|
1727
|
+
# variadic macro needs at least its named parameters, its variable part
|
|
1728
|
+
# possibly empty; a plain one needs exactly its parameters.
|
|
1729
|
+
def match_arity(tok, macro, arguments)
|
|
1730
|
+
named = macro.params.length
|
|
1731
|
+
if named.zero? && !macro.variadic
|
|
1732
|
+
return [] if arguments.length == 1 && arguments[0].empty?
|
|
1733
|
+
|
|
1734
|
+
raise_at(tok, "macro \"#{tok.text}\" passed #{arguments.length} arguments, but takes just 0")
|
|
1735
|
+
elsif macro.variadic
|
|
1736
|
+
raise_at(tok, arity_message(tok, arguments.length, named, "at least")) if arguments.length < named
|
|
1737
|
+
elsif arguments.length != named
|
|
1738
|
+
raise_at(tok, arity_message(tok, arguments.length, named, "exactly"))
|
|
1739
|
+
end
|
|
1740
|
+
arguments
|
|
1741
|
+
end
|
|
1742
|
+
|
|
1743
|
+
def arity_message(tok, given, wanted, qualifier)
|
|
1744
|
+
"macro \"#{tok.text}\" requires #{qualifier} #{wanted} arguments, but #{given} given"
|
|
1745
|
+
end
|
|
1746
|
+
|
|
1747
|
+
# Fully expands one argument's tokens in isolation (6.10.3.1) before it is
|
|
1748
|
+
# substituted, reusing the same queue algorithm; the tokens keep their own
|
|
1749
|
+
# painting, since the macro being called is not yet in play for them.
|
|
1750
|
+
def expand_argument(argument)
|
|
1751
|
+
expanded = []
|
|
1752
|
+
expand_tokens(argument, expanded)
|
|
1753
|
+
expanded
|
|
1754
|
+
end
|
|
1755
|
+
|
|
1756
|
+
# Builds a macro's substitution by walking its replacement list once
|
|
1757
|
+
# (6.10.3). `invocation` carries a function-like call's arguments, or is nil
|
|
1758
|
+
# for an object-like macro. "#" stringizes the following parameter's raw
|
|
1759
|
+
# argument; "##" pastes the token to its left onto the operand to its right;
|
|
1760
|
+
# a plain parameter becomes its pre-expanded argument, unless it abuts a
|
|
1761
|
+
# "##", where the raw argument is used instead (6.10.3.1p1); every other
|
|
1762
|
+
# token is a literal relocated to the use site and painted with the macro's
|
|
1763
|
+
# name. `span` tracks how many tokens the token just placed contributed, so
|
|
1764
|
+
# a following "##" knows its left operand (0 marks a placemarker).
|
|
1765
|
+
def substitute(tok, macro, invocation)
|
|
1766
|
+
painted = paint(tok)
|
|
1767
|
+
rep = macro.replacement
|
|
1768
|
+
result = []
|
|
1769
|
+
span = 0
|
|
1770
|
+
index = 0
|
|
1771
|
+
while index < rep.length
|
|
1772
|
+
cur = rep[index]
|
|
1773
|
+
following_paste = rep[index + 1]&.punct?("##")
|
|
1774
|
+
if macro.kind == :function && cur.punct?("#")
|
|
1775
|
+
result << stringize(cur, painted, raw_operand(macro, invocation, rep[index + 1]))
|
|
1776
|
+
span = 1
|
|
1777
|
+
index += 2
|
|
1778
|
+
elsif cur.punct?("##")
|
|
1779
|
+
operand = rep[index + 1]
|
|
1780
|
+
if gnu_comma_paste?(macro, invocation, rep[index - 1], operand)
|
|
1781
|
+
span = comma_paste(macro, invocation, result, tok, painted)
|
|
1782
|
+
else
|
|
1783
|
+
right = paste_operand(macro, invocation, operand, tok, painted)
|
|
1784
|
+
span = paste(result, span, right, tok, painted)
|
|
1785
|
+
end
|
|
1786
|
+
index += 2
|
|
1787
|
+
else
|
|
1788
|
+
placed = replacement_tokens(macro, invocation, cur, tok, painted, raw: following_paste)
|
|
1789
|
+
result.concat(placed)
|
|
1790
|
+
span = placed.length
|
|
1791
|
+
index += 1
|
|
1792
|
+
end
|
|
1793
|
+
end
|
|
1794
|
+
result
|
|
1795
|
+
end
|
|
1796
|
+
|
|
1797
|
+
# The tokens a plain (non-operator) replacement element expands to: a
|
|
1798
|
+
# parameter's argument (raw when it abuts "##", else pre-expanded and
|
|
1799
|
+
# memoized), the variable arguments for __VA_ARGS__, or the element itself
|
|
1800
|
+
# relocated and painted.
|
|
1801
|
+
def replacement_tokens(macro, invocation, rep, site, painted, raw:)
|
|
1802
|
+
param = parameter_index(macro, rep)
|
|
1803
|
+
if param
|
|
1804
|
+
raw ? invocation.raw[param] : expanded_argument(invocation, param)
|
|
1805
|
+
elsif variadic_ref?(macro, rep)
|
|
1806
|
+
raw ? raw_variadic(macro, invocation) : variable_arguments(site, macro, invocation, painted)
|
|
1807
|
+
else
|
|
1808
|
+
[relocate(rep, site, painted)]
|
|
1809
|
+
end
|
|
1810
|
+
end
|
|
1811
|
+
|
|
1812
|
+
# The raw (unexpanded) tokens the "#"/"##" operand `operand` denotes: a
|
|
1813
|
+
# parameter's argument or the reconstructed variable-argument sequence. The
|
|
1814
|
+
# operand was checked at #define time to be a parameter or __VA_ARGS__.
|
|
1815
|
+
def raw_operand(macro, invocation, operand)
|
|
1816
|
+
param = parameter_index(macro, operand)
|
|
1817
|
+
param ? invocation.raw[param] : raw_variadic(macro, invocation)
|
|
1818
|
+
end
|
|
1819
|
+
|
|
1820
|
+
# The tokens forming the right side of a "##": a parameter's raw argument
|
|
1821
|
+
# (possibly empty, i.e. a placemarker), or a single relocated literal.
|
|
1822
|
+
def paste_operand(macro, invocation, operand, site, painted)
|
|
1823
|
+
if invocation && parameter_ref?(macro, operand)
|
|
1824
|
+
raw_operand(macro, invocation, operand)
|
|
1825
|
+
else
|
|
1826
|
+
[relocate(operand, site, painted)]
|
|
1827
|
+
end
|
|
1828
|
+
end
|
|
1829
|
+
|
|
1830
|
+
def parameter_index(macro, rep)
|
|
1831
|
+
rep.type == :identifier ? macro.params.index(rep.text) : nil
|
|
1832
|
+
end
|
|
1833
|
+
|
|
1834
|
+
# Whether `rep` is the identifier standing for the variable arguments: the
|
|
1835
|
+
# macro's `va_name` (ISO "__VA_ARGS__" or the GNU written name). In a named
|
|
1836
|
+
# form "__VA_ARGS__" is an ordinary identifier and does not match here.
|
|
1837
|
+
def variadic_ref?(macro, rep)
|
|
1838
|
+
macro.variadic && rep.type == :identifier && rep.text == macro.va_name
|
|
1839
|
+
end
|
|
1840
|
+
|
|
1841
|
+
# One argument's fully expanded tokens, computed on first use and cached in
|
|
1842
|
+
# the invocation so a parameter named several times expands only once.
|
|
1843
|
+
def expanded_argument(invocation, index)
|
|
1844
|
+
invocation.expanded[index] ||= expand_argument(invocation.raw[index])
|
|
1845
|
+
end
|
|
1846
|
+
|
|
1847
|
+
# The tokens __VA_ARGS__ stands for in a plain position: the pre-expanded
|
|
1848
|
+
# variable arguments (those past the named parameters) laid end to end,
|
|
1849
|
+
# separated by comma tokens spelled at the call site and painted as literals.
|
|
1850
|
+
def variable_arguments(site, macro, invocation, painted)
|
|
1851
|
+
result = []
|
|
1852
|
+
(macro.params.length...invocation.raw.length).each_with_index do |arg, position|
|
|
1853
|
+
result << comma_token(site, painted) if position.positive?
|
|
1854
|
+
result.concat(expanded_argument(invocation, arg))
|
|
1855
|
+
end
|
|
1856
|
+
result
|
|
1857
|
+
end
|
|
1858
|
+
|
|
1859
|
+
# The raw variable-argument tokens for a "#"/"##" operand: the unexpanded
|
|
1860
|
+
# arguments past the named parameters, rejoined by the very comma tokens the
|
|
1861
|
+
# call used, so #__VA_ARGS__ reproduces the source spacing exactly.
|
|
1862
|
+
def raw_variadic(macro, invocation)
|
|
1863
|
+
named = macro.params.length
|
|
1864
|
+
result = []
|
|
1865
|
+
(named...invocation.raw.length).each do |arg|
|
|
1866
|
+
result << invocation.commas[arg - 1] if arg > named
|
|
1867
|
+
result.concat(invocation.raw[arg])
|
|
1868
|
+
end
|
|
1869
|
+
result
|
|
1870
|
+
end
|
|
1871
|
+
|
|
1872
|
+
# Stringizes an operand's raw tokens into a string-literal token (6.10.3.2):
|
|
1873
|
+
# the token spellings joined with a single space wherever the source had
|
|
1874
|
+
# whitespace and none at the ends, with " and \ escaped inside string and
|
|
1875
|
+
# character tokens. The result sits at the use site under the literal paint.
|
|
1876
|
+
def stringize(hash, painted, tokens)
|
|
1877
|
+
inner = +""
|
|
1878
|
+
tokens.each_with_index do |t, index|
|
|
1879
|
+
inner << " " if index.positive? && t.space_before
|
|
1880
|
+
inner << stringized_spelling(t)
|
|
1881
|
+
end
|
|
1882
|
+
PPToken.new(
|
|
1883
|
+
type: :string, text: "\"#{inner}\"",
|
|
1884
|
+
filename: hash.filename, line: hash.line, column: hash.column,
|
|
1885
|
+
source_line: hash.source_line, space_before: hash.space_before, suppress: painted
|
|
1886
|
+
)
|
|
1887
|
+
end
|
|
1888
|
+
|
|
1889
|
+
# A token's contribution to a stringized argument: verbatim, except inside a
|
|
1890
|
+
# string literal or character constant, where each " and \ gains a backslash.
|
|
1891
|
+
def stringized_spelling(token)
|
|
1892
|
+
if token.type == :string || token.type == :char
|
|
1893
|
+
token.text.gsub(/["\\]/) { |ch| "\\#{ch}" }
|
|
1894
|
+
else
|
|
1895
|
+
token.text
|
|
1896
|
+
end
|
|
1897
|
+
end
|
|
1898
|
+
|
|
1899
|
+
# Concatenates a "##"'s two operands (6.10.3.3). `left_span` is how many
|
|
1900
|
+
# tokens the left operand placed; 0 means it was a placemarker, so the paste
|
|
1901
|
+
# is just the right operand, and an empty right operand likewise leaves the
|
|
1902
|
+
# left alone. Otherwise the left operand's last token and the right's first
|
|
1903
|
+
# are spelled together and re-lexed into one token. Returns the new span so
|
|
1904
|
+
# a chained "##" pastes onto this result. Left to right by construction.
|
|
1905
|
+
def paste(result, left_span, right, site, painted)
|
|
1906
|
+
return concat_span(result, right) if left_span.zero?
|
|
1907
|
+
return left_span if right.empty?
|
|
1908
|
+
|
|
1909
|
+
left = result.pop
|
|
1910
|
+
result << fuse(left, right.first, site, painted)
|
|
1911
|
+
result.concat(right[1..])
|
|
1912
|
+
right.length
|
|
1913
|
+
end
|
|
1914
|
+
|
|
1915
|
+
def concat_span(result, tokens)
|
|
1916
|
+
result.concat(tokens)
|
|
1917
|
+
tokens.length
|
|
1918
|
+
end
|
|
1919
|
+
|
|
1920
|
+
# Whether a "##" is the GNU comma-paste ", ## <variable-arguments>": a
|
|
1921
|
+
# literal comma written immediately to its left and the variable-part name
|
|
1922
|
+
# immediately to its right, inside a function-like call. Only this exact
|
|
1923
|
+
# shape overrides the ordinary paste; every other "##" keeps its meaning.
|
|
1924
|
+
def gnu_comma_paste?(macro, invocation, left, operand)
|
|
1925
|
+
invocation && left&.punct?(",") && variadic_ref?(macro, operand)
|
|
1926
|
+
end
|
|
1927
|
+
|
|
1928
|
+
# Carries out a GNU comma-paste. It is not a token paste: when the call
|
|
1929
|
+
# supplies no variable arguments the preceding comma, already placed as the
|
|
1930
|
+
# left operand, is dropped and nothing is spliced in. Otherwise the comma
|
|
1931
|
+
# stays and the variable arguments are spliced in expanded, exactly as a
|
|
1932
|
+
# plain use of the variable-part name would produce -- so an argument slot
|
|
1933
|
+
# that is present but empty (a trailing comma) keeps the comma. Returns the
|
|
1934
|
+
# new span.
|
|
1935
|
+
def comma_paste(macro, invocation, result, site, painted)
|
|
1936
|
+
if no_variable_arguments?(macro, invocation)
|
|
1937
|
+
result.pop if result.last&.punct?(",")
|
|
1938
|
+
0
|
|
1939
|
+
else
|
|
1940
|
+
args = variable_arguments(site, macro, invocation, painted)
|
|
1941
|
+
concat_span(result, args)
|
|
1942
|
+
end
|
|
1943
|
+
end
|
|
1944
|
+
|
|
1945
|
+
# Whether the call supplied no variable arguments (so a GNU comma-paste
|
|
1946
|
+
# drops its comma). That holds when nothing was written past the named
|
|
1947
|
+
# parameters, and also -- matching gcc -- when the macro has no named
|
|
1948
|
+
# parameters and the whole argument list is empty ("Z()" for "Z(...)"),
|
|
1949
|
+
# which reads as zero variable arguments, not one empty one. A trailing
|
|
1950
|
+
# comma with nothing after it ("F(a,)") does supply one empty argument.
|
|
1951
|
+
def no_variable_arguments?(macro, invocation)
|
|
1952
|
+
return true if invocation.raw.length <= macro.params.length
|
|
1953
|
+
|
|
1954
|
+
macro.params.empty? && invocation.raw.length == 1 && invocation.raw[0].empty?
|
|
1955
|
+
end
|
|
1956
|
+
|
|
1957
|
+
# Fuses two tokens into one by re-lexing their joined spelling through the
|
|
1958
|
+
# scanner (6.10.3.3p3): the result must be exactly one preprocessing token,
|
|
1959
|
+
# or the paste is ill-formed. It takes the left token's leading space and
|
|
1960
|
+
# the literal paint, and is located at the use site for rescanning.
|
|
1961
|
+
def fuse(left, right, site, painted)
|
|
1962
|
+
spelled = Scanner.new(left.text + right.text, filename: site.filename).scan.reject(&:eof?)
|
|
1963
|
+
unless spelled.length == 1
|
|
1964
|
+
raise_at(site, "pasting \"#{left.text}\" and \"#{right.text}\" does not give a valid preprocessing token")
|
|
1965
|
+
end
|
|
1966
|
+
|
|
1967
|
+
fused = spelled.first
|
|
1968
|
+
PPToken.new(
|
|
1969
|
+
type: fused.type, text: fused.text,
|
|
1970
|
+
filename: site.filename, line: site.line, column: site.column,
|
|
1971
|
+
source_line: site.source_line, space_before: left.space_before, suppress: painted
|
|
1972
|
+
)
|
|
1973
|
+
end
|
|
1974
|
+
|
|
1975
|
+
# The painting a token produced by expanding `tok` carries: `tok`'s own
|
|
1976
|
+
# suppression set plus the macro name now being expanded, frozen so the
|
|
1977
|
+
# shared list is never mutated by a later token's painting.
|
|
1978
|
+
def paint(tok)
|
|
1979
|
+
(tok.suppress + [tok.text]).freeze
|
|
1980
|
+
end
|
|
1981
|
+
|
|
1982
|
+
def comma_token(site, suppress)
|
|
1983
|
+
PPToken.new(
|
|
1984
|
+
type: :punct, text: ",",
|
|
1985
|
+
filename: site.filename, line: site.line, column: site.column,
|
|
1986
|
+
source_line: site.source_line, suppress: suppress
|
|
1987
|
+
)
|
|
1988
|
+
end
|
|
1989
|
+
|
|
1990
|
+
# A copy of a replacement token relocated to the invocation site (so tokens
|
|
1991
|
+
# produced by expansion are diagnosed at the point of use, not at the
|
|
1992
|
+
# #define that spelled them) and carrying its computed painting.
|
|
1993
|
+
def relocate(rep, site, suppress)
|
|
1994
|
+
PPToken.new(
|
|
1995
|
+
type: rep.type, text: rep.text,
|
|
1996
|
+
filename: site.filename, line: site.line, column: site.column,
|
|
1997
|
+
source_line: site.source_line, space_before: rep.space_before, suppress: suppress
|
|
1998
|
+
)
|
|
1999
|
+
end
|
|
2000
|
+
|
|
2001
|
+
def raise_at(pp, description)
|
|
2002
|
+
raise CompileError.new(
|
|
2003
|
+
description,
|
|
2004
|
+
filename: pp.filename, line: pp.line, column: pp.column,
|
|
2005
|
+
source_line: pp.source_line
|
|
2006
|
+
)
|
|
2007
|
+
end
|
|
2008
|
+
|
|
2009
|
+
# As #raise_at, but for a Front::Token (produced during #if evaluation)
|
|
2010
|
+
# rather than a preprocessing token; both expose the same location fields.
|
|
2011
|
+
def raise_at_front(token, description)
|
|
2012
|
+
raise CompileError.new(
|
|
2013
|
+
description,
|
|
2014
|
+
filename: token.filename, line: token.line, column: token.column,
|
|
2015
|
+
source_line: token.source_line
|
|
2016
|
+
)
|
|
2017
|
+
end
|
|
2018
|
+
end
|
|
2019
|
+
end
|
|
2020
|
+
end
|