GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_options-inl.h Lines: 200 206 97.1 %
Date: 2022-12-31 04:22:30 Branches: 126 141 89.4 %

Line Branch Exec Source
1
#ifndef SRC_NODE_OPTIONS_INL_H_
2
#define SRC_NODE_OPTIONS_INL_H_
3
4
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5
6
#include <cstdlib>
7
#include "node_options.h"
8
#include "util.h"
9
10
namespace node {
11
12
624001
PerIsolateOptions* PerProcessOptions::get_per_isolate_options() {
13
624001
  return per_isolate.get();
14
}
15
16
583744
EnvironmentOptions* PerIsolateOptions::get_per_env_options() {
17
583744
  return per_env.get();
18
}
19
20
namespace options_parser {
21
22
template <typename Options>
23
937656
void OptionsParser<Options>::AddOption(const char* name,
24
                                       const char* help_text,
25
                                       bool Options::* field,
26
                                       OptionEnvvarSettings env_setting,
27
                                       bool default_is_true) {
28
937656
  options_.emplace(name,
29
                   OptionInfo{kBoolean,
30
                              std::make_shared<SimpleOptionField<bool>>(field),
31
                              env_setting,
32
                              help_text,
33
                              default_is_true});
34
937656
}
35
36
template <typename Options>
37
17364
void OptionsParser<Options>::AddOption(const char* name,
38
                                       const char* help_text,
39
                                       uint64_t Options::* field,
40
                                       OptionEnvvarSettings env_setting) {
41
17364
  options_.emplace(
42
      name,
43
      OptionInfo{kUInteger,
44
                 std::make_shared<SimpleOptionField<uint64_t>>(field),
45
                 env_setting,
46
                 help_text});
47
17364
}
48
49
template <typename Options>
50
46304
void OptionsParser<Options>::AddOption(const char* name,
51
                                       const char* help_text,
52
                                       int64_t Options::* field,
53
                                       OptionEnvvarSettings env_setting) {
54
46304
  options_.emplace(
55
      name,
56
      OptionInfo{kInteger,
57
                 std::make_shared<SimpleOptionField<int64_t>>(field),
58
                 env_setting,
59
                 help_text});
60
46304
}
61
62
template <typename Options>
63
312552
void OptionsParser<Options>::AddOption(const char* name,
64
                                       const char* help_text,
65
                                       std::string Options::* field,
66
                                       OptionEnvvarSettings env_setting) {
67
312552
  options_.emplace(
68
      name,
69
      OptionInfo{kString,
70
                 std::make_shared<SimpleOptionField<std::string>>(field),
71
                 env_setting,
72
                 help_text});
73
312552
}
74
75
template <typename Options>
76
104184
void OptionsParser<Options>::AddOption(
77
    const char* name,
78
    const char* help_text,
79
    std::vector<std::string> Options::* field,
80
    OptionEnvvarSettings env_setting) {
81
104184
  options_.emplace(name, OptionInfo {
82
    kStringList,
83
    std::make_shared<SimpleOptionField<std::vector<std::string>>>(field),
84
    env_setting,
85
    help_text
86
  });
87
104184
}
88
89
template <typename Options>
90
5788
void OptionsParser<Options>::AddOption(const char* name,
91
                                       const char* help_text,
92
                                       HostPort Options::* field,
93
                                       OptionEnvvarSettings env_setting) {
94
5788
  options_.emplace(
95
      name,
96
      OptionInfo{kHostPort,
97
                 std::make_shared<SimpleOptionField<HostPort>>(field),
98
                 env_setting,
99
                 help_text});
100
5788
}
101
102
template <typename Options>
103
115760
void OptionsParser<Options>::AddOption(const char* name,
104
                                       const char* help_text,
105
                                       NoOp no_op_tag,
106
                                       OptionEnvvarSettings env_setting) {
107
115760
  options_.emplace(name, OptionInfo{kNoOp, nullptr, env_setting, help_text});
108
115760
}
109
110
template <typename Options>
111
162064
void OptionsParser<Options>::AddOption(const char* name,
112
                                       const char* help_text,
113
                                       V8Option v8_option_tag,
114
                                       OptionEnvvarSettings env_setting) {
115
162064
  options_.emplace(name,
116
                   OptionInfo{kV8Option, nullptr, env_setting, help_text});
117
162064
}
118
119
template <typename Options>
120
185216
void OptionsParser<Options>::AddAlias(const char* from,
121
                                      const char* to) {
122
370432
  aliases_[from] = { to };
123
185216
}
124
125
template <typename Options>
126
69456
void OptionsParser<Options>::AddAlias(const char* from,
127
                                      const std::vector<std::string>& to) {
128
69456
  aliases_[from] = to;
129
69456
}
130
131
template <typename Options>
132
69456
void OptionsParser<Options>::AddAlias(
133
    const char* from,
134
    const std::initializer_list<std::string>& to) {
135
69456
  AddAlias(from, std::vector<std::string>(to));
136
69456
}
137
138
template <typename Options>
139
127336
void OptionsParser<Options>::Implies(const char* from,
140
                                     const char* to) {
141
127336
  auto it = options_.find(to);
142
127336
  CHECK_NE(it, options_.end());
143

127336
  CHECK(it->second.type == kBoolean || it->second.type == kV8Option);
144
254672
  implications_.emplace(
145
254672
      from, Implication{it->second.type, to, it->second.field, true});
146
127336
}
147
148
template <typename Options>
149
23152
void OptionsParser<Options>::ImpliesNot(const char* from,
150
                                        const char* to) {
151
23152
  auto it = options_.find(to);
152
23152
  CHECK_NE(it, options_.end());
153
23152
  CHECK_EQ(it->second.type, kBoolean);
154
46304
  implications_.emplace(
155
46304
      from, Implication{it->second.type, to, it->second.field, false});
156
23152
}
157
158
template <typename Options>
159
template <typename OriginalField, typename ChildOptions>
160
2708784
auto OptionsParser<Options>::Convert(
161
    std::shared_ptr<OriginalField> original,
162
    ChildOptions* (Options::* get_child)()) {
163
  // If we have a field on ChildOptions, and we want to access it from an
164
  // Options instance, we call get_child() on the original Options and then
165
  // access it, i.e. this class implements a kind of function chaining.
166
  struct AdaptedField : BaseOptionField {
167
1232391
    void* LookupImpl(Options* options) const override {
168
1232391
      return original->LookupImpl((options->*get_child)());
169
    }
170
171
1354392
    AdaptedField(
172
        std::shared_ptr<OriginalField> original,
173
        ChildOptions* (Options::* get_child)())
174
1354392
          : original(original), get_child(get_child) {}
175
176
    std::shared_ptr<OriginalField> original;
177
    ChildOptions* (Options::* get_child)();
178
  };
179
180
  return std::shared_ptr<BaseOptionField>(
181
2708784
      new AdaptedField(original, get_child));
182
}
183
template <typename Options>
184
template <typename ChildOptions>
185
2523568
auto OptionsParser<Options>::Convert(
186
    typename OptionsParser<ChildOptions>::OptionInfo original,
187
    ChildOptions* (Options::* get_child)()) {
188
2523568
  return OptionInfo{original.type,
189
2523568
                    Convert(original.field, get_child),
190
2523568
                    original.env_setting,
191
2523568
                    original.help_text,
192
2523568
                    original.default_is_true};
193
}
194
195
template <typename Options>
196
template <typename ChildOptions>
197
185216
auto OptionsParser<Options>::Convert(
198
    typename OptionsParser<ChildOptions>::Implication original,
199
    ChildOptions* (Options::* get_child)()) {
200
  return Implication{
201
185216
      original.type,
202
185216
      original.name,
203
185216
      Convert(original.target_field, get_child),
204
185216
      original.target_value,
205
185216
  };
206
}
207
208
template <typename Options>
209
template <typename ChildOptions>
210
34728
void OptionsParser<Options>::Insert(
211
    const OptionsParser<ChildOptions>& child_options_parser,
212
    ChildOptions* (Options::* get_child)()) {
213
34728
  aliases_.insert(std::begin(child_options_parser.aliases_),
214
34728
                  std::end(child_options_parser.aliases_));
215
216
2558296
  for (const auto& pair : child_options_parser.options_)
217
2523568
    options_.emplace(pair.first, Convert(pair.second, get_child));
218
219
219944
  for (const auto& pair : child_options_parser.implications_)
220
185216
    implications_.emplace(pair.first, Convert(pair.second, get_child));
221
34728
}
222
223
19
inline std::string NotAllowedInEnvErr(const std::string& arg) {
224
19
  return arg + " is not allowed in NODE_OPTIONS";
225
}
226
227
7
inline std::string RequiresArgumentErr(const std::string& arg) {
228
7
  return arg + " requires an argument";
229
}
230
231
1
inline std::string NegationImpliesBooleanError(const std::string& arg) {
232
1
  return arg + " is an invalid negation because it is not a boolean option";
233
}
234
235
// We store some of the basic information around a single Parse call inside
236
// this struct, to separate storage of command line arguments and their
237
// handling. In particular, this makes it easier to introduce 'synthetic'
238
// arguments that get inserted by expanding option aliases.
239
struct ArgsInfo {
240
  // Generally, the idea here is that the first entry in `*underlying` stores
241
  // the "0th" argument (the program name), then `synthetic_args` are inserted,
242
  // followed by the remainder of `*underlying`.
243
  std::vector<std::string>* underlying;
244
  std::vector<std::string> synthetic_args;
245
246
  std::vector<std::string>* exec_args;
247
248
12045
  ArgsInfo(std::vector<std::string>* args,
249
           std::vector<std::string>* exec_args)
250
12045
    : underlying(args), exec_args(exec_args) {}
251
252
18686
  size_t remaining() const {
253
    // -1 to account for the program name.
254
18686
    return underlying->size() - 1 + synthetic_args.size();
255
  }
256
257
18686
  bool empty() const { return remaining() == 0; }
258
12045
  const std::string& program_name() const { return underlying->at(0); }
259
260
25220
  std::string& first() {
261
25220
    return synthetic_args.empty() ? underlying->at(1) : synthetic_args.front();
262
  }
263
264
3789
  std::string pop_first() {
265
3789
    std::string ret = std::move(first());
266
3789
    if (synthetic_args.empty()) {
267
      // Only push arguments to `exec_args` that were also originally passed
268
      // on the command line (i.e. not generated through alias expansion).
269
      // '--' is a special case here since its purpose is to end `exec_argv`,
270
      // which is why we do not include it.
271

3517
      if (exec_args != nullptr && ret != "--")
272
3392
        exec_args->push_back(ret);
273
3517
      underlying->erase(underlying->begin() + 1);
274
    } else {
275
272
      synthetic_args.erase(synthetic_args.begin());
276
    }
277
3789
    return ret;
278
  }
279
};
280
281
template <typename Options>
282
24090
void OptionsParser<Options>::Parse(
283
    std::vector<std::string>* const orig_args,
284
    std::vector<std::string>* const exec_args,
285
    std::vector<std::string>* const v8_args,
286
    Options* const options,
287
    OptionEnvvarSettings required_env_settings,
288
    std::vector<std::string>* const errors) const {
289
48180
  ArgsInfo args(orig_args, exec_args);
290
291
  // The first entry is the process name. Make sure it ends up in the V8 argv,
292
  // since V8::SetFlagsFromCommandLine() expects that to hold true for that
293
  // array as well.
294
24090
  if (v8_args->empty())
295
24090
    v8_args->push_back(args.program_name());
296
297

29644
  while (!args.empty() && errors->empty()) {
298

16062
    if (args.first().size() <= 1 || args.first()[0] != '-') break;
299
300
    // We know that we're either going to consume this
301
    // argument or fail completely.
302
5638
    const std::string arg = args.pop_first();
303
304
5638
    if (arg == "--") {
305
32
      if (required_env_settings == kAllowedInEnvvar)
306
2
        errors->push_back(NotAllowedInEnvErr("--"));
307
32
      break;
308
    }
309
310
    // Only allow --foo=bar notation for options starting with double dashes.
311
    // (E.g. -e=a is not allowed as shorthand for --eval=a, which would
312
    // otherwise be the result of alias expansion.)
313
5606
    const std::string::size_type equals_index =
314

5606
        arg[0] == '-' && arg[1] == '-' ? arg.find('=') : std::string::npos;
315
5606
    std::string name =
316
      equals_index == std::string::npos ? arg : arg.substr(0, equals_index);
317
318
    // Store the 'original name' of the argument. This name differs from
319
    // 'name' in that it contains a possible '=' sign and is not affected
320
    // by alias expansion.
321
5606
    std::string original_name = name;
322
5606
    if (equals_index != std::string::npos)
323
554
      original_name += '=';
324
325
5627
    auto missing_argument = [&]() {
326
7
      errors->push_back(RequiresArgumentErr(original_name));
327
    };
328
329
    // Normalize by replacing `_` with `-` in options.
330
66560
    for (std::string::size_type i = 2; i < name.size(); ++i) {
331
60954
      if (name[i] == '_')
332
78
        name[i] = '-';
333
    }
334
335
    // Convert --no-foo to --foo and keep in mind that we're negating.
336
5606
    bool is_negation = false;
337
5606
    if (name.find("--no-") == 0) {
338
542
      name.erase(2, 3);  // remove no-
339
542
      is_negation = true;
340
    }
341
342
    {
343
5606
      auto it = aliases_.end();
344
      // Expand aliases:
345
      // - If `name` can be found in `aliases_`.
346
      // - If `name` + '=' can be found in `aliases_`.
347
      // - If `name` + " <arg>" can be found in `aliases_`, and we have
348
      //   a subsequent argument that does not start with '-' itself.
349
15418
      while ((it = aliases_.find(name)) != aliases_.end() ||
350
738
             (equals_index != std::string::npos &&
351

29292
              (it = aliases_.find(name + '=')) != aliases_.end()) ||
352
5778
             (!args.empty() &&
353
5458
                 !args.first().empty() &&
354

9338
                 args.first()[0] != '-' &&
355

11414
              (it = aliases_.find(name + " <arg>")) != aliases_.end())) {
356
1928
        const std::string prev_name = std::move(name);
357
1928
        const std::vector<std::string>& expansion = it->second;
358
359
        // Use the first entry in the expansion as the new 'name'.
360
1928
        name = expansion.front();
361
362
1928
        if (expansion.size() > 1) {
363
          // The other arguments, if any, are going to be handled later.
364
1638
          args.synthetic_args.insert(
365
546
              args.synthetic_args.begin(),
366
1092
              expansion.begin() + 1,
367
              expansion.end());
368
        }
369
370
1928
        if (name == prev_name) break;
371
      }
372
    }
373
374
5606
    auto it = options_.find(name);
375
376
5606
    if ((it == options_.end() ||
377


5606
         it->second.env_setting == kDisallowedInEnvvar) &&
378
        required_env_settings == kAllowedInEnvvar) {
379
36
      errors->push_back(NotAllowedInEnvErr(original_name));
380
36
      break;
381
    }
382
383
    {
384
11140
      std::string implied_name = name;
385
5570
      if (is_negation) {
386
        // Implications for negated options are defined with "--no-".
387
542
        implied_name.insert(2, "no-");
388
      }
389
5570
      auto implications = implications_.equal_range(implied_name);
390
6694
      for (auto it = implications.first; it != implications.second; ++it) {
391
1124
        if (it->second.type == kV8Option) {
392
2
          v8_args->push_back(it->second.name);
393
        } else {
394
1122
          *it->second.target_field->template Lookup<bool>(options) =
395
1122
              it->second.target_value;
396
        }
397
      }
398
    }
399
400
5570
    if (it == options_.end()) {
401
292
      v8_args->push_back(arg);
402
292
      continue;
403
    }
404
405
5278
    const OptionInfo& info = it->second;
406
407
    // Some V8 options can be negated and they are validated by V8 later.
408

5278
    if (is_negation && info.type != kBoolean && info.type != kV8Option) {
409
2
      errors->push_back(NegationImpliesBooleanError(arg));
410
2
      break;
411
    }
412
413
5276
    std::string value;
414

5276
    if (info.type != kBoolean && info.type != kNoOp && info.type != kV8Option) {
415
2472
      if (equals_index != std::string::npos) {
416
522
        value = arg.substr(equals_index + 1);
417
522
        if (value.empty()) {
418
4
          missing_argument();
419
4
          break;
420
        }
421
      } else {
422
1950
        if (args.empty()) {
423
10
          missing_argument();
424
10
          break;
425
        }
426
427
1940
        value = args.pop_first();
428
429

1940
        if (!value.empty() && value[0] == '-') {
430
          missing_argument();
431
          break;
432
        } else {
433


1940
          if (!value.empty() && value[0] == '\\' && value[1] == '-')
434
2
            value = value.substr(1);  // Treat \- as escaping an -.
435
        }
436
      }
437
    }
438
439


5262
    switch (info.type) {
440
2708
      case kBoolean:
441
2708
        *Lookup<bool>(info.field, options) = !is_negation;
442
2708
        break;
443
14
      case kInteger:
444
14
        *Lookup<int64_t>(info.field, options) = std::atoll(value.c_str());
445
14
        break;
446
62
      case kUInteger:
447
62
        *Lookup<uint64_t>(info.field, options) = std::stoull(value);
448
62
        break;
449
1690
      case kString:
450
1690
        *Lookup<std::string>(info.field, options) = value;
451
1690
        break;
452
396
      case kStringList:
453
792
        Lookup<std::vector<std::string>>(info.field, options)
454
396
            ->emplace_back(std::move(value));
455
396
        break;
456
296
      case kHostPort:
457
296
        Lookup<HostPort>(info.field, options)
458
            ->Update(SplitHostPort(value, errors));
459
296
        break;
460
      case kNoOp:
461
        break;
462
96
      case kV8Option:
463
96
        v8_args->push_back(arg);
464
96
        break;
465
      default:
466
        UNREACHABLE();
467
    }
468
  }
469
24090
  options->CheckOptions(errors, orig_args);
470
}
471
472
}  // namespace options_parser
473
}  // namespace node
474
475
#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
476
477
#endif  // SRC_NODE_OPTIONS_INL_H_