Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Public Member Functions | Private Member Functions | List of all members
dart::kernel::ScopeBuilder Class Reference

#include <scope_builder.h>

Public Member Functions

 ScopeBuilder (ParsedFunction *parsed_function)
 
virtual ~ScopeBuilder ()=default
 
ScopeBuildingResultBuildScopes ()
 

Private Member Functions

virtual void ReportUnexpectedTag (const char *variant, Tag tag)
 

Detailed Description

Definition at line 23 of file scope_builder.h.

Constructor & Destructor Documentation

◆ ScopeBuilder()

dart::kernel::ScopeBuilder::ScopeBuilder ( ParsedFunction parsed_function)
explicit

Definition at line 20 of file scope_builder.cc.

21 : result_(nullptr),
22 parsed_function_(parsed_function),
23 translation_helper_(Thread::Current()),
24 zone_(translation_helper_.zone()),
25 current_function_scope_(nullptr),
26 scope_(nullptr),
27 depth_(0),
28 name_index_(0),
29 needs_expr_temp_(false),
30 helper_(
31 zone_,
32 &translation_helper_,
33 TypedDataView::Handle(Z, parsed_function->function().KernelLibrary()),
34 parsed_function->function().KernelLibraryOffset()),
35 constant_reader_(&helper_, &active_class_),
36 inferred_type_metadata_helper_(&helper_, &constant_reader_),
37 inferred_arg_type_metadata_helper_(
38 &helper_,
39 &constant_reader_,
41 procedure_attributes_metadata_helper_(&helper_),
42 type_translator_(&helper_,
43 &constant_reader_,
44 &active_class_,
45 /*finalize=*/true) {
46 const auto& kernel_program_info = KernelProgramInfo::Handle(
47 Z, parsed_function->function().KernelProgramInfo());
48 H.InitFromKernelProgramInfo(kernel_program_info);
49 ASSERT(type_translator_.active_class_ == &active_class_);
50}
#define Z
static Object & Handle()
Definition object.h:407
static Thread * Current()
Definition thread.h:361
#define ASSERT(E)
Definition SkMD5.cpp:130

◆ ~ScopeBuilder()

virtual dart::kernel::ScopeBuilder::~ScopeBuilder ( )
virtualdefault

Member Function Documentation

◆ BuildScopes()

ScopeBuildingResult * dart::kernel::ScopeBuilder::BuildScopes ( )

Definition at line 52 of file scope_builder.cc.

52 {
53 if (result_ != nullptr) return result_;
54
55 ASSERT(scope_ == nullptr && depth_.loop_ == 0 && depth_.function_ == 0);
56 result_ = new (Z) ScopeBuildingResult();
57
58 const Function& function = parsed_function_->function();
59
60 // Setup an [ActiveClassScope] and an [ActiveMemberScope] which will be used
61 // e.g. for type translation.
62 const Class& klass = Class::Handle(Z, function.Owner());
63
64 Function& outermost_function =
65 Function::Handle(Z, function.GetOutermostFunction());
66
67 ActiveClassScope active_class_scope(&active_class_, &klass);
68 ActiveMemberScope active_member(&active_class_, &outermost_function);
69 FunctionType& signature = FunctionType::Handle(Z, function.signature());
70 ActiveTypeParametersScope active_type_params(&active_class_, function,
71 &signature, Z);
72
73 LocalScope* enclosing_scope = nullptr;
74 if (function.IsImplicitClosureFunction() && !function.is_static()) {
75 // Create artificial enclosing scope for the tear-off that contains
76 // captured receiver value. This ensure that AssertAssignable will correctly
77 // load instantiator type arguments if they are needed.
78 Class& klass = Class::Handle(Z, function.Owner());
79 Type& klass_type = H.GetDeclarationType(klass);
80 LocalVariable* receiver_variable =
81 MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
82 Symbols::This(), klass_type);
83 parsed_function_->set_receiver_var(receiver_variable);
84 receiver_variable->set_is_captured();
85 enclosing_scope = new (Z) LocalScope(nullptr, 0, 0);
86 enclosing_scope->set_context_level(0);
87 enclosing_scope->AddVariable(receiver_variable);
88 enclosing_scope->AddContextVariable(receiver_variable);
89 } else if (function.HasParent()) {
90 enclosing_scope = LocalScope::RestoreOuterScope(
91 ContextScope::Handle(Z, function.context_scope()));
92 }
93 current_function_scope_ = scope_ = new (Z) LocalScope(enclosing_scope, 0, 0);
94 scope_->set_begin_token_pos(function.token_pos());
95 scope_->set_end_token_pos(function.end_token_pos());
96
97 if (function.IsSuspendableFunction()) {
98 LocalVariable* suspend_state_var =
99 MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
100 Symbols::SuspendStateVar(), AbstractType::dynamic_type());
101 suspend_state_var->set_is_forced_stack();
102 suspend_state_var->set_invisible(true);
103 scope_->AddVariable(suspend_state_var);
104 parsed_function_->set_suspend_state_var(suspend_state_var);
105 }
106
107 // Add function type arguments variable before current context variable.
108 if (function.IsGeneric() || function.HasGenericParent()) {
109 LocalVariable* type_args_var = MakeVariable(
110 TokenPosition::kNoSource, TokenPosition::kNoSource,
111 Symbols::FunctionTypeArgumentsVar(), AbstractType::dynamic_type());
112 scope_->AddVariable(type_args_var);
113 parsed_function_->set_function_type_arguments(type_args_var);
114 }
115
116 if (parsed_function_->has_arg_desc_var()) {
117 scope_->AddVariable(parsed_function_->arg_desc_var());
118 }
119
120 LocalVariable* context_var = parsed_function_->current_context_var();
121 context_var->set_is_forced_stack();
122 scope_->AddVariable(context_var);
123
124 parsed_function_->set_scope(scope_);
125
126 helper_.SetOffset(function.kernel_offset());
127
128 FunctionNodeHelper function_node_helper(&helper_);
129 const ProcedureAttributesMetadata attrs =
130 procedure_attributes_metadata_helper_.GetProcedureAttributes(
131 function.kernel_offset());
132
133 switch (function.kind()) {
134 case UntaggedFunction::kImplicitClosureFunction: {
135 const auto& parent = Function::Handle(Z, function.parent_function());
136 const auto& target =
137 Function::Handle(Z, function.ImplicitClosureTarget(Z));
138
139 // For BuildGraphOfNoSuchMethodForwarder, since closures no longer
140 // require arg_desc_var in all cases.
141 if (target.IsNull() ||
142 (parent.num_fixed_parameters() != target.num_fixed_parameters())) {
143 needs_expr_temp_ = true;
144 }
145 }
147 case UntaggedFunction::kClosureFunction:
148 case UntaggedFunction::kRegularFunction:
149 case UntaggedFunction::kGetterFunction:
150 case UntaggedFunction::kSetterFunction:
151 case UntaggedFunction::kConstructor: {
152 const Tag tag = helper_.PeekTag();
153 helper_.ReadUntilFunctionNode();
154 function_node_helper.ReadUntilExcluding(
156 // NOTE: FunctionNode is read further below the if.
157
158 if (function.is_ffi_native() || function.IsFfiCallClosure()) {
159 needs_expr_temp_ = true;
160 // Calls with handles need try/catch variables.
161 if (function.FfiCSignatureContainsHandles()) {
162 ++depth_.try_;
163 AddTryVariables();
164 --depth_.try_;
165 ++depth_.catch_;
166 AddCatchVariables();
167 FinalizeCatchVariables();
168 --depth_.catch_;
169 }
170 }
173 function)) {
174 needs_expr_temp_ = true;
175 }
176 intptr_t pos = 0;
177 if (function.IsClosureFunction()) {
178 LocalVariable* closure_parameter = MakeVariable(
179 TokenPosition::kNoSource, TokenPosition::kNoSource,
180 Symbols::ClosureParameter(), AbstractType::dynamic_type());
181 closure_parameter->set_is_forced_stack();
182 scope_->InsertParameterAt(pos++, closure_parameter);
183 } else if (!function.is_static()) {
184 // We use [is_static] instead of [IsStaticFunction] because the latter
185 // returns `false` for constructors.
186 Class& klass = Class::Handle(Z, function.Owner());
187 Type& klass_type = H.GetDeclarationType(klass);
188 LocalVariable* variable =
189 MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
190 Symbols::This(), klass_type);
191 scope_->InsertParameterAt(pos++, variable);
192 parsed_function_->set_receiver_var(variable);
193
194 // We visit instance field initializers because they might contain
195 // [Let] expressions and we need to have a mapping.
196 if (tag == kConstructor) {
197 Class& parent_class = Class::Handle(Z, function.Owner());
198 Array& class_fields = Array::Handle(Z, parent_class.fields());
199 Field& class_field = Field::Handle(Z);
200 for (intptr_t i = 0; i < class_fields.Length(); ++i) {
201 class_field ^= class_fields.At(i);
202 if (!class_field.is_static()) {
203 const auto& kernel_data =
204 TypedDataView::Handle(Z, class_field.KernelLibrary());
205 ASSERT(!kernel_data.IsNull());
206 intptr_t field_offset = class_field.kernel_offset();
207 AlternativeReadingScopeWithNewData alt(
208 &helper_.reader_, &kernel_data, field_offset);
209 FieldHelper field_helper(&helper_);
210 field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
211 Tag initializer_tag =
212 helper_.ReadTag(); // read first part of initializer.
213 if (initializer_tag == kSomething) {
214 EnterScope(field_offset);
215 VisitExpression(); // read initializer.
216 ExitScope(field_helper.position_, field_helper.end_position_);
217 }
218 }
219 }
220 }
221 } else if (function.IsFactory()) {
222 LocalVariable* variable = MakeVariable(
223 TokenPosition::kNoSource, TokenPosition::kNoSource,
224 Symbols::TypeArgumentsParameter(), AbstractType::dynamic_type());
225 scope_->InsertParameterAt(pos++, variable);
226 result_->type_arguments_variable = variable;
227 }
228
229 ParameterTypeCheckMode type_check_mode =
230 kTypeCheckForNonDynamicallyInvokedMethod;
231 if (function.is_static()) {
232 // In static functions we don't check anything.
233 type_check_mode = kTypeCheckForStaticFunction;
234 } else if (function.IsImplicitClosureFunction()) {
235 // All non-covariant checks are either performed by the type system,
236 // or by a dynamic closure call dispatcher/mirror if dynamically
237 // invoked. For covariant checks, static targets never have covariant
238 // arguments and dynamic targets do their own covariant checking.
239 // Thus, implicit closure functions perform no checking internally.
240 type_check_mode = kTypeCheckForImplicitClosureFunction;
241 }
242
243 // Continue reading FunctionNode:
244 // read positional_parameters and named_parameters.
245 AddPositionalAndNamedParameters(pos, type_check_mode, attrs);
246
247 if (function.IsSuspendableFunction()) {
248 // Read return type which is used to create a result of
249 // async/async*/sync* function. It may reference receiver or type
250 // arguments of the enclosing function which need to be captured.
251 VisitDartType();
252
253 // Visit optional future value type.
254 if (helper_.ReadTag() == kSomething) {
255 VisitDartType();
256 }
257 }
258
259 // We generate a synthetic body for implicit closure functions - which
260 // will forward the call to the real function.
261 // -> see BuildGraphOfImplicitClosureFunction
262 if (!function.IsImplicitClosureFunction()) {
263 helper_.SetOffset(function.kernel_offset());
264 first_body_token_position_ = TokenPosition::kNoSource;
265 VisitNode();
266
267 // TODO(jensj): HACK: Push the begin token to after any parameters to
268 // avoid crash when breaking on definition line of async method in
269 // debugger. It seems that another scope needs to be added
270 // in which captures are made, but I can't make that work.
271 // This 'solution' doesn't crash, but I cannot see the parameters at
272 // that particular breakpoint either.
273 // Also push the end token to after the "}" to avoid crashing on
274 // stepping past the last line (to the "}" character).
275 if (first_body_token_position_.IsReal()) {
276 scope_->set_begin_token_pos(first_body_token_position_);
277 }
278 if (scope_->end_token_pos().IsReal()) {
279 scope_->set_end_token_pos(scope_->end_token_pos().Next());
280 }
281 }
282 break;
283 }
284 case UntaggedFunction::kImplicitGetter:
285 case UntaggedFunction::kImplicitSetter: {
286 ASSERT(helper_.PeekTag() == kField);
287 const bool is_setter = function.IsImplicitSetterFunction();
288 const bool is_method = !function.IsStaticFunction();
289 const auto& field = Field::Handle(Z, function.accessor_field());
290 intptr_t pos = 0;
291 if (is_method) {
292 Class& klass = Class::Handle(Z, function.Owner());
293 Type& klass_type = H.GetDeclarationType(klass);
294 LocalVariable* variable =
295 MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
296 Symbols::This(), klass_type);
297 scope_->InsertParameterAt(pos++, variable);
298 parsed_function_->set_receiver_var(variable);
299 }
300 if (is_setter) {
301 if (CompilerState::Current().is_aot()) {
302 const intptr_t kernel_offset = field.kernel_offset();
303 const InferredTypeMetadata parameter_type =
304 inferred_type_metadata_helper_.GetInferredType(kernel_offset);
305 result_->setter_value = MakeVariable(
306 TokenPosition::kNoSource, TokenPosition::kNoSource,
307 Symbols::Value(),
308 AbstractType::ZoneHandle(Z, function.ParameterTypeAt(pos)),
309 LocalVariable::kNoKernelOffset, /*is_late=*/false,
310 /*inferred_type=*/nullptr,
311 /*inferred_arg_type=*/&parameter_type);
312 } else {
313 result_->setter_value = MakeVariable(
314 TokenPosition::kNoSource, TokenPosition::kNoSource,
315 Symbols::Value(),
316 AbstractType::ZoneHandle(Z, function.ParameterTypeAt(pos)));
317 }
318 scope_->InsertParameterAt(pos++, result_->setter_value);
319
320 if (is_method) {
321 if (field.is_covariant()) {
323 } else if (!field.is_generic_covariant_impl() ||
324 (!attrs.has_non_this_uses && !attrs.has_tearoff_uses)) {
327 }
328 }
329 }
330 break;
331 }
332 case UntaggedFunction::kImplicitStaticGetter: {
333 ASSERT(helper_.PeekTag() == kField);
334 ASSERT(function.IsStaticFunction());
335 // In addition to static field initializers, scopes/local variables
336 // are needed for implicit getters of static const fields, in order to
337 // be able to evaluate their initializers in constant evaluator.
338 if (Field::Handle(Z, function.accessor_field()).is_const()) {
339 VisitNode();
340 }
341 break;
342 }
343 case UntaggedFunction::kFieldInitializer: {
344 ASSERT(helper_.PeekTag() == kField);
345 if (!function.is_static()) {
346 Class& klass = Class::Handle(Z, function.Owner());
347 Type& klass_type = H.GetDeclarationType(klass);
348 LocalVariable* variable =
349 MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
350 Symbols::This(), klass_type);
351 scope_->InsertParameterAt(0, variable);
352 parsed_function_->set_receiver_var(variable);
353 }
354 VisitNode();
355 break;
356 }
357 case UntaggedFunction::kDynamicInvocationForwarder: {
358 const String& name = String::Handle(Z, function.name());
360
361 const auto& target = Function::ZoneHandle(Z, function.ForwardingTarget());
362 ASSERT(!target.IsNull());
363
366 function)) {
367 needs_expr_temp_ = true;
368 }
369
370 if (helper_.PeekTag() == kField) {
371 // Create [this] variable.
372 const Class& klass = Class::Handle(Z, function.Owner());
373 parsed_function_->set_receiver_var(
374 MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
375 Symbols::This(), H.GetDeclarationType(klass)));
376 scope_->InsertParameterAt(0, parsed_function_->receiver_var());
377
378 // Create setter value variable.
379 if (target.IsImplicitSetterFunction()) {
380 result_->setter_value = MakeVariable(
381 TokenPosition::kNoSource, TokenPosition::kNoSource,
382 Symbols::Value(),
383 AbstractType::ZoneHandle(Z, function.ParameterTypeAt(1)));
384 scope_->InsertParameterAt(1, result_->setter_value);
385 }
386 break;
387 }
388
389 // We do not create dyn:* forwarders for method extractors, since those
390 // can never return unboxed values (they return a closure).
391 ASSERT(!target.IsMethodExtractor());
392
393 helper_.ReadUntilFunctionNode();
394 function_node_helper.ReadUntilExcluding(
396
397 // Create [this] variable.
398 intptr_t pos = 0;
399 Class& klass = Class::Handle(Z, function.Owner());
400 parsed_function_->set_receiver_var(
401 MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
402 Symbols::This(), H.GetDeclarationType(klass)));
403 scope_->InsertParameterAt(pos++, parsed_function_->receiver_var());
404
405 // Create all positional and named parameters.
406 AddPositionalAndNamedParameters(
407 pos, kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod,
408 attrs);
409 break;
410 }
411 case UntaggedFunction::kMethodExtractor: {
412 // Add a receiver parameter. Though it is captured, we emit code to
413 // explicitly copy it to a freshly-allocated closure.
414 // Therefore, it isn't necessary to mark it as captured here.
415 Class& klass = Class::Handle(Z, function.Owner());
416 Type& klass_type = H.GetDeclarationType(klass);
417 LocalVariable* variable =
418 MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
419 Symbols::This(), klass_type);
420 scope_->InsertParameterAt(0, variable);
421 parsed_function_->set_receiver_var(variable);
422 break;
423 }
424 case UntaggedFunction::kFfiTrampoline: {
425 needs_expr_temp_ = true;
426 // Callbacks need try/catch variables.
427 ++depth_.try_;
428 AddTryVariables();
429 --depth_.try_;
430 ++depth_.catch_;
431 AddCatchVariables();
432 FinalizeCatchVariables();
433 --depth_.catch_;
435 }
436 case UntaggedFunction::kInvokeFieldDispatcher: {
437 if (function.IsDynamicClosureCallDispatcher()) {
438 auto const vars = parsed_function_->EnsureDynamicClosureCallVars();
439 ASSERT(vars != nullptr);
440#define ADD_VAR(Name, _, __) scope_->AddVariable(vars->Name);
442#undef ADD_VAR
443 for (auto const& v : vars->named_argument_parameter_indices) {
444 scope_->AddVariable(v);
445 }
446 }
447 }
449 case UntaggedFunction::kNoSuchMethodDispatcher: {
450 for (intptr_t i = 0; i < function.NumParameters(); ++i) {
451 LocalVariable* variable = MakeVariable(
452 TokenPosition::kNoSource, TokenPosition::kNoSource,
453 String::ZoneHandle(Z, function.ParameterNameAt(i)),
454 AbstractType::ZoneHandle(Z, function.IsFfiCallbackTrampoline()
455 ? function.ParameterTypeAt(i)
456 : Object::dynamic_type().ptr()));
457 bool added = scope_->InsertParameterAt(i, variable);
458 ASSERT(added);
459 }
460 break;
461 }
462 case UntaggedFunction::kRecordFieldGetter: {
463 needs_expr_temp_ = true;
464 // Add a receiver parameter.
465 Class& klass = Class::Handle(Z, function.Owner());
466 parsed_function_->set_receiver_var(
467 MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
468 Symbols::This(), H.GetDeclarationType(klass)));
469 scope_->InsertParameterAt(0, parsed_function_->receiver_var());
470 break;
471 }
472 case UntaggedFunction::kIrregexpFunction:
473 UNREACHABLE();
474 }
475 if (needs_expr_temp_) {
476 parsed_function_->EnsureExpressionTemp();
477 }
478 if (parsed_function_->has_expression_temp_var()) {
479 scope_->AddVariable(parsed_function_->expression_temp_var());
480 }
481 if (parsed_function_->function().MayHaveUncheckedEntryPoint()) {
482 scope_->AddVariable(parsed_function_->EnsureEntryPointsTemp());
483 }
484
485 parsed_function_->AllocateVariables();
486
487 // :suspend_state variable should be allocated to a fixed location in
488 // the stack frame.
489 RELEASE_ASSERT((parsed_function_->suspend_state_var() == nullptr) ||
490 (parsed_function_->suspend_state_var()->index().value() ==
492
493 return result_;
494}
SkPoint pos
#define UNREACHABLE()
Definition assert.h:248
#define RELEASE_ASSERT(cond)
Definition assert.h:327
static CompilerState & Current()
static bool IsDynamicInvocationForwarderName(const String &name)
Definition object.cc:4240
bool MayHaveUncheckedEntryPoint() const
Definition object.cc:11499
void set_begin_token_pos(TokenPosition value)
Definition scopes.h:344
void set_end_token_pos(TokenPosition value)
Definition scopes.h:347
TokenPosition end_token_pos() const
Definition scopes.h:346
static LocalScope * RestoreOuterScope(const ContextScope &context_scope)
Definition scopes.cc:518
bool AddVariable(LocalVariable *variable)
Definition scopes.cc:57
bool InsertParameterAt(intptr_t pos, LocalVariable *parameter)
Definition scopes.cc:72
VariableIndex index() const
Definition scopes.h:202
void set_is_explicit_covariant_parameter()
Definition scopes.h:171
void set_type_check_mode(TypeCheckMode mode)
Definition scopes.h:199
static constexpr intptr_t kNoKernelOffset
Definition scopes.h:77
void set_is_forced_stack()
Definition scopes.h:156
static Object & ZoneHandle()
Definition object.h:419
void set_function_type_arguments(LocalVariable *function_type_arguments)
Definition parser.h:91
LocalVariable * expression_temp_var() const
Definition parser.h:151
const Function & function() const
Definition parser.h:73
LocalVariable * EnsureExpressionTemp()
Definition parser.cc:134
LocalVariable * EnsureEntryPointsTemp()
Definition parser.cc:146
LocalVariable * arg_desc_var() const
Definition parser.h:131
bool has_arg_desc_var() const
Definition parser.h:130
void set_receiver_var(LocalVariable *value)
Definition parser.h:137
void set_scope(LocalScope *scope)
Definition parser.h:77
void AllocateVariables()
Definition parser.cc:180
LocalVariable * current_context_var() const
Definition parser.h:128
LocalVariable * suspend_state_var() const
Definition parser.h:103
LocalVariable * receiver_var() const
Definition parser.h:133
bool has_expression_temp_var() const
Definition parser.h:159
void set_suspend_state_var(LocalVariable *suspend_state_var)
Definition parser.h:104
DynamicClosureCallVars * EnsureDynamicClosureCallVars()
Definition parser.cc:336
static constexpr intptr_t kSuspendStateVarIndex
Definition object.h:12591
static const String & This()
Definition symbols.h:691
TokenPosition Next()
int value() const
Definition scopes.h:69
static bool IsExpressionTempVarUsedInRecognizedMethodFlowGraph(const Function &function)
static bool IsRecognizedMethodForFlowGraph(const Function &function)
InferredTypeMetadata GetInferredType(intptr_t node_offset, bool read_constant=true)
Tag ReadTag(uint8_t *payload=nullptr)
Tag PeekTag(uint8_t *payload=nullptr)
ProcedureAttributesMetadata GetProcedureAttributes(intptr_t node_offset)
uint32_t * target
Dart_NativeFunction function
Definition fuchsia.cc:51
const char *const name
#define FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(V)
Definition parser.h:273
#define FALL_THROUGH
Definition globals.h:15
#define ADD_VAR(Name, _, __)

◆ ReportUnexpectedTag()

void dart::kernel::ScopeBuilder::ReportUnexpectedTag ( const char *  variant,
Tag  tag 
)
privatevirtual

Definition at line 496 of file scope_builder.cc.

496 {
497 const auto& script = Script::Handle(Z, Script());
498 H.ReportError(script, TokenPosition::kNoSource,
499 "Unexpected tag %d (%s) in %s, expected %s", tag,
500 Reader::TagName(tag),
501 parsed_function_->function().ToQualifiedCString(), variant);
502}
const char * ToQualifiedCString() const
Definition object.cc:9834
static const char * TagName(Tag tag)

The documentation for this class was generated from the following files: