6378a26212
This CL fixes two bugs related to scoping: 1. Empty variable name lookup in outer functions failing. 2. Let expressions not pushing and popping contexts. Additionally, this CL introduces a `--print-scoping` debug flag to ease debugging scoping issues in the future. Variable lookup is now only using kernel offset, unless the offset is `kNoKernelOffset`. In that case, the names are used. All synthetic variables in the VM now use `kNoKernelOffset` as their offset. The let expressions not pushing contexts is fixed by pushing context. The context is only pushed if the scope captures variables. So this should not lead to performance regressions. (Any lacking pushing of scopes should have lead to crashes.) TEST=tests/ffi/regress_56412_2_test.dart TEST=tests/ffi/regress_56412_test.dart Closes: https://github.com/dart-lang/sdk/issues/56412 Change-Id: I85fc4a161b833df80ce8f2911d618aa2ac9797eb Cq-Include-Trybots: dart/try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-asan-linux-release-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-msan-linux-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-tsan-linux-release-x64-try,vm-aot-ubsan-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-appjit-linux-debug-x64-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-eager-optimization-linux-release-ia32-try,vm-eager-optimization-linux-release-x64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-arm64-try,vm-fuchsia-release-x64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-arm64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/380983 Reviewed-by: Slava Egorov <vegorov@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
242 lines
7.6 KiB
C++
242 lines
7.6 KiB
C++
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_COMPILER_FRONTEND_SCOPE_BUILDER_H_
|
|
#define RUNTIME_VM_COMPILER_FRONTEND_SCOPE_BUILDER_H_
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
#error "AOT runtime should not use compiler sources (including header files)"
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#include "vm/compiler/frontend/constant_reader.h"
|
|
#include "vm/compiler/frontend/kernel_translation_helper.h"
|
|
#include "vm/hash_map.h"
|
|
#include "vm/object.h"
|
|
#include "vm/parser.h" // For ParsedFunction.
|
|
|
|
namespace dart {
|
|
namespace kernel {
|
|
|
|
class ScopeBuildingResult;
|
|
|
|
class ScopeBuilder {
|
|
public:
|
|
explicit ScopeBuilder(ParsedFunction* parsed_function);
|
|
|
|
virtual ~ScopeBuilder() = default;
|
|
|
|
ScopeBuildingResult* BuildScopes();
|
|
|
|
private:
|
|
void VisitField();
|
|
|
|
void VisitProcedure();
|
|
|
|
void VisitConstructor();
|
|
|
|
void VisitFunctionNode();
|
|
void VisitNode();
|
|
void VisitInitializer();
|
|
void VisitExpression();
|
|
void VisitStatement();
|
|
void VisitListOfExpressions();
|
|
void VisitListOfNamedExpressions();
|
|
void VisitArguments();
|
|
void VisitVariableDeclaration();
|
|
void VisitVariableGet(intptr_t declaration_binary_offset);
|
|
void VisitDartType();
|
|
void VisitInterfaceType(bool simple);
|
|
void VisitFunctionType(bool simple);
|
|
void VisitRecordType();
|
|
void VisitTypeParameterType();
|
|
void VisitIntersectionType();
|
|
void VisitExtensionType();
|
|
void VisitFutureOrType();
|
|
void HandleLocalFunction(intptr_t parent_kernel_offset);
|
|
|
|
AbstractType& BuildAndVisitVariableType();
|
|
|
|
void EnterScope(intptr_t kernel_offset);
|
|
void ExitScope(TokenPosition start_position, TokenPosition end_position);
|
|
|
|
virtual void ReportUnexpectedTag(const char* variant, Tag tag);
|
|
|
|
// This enum controls which parameters would be marked as requiring type
|
|
// check on the callee side.
|
|
enum ParameterTypeCheckMode {
|
|
// All parameters will be checked.
|
|
kTypeCheckAllParameters,
|
|
|
|
// Only parameters marked as covariant or generic-covariant-impl will be
|
|
// checked.
|
|
kTypeCheckForNonDynamicallyInvokedMethod,
|
|
|
|
// Only parameters *not* marked as covariant or generic-covariant-impl will
|
|
// be checked. The rest would be checked in the method itself.
|
|
// Inverse of kTypeCheckForNonDynamicallyInvokedMethod.
|
|
kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod,
|
|
|
|
// No parameters will be checked.
|
|
kTypeCheckForStaticFunction,
|
|
|
|
// No non-covariant checks are performed, and any covariant checks are
|
|
// performed by the target.
|
|
kTypeCheckForImplicitClosureFunction,
|
|
};
|
|
|
|
// This assumes that the reader is at a FunctionNode,
|
|
// about to read the positional parameters.
|
|
void AddPositionalAndNamedParameters(
|
|
intptr_t pos,
|
|
ParameterTypeCheckMode type_check_mode,
|
|
const ProcedureAttributesMetadata& attrs);
|
|
|
|
// This assumes that the reader is at a FunctionNode,
|
|
// about to read a parameter (i.e. VariableDeclaration).
|
|
void AddVariableDeclarationParameter(
|
|
intptr_t pos,
|
|
ParameterTypeCheckMode type_check_mode,
|
|
const ProcedureAttributesMetadata& attrs);
|
|
|
|
LocalVariable* MakeVariable(
|
|
TokenPosition declaration_pos,
|
|
TokenPosition token_pos,
|
|
const String& name,
|
|
const AbstractType& type,
|
|
intptr_t kernel_offset = LocalVariable::kNoKernelOffset,
|
|
bool is_late = false,
|
|
const InferredTypeMetadata* inferred_type_md = nullptr,
|
|
const InferredTypeMetadata* inferred_arg_type_md = nullptr);
|
|
|
|
void AddExceptionVariable(GrowableArray<LocalVariable*>* variables,
|
|
const char* prefix,
|
|
intptr_t nesting_depth);
|
|
|
|
void FinalizeExceptionVariable(GrowableArray<LocalVariable*>* variables,
|
|
GrowableArray<LocalVariable*>* raw_variables,
|
|
const String& symbol,
|
|
intptr_t nesting_depth);
|
|
|
|
void AddTryVariables();
|
|
void AddCatchVariables();
|
|
void FinalizeCatchVariables();
|
|
void AddSwitchVariable();
|
|
|
|
// Record an assignment or reference to a variable. If the occurrence is
|
|
// in a nested function, ensure that the variable is handled properly as a
|
|
// captured variable.
|
|
LocalVariable* LookupVariable(intptr_t declaration_binary_offset);
|
|
|
|
const String& GenerateName(const char* prefix, intptr_t suffix);
|
|
|
|
void HandleLoadReceiver();
|
|
void HandleSpecialLoad(LocalVariable** variable,
|
|
const String& symbol,
|
|
intptr_t kernel_offset);
|
|
|
|
ScriptPtr Script() { return active_class_.ActiveScript(); }
|
|
|
|
struct DepthState {
|
|
explicit DepthState(intptr_t function)
|
|
: loop_(0), function_(function), try_(0), catch_(0), finally_(0) {}
|
|
|
|
intptr_t loop_;
|
|
intptr_t function_;
|
|
intptr_t try_;
|
|
intptr_t catch_;
|
|
intptr_t finally_;
|
|
};
|
|
|
|
ScopeBuildingResult* result_;
|
|
ParsedFunction* parsed_function_;
|
|
|
|
ActiveClass active_class_;
|
|
|
|
TranslationHelper translation_helper_;
|
|
Zone* zone_;
|
|
|
|
LocalScope* current_function_scope_;
|
|
LocalScope* scope_;
|
|
DepthState depth_;
|
|
|
|
bool needs_expr_temp_;
|
|
TokenPosition first_body_token_position_ = TokenPosition::kNoSource;
|
|
|
|
KernelReaderHelper helper_;
|
|
ConstantReader constant_reader_;
|
|
InferredTypeMetadataHelper inferred_type_metadata_helper_;
|
|
InferredTypeMetadataHelper inferred_arg_type_metadata_helper_;
|
|
ProcedureAttributesMetadataHelper procedure_attributes_metadata_helper_;
|
|
TypeTranslator type_translator_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ScopeBuilder);
|
|
};
|
|
|
|
struct FunctionScope {
|
|
intptr_t kernel_offset;
|
|
LocalScope* scope;
|
|
};
|
|
|
|
class ScopeBuildingResult : public ZoneAllocated {
|
|
public:
|
|
ScopeBuildingResult()
|
|
: type_arguments_variable(nullptr),
|
|
switch_variable(nullptr),
|
|
finally_return_variable(nullptr),
|
|
setter_value(nullptr),
|
|
raw_variable_counter_(0) {}
|
|
|
|
bool IsClosureWithEmptyContext(intptr_t function_node_offset) {
|
|
for (intptr_t i = 0; i < closure_offsets_without_captures.length(); ++i) {
|
|
if (closure_offsets_without_captures[i] == function_node_offset) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
IntMap<LocalVariable*> locals;
|
|
IntMap<LocalScope*> scopes;
|
|
GrowableArray<FunctionScope> function_scopes;
|
|
|
|
// Only non-null for factory constructor functions.
|
|
LocalVariable* type_arguments_variable;
|
|
|
|
// Non-nullptr when the function contains a switch statement.
|
|
LocalVariable* switch_variable;
|
|
|
|
// Non-nullptr when the function contains a return inside a finally block.
|
|
LocalVariable* finally_return_variable;
|
|
|
|
// Non-nullptr when the function is a setter.
|
|
LocalVariable* setter_value;
|
|
|
|
// Variables used in exception handlers, one per exception handler nesting
|
|
// level.
|
|
GrowableArray<LocalVariable*> exception_variables;
|
|
GrowableArray<LocalVariable*> stack_trace_variables;
|
|
GrowableArray<LocalVariable*> catch_context_variables;
|
|
|
|
// These are used to access the raw exception/stacktrace variables (and are
|
|
// used to put them into the captured variables in the context).
|
|
GrowableArray<LocalVariable*> raw_exception_variables;
|
|
GrowableArray<LocalVariable*> raw_stack_trace_variables;
|
|
intptr_t raw_variable_counter_;
|
|
|
|
// Remembers closure function kernel offsets that do not capture any
|
|
// variables.
|
|
GrowableArray<intptr_t> closure_offsets_without_captures;
|
|
|
|
void PrintTo(BaseTextBuffer* f) const;
|
|
const char* ToCString() const;
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(ScopeBuildingResult);
|
|
};
|
|
|
|
} // namespace kernel
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_COMPILER_FRONTEND_SCOPE_BUILDER_H_
|